From b1366ec207d75a87ba816cfdd85af8fdaa36da5b Mon Sep 17 00:00:00 2001 From: fedoras Date: Thu, 11 Dec 2025 06:16:21 +0300 Subject: [PATCH 1/3] add createRecordWithSeed action --- clients/js/src/actions.ts | 66 +++++++++++++++++++- clients/js/test/createWithSeed.test.ts | 85 ++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 clients/js/test/createWithSeed.test.ts diff --git a/clients/js/src/actions.ts b/clients/js/src/actions.ts index 747125b..24067fe 100644 --- a/clients/js/src/actions.ts +++ b/clients/js/src/actions.ts @@ -1,14 +1,16 @@ import { Address, + createAddressWithSeed, generateKeyPairSigner, GetBalanceApi, GetMinimumBalanceForRentExemptionApi, Instruction, KeyPairSigner, + ReadonlyUint8Array, Rpc, TransactionSigner, } from '@solana/kit'; -import { getCreateAccountInstruction, getTransferSolInstruction } from '@solana-program/system'; +import { getCreateAccountInstruction, getCreateAccountWithSeedInstruction, getTransferSolInstruction } from '@solana-program/system'; import { getCloseAccountInstruction, getInitializeInstruction, @@ -72,6 +74,68 @@ export async function createRecord({ }; } +export interface CreateRecordWithSeedArgs { + rpc: Rpc; + payer: KeyPairSigner; + authority: Address; + dataLength: number | bigint; + programId?: Address; + seed: ReadonlyUint8Array | string; + /** Optional: Provide your own keypair for the base account. If not provided, the payer is used as base. */ + baseAccount?: KeyPairSigner; +} + +export interface CreateRecordWithSeedResult { + recordAccount: Address; + ixs: Instruction[]; +} + +/** + * High-level function to create and initialize a Record Account with seed. + * Handles rent calculation and system account creation with seed. + */ +export async function createRecordWithSeed({ + rpc, + payer, + authority, + dataLength, + programId = SPL_RECORD_PROGRAM_ADDRESS, + baseAccount = payer, + seed +}: CreateRecordWithSeedArgs): Promise { + const space = RECORD_META_DATA_SIZE + BigInt(dataLength); + const amount = await rpc.getMinimumBalanceForRentExemption(space).send(); + const recordAccount = await createAddressWithSeed({ + baseAddress: baseAccount.address, + seed, + programAddress: programId, + }) + + const createAccountIx = getCreateAccountWithSeedInstruction({ + payer: payer, + newAccount: recordAccount, + baseAccount: baseAccount, + base: baseAccount.address, + seed: typeof seed === 'string' ? seed : new TextDecoder().decode(seed), + amount, + space, + programAddress: programId, + }); + + const initializeIx = getInitializeInstruction( + { + recordAccount, + authority, + }, + { programAddress: programId }, + ); + + return { + recordAccount, + ixs: [createAccountIx, initializeIx], + }; +} + export interface WriteRecordArgs { recordAccount: Address; authority: TransactionSigner; diff --git a/clients/js/test/createWithSeed.test.ts b/clients/js/test/createWithSeed.test.ts new file mode 100644 index 0000000..becd93c --- /dev/null +++ b/clients/js/test/createWithSeed.test.ts @@ -0,0 +1,85 @@ +import { generateKeyPairSigner } from '@solana/kit'; +import test from 'ava'; +import { + createRecordWithSeed, + fetchRecordData +} from '../src'; +import { + createDefaultSolanaClient, + generateKeyPairSignerWithSol, + sendAndConfirmInstructions, +} from './_setup'; + +test('create record with string seed', async t => { + const client = createDefaultSolanaClient(); + const payer = await generateKeyPairSignerWithSol(client); + + const initialRecordSize = 0n; + const seed = 'test-seed'; + + // Initialize + const { recordAccount, ixs: createIxs } = await createRecordWithSeed({ + rpc: client.rpc, + payer, + authority: payer.address, + dataLength: initialRecordSize, + seed + }); + + await sendAndConfirmInstructions(client, payer, createIxs); + + // Verify Initialize + let accountData = await fetchRecordData(client.rpc, recordAccount); + t.is(accountData.data.version, 1); + t.is(accountData.data.authority, payer.address); +}); + +test('create record with uint8array seed', async t => { + const client = createDefaultSolanaClient(); + const payer = await generateKeyPairSignerWithSol(client); + + const initialRecordSize = 0n; + const seed = new Uint8Array([0, 1, 2, 3, 4]); + + // Initialize + const { recordAccount, ixs: createIxs } = await createRecordWithSeed({ + rpc: client.rpc, + payer, + authority: payer.address, + dataLength: initialRecordSize, + seed + }); + + await sendAndConfirmInstructions(client, payer, createIxs); + + // Verify Initialize + let accountData = await fetchRecordData(client.rpc, recordAccount); + t.is(accountData.data.version, 1); + t.is(accountData.data.authority, payer.address); +}); + +test('create record with external base account', async t => { + const client = createDefaultSolanaClient(); + const payer = await generateKeyPairSignerWithSol(client); + const baseAccount = await generateKeyPairSigner(); + + const initialRecordSize = 0n; + const seed = 'test-seed'; + + // Initialize + const { recordAccount, ixs: createIxs } = await createRecordWithSeed({ + rpc: client.rpc, + payer, + authority: payer.address, + dataLength: initialRecordSize, + seed, + baseAccount: baseAccount + }); + + await sendAndConfirmInstructions(client, payer, createIxs); + + // Verify Initialize + let accountData = await fetchRecordData(client.rpc, recordAccount); + t.is(accountData.data.version, 1); + t.is(accountData.data.authority, payer.address); +}); From 36c0d62cb35f915a864c490176c0f1db3845dcc2 Mon Sep 17 00:00:00 2001 From: fedoras Date: Thu, 11 Dec 2025 06:23:59 +0300 Subject: [PATCH 2/3] upd tests to check for expected address --- clients/js/test/createWithSeed.test.ts | 29 ++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/clients/js/test/createWithSeed.test.ts b/clients/js/test/createWithSeed.test.ts index becd93c..d4b5d69 100644 --- a/clients/js/test/createWithSeed.test.ts +++ b/clients/js/test/createWithSeed.test.ts @@ -1,8 +1,9 @@ -import { generateKeyPairSigner } from '@solana/kit'; +import { createAddressWithSeed, generateKeyPairSigner } from '@solana/kit'; import test from 'ava'; import { createRecordWithSeed, - fetchRecordData + fetchRecordData, + SPL_RECORD_PROGRAM_ADDRESS } from '../src'; import { createDefaultSolanaClient, @@ -17,6 +18,12 @@ test('create record with string seed', async t => { const initialRecordSize = 0n; const seed = 'test-seed'; + const expectedRecordAccount = await createAddressWithSeed({ + baseAddress: payer.address, + seed, + programAddress: SPL_RECORD_PROGRAM_ADDRESS + }) + // Initialize const { recordAccount, ixs: createIxs } = await createRecordWithSeed({ rpc: client.rpc, @@ -26,6 +33,8 @@ test('create record with string seed', async t => { seed }); + t.deepEqual(recordAccount, expectedRecordAccount); + await sendAndConfirmInstructions(client, payer, createIxs); // Verify Initialize @@ -41,6 +50,12 @@ test('create record with uint8array seed', async t => { const initialRecordSize = 0n; const seed = new Uint8Array([0, 1, 2, 3, 4]); + const expectedRecordAccount = await createAddressWithSeed({ + baseAddress: payer.address, + seed, + programAddress: SPL_RECORD_PROGRAM_ADDRESS + }) + // Initialize const { recordAccount, ixs: createIxs } = await createRecordWithSeed({ rpc: client.rpc, @@ -50,6 +65,8 @@ test('create record with uint8array seed', async t => { seed }); + t.deepEqual(recordAccount, expectedRecordAccount); + await sendAndConfirmInstructions(client, payer, createIxs); // Verify Initialize @@ -66,6 +83,12 @@ test('create record with external base account', async t => { const initialRecordSize = 0n; const seed = 'test-seed'; + const expectedRecordAccount = await createAddressWithSeed({ + baseAddress: baseAccount.address, + seed, + programAddress: SPL_RECORD_PROGRAM_ADDRESS + }) + // Initialize const { recordAccount, ixs: createIxs } = await createRecordWithSeed({ rpc: client.rpc, @@ -76,6 +99,8 @@ test('create record with external base account', async t => { baseAccount: baseAccount }); + t.deepEqual(recordAccount, expectedRecordAccount); + await sendAndConfirmInstructions(client, payer, createIxs); // Verify Initialize From efab209fba152ca21f3f804c3e82f5e104e57f1a Mon Sep 17 00:00:00 2001 From: fedoras Date: Thu, 11 Dec 2025 06:25:57 +0300 Subject: [PATCH 3/3] fix prettier errors --- clients/js/src/actions.ts | 10 +++++++--- clients/js/test/createWithSeed.test.ts | 24 ++++++++++-------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/clients/js/src/actions.ts b/clients/js/src/actions.ts index 24067fe..0e77168 100644 --- a/clients/js/src/actions.ts +++ b/clients/js/src/actions.ts @@ -10,7 +10,11 @@ import { Rpc, TransactionSigner, } from '@solana/kit'; -import { getCreateAccountInstruction, getCreateAccountWithSeedInstruction, getTransferSolInstruction } from '@solana-program/system'; +import { + getCreateAccountInstruction, + getCreateAccountWithSeedInstruction, + getTransferSolInstruction, +} from '@solana-program/system'; import { getCloseAccountInstruction, getInitializeInstruction, @@ -101,7 +105,7 @@ export async function createRecordWithSeed({ dataLength, programId = SPL_RECORD_PROGRAM_ADDRESS, baseAccount = payer, - seed + seed, }: CreateRecordWithSeedArgs): Promise { const space = RECORD_META_DATA_SIZE + BigInt(dataLength); const amount = await rpc.getMinimumBalanceForRentExemption(space).send(); @@ -109,7 +113,7 @@ export async function createRecordWithSeed({ baseAddress: baseAccount.address, seed, programAddress: programId, - }) + }); const createAccountIx = getCreateAccountWithSeedInstruction({ payer: payer, diff --git a/clients/js/test/createWithSeed.test.ts b/clients/js/test/createWithSeed.test.ts index d4b5d69..2acf0ec 100644 --- a/clients/js/test/createWithSeed.test.ts +++ b/clients/js/test/createWithSeed.test.ts @@ -1,10 +1,6 @@ import { createAddressWithSeed, generateKeyPairSigner } from '@solana/kit'; import test from 'ava'; -import { - createRecordWithSeed, - fetchRecordData, - SPL_RECORD_PROGRAM_ADDRESS -} from '../src'; +import { createRecordWithSeed, fetchRecordData, SPL_RECORD_PROGRAM_ADDRESS } from '../src'; import { createDefaultSolanaClient, generateKeyPairSignerWithSol, @@ -21,8 +17,8 @@ test('create record with string seed', async t => { const expectedRecordAccount = await createAddressWithSeed({ baseAddress: payer.address, seed, - programAddress: SPL_RECORD_PROGRAM_ADDRESS - }) + programAddress: SPL_RECORD_PROGRAM_ADDRESS, + }); // Initialize const { recordAccount, ixs: createIxs } = await createRecordWithSeed({ @@ -30,7 +26,7 @@ test('create record with string seed', async t => { payer, authority: payer.address, dataLength: initialRecordSize, - seed + seed, }); t.deepEqual(recordAccount, expectedRecordAccount); @@ -53,8 +49,8 @@ test('create record with uint8array seed', async t => { const expectedRecordAccount = await createAddressWithSeed({ baseAddress: payer.address, seed, - programAddress: SPL_RECORD_PROGRAM_ADDRESS - }) + programAddress: SPL_RECORD_PROGRAM_ADDRESS, + }); // Initialize const { recordAccount, ixs: createIxs } = await createRecordWithSeed({ @@ -62,7 +58,7 @@ test('create record with uint8array seed', async t => { payer, authority: payer.address, dataLength: initialRecordSize, - seed + seed, }); t.deepEqual(recordAccount, expectedRecordAccount); @@ -86,8 +82,8 @@ test('create record with external base account', async t => { const expectedRecordAccount = await createAddressWithSeed({ baseAddress: baseAccount.address, seed, - programAddress: SPL_RECORD_PROGRAM_ADDRESS - }) + programAddress: SPL_RECORD_PROGRAM_ADDRESS, + }); // Initialize const { recordAccount, ixs: createIxs } = await createRecordWithSeed({ @@ -96,7 +92,7 @@ test('create record with external base account', async t => { authority: payer.address, dataLength: initialRecordSize, seed, - baseAccount: baseAccount + baseAccount: baseAccount, }); t.deepEqual(recordAccount, expectedRecordAccount);