فهرست منبع

fix: Base is optional (#57)

* fix: Base is optional

* No, only create with seed

* Add tests

* Testing without helper and revert lamports to amount

* Run prettier fix after generating clients

* Update pnpm-lock.yaml

* Add toolchain to Rust client for formatting

* Use formatting toolchain instead

---------

Co-authored-by: Loris Leiva <loris.leiva@gmail.com>
Pierre 1 ماه پیش
والد
کامیت
5c1a09e092

+ 12 - 151
Cargo.lock

@@ -372,15 +372,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "bitmaps"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
-dependencies = [
- "typenum",
-]
-
 [[package]]
 name = "blake3"
 version = "1.6.1"
@@ -1356,12 +1347,6 @@ version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
 
-[[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-
 [[package]]
 name = "hermit-abi"
 version = "0.1.19"
@@ -1704,22 +1689,6 @@ dependencies = [
  "icu_properties",
 ]
 
-[[package]]
-name = "im"
-version = "15.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9"
-dependencies = [
- "bitmaps",
- "rand_core 0.6.4",
- "rand_xoshiro",
- "rayon",
- "serde",
- "sized-chunks",
- "typenum",
- "version_check",
-]
-
 [[package]]
 name = "indexmap"
 version = "1.9.3"
@@ -2598,15 +2567,6 @@ dependencies = [
  "rand_core 0.5.1",
 ]
 
-[[package]]
-name = "rand_xoshiro"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
-dependencies = [
- "rand_core 0.6.4",
-]
-
 [[package]]
 name = "raw-cpuid"
 version = "11.3.0"
@@ -3135,16 +3095,6 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
 
-[[package]]
-name = "sized-chunks"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
-dependencies = [
- "bitmaps",
- "typenum",
-]
-
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -3364,7 +3314,7 @@ dependencies = [
  "solana-pubkey",
  "solana-signature",
  "solana-signer",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
  "solana-transaction",
  "solana-transaction-error",
 ]
@@ -3559,7 +3509,7 @@ dependencies = [
  "solana-nonce",
  "solana-pubkey",
  "solana-sdk-ids",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
  "thiserror 2.0.12",
 ]
 
@@ -3579,7 +3529,7 @@ dependencies = [
  "solana-pubkey",
  "solana-rent",
  "solana-sdk-ids",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
 ]
 
 [[package]]
@@ -3619,36 +3569,6 @@ dependencies = [
  "solana-native-token",
 ]
 
-[[package]]
-name = "solana-frozen-abi"
-version = "2.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "685197cf2304e5d26973c72286abb8eb503eede4555b05dbe853d236fd74132c"
-dependencies = [
- "bs58",
- "bv",
- "im",
- "log",
- "memmap2",
- "serde",
- "serde_derive",
- "serde_with",
- "sha2 0.10.8",
- "solana-frozen-abi-macro",
- "thiserror 2.0.12",
-]
-
-[[package]]
-name = "solana-frozen-abi-macro"
-version = "2.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b83f88a126213cbcb57672c5e70ddb9791eff9b480e9f39fe9285fd2abca66fa"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.87",
-]
-
 [[package]]
 name = "solana-genesis-config"
 version = "2.2.1"
@@ -3823,7 +3743,7 @@ dependencies = [
  "solana-instruction",
  "solana-pubkey",
  "solana-sdk-ids",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
 ]
 
 [[package]]
@@ -3838,7 +3758,7 @@ dependencies = [
  "solana-instruction",
  "solana-pubkey",
  "solana-sdk-ids",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
 ]
 
 [[package]]
@@ -3878,7 +3798,7 @@ dependencies = [
  "solana-sanitize",
  "solana-sdk-ids",
  "solana-short-vec",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
  "solana-transaction-error",
  "wasm-bindgen",
 ]
@@ -4143,7 +4063,7 @@ dependencies = [
  "solana-slot-history",
  "solana-stable-layout",
  "solana-stake-interface",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
  "solana-sysvar",
  "solana-sysvar-id",
  "solana-vote-interface",
@@ -4226,8 +4146,6 @@ dependencies = [
  "solana-atomic-u64",
  "solana-decode-error",
  "solana-define-syscall",
- "solana-frozen-abi",
- "solana-frozen-abi-macro",
  "solana-sanitize",
  "solana-sha256-hasher",
  "wasm-bindgen",
@@ -4776,7 +4694,7 @@ dependencies = [
  "solana-instruction",
  "solana-program-error",
  "solana-pubkey",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
  "solana-sysvar-id",
 ]
 
@@ -4849,38 +4767,6 @@ dependencies = [
  "thiserror 1.0.69",
 ]
 
-[[package]]
-name = "solana-system-interface"
-version = "1.0.0"
-dependencies = [
- "anyhow",
- "borsh 1.5.5",
- "js-sys",
- "num-traits",
- "serde",
- "serde_derive",
- "solana-account-info",
- "solana-cpi",
- "solana-decode-error",
- "solana-example-mocks",
- "solana-frozen-abi",
- "solana-frozen-abi-macro",
- "solana-instruction",
- "solana-logger",
- "solana-msg",
- "solana-nonce",
- "solana-program-entrypoint",
- "solana-program-error",
- "solana-pubkey",
- "solana-system-interface 1.0.0",
- "solana-sysvar",
- "solana-sysvar-id",
- "static_assertions",
- "strum",
- "strum_macros",
- "wasm-bindgen",
-]
-
 [[package]]
 name = "solana-system-interface"
 version = "1.0.0"
@@ -4908,7 +4794,7 @@ dependencies = [
  "solana-message",
  "solana-pubkey",
  "solana-signer",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
  "solana-transaction",
 ]
 
@@ -4983,7 +4869,7 @@ dependencies = [
  "solana-rpc-client-api",
  "solana-signature",
  "solana-signer",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
  "solana-transaction",
  "solana-transaction-error",
 ]
@@ -5063,7 +4949,7 @@ dependencies = [
  "solana-short-vec",
  "solana-signature",
  "solana-signer",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
  "solana-transaction-error",
  "wasm-bindgen",
 ]
@@ -5194,7 +5080,7 @@ dependencies = [
  "solana-serde-varint",
  "solana-serialize-utils",
  "solana-short-vec",
- "solana-system-interface 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solana-system-interface",
 ]
 
 [[package]]
@@ -5222,37 +5108,12 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 
-[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
 [[package]]
 name = "strsim"
 version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
-[[package]]
-name = "strum"
-version = "0.24.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
-
-[[package]]
-name = "strum_macros"
-version = "0.24.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "rustversion",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "subtle"
 version = "2.6.1"

+ 1 - 1
clients/js/package.json

@@ -53,7 +53,7 @@
     "@typescript-eslint/parser": "^7.16.1",
     "ava": "^6.1.3",
     "eslint": "^8.57.0",
-    "prettier": "^3.3.3",
+    "prettier": "^3.6.2",
     "rimraf": "^5.0.5",
     "tsup": "^8.1.2",
     "typedoc": "^0.25.12",

+ 5 - 5
clients/js/pnpm-lock.yaml

@@ -33,8 +33,8 @@ importers:
         specifier: ^8.57.0
         version: 8.57.0
       prettier:
-        specifier: ^3.3.3
-        version: 3.3.3
+        specifier: ^3.6.2
+        version: 3.6.2
       rimraf:
         specifier: ^5.0.5
         version: 5.0.5
@@ -1662,8 +1662,8 @@ packages:
     resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
     engines: {node: '>= 0.8.0'}
 
-  prettier@3.3.3:
-    resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==}
+  prettier@3.6.2:
+    resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==}
     engines: {node: '>=14'}
     hasBin: true
 
@@ -3696,7 +3696,7 @@ snapshots:
 
   prelude-ls@1.2.1: {}
 
-  prettier@3.3.3: {}
+  prettier@3.6.2: {}
 
   pretty-ms@9.0.0:
     dependencies:

+ 1 - 1
clients/js/src/generated/instructions/advanceNonceAccount.ts

@@ -142,7 +142,7 @@ export function getAdvanceNonceAccountInstruction<
       'SysvarRecentB1ockHashes11111111111111111111' as Address<'SysvarRecentB1ockHashes11111111111111111111'>;
   }
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [
       getAccountMeta(accounts.nonceAccount),

+ 1 - 1
clients/js/src/generated/instructions/allocate.ts

@@ -111,7 +111,7 @@ export function getAllocateInstruction<
   // Original args.
   const args = { ...input };
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [getAccountMeta(accounts.newAccount)],
     programAddress,

+ 1 - 1
clients/js/src/generated/instructions/allocateWithSeed.ts

@@ -152,7 +152,7 @@ export function getAllocateWithSeedInstruction<
   // Original args.
   const args = { ...input };
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [
       getAccountMeta(accounts.newAccount),

+ 1 - 1
clients/js/src/generated/instructions/assign.ts

@@ -114,7 +114,7 @@ export function getAssignInstruction<
   // Original args.
   const args = { ...input };
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [getAccountMeta(accounts.account)],
     programAddress,

+ 1 - 1
clients/js/src/generated/instructions/assignWithSeed.ts

@@ -147,7 +147,7 @@ export function getAssignWithSeedInstruction<
   // Original args.
   const args = { ...input };
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [
       getAccountMeta(accounts.account),

+ 1 - 1
clients/js/src/generated/instructions/authorizeNonceAccount.ts

@@ -137,7 +137,7 @@ export function getAuthorizeNonceAccountInstruction<
   // Original args.
   const args = { ...input };
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [
       getAccountMeta(accounts.nonceAccount),

+ 1 - 1
clients/js/src/generated/instructions/createAccount.ts

@@ -155,7 +155,7 @@ export function getCreateAccountInstruction<
     0
   );
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [
       getAccountMeta(accounts.payer),

+ 24 - 11
clients/js/src/generated/instructions/createAccountWithSeed.ts

@@ -49,7 +49,10 @@ export type CreateAccountWithSeedInstruction<
   TProgram extends string = typeof SYSTEM_PROGRAM_ADDRESS,
   TAccountPayer extends string | AccountMeta<string> = string,
   TAccountNewAccount extends string | AccountMeta<string> = string,
-  TAccountBaseAccount extends string | AccountMeta<string> = string,
+  TAccountBaseAccount extends
+    | string
+    | AccountMeta<string>
+    | undefined = undefined,
   TRemainingAccounts extends readonly AccountMeta<string>[] = [],
 > = Instruction<TProgram> &
   InstructionWithData<ReadonlyUint8Array> &
@@ -62,10 +65,14 @@ export type CreateAccountWithSeedInstruction<
       TAccountNewAccount extends string
         ? WritableAccount<TAccountNewAccount>
         : TAccountNewAccount,
-      TAccountBaseAccount extends string
-        ? ReadonlySignerAccount<TAccountBaseAccount> &
-            AccountSignerMeta<TAccountBaseAccount>
-        : TAccountBaseAccount,
+      ...(TAccountBaseAccount extends undefined
+        ? []
+        : [
+            TAccountBaseAccount extends string
+              ? ReadonlySignerAccount<TAccountBaseAccount> &
+                  AccountSignerMeta<TAccountBaseAccount>
+              : TAccountBaseAccount,
+          ]),
       ...TRemainingAccounts,
     ]
   >;
@@ -132,7 +139,7 @@ export type CreateAccountWithSeedInput<
 > = {
   payer: TransactionSigner<TAccountPayer>;
   newAccount: Address<TAccountNewAccount>;
-  baseAccount: TransactionSigner<TAccountBaseAccount>;
+  baseAccount?: TransactionSigner<TAccountBaseAccount>;
   base: CreateAccountWithSeedInstructionDataArgs['base'];
   seed: CreateAccountWithSeedInstructionDataArgs['seed'];
   amount: CreateAccountWithSeedInstructionDataArgs['amount'];
@@ -175,13 +182,13 @@ export function getCreateAccountWithSeedInstruction<
   // Original args.
   const args = { ...input };
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [
       getAccountMeta(accounts.payer),
       getAccountMeta(accounts.newAccount),
       getAccountMeta(accounts.baseAccount),
-    ],
+    ].filter(<T>(x: T | undefined): x is T => x !== undefined),
     programAddress,
     data: getCreateAccountWithSeedInstructionDataEncoder().encode(
       args as CreateAccountWithSeedInstructionDataArgs
@@ -204,7 +211,7 @@ export type ParsedCreateAccountWithSeedInstruction<
   accounts: {
     payer: TAccountMetas[0];
     newAccount: TAccountMetas[1];
-    baseAccount: TAccountMetas[2];
+    baseAccount?: TAccountMetas[2] | undefined;
   };
   data: CreateAccountWithSeedInstructionData;
 };
@@ -217,7 +224,7 @@ export function parseCreateAccountWithSeedInstruction<
     InstructionWithAccounts<TAccountMetas> &
     InstructionWithData<ReadonlyUint8Array>
 ): ParsedCreateAccountWithSeedInstruction<TProgram, TAccountMetas> {
-  if (instruction.accounts.length < 3) {
+  if (instruction.accounts.length < 2) {
     // TODO: Coded error.
     throw new Error('Not enough accounts');
   }
@@ -227,12 +234,18 @@ export function parseCreateAccountWithSeedInstruction<
     accountIndex += 1;
     return accountMeta;
   };
+  let optionalAccountsRemaining = instruction.accounts.length - 2;
+  const getNextOptionalAccount = () => {
+    if (optionalAccountsRemaining === 0) return undefined;
+    optionalAccountsRemaining -= 1;
+    return getNextAccount();
+  };
   return {
     programAddress: instruction.programAddress,
     accounts: {
       payer: getNextAccount(),
       newAccount: getNextAccount(),
-      baseAccount: getNextAccount(),
+      baseAccount: getNextOptionalAccount(),
     },
     data: getCreateAccountWithSeedInstructionDataDecoder().decode(
       instruction.data

+ 1 - 1
clients/js/src/generated/instructions/initializeNonceAccount.ts

@@ -161,7 +161,7 @@ export function getInitializeNonceAccountInstruction<
       'SysvarRent111111111111111111111111111111111' as Address<'SysvarRent111111111111111111111111111111111'>;
   }
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [
       getAccountMeta(accounts.nonceAccount),

+ 1 - 1
clients/js/src/generated/instructions/transferSol.ts

@@ -129,7 +129,7 @@ export function getTransferSolInstruction<
   // Original args.
   const args = { ...input };
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [
       getAccountMeta(accounts.source),

+ 1 - 1
clients/js/src/generated/instructions/transferSolWithSeed.ts

@@ -163,7 +163,7 @@ export function getTransferSolWithSeedInstruction<
   // Original args.
   const args = { ...input };
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [
       getAccountMeta(accounts.source),

+ 1 - 1
clients/js/src/generated/instructions/upgradeNonceAccount.ts

@@ -101,7 +101,7 @@ export function getUpgradeNonceAccountInstruction<
     ResolvedAccount
   >;
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [getAccountMeta(accounts.nonceAccount)],
     programAddress,

+ 1 - 1
clients/js/src/generated/instructions/withdrawNonceAccount.ts

@@ -188,7 +188,7 @@ export function getWithdrawNonceAccountInstruction<
       'SysvarRent111111111111111111111111111111111' as Address<'SysvarRent111111111111111111111111111111111'>;
   }
 
-  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'omitted');
   const instruction = {
     accounts: [
       getAccountMeta(accounts.nonceAccount),

+ 110 - 0
clients/js/test/createAccountWithSeed.test.ts

@@ -0,0 +1,110 @@
+import {
+  appendTransactionMessageInstruction,
+  createAddressWithSeed,
+  fetchEncodedAccount,
+  generateKeyPairSigner,
+  pipe,
+} from '@solana/kit';
+import test from 'ava';
+import { getCreateAccountWithSeedInstruction } from '../src';
+import {
+  createDefaultSolanaClient,
+  createDefaultTransaction,
+  generateKeyPairSignerWithSol,
+  signAndSendTransaction,
+} from './_setup';
+
+test('it creates a new empty account when base is not payer', async (t) => {
+  const client = createDefaultSolanaClient();
+  const space = 42n;
+  const [payer, program, lamports] = await Promise.all([
+    generateKeyPairSignerWithSol(client),
+    generateKeyPairSigner(),
+    client.rpc.getMinimumBalanceForRentExemption(space).send(),
+  ]);
+  const baseAccount = await generateKeyPairSigner();
+
+  const programAddress = program.address;
+  const SEED = '123456789';
+  const newAccount = await createAddressWithSeed({
+    baseAddress: baseAccount.address,
+    programAddress,
+    seed: SEED,
+  });
+
+  // When we call createAccountWithSeed in a transaction.
+  const createAccount = getCreateAccountWithSeedInstruction({
+    payer,
+    newAccount,
+    baseAccount,
+    base: baseAccount.address,
+    seed: SEED,
+    space,
+    amount: lamports,
+    programAddress,
+  });
+  await pipe(
+    await createDefaultTransaction(client, payer),
+    (tx) => appendTransactionMessageInstruction(createAccount, tx),
+    (tx) => signAndSendTransaction(client, tx)
+  );
+
+  // Then we expect the following account data.
+  const fetchedAccount = await fetchEncodedAccount(client.rpc, newAccount);
+  t.deepEqual(fetchedAccount, {
+    executable: false,
+    lamports,
+    programAddress,
+    address: newAccount,
+    data: new Uint8Array(Array.from({ length: 42 }, () => 0)),
+    exists: true,
+    space: 42n,
+  });
+});
+
+test('it creates a new empty account when base is payer', async (t) => {
+  const client = createDefaultSolanaClient();
+  const space = 42n;
+  const [payer, program, lamports] = await Promise.all([
+    generateKeyPairSignerWithSol(client),
+    generateKeyPairSigner(),
+    client.rpc.getMinimumBalanceForRentExemption(space).send(),
+  ]);
+  const baseAddress = payer.address;
+
+  const programAddress = program.address;
+  const SEED = '123456789';
+  const newAccount = await createAddressWithSeed({
+    baseAddress,
+    programAddress,
+    seed: SEED,
+  });
+
+  // When we call createAccountWithSeed in a transaction.
+  const createAccount = getCreateAccountWithSeedInstruction({
+    payer,
+    newAccount,
+    base: baseAddress,
+    seed: SEED,
+    space,
+    amount: lamports,
+    programAddress,
+  });
+  await pipe(
+    await createDefaultTransaction(client, payer),
+    (tx) => appendTransactionMessageInstruction(createAccount, tx),
+    (tx) => signAndSendTransaction(client, tx)
+  );
+
+  // Then we expect the following account data.
+  const fetchedAccount = await fetchEncodedAccount(client.rpc, newAccount);
+  t.deepEqual(fetchedAccount, {
+    executable: false,
+    lamports,
+    programAddress,
+    address: newAccount,
+    data: new Uint8Array(Array.from({ length: 42 }, () => 0)),
+    exists: true,
+    space: 42n,
+  });
+});

+ 31 - 23
clients/rust/src/generated/instructions/create_account_with_seed.rs

@@ -17,7 +17,7 @@ pub struct CreateAccountWithSeed {
 
     pub new_account: solana_program::pubkey::Pubkey,
 
-    pub base_account: solana_program::pubkey::Pubkey,
+    pub base_account: Option<solana_program::pubkey::Pubkey>,
 }
 
 impl CreateAccountWithSeed {
@@ -42,10 +42,12 @@ impl CreateAccountWithSeed {
             self.new_account,
             false,
         ));
-        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
-            self.base_account,
-            true,
-        ));
+        if let Some(base_account) = self.base_account {
+            accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+                base_account,
+                true,
+            ));
+        }
         accounts.extend_from_slice(remaining_accounts);
         let mut data = borsh::to_vec(&CreateAccountWithSeedInstructionData::new()).unwrap();
         let mut args = borsh::to_vec(&args).unwrap();
@@ -93,7 +95,7 @@ pub struct CreateAccountWithSeedInstructionArgs {
 ///
 ///   0. `[writable, signer]` payer
 ///   1. `[writable]` new_account
-///   2. `[signer]` base_account
+///   2. `[signer, optional]` base_account
 #[derive(Clone, Debug, Default)]
 pub struct CreateAccountWithSeedBuilder {
     payer: Option<solana_program::pubkey::Pubkey>,
@@ -121,9 +123,13 @@ impl CreateAccountWithSeedBuilder {
         self.new_account = Some(new_account);
         self
     }
+    /// `[optional account]`
     #[inline(always)]
-    pub fn base_account(&mut self, base_account: solana_program::pubkey::Pubkey) -> &mut Self {
-        self.base_account = Some(base_account);
+    pub fn base_account(
+        &mut self,
+        base_account: Option<solana_program::pubkey::Pubkey>,
+    ) -> &mut Self {
+        self.base_account = base_account;
         self
     }
     #[inline(always)]
@@ -174,7 +180,7 @@ impl CreateAccountWithSeedBuilder {
         let accounts = CreateAccountWithSeed {
             payer: self.payer.expect("payer is not set"),
             new_account: self.new_account.expect("new_account is not set"),
-            base_account: self.base_account.expect("base_account is not set"),
+            base_account: self.base_account,
         };
         let args = CreateAccountWithSeedInstructionArgs {
             base: self.base.clone().expect("base is not set"),
@@ -197,7 +203,7 @@ pub struct CreateAccountWithSeedCpiAccounts<'a, 'b> {
 
     pub new_account: &'b solana_program::account_info::AccountInfo<'a>,
 
-    pub base_account: &'b solana_program::account_info::AccountInfo<'a>,
+    pub base_account: Option<&'b solana_program::account_info::AccountInfo<'a>>,
 }
 
 /// `create_account_with_seed` CPI instruction.
@@ -209,7 +215,7 @@ pub struct CreateAccountWithSeedCpi<'a, 'b> {
 
     pub new_account: &'b solana_program::account_info::AccountInfo<'a>,
 
-    pub base_account: &'b solana_program::account_info::AccountInfo<'a>,
+    pub base_account: Option<&'b solana_program::account_info::AccountInfo<'a>>,
     /// The arguments for the instruction.
     pub __args: CreateAccountWithSeedInstructionArgs,
 }
@@ -271,10 +277,12 @@ impl<'a, 'b> CreateAccountWithSeedCpi<'a, 'b> {
             *self.new_account.key,
             false,
         ));
-        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
-            *self.base_account.key,
-            true,
-        ));
+        if let Some(base_account) = self.base_account {
+            accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+                *base_account.key,
+                true,
+            ));
+        }
         remaining_accounts.iter().for_each(|remaining_account| {
             accounts.push(solana_program::instruction::AccountMeta {
                 pubkey: *remaining_account.0.key,
@@ -295,7 +303,9 @@ impl<'a, 'b> CreateAccountWithSeedCpi<'a, 'b> {
         account_infos.push(self.__program.clone());
         account_infos.push(self.payer.clone());
         account_infos.push(self.new_account.clone());
-        account_infos.push(self.base_account.clone());
+        if let Some(base_account) = self.base_account {
+            account_infos.push(base_account.clone());
+        }
         remaining_accounts
             .iter()
             .for_each(|remaining_account| account_infos.push(remaining_account.0.clone()));
@@ -314,7 +324,7 @@ impl<'a, 'b> CreateAccountWithSeedCpi<'a, 'b> {
 ///
 ///   0. `[writable, signer]` payer
 ///   1. `[writable]` new_account
-///   2. `[signer]` base_account
+///   2. `[signer, optional]` base_account
 #[derive(Clone, Debug)]
 pub struct CreateAccountWithSeedCpiBuilder<'a, 'b> {
     instruction: Box<CreateAccountWithSeedCpiBuilderInstruction<'a, 'b>>,
@@ -349,12 +359,13 @@ impl<'a, 'b> CreateAccountWithSeedCpiBuilder<'a, 'b> {
         self.instruction.new_account = Some(new_account);
         self
     }
+    /// `[optional account]`
     #[inline(always)]
     pub fn base_account(
         &mut self,
-        base_account: &'b solana_program::account_info::AccountInfo<'a>,
+        base_account: Option<&'b solana_program::account_info::AccountInfo<'a>>,
     ) -> &mut Self {
-        self.instruction.base_account = Some(base_account);
+        self.instruction.base_account = base_account;
         self
     }
     #[inline(always)]
@@ -444,10 +455,7 @@ impl<'a, 'b> CreateAccountWithSeedCpiBuilder<'a, 'b> {
                 .new_account
                 .expect("new_account is not set"),
 
-            base_account: self
-                .instruction
-                .base_account
-                .expect("base_account is not set"),
+            base_account: self.instruction.base_account,
             __args: args,
         };
         instruction.invoke_signed_with_remaining_accounts(

+ 4 - 3
codama.mjs

@@ -1,8 +1,8 @@
 import path from 'node:path';
+import fs from 'node:fs';
 
-const { default: prettierOptions } = await import(
-  path.resolve('clients', 'js', '.prettierrc.json'),
-  { with: { type: 'json' } }
+const prettierOptions = JSON.parse(
+  fs.readFileSync(path.join('clients', 'js', '.prettierrc.json'), 'utf-8')
 );
 
 export default {
@@ -21,6 +21,7 @@ export default {
           anchorTraits: false,
           crateFolder: 'clients/rust',
           formatCode: true,
+          toolchain: '+nightly-2025-02-16',
         },
       ],
     },

+ 1 - 1
package.json

@@ -2,7 +2,7 @@
   "private": true,
   "scripts": {
     "generate": "pnpm generate:clients",
-    "generate:clients": "codama run --all"
+    "generate:clients": "codama run --all && (cd clients/js && pnpm format:fix)"
   },
   "devDependencies": {
     "@codama/renderers-js": "^1.3",

+ 14 - 14
program/idl.json

@@ -130,7 +130,7 @@
         "name": "createAccount",
         "idlName": "CreateAccount",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -174,7 +174,7 @@
         "name": "assign",
         "idlName": "Assign",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -230,7 +230,7 @@
         "name": "transferSol",
         "idlName": "TransferSol",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -257,7 +257,7 @@
             "name": "baseAccount",
             "isWritable": false,
             "isSigner": true,
-            "isOptional": false,
+            "isOptional": true,
             "docs": []
           }
         ],
@@ -331,7 +331,7 @@
         "name": "createAccountWithSeed",
         "idlName": "CreateAccountWithSeed",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -389,7 +389,7 @@
         "name": "advanceNonceAccount",
         "idlName": "AdvanceNonceAccount",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -477,7 +477,7 @@
         "name": "withdrawNonceAccount",
         "idlName": "WithdrawNonceAccount",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -545,7 +545,7 @@
         "name": "initializeNonceAccount",
         "idlName": "InitializeNonceAccount",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -597,7 +597,7 @@
         "name": "authorizeNonceAccount",
         "idlName": "AuthorizeNonceAccount",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -645,7 +645,7 @@
         "name": "allocate",
         "idlName": "Allocate",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -727,7 +727,7 @@
         "name": "allocateWithSeed",
         "idlName": "AllocateWithSeed",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -799,7 +799,7 @@
         "name": "assignWithSeed",
         "idlName": "AssignWithSeed",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -883,7 +883,7 @@
         "name": "transferSolWithSeed",
         "idlName": "TransferSolWithSeed",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       },
       {
         "kind": "instructionNode",
@@ -921,7 +921,7 @@
         "name": "upgradeNonceAccount",
         "idlName": "UpgradeNonceAccount",
         "docs": [],
-        "optionalAccountStrategy": "programId"
+        "optionalAccountStrategy": "omitted"
       }
     ],
     "definedTypes": [