diff --git a/CHANGELOG.md b/CHANGELOG.md index 08ed37002..fd87dd5c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ incremented for features. ### Features * lang: Add `SystemAccount<'info>` account type for generic wallet addresses or accounts owned by the system program ([#954](https://github.com/project-serum/anchor/pull/954)) +* ts: Add inputs `postInstructions` and `preInstructions` as a replacement for (the now deprecated) `instructions` ### Fixes diff --git a/ts/src/program/context.ts b/ts/src/program/context.ts index 98eeb3b9b..5d82cff51 100644 --- a/ts/src/program/context.ts +++ b/ts/src/program/context.ts @@ -28,11 +28,24 @@ export type Context = { signers?: Array; /** + * @deprecated use preInstructions instead. * Instructions to run *before* a given method. Often this is used, for * example to create accounts prior to executing a method. */ instructions?: TransactionInstruction[]; + /** + * Instructions to run *before* a given method. Often this is used, for + * example to create accounts prior to executing a method. + */ + preInstructions?: TransactionInstruction[]; + + /** + * Instructions to run *after* a given method. Often this is used, for + * example to close accounts after executing a method. + */ + postInstructions?: TransactionInstruction[]; + /** * Commitment parameters to use for a transaction. */ diff --git a/ts/src/program/namespace/transaction.ts b/ts/src/program/namespace/transaction.ts index 03fa171f3..d22a9e23a 100644 --- a/ts/src/program/namespace/transaction.ts +++ b/ts/src/program/namespace/transaction.ts @@ -16,8 +16,13 @@ export default class TransactionFactory { const txFn: TransactionFn = (...args): Transaction => { const [, ctx] = splitArgsAndCtx(idlIx, [...args]); const tx = new Transaction(); + if (ctx.preInstructions && ctx.instructions) { + throw new Error("instructions is deprecated, use preInstructions"); + } + ctx.preInstructions?.forEach((ix) => tx.add(ix)); ctx.instructions?.forEach((ix) => tx.add(ix)); tx.add(ixFn(...args)); + ctx.postInstructions?.forEach((ix) => tx.add(ix)); return tx; }; diff --git a/ts/tests/transaction.spec.ts b/ts/tests/transaction.spec.ts new file mode 100644 index 000000000..403124815 --- /dev/null +++ b/ts/tests/transaction.spec.ts @@ -0,0 +1,71 @@ +import TransactionFactory from "../src/program/namespace/transaction"; +import InstructionFactory from "../src/program/namespace/instruction"; +import { Coder } from "../src"; +import { PublicKey, TransactionInstruction } from "@solana/web3.js"; + +describe("Transaction", () => { + const preIx = new TransactionInstruction({ + keys: [], + programId: PublicKey.default, + data: Buffer.from("pre"), + }); + const postIx = new TransactionInstruction({ + keys: [], + programId: PublicKey.default, + data: Buffer.from("post"), + }); + const idl = { + version: "0.0.0", + name: "basic_0", + instructions: [ + { + name: "initialize", + accounts: [], + args: [], + }, + ], + }; + + it("should add pre instructions before method ix", async () => { + const coder = new Coder(idl); + const programId = PublicKey.default; + const ixItem = InstructionFactory.build( + idl.instructions[0], + (ixName, ix) => coder.instruction.encode(ixName, ix), + programId + ); + const txItem = TransactionFactory.build(idl.instructions[0], ixItem); + const tx = txItem({ accounts: {}, preInstructions: [preIx] }); + expect(tx.instructions.length).toBe(2); + expect(tx.instructions[0]).toMatchObject(preIx); + }); + + it("should add post instructions after method ix", async () => { + const coder = new Coder(idl); + const programId = PublicKey.default; + const ixItem = InstructionFactory.build( + idl.instructions[0], + (ixName, ix) => coder.instruction.encode(ixName, ix), + programId + ); + const txItem = TransactionFactory.build(idl.instructions[0], ixItem); + const tx = txItem({ accounts: {}, postInstructions: [postIx] }); + expect(tx.instructions.length).toBe(2); + expect(tx.instructions[1]).toMatchObject(postIx); + }); + + it("should throw error if both preInstructions and instructions are used", async () => { + const coder = new Coder(idl); + const programId = PublicKey.default; + const ixItem = InstructionFactory.build( + idl.instructions[0], + (ixName, ix) => coder.instruction.encode(ixName, ix), + programId + ); + const txItem = TransactionFactory.build(idl.instructions[0], ixItem); + + expect(() => + txItem({ accounts: {}, preInstructions: [preIx], instructions: [preIx] }) + ).toThrow(new Error("instructions is deprecated, use preInstructions")); + }); +});