From 2ef170f03b740fef26138554eb6a8086ea701a53 Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Fri, 17 May 2024 14:01:21 +0300 Subject: [PATCH] Add ATA Instruction discriminators in e2e tests This only affects the tests so no changeset is needed. --- packages/renderers-js/e2e/token/idl.json | 66 +++++++++++++++++- .../instructions/createAssociatedToken.ts | 46 ++++++++++++- .../createAssociatedTokenIdempotent.ts | 48 ++++++++++++- .../recoverNestedAssociatedToken.ts | 48 ++++++++++++- .../src/generated/programs/associatedToken.ts | 21 +++++- .../token/test/createAssociatedToken.test.ts | 67 +++++++++++++++++++ .../createAssociatedTokenIdempotent.test.ts | 67 +++++++++++++++++++ 7 files changed, 356 insertions(+), 7 deletions(-) create mode 100644 packages/renderers-js/e2e/token/test/createAssociatedToken.test.ts create mode 100644 packages/renderers-js/e2e/token/test/createAssociatedTokenIdempotent.test.ts diff --git a/packages/renderers-js/e2e/token/idl.json b/packages/renderers-js/e2e/token/idl.json index 6e85422a..60f54eed 100644 --- a/packages/renderers-js/e2e/token/idl.json +++ b/packages/renderers-js/e2e/token/idl.json @@ -2384,7 +2384,27 @@ } } ], - "arguments": [], + "arguments": [ + { + "kind": "instructionArgumentNode", + "name": "discriminator", + "type": { + "kind": "numberTypeNode", + "format": "u8", + "endian": "le" + }, + "docs": [], + "defaultValue": { "kind": "numberValueNode", "number": 0 }, + "defaultValueStrategy": "omitted" + } + ], + "discriminators": [ + { + "kind": "fieldDiscriminatorNode", + "name": "discriminator", + "offset": 0 + } + ], "name": "createAssociatedToken", "docs": [ "Creates an associated token account for the given wallet address and", @@ -2486,7 +2506,27 @@ } } ], - "arguments": [], + "arguments": [ + { + "kind": "instructionArgumentNode", + "name": "discriminator", + "type": { + "kind": "numberTypeNode", + "format": "u8", + "endian": "le" + }, + "docs": [], + "defaultValue": { "kind": "numberValueNode", "number": 1 }, + "defaultValueStrategy": "omitted" + } + ], + "discriminators": [ + { + "kind": "fieldDiscriminatorNode", + "name": "discriminator", + "offset": 0 + } + ], "name": "createAssociatedTokenIdempotent", "docs": [ "Creates an associated token account for the given wallet address and", @@ -2662,7 +2702,27 @@ } } ], - "arguments": [], + "arguments": [ + { + "kind": "instructionArgumentNode", + "name": "discriminator", + "type": { + "kind": "numberTypeNode", + "format": "u8", + "endian": "le" + }, + "docs": [], + "defaultValue": { "kind": "numberValueNode", "number": 2 }, + "defaultValueStrategy": "omitted" + } + ], + "discriminators": [ + { + "kind": "fieldDiscriminatorNode", + "name": "discriminator", + "offset": 0 + } + ], "name": "recoverNestedAssociatedToken", "docs": [ "Transfers from and closes a nested associated token account: an", diff --git a/packages/renderers-js/e2e/token/src/generated/instructions/createAssociatedToken.ts b/packages/renderers-js/e2e/token/src/generated/instructions/createAssociatedToken.ts index a2f1cde3..b9e839de 100644 --- a/packages/renderers-js/e2e/token/src/generated/instructions/createAssociatedToken.ts +++ b/packages/renderers-js/e2e/token/src/generated/instructions/createAssociatedToken.ts @@ -8,14 +8,24 @@ import { Address, + Codec, + Decoder, + Encoder, IAccountMeta, IAccountSignerMeta, IInstruction, IInstructionWithAccounts, + IInstructionWithData, ReadonlyAccount, TransactionSigner, WritableAccount, WritableSignerAccount, + combineCodec, + getStructDecoder, + getStructEncoder, + getU8Decoder, + getU8Encoder, + transformEncoder, } from '@solana/web3.js'; import { findAssociatedTokenPda } from '../pdas'; import { ASSOCIATED_TOKEN_PROGRAM_ADDRESS } from '../programs'; @@ -39,6 +49,7 @@ export type CreateAssociatedTokenInstruction< | IAccountMeta = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', TRemainingAccounts extends readonly IAccountMeta[] = [], > = IInstruction & + IInstructionWithData & IInstructionWithAccounts< [ TAccountPayer extends string @@ -62,6 +73,31 @@ export type CreateAssociatedTokenInstruction< ] >; +export type CreateAssociatedTokenInstructionData = { discriminator: number }; + +export type CreateAssociatedTokenInstructionDataArgs = {}; + +export function getCreateAssociatedTokenInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([['discriminator', getU8Encoder()]]), + (value) => ({ ...value, discriminator: 0 }) + ); +} + +export function getCreateAssociatedTokenInstructionDataDecoder(): Decoder { + return getStructDecoder([['discriminator', getU8Decoder()]]); +} + +export function getCreateAssociatedTokenInstructionDataCodec(): Codec< + CreateAssociatedTokenInstructionDataArgs, + CreateAssociatedTokenInstructionData +> { + return combineCodec( + getCreateAssociatedTokenInstructionDataEncoder(), + getCreateAssociatedTokenInstructionDataDecoder() + ); +} + export type CreateAssociatedTokenAsyncInput< TAccountPayer extends string = string, TAccountAta extends string = string, @@ -156,6 +192,7 @@ export async function getCreateAssociatedTokenInstructionAsync< getAccountMeta(accounts.tokenProgram), ], programAddress, + data: getCreateAssociatedTokenInstructionDataEncoder().encode({}), } as CreateAssociatedTokenInstruction< typeof ASSOCIATED_TOKEN_PROGRAM_ADDRESS, TAccountPayer, @@ -254,6 +291,7 @@ export function getCreateAssociatedTokenInstruction< getAccountMeta(accounts.tokenProgram), ], programAddress, + data: getCreateAssociatedTokenInstructionDataEncoder().encode({}), } as CreateAssociatedTokenInstruction< typeof ASSOCIATED_TOKEN_PROGRAM_ADDRESS, TAccountPayer, @@ -286,13 +324,16 @@ export type ParsedCreateAssociatedTokenInstruction< /** SPL Token program. */ tokenProgram: TAccountMetas[5]; }; + data: CreateAssociatedTokenInstructionData; }; export function parseCreateAssociatedTokenInstruction< TProgram extends string, TAccountMetas extends readonly IAccountMeta[], >( - instruction: IInstruction & IInstructionWithAccounts + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData ): ParsedCreateAssociatedTokenInstruction { if (instruction.accounts.length < 6) { // TODO: Coded error. @@ -314,5 +355,8 @@ export function parseCreateAssociatedTokenInstruction< systemProgram: getNextAccount(), tokenProgram: getNextAccount(), }, + data: getCreateAssociatedTokenInstructionDataDecoder().decode( + instruction.data + ), }; } diff --git a/packages/renderers-js/e2e/token/src/generated/instructions/createAssociatedTokenIdempotent.ts b/packages/renderers-js/e2e/token/src/generated/instructions/createAssociatedTokenIdempotent.ts index 39de5763..fc2de61a 100644 --- a/packages/renderers-js/e2e/token/src/generated/instructions/createAssociatedTokenIdempotent.ts +++ b/packages/renderers-js/e2e/token/src/generated/instructions/createAssociatedTokenIdempotent.ts @@ -8,14 +8,24 @@ import { Address, + Codec, + Decoder, + Encoder, IAccountMeta, IAccountSignerMeta, IInstruction, IInstructionWithAccounts, + IInstructionWithData, ReadonlyAccount, TransactionSigner, WritableAccount, WritableSignerAccount, + combineCodec, + getStructDecoder, + getStructEncoder, + getU8Decoder, + getU8Encoder, + transformEncoder, } from '@solana/web3.js'; import { findAssociatedTokenPda } from '../pdas'; import { ASSOCIATED_TOKEN_PROGRAM_ADDRESS } from '../programs'; @@ -39,6 +49,7 @@ export type CreateAssociatedTokenIdempotentInstruction< | IAccountMeta = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', TRemainingAccounts extends readonly IAccountMeta[] = [], > = IInstruction & + IInstructionWithData & IInstructionWithAccounts< [ TAccountPayer extends string @@ -62,6 +73,33 @@ export type CreateAssociatedTokenIdempotentInstruction< ] >; +export type CreateAssociatedTokenIdempotentInstructionData = { + discriminator: number; +}; + +export type CreateAssociatedTokenIdempotentInstructionDataArgs = {}; + +export function getCreateAssociatedTokenIdempotentInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([['discriminator', getU8Encoder()]]), + (value) => ({ ...value, discriminator: 1 }) + ); +} + +export function getCreateAssociatedTokenIdempotentInstructionDataDecoder(): Decoder { + return getStructDecoder([['discriminator', getU8Decoder()]]); +} + +export function getCreateAssociatedTokenIdempotentInstructionDataCodec(): Codec< + CreateAssociatedTokenIdempotentInstructionDataArgs, + CreateAssociatedTokenIdempotentInstructionData +> { + return combineCodec( + getCreateAssociatedTokenIdempotentInstructionDataEncoder(), + getCreateAssociatedTokenIdempotentInstructionDataDecoder() + ); +} + export type CreateAssociatedTokenIdempotentAsyncInput< TAccountPayer extends string = string, TAccountAta extends string = string, @@ -156,6 +194,7 @@ export async function getCreateAssociatedTokenIdempotentInstructionAsync< getAccountMeta(accounts.tokenProgram), ], programAddress, + data: getCreateAssociatedTokenIdempotentInstructionDataEncoder().encode({}), } as CreateAssociatedTokenIdempotentInstruction< typeof ASSOCIATED_TOKEN_PROGRAM_ADDRESS, TAccountPayer, @@ -254,6 +293,7 @@ export function getCreateAssociatedTokenIdempotentInstruction< getAccountMeta(accounts.tokenProgram), ], programAddress, + data: getCreateAssociatedTokenIdempotentInstructionDataEncoder().encode({}), } as CreateAssociatedTokenIdempotentInstruction< typeof ASSOCIATED_TOKEN_PROGRAM_ADDRESS, TAccountPayer, @@ -286,13 +326,16 @@ export type ParsedCreateAssociatedTokenIdempotentInstruction< /** SPL Token program. */ tokenProgram: TAccountMetas[5]; }; + data: CreateAssociatedTokenIdempotentInstructionData; }; export function parseCreateAssociatedTokenIdempotentInstruction< TProgram extends string, TAccountMetas extends readonly IAccountMeta[], >( - instruction: IInstruction & IInstructionWithAccounts + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData ): ParsedCreateAssociatedTokenIdempotentInstruction { if (instruction.accounts.length < 6) { // TODO: Coded error. @@ -314,5 +357,8 @@ export function parseCreateAssociatedTokenIdempotentInstruction< systemProgram: getNextAccount(), tokenProgram: getNextAccount(), }, + data: getCreateAssociatedTokenIdempotentInstructionDataDecoder().decode( + instruction.data + ), }; } diff --git a/packages/renderers-js/e2e/token/src/generated/instructions/recoverNestedAssociatedToken.ts b/packages/renderers-js/e2e/token/src/generated/instructions/recoverNestedAssociatedToken.ts index 0a96677b..0bda5cfa 100644 --- a/packages/renderers-js/e2e/token/src/generated/instructions/recoverNestedAssociatedToken.ts +++ b/packages/renderers-js/e2e/token/src/generated/instructions/recoverNestedAssociatedToken.ts @@ -8,14 +8,24 @@ import { Address, + Codec, + Decoder, + Encoder, IAccountMeta, IAccountSignerMeta, IInstruction, IInstructionWithAccounts, + IInstructionWithData, ReadonlyAccount, TransactionSigner, WritableAccount, WritableSignerAccount, + combineCodec, + getStructDecoder, + getStructEncoder, + getU8Decoder, + getU8Encoder, + transformEncoder, } from '@solana/web3.js'; import { findAssociatedTokenPda } from '../pdas'; import { ASSOCIATED_TOKEN_PROGRAM_ADDRESS } from '../programs'; @@ -44,6 +54,7 @@ export type RecoverNestedAssociatedTokenInstruction< | IAccountMeta = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', TRemainingAccounts extends readonly IAccountMeta[] = [], > = IInstruction & + IInstructionWithData & IInstructionWithAccounts< [ TAccountNestedAssociatedAccountAddress extends string @@ -72,6 +83,33 @@ export type RecoverNestedAssociatedTokenInstruction< ] >; +export type RecoverNestedAssociatedTokenInstructionData = { + discriminator: number; +}; + +export type RecoverNestedAssociatedTokenInstructionDataArgs = {}; + +export function getRecoverNestedAssociatedTokenInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([['discriminator', getU8Encoder()]]), + (value) => ({ ...value, discriminator: 2 }) + ); +} + +export function getRecoverNestedAssociatedTokenInstructionDataDecoder(): Decoder { + return getStructDecoder([['discriminator', getU8Decoder()]]); +} + +export function getRecoverNestedAssociatedTokenInstructionDataCodec(): Codec< + RecoverNestedAssociatedTokenInstructionDataArgs, + RecoverNestedAssociatedTokenInstructionData +> { + return combineCodec( + getRecoverNestedAssociatedTokenInstructionDataEncoder(), + getRecoverNestedAssociatedTokenInstructionDataDecoder() + ); +} + export type RecoverNestedAssociatedTokenAsyncInput< TAccountNestedAssociatedAccountAddress extends string = string, TAccountNestedTokenMintAddress extends string = string, @@ -203,6 +241,7 @@ export async function getRecoverNestedAssociatedTokenInstructionAsync< getAccountMeta(accounts.tokenProgram), ], programAddress, + data: getRecoverNestedAssociatedTokenInstructionDataEncoder().encode({}), } as RecoverNestedAssociatedTokenInstruction< typeof ASSOCIATED_TOKEN_PROGRAM_ADDRESS, TAccountNestedAssociatedAccountAddress, @@ -321,6 +360,7 @@ export function getRecoverNestedAssociatedTokenInstruction< getAccountMeta(accounts.tokenProgram), ], programAddress, + data: getRecoverNestedAssociatedTokenInstructionDataEncoder().encode({}), } as RecoverNestedAssociatedTokenInstruction< typeof ASSOCIATED_TOKEN_PROGRAM_ADDRESS, TAccountNestedAssociatedAccountAddress, @@ -356,13 +396,16 @@ export type ParsedRecoverNestedAssociatedTokenInstruction< /** SPL Token program. */ tokenProgram: TAccountMetas[6]; }; + data: RecoverNestedAssociatedTokenInstructionData; }; export function parseRecoverNestedAssociatedTokenInstruction< TProgram extends string, TAccountMetas extends readonly IAccountMeta[], >( - instruction: IInstruction & IInstructionWithAccounts + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData ): ParsedRecoverNestedAssociatedTokenInstruction { if (instruction.accounts.length < 7) { // TODO: Coded error. @@ -385,5 +428,8 @@ export function parseRecoverNestedAssociatedTokenInstruction< walletAddress: getNextAccount(), tokenProgram: getNextAccount(), }, + data: getRecoverNestedAssociatedTokenInstructionDataDecoder().decode( + instruction.data + ), }; } diff --git a/packages/renderers-js/e2e/token/src/generated/programs/associatedToken.ts b/packages/renderers-js/e2e/token/src/generated/programs/associatedToken.ts index 105907c5..fc23d3cb 100644 --- a/packages/renderers-js/e2e/token/src/generated/programs/associatedToken.ts +++ b/packages/renderers-js/e2e/token/src/generated/programs/associatedToken.ts @@ -6,7 +6,7 @@ * @see https://github.com/kinobi-so/kinobi */ -import { Address } from '@solana/web3.js'; +import { Address, containsBytes, getU8Encoder } from '@solana/web3.js'; import { ParsedCreateAssociatedTokenIdempotentInstruction, ParsedCreateAssociatedTokenInstruction, @@ -22,6 +22,25 @@ export enum AssociatedTokenInstruction { RecoverNestedAssociatedToken, } +export function identifyAssociatedTokenInstruction( + instruction: { data: Uint8Array } | Uint8Array +): AssociatedTokenInstruction { + const data = + instruction instanceof Uint8Array ? instruction : instruction.data; + if (containsBytes(data, getU8Encoder().encode(0), 0)) { + return AssociatedTokenInstruction.CreateAssociatedToken; + } + if (containsBytes(data, getU8Encoder().encode(1), 0)) { + return AssociatedTokenInstruction.CreateAssociatedTokenIdempotent; + } + if (containsBytes(data, getU8Encoder().encode(2), 0)) { + return AssociatedTokenInstruction.RecoverNestedAssociatedToken; + } + throw new Error( + 'The provided instruction could not be identified as a associatedToken instruction.' + ); +} + export type ParsedAssociatedTokenInstruction< TProgram extends string = 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL', > = diff --git a/packages/renderers-js/e2e/token/test/createAssociatedToken.test.ts b/packages/renderers-js/e2e/token/test/createAssociatedToken.test.ts new file mode 100644 index 00000000..6a5177c0 --- /dev/null +++ b/packages/renderers-js/e2e/token/test/createAssociatedToken.test.ts @@ -0,0 +1,67 @@ +import { + Account, + appendTransactionMessageInstruction, + generateKeyPairSigner, + none, + pipe, +} from '@solana/web3.js'; +import test from 'ava'; +import { + AccountState, + TOKEN_PROGRAM_ADDRESS, + Token, + fetchToken, + findAssociatedTokenPda, + getCreateAssociatedTokenInstructionAsync, +} from '../src'; +import { + createDefaultSolanaClient, + createDefaultTransaction, + createMint, + generateKeyPairSignerWithSol, + signAndSendTransaction, +} from './_setup'; + +test('it creates a new associated token account', async (t) => { + // Given a mint account, its mint authority and a token owner. + const client = createDefaultSolanaClient(); + const [payer, mintAuthority, owner] = await Promise.all([ + generateKeyPairSignerWithSol(client), + generateKeyPairSigner(), + generateKeyPairSigner(), + ]); + const mint = await createMint(client, payer, mintAuthority.address); + + // When we create and initialize a token account at this address. + const createAta = await getCreateAssociatedTokenInstructionAsync({ + payer, + mint, + owner: owner.address, + }); + + await pipe( + await createDefaultTransaction(client, payer), + (tx) => appendTransactionMessageInstruction(createAta, tx), + (tx) => signAndSendTransaction(client, tx) + ); + + // Then we expect the token account to exist and have the following data. + const [ata] = await findAssociatedTokenPda({ + mint, + owner: owner.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + t.like(await fetchToken(client.rpc, ata), >{ + address: ata, + data: { + mint, + owner: owner.address, + amount: 0n, + delegate: none(), + state: AccountState.Initialized, + isNative: none(), + delegatedAmount: 0n, + closeAuthority: none(), + }, + }); +}); diff --git a/packages/renderers-js/e2e/token/test/createAssociatedTokenIdempotent.test.ts b/packages/renderers-js/e2e/token/test/createAssociatedTokenIdempotent.test.ts new file mode 100644 index 00000000..0d0051dc --- /dev/null +++ b/packages/renderers-js/e2e/token/test/createAssociatedTokenIdempotent.test.ts @@ -0,0 +1,67 @@ +import { + Account, + appendTransactionMessageInstruction, + generateKeyPairSigner, + none, + pipe, +} from '@solana/web3.js'; +import test from 'ava'; +import { + AccountState, + TOKEN_PROGRAM_ADDRESS, + Token, + fetchToken, + findAssociatedTokenPda, + getCreateAssociatedTokenIdempotentInstructionAsync, +} from '../src'; +import { + createDefaultSolanaClient, + createDefaultTransaction, + createMint, + generateKeyPairSignerWithSol, + signAndSendTransaction, +} from './_setup'; + +test('it creates a new associated token account', async (t) => { + // Given a mint account, its mint authority and a token owner. + const client = createDefaultSolanaClient(); + const [payer, mintAuthority, owner] = await Promise.all([ + generateKeyPairSignerWithSol(client), + generateKeyPairSigner(), + generateKeyPairSigner(), + ]); + const mint = await createMint(client, payer, mintAuthority.address); + + // When we create and initialize a token account at this address. + const createAta = await getCreateAssociatedTokenIdempotentInstructionAsync({ + payer, + mint, + owner: owner.address, + }); + + await pipe( + await createDefaultTransaction(client, payer), + (tx) => appendTransactionMessageInstruction(createAta, tx), + (tx) => signAndSendTransaction(client, tx) + ); + + // Then we expect the token account to exist and have the following data. + const [ata] = await findAssociatedTokenPda({ + mint, + owner: owner.address, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }); + t.like(await fetchToken(client.rpc, ata), >{ + address: ata, + data: { + mint, + owner: owner.address, + amount: 0n, + delegate: none(), + state: AccountState.Initialized, + isNative: none(), + delegatedAmount: 0n, + closeAuthority: none(), + }, + }); +});