2022-03-24 11:40:38 -07:00
|
|
|
import assert from "assert";
|
2022-12-16 05:21:50 -08:00
|
|
|
import * as anchor from "@coral-xyz/anchor";
|
2022-03-24 11:40:38 -07:00
|
|
|
import * as borsh from "borsh";
|
2022-12-16 05:21:50 -08:00
|
|
|
import { Program } from "@coral-xyz/anchor";
|
2022-03-24 11:40:38 -07:00
|
|
|
import { Callee } from "../target/types/callee";
|
|
|
|
import { Caller } from "../target/types/caller";
|
2022-04-17 12:54:28 -07:00
|
|
|
import { ConfirmOptions } from "@solana/web3.js";
|
2022-03-24 11:40:38 -07:00
|
|
|
|
|
|
|
const { SystemProgram } = anchor.web3;
|
|
|
|
|
|
|
|
describe("CPI return", () => {
|
2022-04-11 12:48:58 -07:00
|
|
|
const provider = anchor.AnchorProvider.env();
|
2022-03-24 11:40:38 -07:00
|
|
|
anchor.setProvider(provider);
|
|
|
|
|
|
|
|
const callerProgram = anchor.workspace.Caller as Program<Caller>;
|
|
|
|
const calleeProgram = anchor.workspace.Callee as Program<Callee>;
|
|
|
|
|
|
|
|
const getReturnLog = (confirmedTransaction) => {
|
|
|
|
const prefix = "Program return: ";
|
|
|
|
let log = confirmedTransaction.meta.logMessages.find((log) =>
|
|
|
|
log.startsWith(prefix)
|
|
|
|
);
|
|
|
|
log = log.slice(prefix.length);
|
|
|
|
const [key, data] = log.split(" ", 2);
|
|
|
|
const buffer = Buffer.from(data, "base64");
|
|
|
|
return [key, data, buffer];
|
|
|
|
};
|
|
|
|
|
|
|
|
const cpiReturn = anchor.web3.Keypair.generate();
|
|
|
|
|
2022-04-17 12:54:28 -07:00
|
|
|
const confirmOptions: ConfirmOptions = { commitment: "confirmed" };
|
2022-03-24 11:40:38 -07:00
|
|
|
|
|
|
|
it("can initialize", async () => {
|
|
|
|
await calleeProgram.methods
|
|
|
|
.initialize()
|
|
|
|
.accounts({
|
|
|
|
account: cpiReturn.publicKey,
|
|
|
|
user: provider.wallet.publicKey,
|
|
|
|
systemProgram: SystemProgram.programId,
|
|
|
|
})
|
|
|
|
.signers([cpiReturn])
|
|
|
|
.rpc();
|
|
|
|
});
|
|
|
|
|
|
|
|
it("can return u64 from a cpi", async () => {
|
|
|
|
const tx = await callerProgram.methods
|
|
|
|
.cpiCallReturnU64()
|
|
|
|
.accounts({
|
|
|
|
cpiReturn: cpiReturn.publicKey,
|
|
|
|
cpiReturnProgram: calleeProgram.programId,
|
|
|
|
})
|
|
|
|
.rpc(confirmOptions);
|
|
|
|
let t = await provider.connection.getTransaction(tx, {
|
|
|
|
commitment: "confirmed",
|
|
|
|
});
|
|
|
|
|
|
|
|
const [key, data, buffer] = getReturnLog(t);
|
|
|
|
assert.equal(key, calleeProgram.programId);
|
|
|
|
|
|
|
|
// Check for matching log on receive side
|
|
|
|
let receiveLog = t.meta.logMessages.find(
|
|
|
|
(log) => log == `Program data: ${data}`
|
|
|
|
);
|
|
|
|
assert(receiveLog !== undefined);
|
|
|
|
|
|
|
|
const reader = new borsh.BinaryReader(buffer);
|
|
|
|
assert.equal(reader.readU64().toNumber(), 10);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("can make a non-cpi call to a function that returns a u64", async () => {
|
|
|
|
const tx = await calleeProgram.methods
|
|
|
|
.returnU64()
|
|
|
|
.accounts({
|
|
|
|
account: cpiReturn.publicKey,
|
|
|
|
})
|
|
|
|
.rpc(confirmOptions);
|
|
|
|
let t = await provider.connection.getTransaction(tx, {
|
|
|
|
commitment: "confirmed",
|
|
|
|
});
|
|
|
|
const [key, , buffer] = getReturnLog(t);
|
|
|
|
assert.equal(key, calleeProgram.programId);
|
|
|
|
const reader = new borsh.BinaryReader(buffer);
|
|
|
|
assert.equal(reader.readU64().toNumber(), 10);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("can return a struct from a cpi", async () => {
|
|
|
|
const tx = await callerProgram.methods
|
|
|
|
.cpiCallReturnStruct()
|
|
|
|
.accounts({
|
|
|
|
cpiReturn: cpiReturn.publicKey,
|
|
|
|
cpiReturnProgram: calleeProgram.programId,
|
|
|
|
})
|
|
|
|
.rpc(confirmOptions);
|
|
|
|
let t = await provider.connection.getTransaction(tx, {
|
|
|
|
commitment: "confirmed",
|
|
|
|
});
|
|
|
|
|
|
|
|
const [key, data, buffer] = getReturnLog(t);
|
|
|
|
assert.equal(key, calleeProgram.programId);
|
|
|
|
|
|
|
|
// Check for matching log on receive side
|
|
|
|
let receiveLog = t.meta.logMessages.find(
|
|
|
|
(log) => log == `Program data: ${data}`
|
|
|
|
);
|
|
|
|
assert(receiveLog !== undefined);
|
|
|
|
|
|
|
|
// Deserialize the struct and validate
|
|
|
|
class Assignable {
|
|
|
|
constructor(properties) {
|
|
|
|
Object.keys(properties).map((key) => {
|
|
|
|
this[key] = properties[key];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class Data extends Assignable {}
|
|
|
|
const schema = new Map([
|
|
|
|
[Data, { kind: "struct", fields: [["value", "u64"]] }],
|
|
|
|
]);
|
|
|
|
const deserialized = borsh.deserialize(schema, Data, buffer);
|
2022-10-13 20:06:51 -07:00
|
|
|
// @ts-ignore
|
2022-03-24 11:40:38 -07:00
|
|
|
assert(deserialized.value.toNumber() === 11);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("can return a vec from a cpi", async () => {
|
|
|
|
const tx = await callerProgram.methods
|
|
|
|
.cpiCallReturnVec()
|
|
|
|
.accounts({
|
|
|
|
cpiReturn: cpiReturn.publicKey,
|
|
|
|
cpiReturnProgram: calleeProgram.programId,
|
|
|
|
})
|
|
|
|
.rpc(confirmOptions);
|
|
|
|
let t = await provider.connection.getTransaction(tx, {
|
|
|
|
commitment: "confirmed",
|
|
|
|
});
|
|
|
|
|
|
|
|
const [key, data, buffer] = getReturnLog(t);
|
|
|
|
assert.equal(key, calleeProgram.programId);
|
|
|
|
|
|
|
|
// Check for matching log on receive side
|
|
|
|
let receiveLog = t.meta.logMessages.find(
|
|
|
|
(log) => log == `Program data: ${data}`
|
|
|
|
);
|
|
|
|
assert(receiveLog !== undefined);
|
|
|
|
|
|
|
|
const reader = new borsh.BinaryReader(buffer);
|
|
|
|
const array = reader.readArray(() => reader.readU8());
|
|
|
|
assert.deepStrictEqual(array, [12, 13, 14, 100]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("sets a return value in idl", async () => {
|
2022-04-17 12:54:28 -07:00
|
|
|
// @ts-expect-error
|
2022-03-24 11:40:38 -07:00
|
|
|
const returnu64Instruction = calleeProgram._idl.instructions.find(
|
|
|
|
(f) => f.name == "returnU64"
|
|
|
|
);
|
|
|
|
assert.equal(returnu64Instruction.returns, "u64");
|
|
|
|
|
2022-04-17 12:54:28 -07:00
|
|
|
// @ts-expect-error
|
2022-03-24 11:40:38 -07:00
|
|
|
const returnStructInstruction = calleeProgram._idl.instructions.find(
|
|
|
|
(f) => f.name == "returnStruct"
|
|
|
|
);
|
|
|
|
assert.deepStrictEqual(returnStructInstruction.returns, {
|
|
|
|
defined: "StructReturn",
|
|
|
|
});
|
|
|
|
});
|
2022-04-03 16:15:06 -07:00
|
|
|
|
|
|
|
it("can return a u64 via view", async () => {
|
2022-04-17 12:54:28 -07:00
|
|
|
// @ts-expect-error
|
2022-04-03 16:15:06 -07:00
|
|
|
assert(new anchor.BN(99).eq(await callerProgram.views.returnU64()));
|
|
|
|
// Via methods API
|
|
|
|
assert(
|
|
|
|
new anchor.BN(99).eq(await callerProgram.methods.returnU64().view())
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("can return a struct via view", async () => {
|
2022-04-17 12:54:28 -07:00
|
|
|
// @ts-expect-error
|
2022-04-03 16:15:06 -07:00
|
|
|
const struct = await callerProgram.views.returnStruct();
|
|
|
|
assert(struct.a.eq(new anchor.BN(1)));
|
|
|
|
assert(struct.b.eq(new anchor.BN(2)));
|
|
|
|
// Via methods API
|
|
|
|
const struct2 = await callerProgram.methods.returnStruct().view();
|
|
|
|
assert(struct2.a.eq(new anchor.BN(1)));
|
|
|
|
assert(struct2.b.eq(new anchor.BN(2)));
|
|
|
|
});
|
|
|
|
|
|
|
|
it("can return a vec via view", async () => {
|
2022-04-17 12:54:28 -07:00
|
|
|
// @ts-expect-error
|
2022-04-03 16:15:06 -07:00
|
|
|
const vec = await callerProgram.views.returnVec();
|
|
|
|
assert(vec[0].eq(new anchor.BN(1)));
|
|
|
|
assert(vec[1].eq(new anchor.BN(2)));
|
|
|
|
assert(vec[2].eq(new anchor.BN(3)));
|
|
|
|
// Via methods API
|
|
|
|
const vec2 = await callerProgram.methods.returnVec().view();
|
|
|
|
assert(vec2[0].eq(new anchor.BN(1)));
|
|
|
|
assert(vec2[1].eq(new anchor.BN(2)));
|
|
|
|
assert(vec2[2].eq(new anchor.BN(3)));
|
|
|
|
});
|
|
|
|
|
|
|
|
it("can return a u64 from an account via view", async () => {
|
|
|
|
const value = new anchor.BN(10);
|
|
|
|
assert(
|
|
|
|
value.eq(
|
|
|
|
await calleeProgram.methods
|
|
|
|
.returnU64FromAccount()
|
|
|
|
.accounts({ account: cpiReturn.publicKey })
|
|
|
|
.view()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("cant call view on mutable instruction", async () => {
|
|
|
|
assert.equal(calleeProgram.views.initialize, undefined);
|
|
|
|
try {
|
|
|
|
await calleeProgram.methods
|
|
|
|
.initialize()
|
|
|
|
.accounts({
|
|
|
|
account: cpiReturn.publicKey,
|
|
|
|
user: provider.wallet.publicKey,
|
|
|
|
systemProgram: SystemProgram.programId,
|
|
|
|
})
|
|
|
|
.signers([cpiReturn])
|
|
|
|
.view();
|
|
|
|
} catch (e) {
|
|
|
|
assert(e.message.includes("Method does not support views"));
|
|
|
|
}
|
|
|
|
});
|
2022-03-24 11:40:38 -07:00
|
|
|
});
|