@solana/program-client-core 6.3.1 → 6.3.2-canary-20260313143218

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solana/program-client-core",
3
- "version": "6.3.1",
3
+ "version": "6.3.2-canary-20260313143218",
4
4
  "description": "Core utilities for building Solana program clients",
5
5
  "homepage": "https://www.solanakit.com/api#solanaprogram-client-core",
6
6
  "exports": {
@@ -33,7 +33,8 @@
33
33
  "types": "./dist/types/index.d.ts",
34
34
  "type": "commonjs",
35
35
  "files": [
36
- "./dist/"
36
+ "./dist/",
37
+ "./src/"
37
38
  ],
38
39
  "sideEffects": false,
39
40
  "keywords": [
@@ -55,15 +56,15 @@
55
56
  "maintained node versions"
56
57
  ],
57
58
  "dependencies": {
58
- "@solana/accounts": "6.3.1",
59
- "@solana/addresses": "6.3.1",
60
- "@solana/codecs-core": "6.3.1",
61
- "@solana/errors": "6.3.1",
62
- "@solana/instruction-plans": "6.3.1",
63
- "@solana/instructions": "6.3.1",
64
- "@solana/signers": "6.3.1",
65
- "@solana/rpc-api": "6.3.1",
66
- "@solana/plugin-interfaces": "6.3.1"
59
+ "@solana/accounts": "6.3.2-canary-20260313143218",
60
+ "@solana/codecs-core": "6.3.2-canary-20260313143218",
61
+ "@solana/addresses": "6.3.2-canary-20260313143218",
62
+ "@solana/errors": "6.3.2-canary-20260313143218",
63
+ "@solana/instruction-plans": "6.3.2-canary-20260313143218",
64
+ "@solana/instructions": "6.3.2-canary-20260313143218",
65
+ "@solana/signers": "6.3.2-canary-20260313143218",
66
+ "@solana/rpc-api": "6.3.2-canary-20260313143218",
67
+ "@solana/plugin-interfaces": "6.3.2-canary-20260313143218"
67
68
  },
68
69
  "peerDependencies": {
69
70
  "typescript": "^5.0.0"
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * This package contains types and utilities for building Solana program clients.
3
+ * This is mainly used by the JavaScript Codama renderer to generate
4
+ * Kit-compatible program clients.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ export * from './instruction-input-resolution';
9
+ export * from './instructions';
10
+ export * from './self-fetch-functions';
11
+ export * from './self-plan-and-send-functions';
@@ -0,0 +1,216 @@
1
+ import { type Address, isProgramDerivedAddress, type ProgramDerivedAddress } from '@solana/addresses';
2
+ import {
3
+ SOLANA_ERROR__PROGRAM_CLIENTS__RESOLVED_INSTRUCTION_INPUT_MUST_BE_NON_NULL,
4
+ SOLANA_ERROR__PROGRAM_CLIENTS__UNEXPECTED_RESOLVED_INSTRUCTION_INPUT_TYPE,
5
+ SolanaError,
6
+ } from '@solana/errors';
7
+ import { type AccountMeta, AccountRole, upgradeRoleToSigner } from '@solana/instructions';
8
+ import { type AccountSignerMeta, isTransactionSigner, type TransactionSigner } from '@solana/signers';
9
+
10
+ /**
11
+ * Ensures a resolved instruction input is not null or undefined.
12
+ *
13
+ * This function is used during instruction resolution to validate that
14
+ * required inputs have been properly resolved to a non-null value.
15
+ *
16
+ * @typeParam T - The expected type of the resolved input value.
17
+ *
18
+ * @param inputName - The name of the instruction input, used in error messages.
19
+ * @param value - The resolved value to validate.
20
+ * @returns The validated non-null value.
21
+ *
22
+ * @throws Throws a {@link SolanaError} if the value is null or undefined.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const resolvedAuthority = getNonNullResolvedInstructionInput(
27
+ * 'authority',
28
+ * maybeAuthority
29
+ * );
30
+ * // resolvedAuthority is guaranteed to be non-null here.
31
+ * ```
32
+ */
33
+ export function getNonNullResolvedInstructionInput<T>(inputName: string, value: T | null | undefined): T {
34
+ if (value === null || value === undefined) {
35
+ throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__RESOLVED_INSTRUCTION_INPUT_MUST_BE_NON_NULL, {
36
+ inputName,
37
+ });
38
+ }
39
+ return value;
40
+ }
41
+
42
+ /**
43
+ * Extracts the address from a resolved instruction account.
44
+ *
45
+ * A resolved instruction account can be an {@link Address}, a {@link ProgramDerivedAddress},
46
+ * or a {@link TransactionSigner}. This function extracts the underlying address from
47
+ * any of these types.
48
+ *
49
+ * @typeParam T - The address type, defaults to `string`.
50
+ *
51
+ * @param inputName - The name of the instruction input, used in error messages.
52
+ * @param value - The resolved account value to extract the address from.
53
+ * @returns The extracted address.
54
+ *
55
+ * @throws Throws a {@link SolanaError} if the value is null or undefined.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const address = getAddressFromResolvedInstructionAccount('mint', resolvedMint);
60
+ * ```
61
+ */
62
+ export function getAddressFromResolvedInstructionAccount<T extends string = string>(
63
+ inputName: string,
64
+ value: ResolvedInstructionAccount<T>['value'] | undefined,
65
+ ): Address<T> {
66
+ const nonNullValue = getNonNullResolvedInstructionInput(inputName, value);
67
+ if (typeof value === 'object' && 'address' in nonNullValue) {
68
+ return nonNullValue.address;
69
+ }
70
+ if (Array.isArray(nonNullValue)) {
71
+ return nonNullValue[0] as Address<T>;
72
+ }
73
+ return nonNullValue as Address<T>;
74
+ }
75
+
76
+ /**
77
+ * Extracts a {@link ProgramDerivedAddress} from a resolved instruction account.
78
+ *
79
+ * This function validates that the resolved account is a PDA and returns it.
80
+ * Use this when you need access to both the address and the bump seed of a PDA.
81
+ *
82
+ * @typeParam T - The address type, defaults to `string`.
83
+ *
84
+ * @param inputName - The name of the instruction input, used in error messages.
85
+ * @param value - The resolved account value expected to be a PDA.
86
+ * @returns The program-derived address.
87
+ *
88
+ * @throws Throws a {@link SolanaError} if the value is not a {@link ProgramDerivedAddress}.
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * const pda = getResolvedInstructionAccountAsProgramDerivedAddress('metadata', resolvedMetadata);
93
+ * const [address, bump] = pda;
94
+ * ```
95
+ */
96
+ export function getResolvedInstructionAccountAsProgramDerivedAddress<T extends string = string>(
97
+ inputName: string,
98
+ value: ResolvedInstructionAccount<T>['value'] | undefined,
99
+ ): ProgramDerivedAddress<T> {
100
+ if (!isProgramDerivedAddress(value)) {
101
+ throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__UNEXPECTED_RESOLVED_INSTRUCTION_INPUT_TYPE, {
102
+ expectedType: 'ProgramDerivedAddress',
103
+ inputName,
104
+ });
105
+ }
106
+ return value;
107
+ }
108
+
109
+ /**
110
+ * Extracts a {@link TransactionSigner} from a resolved instruction account.
111
+ *
112
+ * This function validates that the resolved account is a transaction signer and returns it.
113
+ * Use this when you need the resolved account to be a signer.
114
+ *
115
+ * @typeParam T - The address type, defaults to `string`.
116
+ *
117
+ * @param inputName - The name of the instruction input, used in error messages.
118
+ * @param value - The resolved account value expected to be a signer.
119
+ * @returns The transaction signer.
120
+ *
121
+ * @throws Throws a {@link SolanaError} if the value is not a {@link TransactionSigner}.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const signer = getResolvedInstructionAccountAsTransactionSigner('authority', resolvedAuthority);
126
+ * ```
127
+ */
128
+ export function getResolvedInstructionAccountAsTransactionSigner<T extends string = string>(
129
+ inputName: string,
130
+ value: ResolvedInstructionAccount<T>['value'] | undefined,
131
+ ): TransactionSigner<T> {
132
+ if (!isResolvedInstructionAccountSigner(value)) {
133
+ throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__UNEXPECTED_RESOLVED_INSTRUCTION_INPUT_TYPE, {
134
+ expectedType: 'TransactionSigner',
135
+ inputName,
136
+ });
137
+ }
138
+ return value;
139
+ }
140
+
141
+ /**
142
+ * Represents a resolved account input for an instruction.
143
+ *
144
+ * During instruction building, account inputs are resolved to this type which
145
+ * captures both the account value and whether it should be marked as writable.
146
+ * The value can be an {@link Address}, a {@link ProgramDerivedAddress}, a
147
+ * {@link TransactionSigner}, or `null` for optional accounts.
148
+ *
149
+ * @typeParam TAddress - The address type, defaults to `string`.
150
+ * @typeParam TValue - The type of the resolved value.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * const mintAccount: ResolvedInstructionAccount = {
155
+ * value: mintAddress,
156
+ * isWritable: true,
157
+ * };
158
+ * ```
159
+ */
160
+ export type ResolvedInstructionAccount<
161
+ TAddress extends string = string,
162
+ TValue extends Address<TAddress> | ProgramDerivedAddress<TAddress> | TransactionSigner<TAddress> | null =
163
+ | Address<TAddress>
164
+ | ProgramDerivedAddress<TAddress>
165
+ | TransactionSigner<TAddress>
166
+ | null,
167
+ > = {
168
+ isWritable: boolean;
169
+ value: TValue;
170
+ };
171
+
172
+ /**
173
+ * Creates a factory function that converts resolved instruction accounts to account metas.
174
+ *
175
+ * The factory handles the conversion of {@link ResolvedInstructionAccount} objects into
176
+ * {@link AccountMeta} or {@link AccountSignerMeta} objects suitable for building instructions.
177
+ * It also determines how to handle optional accounts based on the provided strategy.
178
+ *
179
+ * @param programAddress - The program address, used when optional accounts use the `programId` strategy.
180
+ * @param optionalAccountStrategy - How to handle null account values:
181
+ * - `'omitted'`: Optional accounts are excluded from the instruction entirely.
182
+ * - `'programId'`: Optional accounts are replaced with the program address as a read-only account.
183
+ * @returns A factory function that converts a resolved account to an account meta.
184
+ *
185
+ * @example
186
+ * ```ts
187
+ * const toAccountMeta = getAccountMetaFactory(programAddress, 'programId');
188
+ * const mintMeta = toAccountMeta('mint', resolvedMint);
189
+ * ```
190
+ */
191
+ export function getAccountMetaFactory(programAddress: Address, optionalAccountStrategy: 'omitted' | 'programId') {
192
+ return (inputName: string, account: ResolvedInstructionAccount): AccountMeta | AccountSignerMeta | undefined => {
193
+ if (!account.value) {
194
+ if (optionalAccountStrategy === 'omitted') return;
195
+ return Object.freeze({ address: programAddress, role: AccountRole.READONLY });
196
+ }
197
+
198
+ const writableRole = account.isWritable ? AccountRole.WRITABLE : AccountRole.READONLY;
199
+ const isSigner = isResolvedInstructionAccountSigner(account.value);
200
+ return Object.freeze({
201
+ address: getAddressFromResolvedInstructionAccount(inputName, account.value),
202
+ role: isSigner ? upgradeRoleToSigner(writableRole) : writableRole,
203
+ ...(isSigner ? { signer: account.value } : {}),
204
+ });
205
+ };
206
+ }
207
+
208
+ function isResolvedInstructionAccountSigner(value: unknown): value is TransactionSigner {
209
+ return (
210
+ !!value &&
211
+ typeof value === 'object' &&
212
+ 'address' in value &&
213
+ typeof value.address === 'string' &&
214
+ isTransactionSigner(value as { address: Address })
215
+ );
216
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * An instruction that tracks how many bytes it adds or removes from on-chain accounts.
3
+ *
4
+ * The `byteDelta` indicates the net change in account storage size. A positive value
5
+ * means bytes are being allocated, while a negative value means bytes are being freed.
6
+ * This is useful for calculating how much balance a storage payer must have for a
7
+ * transaction to succeed.
8
+ */
9
+ export type InstructionWithByteDelta = {
10
+ byteDelta: number;
11
+ };
@@ -0,0 +1,156 @@
1
+ import {
2
+ type Account,
3
+ assertAccountExists,
4
+ assertAccountsExist,
5
+ decodeAccount,
6
+ type FetchAccountConfig,
7
+ type FetchAccountsConfig,
8
+ fetchEncodedAccount,
9
+ fetchEncodedAccounts,
10
+ type MaybeAccount,
11
+ } from '@solana/accounts';
12
+ import type { Address } from '@solana/addresses';
13
+ import type { Codec } from '@solana/codecs-core';
14
+ import type { ClientWithRpc } from '@solana/plugin-interfaces';
15
+ import type { GetAccountInfoApi, GetMultipleAccountsApi } from '@solana/rpc-api';
16
+
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ type AnyObjectCodec = Codec<any, object>;
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ type InferTFrom<T> = T extends Codec<infer TFrom, any> ? TFrom : never;
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ type InferTTo<T> = T extends Codec<any, infer TTo> ? TTo : never;
23
+
24
+ /**
25
+ * Methods that allow a codec to fetch and decode accounts directly.
26
+ *
27
+ * These methods are added to codec objects via {@link addSelfFetchFunctions},
28
+ * enabling a fluent API where you can call `.fetch()` directly on a codec
29
+ * to retrieve and decode accounts in one step.
30
+ *
31
+ * @typeParam TFrom - The type that the codec encodes from.
32
+ * @typeParam TTo - The type that the codec decodes to.
33
+ *
34
+ * @example
35
+ * Fetching a single account and asserting it exists.
36
+ * ```ts
37
+ * const account = await myAccountCodec.fetch(address);
38
+ * // account.data is of type TTo.
39
+ * ```
40
+ *
41
+ * @example
42
+ * Fetching a single account that may not exist.
43
+ * ```ts
44
+ * const maybeAccount = await myAccountCodec.fetchMaybe(address);
45
+ * if (maybeAccount.exists) {
46
+ * // maybeAccount.data is of type TTo.
47
+ * }
48
+ * ```
49
+ *
50
+ * @example
51
+ * Fetching multiple accounts at once.
52
+ * ```ts
53
+ * const accounts = await myAccountCodec.fetchAll([addressA, addressB]);
54
+ * // All accounts exist.
55
+ * ```
56
+ *
57
+ * @see {@link addSelfFetchFunctions}
58
+ */
59
+ export type SelfFetchFunctions<TFrom extends object, TTo extends TFrom> = {
60
+ /** Fetches and decodes a single account, throwing if it does not exist. */
61
+ readonly fetch: <TAddress extends string>(
62
+ address: Address<TAddress>,
63
+ config?: FetchAccountConfig,
64
+ ) => Promise<Account<TTo, TAddress>>;
65
+ /** Fetches and decodes multiple accounts, throwing if any do not exist. */
66
+ readonly fetchAll: (addresses: Address[], config?: FetchAccountsConfig) => Promise<Account<TTo>[]>;
67
+ /** Fetches and decodes multiple accounts, returning {@link MaybeAccount} for each. */
68
+ readonly fetchAllMaybe: (addresses: Address[], config?: FetchAccountsConfig) => Promise<MaybeAccount<TTo>[]>;
69
+ /** Fetches and decodes a single account, returning a {@link MaybeAccount}. */
70
+ readonly fetchMaybe: <TAddress extends string>(
71
+ address: Address<TAddress>,
72
+ config?: FetchAccountConfig,
73
+ ) => Promise<MaybeAccount<TTo, TAddress>>;
74
+ };
75
+
76
+ /**
77
+ * Adds self-fetching methods to a codec for retrieving and decoding accounts.
78
+ *
79
+ * This function augments the provided codec with methods that allow it to fetch
80
+ * accounts from the network and decode them in one step. It enables a fluent API
81
+ * where you can call methods like `.fetch()` directly on the codec.
82
+ *
83
+ * @typeParam TFrom - The type that the codec encodes from.
84
+ * @typeParam TTo - The type that the codec decodes to.
85
+ * @typeParam TCodec - The codec type being augmented.
86
+ *
87
+ * @param client - A client that provides RPC access for fetching accounts.
88
+ * @param codec - The codec to augment with self-fetch methods.
89
+ * @returns The codec augmented with {@link SelfFetchFunctions} methods.
90
+ *
91
+ * @example
92
+ * Adding self-fetch functions to an account codec.
93
+ * ```ts
94
+ * import { addSelfFetchFunctions } from '@solana/program-client-core';
95
+ *
96
+ * const myAccountCodec = addSelfFetchFunctions(client, getMyAccountCodec());
97
+ *
98
+ * // Fetch and decode an account in one step.
99
+ * const account = await myAccountCodec.fetch(accountAddress);
100
+ * ```
101
+ *
102
+ * @example
103
+ * Handling accounts that may not exist.
104
+ * ```ts
105
+ * const myAccountCodec = addSelfFetchFunctions(client, getMyAccountCodec());
106
+ *
107
+ * const maybeAccount = await myAccountCodec.fetchMaybe(accountAddress);
108
+ * if (maybeAccount.exists) {
109
+ * console.log('Account data:', maybeAccount.data);
110
+ * } else {
111
+ * console.log(`Account ${maybeAccount.address} does not exist`);
112
+ * }
113
+ * ```
114
+ *
115
+ * @example
116
+ * Fetching multiple accounts at once.
117
+ * ```ts
118
+ * const myAccountCodec = addSelfFetchFunctions(client, getMyAccountCodec());
119
+ *
120
+ * // Throws if any account does not exist.
121
+ * const accounts = await myAccountCodec.fetchAll([addressA, addressB, addressC]);
122
+ *
123
+ * // Returns MaybeAccount for each, allowing some to not exist.
124
+ * const maybeAccounts = await myAccountCodec.fetchAllMaybe([addressA, addressB]);
125
+ * ```
126
+ *
127
+ * @see {@link SelfFetchFunctions}
128
+ */
129
+ export function addSelfFetchFunctions<TCodec extends AnyObjectCodec>(
130
+ client: ClientWithRpc<GetAccountInfoApi & GetMultipleAccountsApi>,
131
+ codec: TCodec,
132
+ ): SelfFetchFunctions<InferTFrom<TCodec>, InferTTo<TCodec>> & TCodec {
133
+ type Functions = SelfFetchFunctions<InferTFrom<TCodec>, InferTTo<TCodec>>;
134
+ type InferredCodec = Codec<InferTFrom<TCodec>, InferTTo<TCodec>>;
135
+ const fetchMaybe: Functions['fetchMaybe'] = async (address, config?) => {
136
+ const maybeAccount = await fetchEncodedAccount(client.rpc, address, config);
137
+ return decodeAccount(maybeAccount, codec as InferredCodec);
138
+ };
139
+ const fetchAllMaybe: Functions['fetchAllMaybe'] = async (addresses, config?) => {
140
+ const maybeAccounts = await fetchEncodedAccounts(client.rpc, addresses, config);
141
+ return maybeAccounts.map(maybeAccount => decodeAccount(maybeAccount, codec as InferredCodec));
142
+ };
143
+ const fetch: Functions['fetch'] = async (address, config?) => {
144
+ const maybeAccount = await fetchMaybe(address, config);
145
+ assertAccountExists(maybeAccount);
146
+ return maybeAccount;
147
+ };
148
+ const fetchAll: Functions['fetchAll'] = async (addresses, config?) => {
149
+ const maybeAccounts = await fetchAllMaybe(addresses, config);
150
+ assertAccountsExist(maybeAccounts);
151
+ return maybeAccounts;
152
+ };
153
+
154
+ const out = { ...codec, fetch, fetchAll, fetchAllMaybe, fetchMaybe };
155
+ return Object.freeze<typeof out>(out);
156
+ }
@@ -0,0 +1,118 @@
1
+ import type { InstructionPlan } from '@solana/instruction-plans';
2
+ import type { Instruction } from '@solana/instructions';
3
+ import type { ClientWithTransactionPlanning, ClientWithTransactionSending } from '@solana/plugin-interfaces';
4
+
5
+ type PlanTransaction = ClientWithTransactionPlanning['planTransaction'];
6
+ type PlanTransactions = ClientWithTransactionPlanning['planTransactions'];
7
+ type SendTransaction = ClientWithTransactionSending['sendTransaction'];
8
+ type SendTransactions = ClientWithTransactionSending['sendTransactions'];
9
+
10
+ /**
11
+ * Methods that allow an instruction or instruction plan to plan and send itself.
12
+ *
13
+ * These methods are added to instruction or instruction plan objects via
14
+ * {@link addSelfPlanAndSendFunctions}, enabling a fluent API where you can call
15
+ * `.sendTransaction()` directly on an instruction without passing it to a separate function.
16
+ *
17
+ * @example
18
+ * Sending a transfer instruction directly.
19
+ * ```ts
20
+ * const result = await getTransferInstruction({ source, destination, amount }).sendTransaction();
21
+ * ```
22
+ *
23
+ * @example
24
+ * Planning multiple transactions from an instruction plan.
25
+ * ```ts
26
+ * const plan = await getComplexInstructionPlan(/* ... *\/).planTransactions();
27
+ * ```
28
+ *
29
+ * @see {@link addSelfPlanAndSendFunctions}
30
+ */
31
+ export type SelfPlanAndSendFunctions = {
32
+ /** Plans a single transaction. */
33
+ planTransaction: (config?: Parameters<PlanTransaction>[1]) => ReturnType<PlanTransaction>;
34
+ /** Plans one or more transactions. */
35
+ planTransactions: (config?: Parameters<PlanTransactions>[1]) => ReturnType<PlanTransactions>;
36
+ /** Sends a single transaction. */
37
+ sendTransaction: (config?: Parameters<SendTransaction>[1]) => ReturnType<SendTransaction>;
38
+ /** Sends one or more transactions. */
39
+ sendTransactions: (config?: Parameters<SendTransactions>[1]) => ReturnType<SendTransactions>;
40
+ };
41
+
42
+ /**
43
+ * Adds self-planning and self-sending methods to an instruction or instruction plan.
44
+ *
45
+ * This function augments the provided instruction or instruction plan with methods
46
+ * that allow it to plan and send itself using the provided client. It enables a fluent API
47
+ * where you can call methods like `.sendTransaction()` directly on the instruction.
48
+ *
49
+ * The function supports both synchronous inputs (instructions, instruction plans) and
50
+ * promise-like inputs, making it suitable for use with async instruction builders.
51
+ *
52
+ * @typeParam TItem - The type of the instruction, instruction plan, or a promise resolving to one.
53
+ *
54
+ * @param client - A client that provides transaction planning and sending capabilities.
55
+ * @param input - The instruction, instruction plan, or promise to augment with self-plan/send methods.
56
+ * @returns The input augmented with {@link SelfPlanAndSendFunctions} methods.
57
+ *
58
+ * @example
59
+ * Adding self-plan and send to a transfer instruction.
60
+ * ```ts
61
+ * import { addSelfPlanAndSendFunctions } from '@solana/program-client-core';
62
+ *
63
+ * const transferInstruction = addSelfPlanAndSendFunctions(
64
+ * client,
65
+ * getTransferInstruction({ payer, source, destination, amount })
66
+ * );
67
+ *
68
+ * // Now you can send directly from the instruction.
69
+ * const result = await transferInstruction.sendTransaction();
70
+ * ```
71
+ *
72
+ * @example
73
+ * Using with an async instruction builder.
74
+ * ```ts
75
+ * const asyncInstruction = addSelfPlanAndSendFunctions(
76
+ * client,
77
+ * fetchAndBuildInstruction(/* ... *\/)
78
+ * );
79
+ *
80
+ * // The promise is augmented with self-plan/send methods.
81
+ * const result = await asyncInstruction.sendTransaction();
82
+ * ```
83
+ *
84
+ * @see {@link SelfPlanAndSendFunctions}
85
+ */
86
+ export function addSelfPlanAndSendFunctions<
87
+ TItem extends Instruction | InstructionPlan | PromiseLike<Instruction> | PromiseLike<InstructionPlan>,
88
+ >(
89
+ client: ClientWithTransactionPlanning & ClientWithTransactionSending,
90
+ input: TItem,
91
+ ): SelfPlanAndSendFunctions & TItem {
92
+ if (isPromiseLike(input)) {
93
+ const newInput = input as SelfPlanAndSendFunctions & TItem;
94
+ newInput.planTransaction = async config => await client.planTransaction(await input, config);
95
+ newInput.planTransactions = async config => await client.planTransactions(await input, config);
96
+ newInput.sendTransaction = async config => await client.sendTransaction(await input, config);
97
+ newInput.sendTransactions = async config => await client.sendTransactions(await input, config);
98
+ return newInput;
99
+ }
100
+
101
+ return Object.freeze(<SelfPlanAndSendFunctions & (Instruction | InstructionPlan)>{
102
+ ...input,
103
+ planTransaction: config => client.planTransaction(input, config),
104
+ planTransactions: config => client.planTransactions(input, config),
105
+ sendTransaction: config => client.sendTransaction(input, config),
106
+ sendTransactions: config => client.sendTransactions(input, config),
107
+ }) as unknown as SelfPlanAndSendFunctions & TItem;
108
+ }
109
+
110
+ function isPromiseLike(
111
+ item: Instruction | InstructionPlan | PromiseLike<Instruction> | PromiseLike<InstructionPlan>,
112
+ ): item is PromiseLike<Instruction> | PromiseLike<InstructionPlan> {
113
+ return (
114
+ !!item &&
115
+ (typeof item === 'object' || typeof item === 'function') &&
116
+ typeof (item as PromiseLike<unknown>).then === 'function'
117
+ );
118
+ }