From d1e9c11930db6c4f06fd574573779129e6efbc10 Mon Sep 17 00:00:00 2001 From: henrye Date: Thu, 2 Feb 2023 13:58:10 +0000 Subject: [PATCH 1/5] update change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aadec8fadc..2e3f397832 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ The minor version will be incremented upon a breaking change and the patch versi - lang: Remove deprecated literal constraint which has been replaced by `#[account(constraint = {})]` ([#2379](https://github.com/coral-xyz/anchor/pull/2379)). - lang: `account(zero_copy)` and `zero_copy` attributes now derive the `bytemuck::Pod` and `bytemuck::Zeroable` traits instead of using `unsafe impl` ([#2330](https://github.com/coral-xyz/anchor/pull/2330)). This imposes useful restrictions on the type, like not having padding bytes and all fields being `Pod` themselves. See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. This change requires adding `bytemuck = { version = "1.4.0", features = ["derive", "min_const_generics"]}` to your `cargo.toml`. Legacy applications can still use `#[account(zero_copy(unsafe))]` and `#[zero_copy(unsafe)]` for the old behavior. - ts: Remove `createProgramAddressSync`, `findProgramAddressSync` (now available in `@solana/web3.js`) and update `associatedAddress` to be synchronous ([#2357](https://github.com/coral-xyz/anchor/pull/2357)). +- ts: Add support for `VersionedTransaction` to `AnchorProvider`, `Transaction` is no longer accepted as an argument ([#2380](https://github.com/coral-xyz/anchor/pull/2380)). ## [0.26.0] - 2022-12-15 From 0e8886971c6495eaba616a276bbbd72b75a8c80e Mon Sep 17 00:00:00 2001 From: henrye Date: Thu, 2 Feb 2023 14:00:39 +0000 Subject: [PATCH 2/5] fix pr issue number --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e3f397832..b0967e0f35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,7 @@ The minor version will be incremented upon a breaking change and the patch versi - lang: Remove deprecated literal constraint which has been replaced by `#[account(constraint = {})]` ([#2379](https://github.com/coral-xyz/anchor/pull/2379)). - lang: `account(zero_copy)` and `zero_copy` attributes now derive the `bytemuck::Pod` and `bytemuck::Zeroable` traits instead of using `unsafe impl` ([#2330](https://github.com/coral-xyz/anchor/pull/2330)). This imposes useful restrictions on the type, like not having padding bytes and all fields being `Pod` themselves. See [bytemuck::Pod](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html) for details. This change requires adding `bytemuck = { version = "1.4.0", features = ["derive", "min_const_generics"]}` to your `cargo.toml`. Legacy applications can still use `#[account(zero_copy(unsafe))]` and `#[zero_copy(unsafe)]` for the old behavior. - ts: Remove `createProgramAddressSync`, `findProgramAddressSync` (now available in `@solana/web3.js`) and update `associatedAddress` to be synchronous ([#2357](https://github.com/coral-xyz/anchor/pull/2357)). -- ts: Add support for `VersionedTransaction` to `AnchorProvider`, `Transaction` is no longer accepted as an argument ([#2380](https://github.com/coral-xyz/anchor/pull/2380)). +- ts: Add support for `VersionedTransaction` to `AnchorProvider`, `Transaction` is no longer accepted as an argument ([#2382](https://github.com/coral-xyz/anchor/pull/2382)). ## [0.26.0] - 2022-12-15 From 681a97ba3f446dd8bcaa22c62af0957997a16297 Mon Sep 17 00:00:00 2001 From: henrye Date: Fri, 3 Feb 2023 16:30:53 +0000 Subject: [PATCH 3/5] update tto versioned transactions; ntermediate commit not done yet --- .../src/program/namespace/transaction.ts | 20 +++---- ts/packages/anchor/src/provider.ts | 55 +++++++++++-------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/ts/packages/anchor/src/program/namespace/transaction.ts b/ts/packages/anchor/src/program/namespace/transaction.ts index 27ee0881e6..e02f66f348 100644 --- a/ts/packages/anchor/src/program/namespace/transaction.ts +++ b/ts/packages/anchor/src/program/namespace/transaction.ts @@ -1,4 +1,4 @@ -import { Transaction } from "@solana/web3.js"; +import { Transaction, TransactionInstruction } from "@solana/web3.js"; import { Idl, IdlInstruction } from "../../idl.js"; import { splitArgsAndCtx } from "../context.js"; import { InstructionFn } from "./instruction.js"; @@ -13,16 +13,16 @@ export default class TransactionFactory { idlIx: I, ixFn: InstructionFn ): TransactionFn { - const txFn: TransactionFn = (...args): Transaction => { + const txFn: TransactionFn = (...args): TransactionInstruction[] => { const [, ctx] = splitArgsAndCtx(idlIx, [...args]); - const tx = new Transaction(); + const tx: TransactionInstruction[] = []; 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)); + ctx.preInstructions?.forEach((ix) => tx.push(ix)); + ctx.instructions?.forEach((ix) => tx.push(ix)); + tx.push(ixFn(...args)); + ctx.postInstructions?.forEach((ix) => tx.push(ix)); return tx; }; @@ -62,12 +62,12 @@ export default class TransactionFactory { export type TransactionNamespace< IDL extends Idl = Idl, I extends AllInstructions = AllInstructions -> = MakeInstructionsNamespace; +> = MakeInstructionsNamespace; /** - * Tx is a function to create a `Transaction` for a given program instruction. + * Tx is a function to create a `TransactionInstruction[]` for a given program instruction. */ export type TransactionFn< IDL extends Idl = Idl, I extends AllInstructions = AllInstructions -> = InstructionContextFn; +> = InstructionContextFn; diff --git a/ts/packages/anchor/src/provider.ts b/ts/packages/anchor/src/provider.ts index e0fd503d7b..e3a7cf8f24 100644 --- a/ts/packages/anchor/src/provider.ts +++ b/ts/packages/anchor/src/provider.ts @@ -2,13 +2,16 @@ import { Connection, Signer, PublicKey, - Transaction, TransactionSignature, ConfirmOptions, SimulatedTransactionResponse, Commitment, SendTransactionError, SendOptions, + TransactionInstruction, + VersionedTransaction, + AddressLookupTableAccount, + TransactionMessage, } from "@solana/web3.js"; import { bs58 } from "./utils/bytes/index.js"; import { isBrowser } from "./utils/common.js"; @@ -22,21 +25,21 @@ export default interface Provider { readonly publicKey?: PublicKey; send?( - tx: Transaction, + tx: TransactionInstruction[], signers?: Signer[], opts?: SendOptions ): Promise; sendAndConfirm?( - tx: Transaction, + tx: TransactionInstruction[], signers?: Signer[], opts?: ConfirmOptions ): Promise; sendAll?( - txWithSigners: { tx: Transaction; signers?: Signer[] }[], + txWithSigners: { tx: TransactionInstruction[]; signers?: Signer[] }[], opts?: ConfirmOptions ): Promise>; simulate?( - tx: Transaction, + tx: TransactionInstruction[], signers?: Signer[], commitment?: Commitment, includeAccounts?: boolean | PublicKey[] @@ -124,24 +127,30 @@ export class AnchorProvider implements Provider { * @param opts Transaction confirmation options. */ async sendAndConfirm( - tx: Transaction, + instructions: TransactionInstruction[], signers?: Signer[], - opts?: ConfirmOptions + opts?: ConfirmOptions, + feePayer?: PublicKey, + lookupTable?: AddressLookupTableAccount[] ): Promise { if (opts === undefined) { opts = this.opts; } - tx.feePayer = tx.feePayer || this.wallet.publicKey; - - tx.recentBlockhash = ( - await this.connection.getLatestBlockhash(opts.preflightCommitment) - ).blockhash; + let tx = new VersionedTransaction( + new TransactionMessage({ + instructions, + payerKey: feePayer ?? this.wallet.publicKey, + recentBlockhash: ( + await this.connection.getLatestBlockhash(opts.preflightCommitment) + ).blockhash, + }).compileToV0Message(lookupTable) + ); tx = await this.wallet.signTransaction(tx); - (signers ?? []).forEach((kp) => { - tx.partialSign(kp); - }); + if (signers) { + tx.sign(signers); + } const rawTx = tx.serialize(); @@ -156,7 +165,7 @@ export class AnchorProvider implements Provider { // because that will see the tx sent with `sendAndConfirmRawTransaction` no matter which // commitment `sendAndConfirmRawTransaction` used const failedTx = await this.connection.getTransaction( - bs58.encode(tx.signature!), + bs58.encode(tx.signatures[0]!), { commitment: "confirmed" } ); if (!failedTx) { @@ -178,7 +187,7 @@ export class AnchorProvider implements Provider { * @param opts Transaction confirmation options. */ async sendAll( - txWithSigners: { tx: Transaction; signers?: Signer[] }[], + txWithSigners: { tx: TransactionInstruction[]; signers?: Signer[] }[], opts?: ConfirmOptions ): Promise> { if (opts === undefined) { @@ -253,7 +262,7 @@ export class AnchorProvider implements Provider { * @param opts Transaction confirmation options. */ async simulate( - tx: Transaction, + tx: TransactionInstruction[], signers?: Signer[], commitment?: Commitment, includeAccounts?: boolean | PublicKey[] @@ -295,7 +304,7 @@ class SimulateError extends Error { } export type SendTxRequest = { - tx: Transaction; + tx: TransactionInstruction[]; signers: Array; }; @@ -303,8 +312,10 @@ export type SendTxRequest = { * Wallet interface for objects that can be used to sign provider transactions. */ export interface Wallet { - signTransaction(tx: Transaction): Promise; - signAllTransactions(txs: Transaction[]): Promise; + signTransaction(tx: VersionedTransaction): Promise; + signAllTransactions( + txs: VersionedTransaction[] + ): Promise; publicKey: PublicKey; } @@ -312,7 +323,7 @@ export interface Wallet { // a better error if 'confirmTransaction` returns an error status async function sendAndConfirmRawTransaction( connection: Connection, - rawTransaction: Buffer, + rawTransaction: Uint8Array, options?: ConfirmOptions ): Promise { const sendOptions = options && { From b20b5dabb1c1be58c4d8ecc439da2c1ad93c29d6 Mon Sep 17 00:00:00 2001 From: henrye Date: Thu, 23 Feb 2023 17:26:23 +0000 Subject: [PATCH 4/5] rename to transactionInstructions everywhere; still lots to fix in provider --- ts/packages/anchor/src/nodewallet.ts | 14 +- ts/packages/anchor/src/program/index.ts | 35 +++-- .../anchor/src/program/namespace/index.ts | 25 ++-- .../anchor/src/program/namespace/methods.ts | 13 +- .../anchor/src/program/namespace/rpc.ts | 8 +- .../anchor/src/program/namespace/simulate.ts | 6 +- ...saction.ts => transaction-instructions.ts} | 24 +-- ts/packages/anchor/src/provider.ts | 19 ++- ts/packages/anchor/src/utils/rpc.ts | 141 +----------------- ts/packages/anchor/tests/transaction.spec.ts | 37 +++-- 10 files changed, 109 insertions(+), 213 deletions(-) rename ts/packages/anchor/src/program/namespace/{transaction.ts => transaction-instructions.ts} (70%) diff --git a/ts/packages/anchor/src/nodewallet.ts b/ts/packages/anchor/src/nodewallet.ts index 6d7924aff3..2072237e41 100644 --- a/ts/packages/anchor/src/nodewallet.ts +++ b/ts/packages/anchor/src/nodewallet.ts @@ -1,5 +1,5 @@ import { Buffer } from "buffer"; -import { Keypair, PublicKey, Transaction } from "@solana/web3.js"; +import { Keypair, PublicKey, VersionedTransaction } from "@solana/web3.js"; import { Wallet } from "./provider"; /** @@ -30,14 +30,18 @@ export default class NodeWallet implements Wallet { return new NodeWallet(payer); } - async signTransaction(tx: Transaction): Promise { - tx.partialSign(this.payer); + async signTransaction( + tx: VersionedTransaction + ): Promise { + tx.sign([this.payer]); return tx; } - async signAllTransactions(txs: Transaction[]): Promise { + async signAllTransactions( + txs: VersionedTransaction[] + ): Promise { return txs.map((t) => { - t.partialSign(this.payer); + t.sign([this.payer]); return t; }); } diff --git a/ts/packages/anchor/src/program/index.ts b/ts/packages/anchor/src/program/index.ts index 58d962f29d..c4219a8105 100644 --- a/ts/packages/anchor/src/program/index.ts +++ b/ts/packages/anchor/src/program/index.ts @@ -6,7 +6,7 @@ import { Coder, BorshCoder } from "../coder/index.js"; import NamespaceFactory, { RpcNamespace, InstructionNamespace, - TransactionNamespace, + TransactionInstructionsNamespace, AccountNamespace, SimulateNamespace, MethodsNamespace, @@ -138,13 +138,13 @@ export class Program { readonly instruction: InstructionNamespace; /** - * The namespace provides functions to build [[Transaction]] objects for each + * The namespace provides functions to build [[TransactionInstruction\[\]]] objects for each * method of a program. * * ## Usage * * ```javascript - * program.transaction.(...args, ctx); + * program.transactionInstructions.(...args, ctx); * ``` * * ## Parameters @@ -159,7 +159,7 @@ export class Program { * To create an instruction for the `increment` method above, * * ```javascript - * const tx = await program.transaction.increment({ + * const tx = await program.transactionInstructions.increment({ * accounts: { * counter, * }, @@ -167,7 +167,7 @@ export class Program { * ``` * @deprecated */ - readonly transaction: TransactionNamespace; + readonly transactionInstructions: TransactionInstructionsNamespace; /** * The namespace provides functions to simulate transactions for each method @@ -283,17 +283,24 @@ export class Program { this._events = new EventManager(this._programId, provider, this._coder); // Dynamic namespaces. - const [rpc, instruction, transaction, account, simulate, methods, views] = - NamespaceFactory.build( - idl, - this._coder, - programId, - provider, - getCustomResolver ?? (() => undefined) - ); + const [ + rpc, + instruction, + transactionInstructions, + account, + simulate, + methods, + views, + ] = NamespaceFactory.build( + idl, + this._coder, + programId, + provider, + getCustomResolver ?? (() => undefined) + ); this.rpc = rpc; this.instruction = instruction; - this.transaction = transaction; + this.transactionInstructions = transactionInstructions; this.account = account; this.simulate = simulate; this.methods = methods; diff --git a/ts/packages/anchor/src/program/namespace/index.ts b/ts/packages/anchor/src/program/namespace/index.ts index b917df2216..28d21359aa 100644 --- a/ts/packages/anchor/src/program/namespace/index.ts +++ b/ts/packages/anchor/src/program/namespace/index.ts @@ -4,7 +4,9 @@ import { Coder } from "../../coder/index.js"; import Provider from "../../provider.js"; import { Idl, IdlInstruction } from "../../idl.js"; import InstructionFactory, { InstructionNamespace } from "./instruction.js"; -import TransactionFactory, { TransactionNamespace } from "./transaction.js"; +import TransactionInstructionsFactory, { + TransactionInstructionsNamespace, +} from "./transaction-instructions.js"; import RpcFactory, { RpcNamespace } from "./rpc.js"; import AccountFactory, { AccountNamespace } from "./account.js"; import SimulateFactory, { SimulateNamespace } from "./simulate.js"; @@ -15,7 +17,10 @@ import { CustomAccountResolver } from "../accounts-resolver.js"; // Re-exports. export { InstructionNamespace, InstructionFn } from "./instruction.js"; -export { TransactionNamespace, TransactionFn } from "./transaction.js"; +export { + TransactionInstructionsNamespace, + TransactionInstructionsFn, +} from "./transaction-instructions.js"; export { RpcNamespace, RpcFn } from "./rpc.js"; export { AccountNamespace, AccountClient, ProgramAccount } from "./account.js"; export { SimulateNamespace, SimulateFn } from "./simulate.js"; @@ -38,7 +43,7 @@ export default class NamespaceFactory { ): [ RpcNamespace, InstructionNamespace, - TransactionNamespace, + TransactionInstructionsNamespace, AccountNamespace, SimulateNamespace, MethodsNamespace, @@ -46,7 +51,7 @@ export default class NamespaceFactory { ] { const rpc: RpcNamespace = {}; const instruction: InstructionNamespace = {}; - const transaction: TransactionNamespace = {}; + const transactionInstructions: TransactionInstructionsNamespace = {}; const simulate: SimulateNamespace = {}; const methods: MethodsNamespace = {}; const view: ViewNamespace = {}; @@ -63,11 +68,11 @@ export default class NamespaceFactory { (ixName, ix) => coder.instruction.encode(ixName, ix), programId ); - const txItem = TransactionFactory.build(idlIx, ixItem); - const rpcItem = RpcFactory.build(idlIx, txItem, idlErrors, provider); + const txIxsItem = TransactionInstructionsFactory.build(idlIx, ixItem); + const rpcItem = RpcFactory.build(idlIx, txIxsItem, idlErrors, provider); const simulateItem = SimulateFactory.build( idlIx, - txItem, + txIxsItem, idlErrors, provider, coder, @@ -80,7 +85,7 @@ export default class NamespaceFactory { programId, idlIx, ixItem, - txItem, + txIxsItem, rpcItem, simulateItem, viewItem, @@ -91,7 +96,7 @@ export default class NamespaceFactory { const name = camelCase(idlIx.name); instruction[name] = ixItem; - transaction[name] = txItem; + transactionInstructions[name] = txIxsItem; rpc[name] = rpcItem; simulate[name] = simulateItem; methods[name] = methodItem; @@ -103,7 +108,7 @@ export default class NamespaceFactory { return [ rpc as RpcNamespace, instruction as InstructionNamespace, - transaction as TransactionNamespace, + transactionInstructions as TransactionInstructionsNamespace, account, simulate as SimulateNamespace, methods as MethodsNamespace, diff --git a/ts/packages/anchor/src/program/namespace/methods.ts b/ts/packages/anchor/src/program/namespace/methods.ts index e29a608f71..c55438ccdb 100644 --- a/ts/packages/anchor/src/program/namespace/methods.ts +++ b/ts/packages/anchor/src/program/namespace/methods.ts @@ -3,7 +3,6 @@ import { ConfirmOptions, PublicKey, Signer, - Transaction, TransactionInstruction, TransactionSignature, } from "@solana/web3.js"; @@ -20,7 +19,7 @@ import { AccountNamespace } from "./account.js"; import { InstructionFn } from "./instruction.js"; import { RpcFn } from "./rpc.js"; import { SimulateFn, SimulateResponse } from "./simulate.js"; -import { TransactionFn } from "./transaction.js"; +import { TransactionInstructionsFn } from "./transaction-instructions.js"; import { AllInstructions, InstructionAccountAddresses, @@ -40,7 +39,7 @@ export class MethodsBuilderFactory { programId: PublicKey, idlIx: AllInstructions, ixFn: InstructionFn, - txFn: TransactionFn, + txIxsFn: TransactionInstructionsFn, rpcFn: RpcFn, simulateFn: SimulateFn, viewFn: ViewFn | undefined, @@ -52,7 +51,7 @@ export class MethodsBuilderFactory { new MethodsBuilder( args, ixFn, - txFn, + txIxsFn, rpcFn, simulateFn, viewFn, @@ -121,7 +120,7 @@ export class MethodsBuilder> { constructor( _args: Array, private _ixFn: InstructionFn, - private _txFn: TransactionFn, + private _txIxsFn: TransactionInstructionsFn, private _rpcFn: RpcFn, private _simulateFn: SimulateFn, private _viewFn: ViewFn | undefined, @@ -299,13 +298,13 @@ export class MethodsBuilder> { }; } - public async transaction(): Promise { + public async transactionInstructions(): Promise { if (this._autoResolveAccounts) { await this._accountsResolver.resolve(); } // @ts-ignore - return this._txFn(...this._args, { + return this._txIxsFn(...this._args, { accounts: this._accounts, signers: this._signers, remainingAccounts: this._remainingAccounts, diff --git a/ts/packages/anchor/src/program/namespace/rpc.ts b/ts/packages/anchor/src/program/namespace/rpc.ts index f61c81bc11..58d4dcb8c2 100644 --- a/ts/packages/anchor/src/program/namespace/rpc.ts +++ b/ts/packages/anchor/src/program/namespace/rpc.ts @@ -2,7 +2,7 @@ import { TransactionSignature } from "@solana/web3.js"; import Provider from "../../provider.js"; import { Idl } from "../../idl.js"; import { splitArgsAndCtx } from "../context.js"; -import { TransactionFn } from "./transaction.js"; +import { TransactionInstructionsFn } from "./transaction-instructions.js"; import { translateError } from "../../error.js"; import { AllInstructions, @@ -13,12 +13,12 @@ import { export default class RpcFactory { public static build>( idlIx: I, - txFn: TransactionFn, + txIxsFn: TransactionInstructionsFn, idlErrors: Map, provider: Provider ): RpcFn { const rpc: RpcFn = async (...args) => { - const tx = txFn(...args); + const txIxs = txIxsFn(...args); const [, ctx] = splitArgsAndCtx(idlIx, [...args]); if (provider.sendAndConfirm === undefined) { throw new Error( @@ -27,7 +27,7 @@ export default class RpcFactory { } try { return await provider.sendAndConfirm( - tx, + txIxs, ctx.signers ?? [], ctx.options ); diff --git a/ts/packages/anchor/src/program/namespace/simulate.ts b/ts/packages/anchor/src/program/namespace/simulate.ts index f73a821511..18f71e5650 100644 --- a/ts/packages/anchor/src/program/namespace/simulate.ts +++ b/ts/packages/anchor/src/program/namespace/simulate.ts @@ -2,7 +2,7 @@ import { PublicKey } from "@solana/web3.js"; import Provider from "../../provider.js"; import { SuccessfulTxSimulationResponse } from "src/utils/rpc.js"; import { splitArgsAndCtx } from "../context.js"; -import { TransactionFn } from "./transaction.js"; +import { TransactionInstructionsFn } from "./transaction-instructions.js"; import { EventParser, Event } from "../event.js"; import { Coder } from "../../coder/index.js"; import { Idl, IdlEvent } from "../../idl.js"; @@ -17,7 +17,7 @@ import { export default class SimulateFactory { public static build>( idlIx: AllInstructions, - txFn: TransactionFn, + txIxsFn: TransactionInstructionsFn, idlErrors: Map, provider: Provider, coder: Coder, @@ -25,7 +25,7 @@ export default class SimulateFactory { idl: IDL ): SimulateFn { const simulate: SimulateFn = async (...args) => { - const tx = txFn(...args); + const tx = txIxsFn(...args); const [, ctx] = splitArgsAndCtx(idlIx, [...args]); let resp: SuccessfulTxSimulationResponse | undefined = undefined; if (provider.simulate === undefined) { diff --git a/ts/packages/anchor/src/program/namespace/transaction.ts b/ts/packages/anchor/src/program/namespace/transaction-instructions.ts similarity index 70% rename from ts/packages/anchor/src/program/namespace/transaction.ts rename to ts/packages/anchor/src/program/namespace/transaction-instructions.ts index e02f66f348..69a3b0358c 100644 --- a/ts/packages/anchor/src/program/namespace/transaction.ts +++ b/ts/packages/anchor/src/program/namespace/transaction-instructions.ts @@ -1,4 +1,4 @@ -import { Transaction, TransactionInstruction } from "@solana/web3.js"; +import { TransactionInstruction } from "@solana/web3.js"; import { Idl, IdlInstruction } from "../../idl.js"; import { splitArgsAndCtx } from "../context.js"; import { InstructionFn } from "./instruction.js"; @@ -8,12 +8,14 @@ import { MakeInstructionsNamespace, } from "./types.js"; -export default class TransactionFactory { +export default class TransactionInstructionsFactory { public static build>( idlIx: I, ixFn: InstructionFn - ): TransactionFn { - const txFn: TransactionFn = (...args): TransactionInstruction[] => { + ): TransactionInstructionsFn { + const txIxsFn: TransactionInstructionsFn = ( + ...args + ): TransactionInstruction[] => { const [, ctx] = splitArgsAndCtx(idlIx, [...args]); const tx: TransactionInstruction[] = []; if (ctx.preInstructions && ctx.instructions) { @@ -26,18 +28,18 @@ export default class TransactionFactory { return tx; }; - return txFn; + return txIxsFn; } } /** - * The namespace provides functions to build [[Transaction]] objects for each + * The namespace provides functions to build [[TransactionInstruction\[\]]] objects for each * method of a program. * * ## Usage * * ```javascript - * program.transaction.(...args, ctx); + * program.transactionInstructions.(...args, ctx); * ``` * * ## Parameters @@ -52,22 +54,22 @@ export default class TransactionFactory { * To create an instruction for the `increment` method above, * * ```javascript - * const tx = await program.transaction.increment({ + * const tx = await program.transactionInstructions.increment({ * accounts: { * counter, * }, * }); * ``` */ -export type TransactionNamespace< +export type TransactionInstructionsNamespace< IDL extends Idl = Idl, I extends AllInstructions = AllInstructions > = MakeInstructionsNamespace; /** - * Tx is a function to create a `TransactionInstruction[]` for a given program instruction. + * TxIxs is a function to create a `TransactionInstruction[]` for a given program instruction. */ -export type TransactionFn< +export type TransactionInstructionsFn< IDL extends Idl = Idl, I extends AllInstructions = AllInstructions > = InstructionContextFn; diff --git a/ts/packages/anchor/src/provider.ts b/ts/packages/anchor/src/provider.ts index e3a7cf8f24..6bbc6d04d4 100644 --- a/ts/packages/anchor/src/provider.ts +++ b/ts/packages/anchor/src/provider.ts @@ -15,31 +15,28 @@ import { } from "@solana/web3.js"; import { bs58 } from "./utils/bytes/index.js"; import { isBrowser } from "./utils/common.js"; -import { - simulateTransaction, - SuccessfulTxSimulationResponse, -} from "./utils/rpc.js"; +import { SuccessfulTxSimulationResponse } from "./utils/rpc.js"; export default interface Provider { readonly connection: Connection; readonly publicKey?: PublicKey; send?( - tx: TransactionInstruction[], + txIxs: TransactionInstruction[], signers?: Signer[], opts?: SendOptions ): Promise; sendAndConfirm?( - tx: TransactionInstruction[], + txIxs: TransactionInstruction[], signers?: Signer[], opts?: ConfirmOptions ): Promise; sendAll?( - txWithSigners: { tx: TransactionInstruction[]; signers?: Signer[] }[], + txWithSigners: { txIxs: TransactionInstruction[]; signers?: Signer[] }[], opts?: ConfirmOptions ): Promise>; simulate?( - tx: TransactionInstruction[], + txIxs: TransactionInstruction[], signers?: Signer[], commitment?: Commitment, includeAccounts?: boolean | PublicKey[] @@ -278,8 +275,10 @@ export class AnchorProvider implements Provider { if (signers) { tx = await this.wallet.signTransaction(tx); } - const result = await simulateTransaction( - this.connection, + // TODO: update to the latest version that passes in versioned transactions + // We have to figure out how to update all of the things above as well. + // Since it has to go txIxs -> TxMsg -> VersionedTransaction + const result = await this.connection.simulateTransaction( tx, signers, commitment, diff --git a/ts/packages/anchor/src/utils/rpc.ts b/ts/packages/anchor/src/utils/rpc.ts index b13868c8b2..e3fa0b7d6d 100644 --- a/ts/packages/anchor/src/utils/rpc.ts +++ b/ts/packages/anchor/src/utils/rpc.ts @@ -5,7 +5,6 @@ import { Connection, PublicKey, TransactionSignature, - Transaction, TransactionInstruction, Commitment, Signer, @@ -13,6 +12,7 @@ import { SimulatedTransactionResponse, SendTransactionError, Context, + VersionedTransaction, } from "@solana/web3.js"; import { chunks } from "../utils/common.js"; import { Address, translateAddress } from "../program/common.js"; @@ -49,8 +49,8 @@ export async function invoke( provider = getProvider(); } - const tx = new Transaction(); - tx.add( + const txInstructions: TransactionInstruction[] = []; + txInstructions.push( new TransactionInstruction({ programId, keys: accounts ?? [], @@ -64,7 +64,7 @@ export async function invoke( ); } - return await provider.sendAndConfirm(tx, []); + return await provider.sendAndConfirm(txInstructions, []); } const GET_MULTIPLE_ACCOUNTS_LIMIT: number = 99; @@ -150,139 +150,6 @@ async function getMultipleAccountsAndContextCore( return accounts; } -// copy from @solana/web3.js that has a commitment param -export async function simulateTransaction( - connection: Connection, - transaction: Transaction, - signers?: Array, - commitment?: Commitment, - includeAccounts?: boolean | Array -): Promise> { - if (signers && signers.length > 0) { - transaction.sign(...signers); - } - - // @ts-expect-error - const message = transaction._compile(); - const signData = message.serialize(); - // @ts-expect-error - const wireTransaction = transaction._serialize(signData); - const encodedTransaction = wireTransaction.toString("base64"); - const config: any = { - encoding: "base64", - commitment: commitment ?? connection.commitment, - }; - - if (includeAccounts) { - const addresses = ( - Array.isArray(includeAccounts) ? includeAccounts : message.nonProgramIds() - ).map((key) => key.toBase58()); - - config["accounts"] = { - encoding: "base64", - addresses, - }; - } - - if (signers) { - config.sigVerify = true; - } - - const args = [encodedTransaction, config]; - // @ts-expect-error - const unsafeRes = await connection._rpcRequest("simulateTransaction", args); - const res = create(unsafeRes, SimulatedTransactionResponseStruct); - if ("error" in res) { - let logs; - if ("data" in res.error) { - logs = res.error.data.logs; - if (logs && Array.isArray(logs)) { - const traceIndent = "\n "; - const logTrace = traceIndent + logs.join(traceIndent); - console.error(res.error.message, logTrace); - } - } - throw new SendTransactionError( - "failed to simulate transaction: " + res.error.message, - logs - ); - } - return res.result; -} - -// copy from @solana/web3.js -function jsonRpcResult(schema: Struct) { - return coerce(createRpcResult(schema), UnknownRpcResult, (value) => { - if ("error" in value) { - return value; - } else { - return { - ...value, - result: create(value.result, schema), - }; - } - }); -} - -// copy from @solana/web3.js -const UnknownRpcResult = createRpcResult(unknown()); - -// copy from @solana/web3.js -function createRpcResult(result: Struct) { - return union([ - pick({ - jsonrpc: literal("2.0"), - id: string(), - result, - }), - pick({ - jsonrpc: literal("2.0"), - id: string(), - error: pick({ - code: unknown(), - message: string(), - data: optional(any()), - }), - }), - ]); -} - -// copy from @solana/web3.js -function jsonRpcResultAndContext(value: Struct) { - return jsonRpcResult( - pick({ - context: pick({ - slot: number(), - }), - value, - }) - ); -} - -// copy from @solana/web3.js -const SimulatedTransactionResponseStruct = jsonRpcResultAndContext( - pick({ - err: nullable(union([pick({}), string()])), - logs: nullable(array(string())), - accounts: optional( - nullable( - array( - nullable( - pick({ - executable: boolean(), - owner: string(), - lamports: number(), - data: array(string()), - rentEpoch: optional(number()), - }) - ) - ) - ) - ), - unitsConsumed: optional(number()), - }) -); - export type SuccessfulTxSimulationResponse = Omit< SimulatedTransactionResponse, "err" diff --git a/ts/packages/anchor/tests/transaction.spec.ts b/ts/packages/anchor/tests/transaction.spec.ts index 4a3e69aea1..81e87641eb 100644 --- a/ts/packages/anchor/tests/transaction.spec.ts +++ b/ts/packages/anchor/tests/transaction.spec.ts @@ -1,9 +1,9 @@ -import TransactionFactory from "../src/program/namespace/transaction"; +import TransactionInstructionsFactory from "../src/program/namespace/transaction-instructions"; import InstructionFactory from "../src/program/namespace/instruction"; import { BorshCoder } from "../src"; import { PublicKey, TransactionInstruction } from "@solana/web3.js"; -describe("Transaction", () => { +describe("Transaction Instructions", () => { const preIx = new TransactionInstruction({ keys: [], programId: PublicKey.default, @@ -34,10 +34,13 @@ describe("Transaction", () => { (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); + const txIxsItem = TransactionInstructionsFactory.build( + idl.instructions[0], + ixItem + ); + const txIxs = txIxsItem({ accounts: {}, preInstructions: [preIx] }); + expect(txIxs.length).toBe(2); + expect(txIxs[0]).toMatchObject(preIx); }); it("should add post instructions after method ix", async () => { @@ -48,10 +51,13 @@ describe("Transaction", () => { (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); + const txIxsItem = TransactionInstructionsFactory.build( + idl.instructions[0], + ixItem + ); + const txIxs = txIxsItem({ accounts: {}, postInstructions: [postIx] }); + expect(txIxs.length).toBe(2); + expect(txIxs[1]).toMatchObject(postIx); }); it("should throw error if both preInstructions and instructions are used", async () => { @@ -62,10 +68,17 @@ describe("Transaction", () => { (ixName, ix) => coder.instruction.encode(ixName, ix), programId ); - const txItem = TransactionFactory.build(idl.instructions[0], ixItem); + const txIxsItem = TransactionInstructionsFactory.build( + idl.instructions[0], + ixItem + ); expect(() => - txItem({ accounts: {}, preInstructions: [preIx], instructions: [preIx] }) + txIxsItem({ + accounts: {}, + preInstructions: [preIx], + instructions: [preIx], + }) ).toThrow(new Error("instructions is deprecated, use preInstructions")); }); }); From 6aacee92067d63f9ecf5264040c10b8a6218630a Mon Sep 17 00:00:00 2001 From: Sid <155190547+SoSoDolo@users.noreply.github.com> Date: Fri, 29 Dec 2023 15:14:52 -0600 Subject: [PATCH 5/5] According to the anchor docs "The seeds can be anything. A pubkey, a string, an array of numbers etc." This supports the 3 mentioned. Also, I included array of strings because I saw elsewhere an array of pubkeys could also be passed. If there are other types that may be expected that are encompassed in that etc. Let me know and I will update --- ts/packages/anchor/src/idl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/packages/anchor/src/idl.ts b/ts/packages/anchor/src/idl.ts index 71e0cd5ff9..5e076c668b 100644 --- a/ts/packages/anchor/src/idl.ts +++ b/ts/packages/anchor/src/idl.ts @@ -67,7 +67,7 @@ export type IdlPda = { programId?: IdlSeed; }; -export type IdlSeed = any; // TODO +export type IdlSeed = string | number[] | string[]; // A nested/recursive version of IdlAccount. export type IdlAccounts = {