From 704d8a220592a5a472bd7726013814b50c991f5b Mon Sep 17 00:00:00 2001 From: Callum McIntyre Date: Thu, 28 Nov 2024 13:34:06 +0000 Subject: [PATCH] Change IInstruction to use ReadonlyUint8Array for data (#3632) * Change IInstruction to use ReadonlyUint8Array for data * Add changeset --- .changeset/spicy-parrots-raise.md | 7 +++++++ examples/deserialize-transaction/src/example.ts | 16 ++++++++++++++-- packages/errors/src/context.ts | 2 +- packages/instructions/package.json | 1 + .../src/__typetests__/instruction-typetest.ts | 9 +++++---- packages/instructions/src/instruction.ts | 9 +++++---- .../src/compile/instructions.ts | 3 ++- .../transaction-messages/src/durable-nonce.ts | 3 ++- pnpm-lock.yaml | 3 +++ 9 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 .changeset/spicy-parrots-raise.md diff --git a/.changeset/spicy-parrots-raise.md b/.changeset/spicy-parrots-raise.md new file mode 100644 index 000000000000..bdc7c841d4b4 --- /dev/null +++ b/.changeset/spicy-parrots-raise.md @@ -0,0 +1,7 @@ +--- +'@solana/transaction-messages': patch +'@solana/instructions': patch +'@solana/errors': patch +--- + +Change `data` field of transaction message instructions to use `ReadonlyUint8Array` diff --git a/examples/deserialize-transaction/src/example.ts b/examples/deserialize-transaction/src/example.ts index 3a9bff8ff1e0..0ec16e4892c1 100644 --- a/examples/deserialize-transaction/src/example.ts +++ b/examples/deserialize-transaction/src/example.ts @@ -381,7 +381,13 @@ if (identifiedInstruction === SystemInstruction.TransferSol) { log.info('[step 4] The first instruction calls the TransferSol instruction of the system program'); // Narrow the type again, the instruction must have accounts to be parsed as a transfer SOL instruction assertIsInstructionWithAccounts(firstInstruction); - const parsedFirstInstruction = parseTransferSolInstruction(firstInstruction); + + // TODO: This can just be `parseTransferSolInstruction(firstInstruction)` when the client is updated + // with the `@solana/web3.js` version that changes the instruction data type to `ReadonlyUint8Array` + const parsedFirstInstruction = parseTransferSolInstruction({ + ...firstInstruction, + data: firstInstruction.data as unknown as Uint8Array, + }); log.info(parsedFirstInstruction, '[step 4] The parsed Transfer SOL instruction'); // This gives us an understanding of what exactly is happening in the instruction @@ -406,7 +412,13 @@ if (secondInstruction.programAddress === MEMO_PROGRAM_ADDRESS) { // We know it's always an addMemo instruction assertIsInstructionWithData(secondInstruction); -const parsedSecondInstruction = parseAddMemoInstruction(secondInstruction); + +// TODO: This can just be `parseAddMemoInstruction(secondInstruction)` when the client is updated +// with the `@solana/web3.js` version that changes the instruction data type to `ReadonlyUint8Array` +const parsedSecondInstruction = parseAddMemoInstruction({ + ...secondInstruction, + data: secondInstruction.data as unknown as Uint8Array, +}); log.info(parsedSecondInstruction, '[step 4] The parsed Add Memo instruction'); log.info(`[step 4] The second instruction adds a memo with message "${parsedSecondInstruction.data.memo}"`); diff --git a/packages/errors/src/context.ts b/packages/errors/src/context.ts index 0a32587f304b..8f2eb08c7ce6 100644 --- a/packages/errors/src/context.ts +++ b/packages/errors/src/context.ts @@ -386,7 +386,7 @@ export type SolanaErrorContext = DefaultUnspecifiedErrorContextToUndefined< instructionErrorContext?: unknown; }; [SOLANA_ERROR__INSTRUCTION__EXPECTED_TO_HAVE_ACCOUNTS]: { - data?: Uint8Array; + data?: ReadonlyUint8Array; programAddress: string; }; [SOLANA_ERROR__INSTRUCTION__EXPECTED_TO_HAVE_DATA]: { diff --git a/packages/instructions/package.json b/packages/instructions/package.json index 0e072a0ae3fb..aa905458aea1 100644 --- a/packages/instructions/package.json +++ b/packages/instructions/package.json @@ -71,6 +71,7 @@ "maintained node versions" ], "dependencies": { + "@solana/codecs-core": "workspace:*", "@solana/errors": "workspace:*" }, "devDependencies": { diff --git a/packages/instructions/src/__typetests__/instruction-typetest.ts b/packages/instructions/src/__typetests__/instruction-typetest.ts index 9b7da8330d0c..b9e7a1d1a9bc 100644 --- a/packages/instructions/src/__typetests__/instruction-typetest.ts +++ b/packages/instructions/src/__typetests__/instruction-typetest.ts @@ -1,4 +1,5 @@ import { Address } from '@solana/addresses'; +import { ReadonlyUint8Array } from '@solana/codecs-core'; import { IAccountLookupMeta, IAccountMeta } from '../accounts'; import { @@ -21,12 +22,12 @@ import { instruction satisfies IInstructionWithAccounts; // @ts-expect-error instruction might not have data - instruction satisfies IInstructionWithData; + instruction satisfies IInstructionWithData; if (isInstructionWithAccounts(instruction) && isInstructionWithData(instruction)) { instruction satisfies IInstruction & IInstructionWithAccounts & - IInstructionWithData; + IInstructionWithData; } } @@ -38,7 +39,7 @@ import { instruction satisfies IInstructionWithAccounts; // @ts-expect-error instruction might not have data - instruction satisfies IInstructionWithData; + instruction satisfies IInstructionWithData; assertIsInstructionWithAccounts(instruction); instruction satisfies IInstruction & IInstructionWithAccounts; @@ -46,7 +47,7 @@ import { assertIsInstructionWithData(instruction); instruction satisfies IInstruction & IInstructionWithAccounts & - IInstructionWithData; + IInstructionWithData; } // narrowing by program address diff --git a/packages/instructions/src/instruction.ts b/packages/instructions/src/instruction.ts index fdb81f4edd75..a7c676bdfd05 100644 --- a/packages/instructions/src/instruction.ts +++ b/packages/instructions/src/instruction.ts @@ -1,4 +1,5 @@ import { Address } from '@solana/addresses'; +import { ReadonlyUint8Array } from '@solana/codecs-core'; import { SOLANA_ERROR__INSTRUCTION__EXPECTED_TO_HAVE_ACCOUNTS, SOLANA_ERROR__INSTRUCTION__EXPECTED_TO_HAVE_DATA, @@ -13,7 +14,7 @@ export interface IInstruction< TAccounts extends readonly (IAccountLookupMeta | IAccountMeta)[] = readonly (IAccountLookupMeta | IAccountMeta)[], > { readonly accounts?: TAccounts; - readonly data?: Uint8Array; + readonly data?: ReadonlyUint8Array; readonly programAddress: Address; } @@ -60,19 +61,19 @@ export function assertIsInstructionWithAccounts< } } -export interface IInstructionWithData extends IInstruction { +export interface IInstructionWithData extends IInstruction { readonly data: TData; } export function isInstructionWithData< - TData extends Uint8Array = Uint8Array, + TData extends ReadonlyUint8Array = ReadonlyUint8Array, TInstruction extends IInstruction = IInstruction, >(instruction: TInstruction): instruction is IInstructionWithData & TInstruction { return instruction.data !== undefined; } export function assertIsInstructionWithData< - TData extends Uint8Array = Uint8Array, + TData extends ReadonlyUint8Array = ReadonlyUint8Array, TInstruction extends IInstruction = IInstruction, >(instruction: TInstruction): asserts instruction is IInstructionWithData & TInstruction { if (instruction.data === undefined) { diff --git a/packages/transaction-messages/src/compile/instructions.ts b/packages/transaction-messages/src/compile/instructions.ts index 2fb0d316980d..69cd67f2ac57 100644 --- a/packages/transaction-messages/src/compile/instructions.ts +++ b/packages/transaction-messages/src/compile/instructions.ts @@ -1,11 +1,12 @@ import { Address } from '@solana/addresses'; +import { ReadonlyUint8Array } from '@solana/codecs-core'; import { IInstruction } from '@solana/instructions'; import { OrderedAccounts } from './accounts'; type CompiledInstruction = Readonly<{ accountIndices?: number[]; - data?: Uint8Array; + data?: ReadonlyUint8Array; programAddressIndex: number; }>; diff --git a/packages/transaction-messages/src/durable-nonce.ts b/packages/transaction-messages/src/durable-nonce.ts index 8b438573e2ae..ecc465783959 100644 --- a/packages/transaction-messages/src/durable-nonce.ts +++ b/packages/transaction-messages/src/durable-nonce.ts @@ -1,4 +1,5 @@ import { Address } from '@solana/addresses'; +import { ReadonlyUint8Array } from '@solana/codecs-core'; import { SOLANA_ERROR__TRANSACTION__EXPECTED_NONCE_LIFETIME, SolanaError } from '@solana/errors'; import { AccountRole, @@ -111,7 +112,7 @@ export function isAdvanceNonceAccountInstruction( ); } -function isAdvanceNonceAccountInstructionData(data: Uint8Array): data is AdvanceNonceAccountInstructionData { +function isAdvanceNonceAccountInstructionData(data: ReadonlyUint8Array): data is AdvanceNonceAccountInstructionData { // AdvanceNonceAccount is the fifth instruction in the System Program (index 4) return data.byteLength === 4 && data[0] === 4 && data[1] === 0 && data[2] === 0 && data[3] === 0; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f7113eae599..ba7f0b08d209 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -521,6 +521,9 @@ importers: packages/instructions: dependencies: + '@solana/codecs-core': + specifier: workspace:* + version: link:../codecs-core '@solana/errors': specifier: workspace:* version: link:../errors