@solana/program-client-core 8.3.0-canary-20260909094827 → 8.3.0

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.
@@ -1,6 +1,36 @@
1
- import { type Address, type ProgramDerivedAddress } from '@solana/addresses';
2
- import { type AccountMeta } from '@solana/instructions';
1
+ import { type Address, type HasAddress, type ProgramDerivedAddress } from '@solana/addresses';
2
+ import { type AccountMeta, type AccountNonSignerMeta, AccountRole } from '@solana/instructions';
3
3
  import { type AccountSignerMeta, type TransactionSigner } from '@solana/signers';
4
+ /**
5
+ * Represents the accepted input values for a non-signer instruction account.
6
+ *
7
+ * Namely, one of the following:
8
+ * - An {@link Address} — the most common case.
9
+ * - Any object exposing an `address` property (see {@link HasAddress}) — e.g. a framework's
10
+ * address wrapper class. Note that {@link TransactionSigner | TransactionSigners} satisfy this
11
+ * shape too, in which case they act as plain address carriers for non-signer accounts.
12
+ * - A {@link ProgramDerivedAddress} — i.e. an `[address, bump]` tuple.
13
+ * - An {@link AccountNonSignerMeta} — i.e. `{ address, role }` — to explicitly override the
14
+ * role derived from the program's IDL, e.g. to mark an account as writable or readonly.
15
+ *
16
+ * @typeParam TAddress - Supply a string literal to define an account having a particular address.
17
+ *
18
+ * @see {@link InstructionSignerInput}
19
+ */
20
+ export type InstructionAccountInput<TAddress extends string = string> = AccountNonSignerMeta<TAddress> | Address<TAddress> | HasAddress<TAddress> | ProgramDerivedAddress<TAddress>;
21
+ /**
22
+ * Represents the accepted input values for a signer instruction account.
23
+ *
24
+ * Namely, one of the following:
25
+ * - A {@link TransactionSigner} — the most common case.
26
+ * - An {@link AccountSignerMeta} — i.e. `{ address, role, signer }` — to explicitly override the
27
+ * role derived from the program's IDL, e.g. to mark a signer account as writable or readonly.
28
+ *
29
+ * @typeParam TAddress - Supply a string literal to define an account having a particular address.
30
+ *
31
+ * @see {@link InstructionAccountInput}
32
+ */
33
+ export type InstructionSignerInput<TAddress extends string = string> = AccountSignerMeta<TAddress> | TransactionSigner<TAddress>;
4
34
  /**
5
35
  * Ensures a resolved instruction input is not null or undefined.
6
36
  *
@@ -29,8 +59,9 @@ export declare function getNonNullResolvedInstructionInput<T>(inputName: string,
29
59
  * Extracts the address from a resolved instruction account.
30
60
  *
31
61
  * A resolved instruction account can be an {@link Address}, a {@link ProgramDerivedAddress},
32
- * or a {@link TransactionSigner}. This function extracts the underlying address from
33
- * any of these types.
62
+ * or any object exposing an `address` property — such as a {@link TransactionSigner}, an
63
+ * account meta, or a framework's address wrapper class (see {@link HasAddress}). This
64
+ * function extracts the underlying address from any of these types.
34
65
  *
35
66
  * @typeParam T - The address type, defaults to `string`.
36
67
  *
@@ -70,7 +101,8 @@ export declare function getResolvedInstructionAccountAsProgramDerivedAddress<T e
70
101
  /**
71
102
  * Extracts a {@link TransactionSigner} from a resolved instruction account.
72
103
  *
73
- * This function validates that the resolved account is a transaction signer and returns it.
104
+ * This function validates that the resolved account is a transaction signer or an
105
+ * {@link AccountSignerMeta} carrying one — and returns the signer.
74
106
  * Use this when you need the resolved account to be a signer.
75
107
  *
76
108
  * @typeParam T - The address type, defaults to `string`.
@@ -90,10 +122,16 @@ export declare function getResolvedInstructionAccountAsTransactionSigner<T exten
90
122
  /**
91
123
  * Represents a resolved account input for an instruction.
92
124
  *
93
- * During instruction building, account inputs are resolved to this type which
94
- * captures both the account value and whether it should be marked as writable.
95
- * The value can be an {@link Address}, a {@link ProgramDerivedAddress}, a
96
- * {@link TransactionSigner}, or `null` for optional accounts.
125
+ * During instruction building, account inputs are resolved to this type which captures
126
+ * the account value alongside the signer and writable flags declared by the program's IDL.
127
+ * The value can be any {@link InstructionAccountInput}, any {@link InstructionSignerInput},
128
+ * or `null` for optional accounts.
129
+ *
130
+ * The optional `isSigner` flag describes whether the IDL requires the account to sign the
131
+ * transaction — with `'either'` meaning the account may or may not be a signer, in which
132
+ * case providing a {@link TransactionSigner} value is what marks it as one. Omitting the
133
+ * flag — e.g. in program clients generated before its introduction — is equivalent to
134
+ * setting it to `'either'`.
97
135
  *
98
136
  * @typeParam TAddress - The address type, defaults to `string`.
99
137
  * @typeParam TValue - The type of the resolved value.
@@ -102,14 +140,123 @@ export declare function getResolvedInstructionAccountAsTransactionSigner<T exten
102
140
  * ```ts
103
141
  * const mintAccount: ResolvedInstructionAccount = {
104
142
  * value: mintAddress,
143
+ * isSigner: false,
105
144
  * isWritable: true,
106
145
  * };
107
146
  * ```
108
147
  */
109
- export type ResolvedInstructionAccount<TAddress extends string = string, TValue extends Address<TAddress> | ProgramDerivedAddress<TAddress> | TransactionSigner<TAddress> | null = Address<TAddress> | ProgramDerivedAddress<TAddress> | TransactionSigner<TAddress> | null> = {
148
+ export type ResolvedInstructionAccount<TAddress extends string = string, TValue extends InstructionAccountInput<TAddress> | InstructionSignerInput<TAddress> | null = InstructionAccountInput<TAddress> | InstructionSignerInput<TAddress> | null> = {
149
+ isSigner?: boolean | 'either';
110
150
  isWritable: boolean;
111
151
  value: TValue;
112
152
  };
153
+ /**
154
+ * Extracts the address type parameter from an instruction account input.
155
+ *
156
+ * Given any {@link InstructionAccountInput} or {@link InstructionSignerInput} — e.g. an
157
+ * {@link Address}, an address-bearing object (see {@link HasAddress}), a
158
+ * {@link ProgramDerivedAddress} or an account meta — this type helper resolves to the
159
+ * branded address string it carries. This allows generated program clients to recover the
160
+ * address type parameter of an account from the caller's input type alone — e.g. via
161
+ * `InstructionAccountInputAddress<TInput['authority']>` — instead of declaring a dedicated
162
+ * address type parameter on the instruction builder.
163
+ *
164
+ * When given a union of inputs, the helper distributes over it, so a union whose members
165
+ * all share the same address brand resolves to that brand. Inputs carrying no brand
166
+ * resolve to `string`.
167
+ *
168
+ * @typeParam TInput - The type of the input provided by the caller for this account.
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * type A = InstructionAccountInputAddress<Address<'1234'>>; // '1234'
173
+ * type B = InstructionAccountInputAddress<TransactionSigner<'1234'>>; // '1234'
174
+ * type C = InstructionAccountInputAddress<ProgramDerivedAddress<'1234'>>; // '1234'
175
+ * type D = InstructionAccountInputAddress<Address>; // string
176
+ * ```
177
+ *
178
+ * @see {@link ResolvedInstructionAccountMeta}
179
+ */
180
+ export type InstructionAccountInputAddress<TInput> = TInput extends HasAddress<infer TAddress> ? TAddress : TInput extends ProgramDerivedAddress<infer TAddress> ? TAddress : TInput extends Address<infer TAddress> ? TAddress : string;
181
+ /**
182
+ * Computes the account meta type produced by an instruction account, based on the input
183
+ * provided by the caller.
184
+ *
185
+ * This type helper mirrors the runtime logic of {@link getAccountMetaFactory} so that
186
+ * generated program clients can accurately type the accounts of the instructions they
187
+ * return. Namely:
188
+ * - When the input carries an explicit `role` — i.e. it is an {@link AccountNonSignerMeta} or an
189
+ * {@link AccountSignerMeta} — the meta type preserves the input's role type: an inline
190
+ * `role: AccountRole.READONLY` override resolves to `ReadonlyAccount`, while a role only
191
+ * known at runtime widens to {@link AccountMeta}. If the input also carries a `signer`,
192
+ * {@link AccountSignerMeta} is used so the attached signer is reflected in the type.
193
+ * - When the input is a {@link TransactionSigner}, the meta type is `TSignerMeta` — e.g.
194
+ * `ReadonlySignerAccount<TAddress> & AccountSignerMeta<TAddress>` for accounts the IDL
195
+ * declares as signers. For non-signer accounts, `TSignerMeta` should be left to its
196
+ * default of `TAddress` so that signers merely act as address carriers.
197
+ * - Otherwise, the helper resolves to `TAddress` — the branded address string that generated
198
+ * instruction types map to the account meta declared by the program's IDL.
199
+ *
200
+ * Note that the checks are wrapped in tuples (`[TInput] extends [...]`) to prevent unions
201
+ * from distributing. If `TInput` is not narrowed to the caller's specific input type — e.g.
202
+ * when a declared input union is provided instead — the helper deterministically falls back
203
+ * to `TAddress`, matching the account meta declared by the program's IDL.
204
+ *
205
+ * @typeParam TInput - The type of the input provided by the caller for this account.
206
+ * @typeParam TAddress - The address type parameter of the account.
207
+ * @typeParam TSignerMeta - The meta type produced when a {@link TransactionSigner} is
208
+ * provided. Defaults to `TAddress`, which treats signers as plain address carriers.
209
+ *
210
+ * @example
211
+ * The instruction builder below captures the caller's input in a single `TInput` type
212
+ * parameter and recovers each account's address type parameter from it using
213
+ * {@link InstructionAccountInputAddress}.
214
+ * ```ts
215
+ * declare function getTransferInstruction<TInput extends TransferInput>(
216
+ * input: TInput,
217
+ * ): TransferInstruction<
218
+ * ResolvedInstructionAccountMeta<
219
+ * TInput['authority'],
220
+ * InstructionAccountInputAddress<TInput['authority']>,
221
+ * ReadonlySignerAccount<InstructionAccountInputAddress<TInput['authority']>> &
222
+ * AccountSignerMeta<InstructionAccountInputAddress<TInput['authority']>>
223
+ * >
224
+ * >;
225
+ * ```
226
+ *
227
+ * Alternatively, instruction builders may keep a dedicated address type parameter per
228
+ * account. In that case, the parameter below must intersect the concrete input type with
229
+ * the inferred `TInput` type parameter (`TransferInput<TAccountAuthority> & TInput`) —
230
+ * referencing the address type parameters only in `TInput`'s constraint makes their
231
+ * inference fall back to `string`. Defaulting `TInput` to the concrete input type keeps
232
+ * call sites with explicit type arguments working.
233
+ * ```ts
234
+ * declare function getTransferInstruction<
235
+ * TAccountAuthority extends string,
236
+ * TInput extends TransferInput<TAccountAuthority> = TransferInput<TAccountAuthority>,
237
+ * >(
238
+ * input: TransferInput<TAccountAuthority> & TInput,
239
+ * ): TransferInstruction<
240
+ * ResolvedInstructionAccountMeta<
241
+ * TInput['authority'],
242
+ * TAccountAuthority,
243
+ * ReadonlySignerAccount<TAccountAuthority> & AccountSignerMeta<TAccountAuthority>
244
+ * >
245
+ * >;
246
+ * ```
247
+ *
248
+ * @see {@link getAccountMetaFactory}
249
+ * @see {@link InstructionAccountInputAddress}
250
+ */
251
+ export type ResolvedInstructionAccountMeta<TInput, TAddress extends string, TSignerMeta = TAddress> = [TInput] extends [
252
+ {
253
+ role: infer TRole extends AccountRole;
254
+ }
255
+ ] ? ([TInput] extends [{
256
+ signer: TransactionSigner<TAddress>;
257
+ }] ? AccountSignerMeta<TAddress> : AccountMeta<TAddress>) & {
258
+ readonly role: TRole;
259
+ } : [TInput] extends [TransactionSigner<TAddress>] ? TSignerMeta : TAddress;
113
260
  /**
114
261
  * Creates a factory function that converts resolved instruction accounts to account metas.
115
262
  *
@@ -117,12 +264,26 @@ export type ResolvedInstructionAccount<TAddress extends string = string, TValue
117
264
  * {@link AccountMeta} or {@link AccountSignerMeta} objects suitable for building instructions.
118
265
  * It also determines how to handle optional accounts based on the provided strategy.
119
266
  *
267
+ * The role of the resulting account meta is determined as follows, in order of precedence:
268
+ * 1. If the value carries an explicit `role` — i.e. it is an {@link AccountNonSignerMeta} or
269
+ * an {@link AccountSignerMeta} — that role is used as-is, regardless of the flags declared
270
+ * by the program's IDL.
271
+ * 2. Otherwise, if the value is a {@link TransactionSigner} and the account's `isSigner` flag
272
+ * is not `false`, the IDL's writable flag is upgraded to the corresponding signer role and
273
+ * the signer is attached to the meta. When `isSigner` is `false`, the signer merely acts
274
+ * as an address carrier and no upgrade occurs. Omitting the flag is equivalent to `'either'`.
275
+ * 3. Otherwise, the IDL's writable flag decides between the readonly and writable roles.
276
+ *
120
277
  * @param programAddress - The program address, used when optional accounts use the `programId` strategy.
121
278
  * @param optionalAccountStrategy - How to handle null account values:
122
279
  * - `'omitted'`: Optional accounts are excluded from the instruction entirely.
123
280
  * - `'programId'`: Optional accounts are replaced with the program address as a read-only account.
124
281
  * @returns A factory function that converts a resolved account to an account meta.
125
282
  *
283
+ * @throws Throws a {@link SolanaError} when the account's `isSigner` flag is `true` but the
284
+ * provided value is neither a {@link TransactionSigner} nor carries an explicit `role`. Use
285
+ * `createNoopSigner()` from `@solana/signers` if the account's signature is provided by other means.
286
+ *
126
287
  * @example
127
288
  * ```ts
128
289
  * const toAccountMeta = getAccountMetaFactory(programAddress, 'programId');
@@ -1 +1 @@
1
- {"version":3,"file":"instruction-input-resolution.d.ts","sourceRoot":"","sources":["../../src/instruction-input-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAMtG,OAAO,EAAE,KAAK,WAAW,EAAoC,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,KAAK,iBAAiB,EAAuB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEtG;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kCAAkC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,CAAC,CAOvG;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wCAAwC,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC9E,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,0BAA0B,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,SAAS,GAC1D,OAAO,CAAC,CAAC,CAAC,CASZ;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oDAAoD,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAC1F,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,0BAA0B,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,SAAS,GAC1D,qBAAqB,CAAC,CAAC,CAAC,CAQ1B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gDAAgD,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EACtF,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,0BAA0B,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,SAAS,GAC1D,iBAAiB,CAAC,CAAC,CAAC,CAQtB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,0BAA0B,CAClC,QAAQ,SAAS,MAAM,GAAG,MAAM,EAChC,MAAM,SAAS,OAAO,CAAC,QAAQ,CAAC,GAAG,qBAAqB,CAAC,QAAQ,CAAC,GAAG,iBAAiB,CAAC,QAAQ,CAAC,GAAG,IAAI,GACjG,OAAO,CAAC,QAAQ,CAAC,GACjB,qBAAqB,CAAC,QAAQ,CAAC,GAC/B,iBAAiB,CAAC,QAAQ,CAAC,GAC3B,IAAI,IACV;IACA,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,OAAO,EAAE,uBAAuB,EAAE,SAAS,GAAG,WAAW,IACnG,WAAW,MAAM,EAAE,SAAS,0BAA0B,KAAG,WAAW,GAAG,iBAAiB,GAAG,SAAS,CAc/G"}
1
+ {"version":3,"file":"instruction-input-resolution.d.ts","sourceRoot":"","sources":["../../src/instruction-input-resolution.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solana/program-client-core",
3
- "version": "8.3.0-canary-20260909094827",
3
+ "version": "8.3.0",
4
4
  "description": "Core utilities for building Solana program clients",
5
5
  "homepage": "https://www.solanakit.com/api#solanaprogram-client-core",
6
6
  "exports": {
@@ -56,15 +56,15 @@
56
56
  "maintained node versions"
57
57
  ],
58
58
  "dependencies": {
59
- "@solana/accounts": "8.3.0-canary-20260909094827",
60
- "@solana/addresses": "8.3.0-canary-20260909094827",
61
- "@solana/codecs-core": "8.3.0-canary-20260909094827",
62
- "@solana/errors": "8.3.0-canary-20260909094827",
63
- "@solana/instruction-plans": "8.3.0-canary-20260909094827",
64
- "@solana/instructions": "8.3.0-canary-20260909094827",
65
- "@solana/plugin-interfaces": "8.3.0-canary-20260909094827",
66
- "@solana/rpc-api": "8.3.0-canary-20260909094827",
67
- "@solana/signers": "8.3.0-canary-20260909094827"
59
+ "@solana/addresses": "8.3.0",
60
+ "@solana/accounts": "8.3.0",
61
+ "@solana/codecs-core": "8.3.0",
62
+ "@solana/instruction-plans": "8.3.0",
63
+ "@solana/errors": "8.3.0",
64
+ "@solana/instructions": "8.3.0",
65
+ "@solana/plugin-interfaces": "8.3.0",
66
+ "@solana/rpc-api": "8.3.0",
67
+ "@solana/signers": "8.3.0"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "typescript": ">=5.4.0"
@@ -1,12 +1,51 @@
1
- import { type Address, isProgramDerivedAddress, type ProgramDerivedAddress } from '@solana/addresses';
1
+ import { type Address, type HasAddress, isProgramDerivedAddress, type ProgramDerivedAddress } from '@solana/addresses';
2
2
  import {
3
3
  SOLANA_ERROR__PROGRAM_CLIENTS__RESOLVED_INSTRUCTION_INPUT_MUST_BE_NON_NULL,
4
+ SOLANA_ERROR__PROGRAM_CLIENTS__RESOLVED_INSTRUCTION_INPUT_MUST_BE_SIGNER,
4
5
  SOLANA_ERROR__PROGRAM_CLIENTS__UNEXPECTED_RESOLVED_INSTRUCTION_INPUT_TYPE,
5
6
  SolanaError,
6
7
  } from '@solana/errors';
7
- import { type AccountMeta, AccountRole, upgradeRoleToSigner } from '@solana/instructions';
8
+ import { type AccountMeta, type AccountNonSignerMeta, AccountRole, upgradeRoleToSigner } from '@solana/instructions';
8
9
  import { type AccountSignerMeta, isTransactionSigner, type TransactionSigner } from '@solana/signers';
9
10
 
11
+ /**
12
+ * Represents the accepted input values for a non-signer instruction account.
13
+ *
14
+ * Namely, one of the following:
15
+ * - An {@link Address} — the most common case.
16
+ * - Any object exposing an `address` property (see {@link HasAddress}) — e.g. a framework's
17
+ * address wrapper class. Note that {@link TransactionSigner | TransactionSigners} satisfy this
18
+ * shape too, in which case they act as plain address carriers for non-signer accounts.
19
+ * - A {@link ProgramDerivedAddress} — i.e. an `[address, bump]` tuple.
20
+ * - An {@link AccountNonSignerMeta} — i.e. `{ address, role }` — to explicitly override the
21
+ * role derived from the program's IDL, e.g. to mark an account as writable or readonly.
22
+ *
23
+ * @typeParam TAddress - Supply a string literal to define an account having a particular address.
24
+ *
25
+ * @see {@link InstructionSignerInput}
26
+ */
27
+ export type InstructionAccountInput<TAddress extends string = string> =
28
+ | AccountNonSignerMeta<TAddress>
29
+ | Address<TAddress>
30
+ | HasAddress<TAddress>
31
+ | ProgramDerivedAddress<TAddress>;
32
+
33
+ /**
34
+ * Represents the accepted input values for a signer instruction account.
35
+ *
36
+ * Namely, one of the following:
37
+ * - A {@link TransactionSigner} — the most common case.
38
+ * - An {@link AccountSignerMeta} — i.e. `{ address, role, signer }` — to explicitly override the
39
+ * role derived from the program's IDL, e.g. to mark a signer account as writable or readonly.
40
+ *
41
+ * @typeParam TAddress - Supply a string literal to define an account having a particular address.
42
+ *
43
+ * @see {@link InstructionAccountInput}
44
+ */
45
+ export type InstructionSignerInput<TAddress extends string = string> =
46
+ | AccountSignerMeta<TAddress>
47
+ | TransactionSigner<TAddress>;
48
+
10
49
  /**
11
50
  * Ensures a resolved instruction input is not null or undefined.
12
51
  *
@@ -43,8 +82,9 @@ export function getNonNullResolvedInstructionInput<T>(inputName: string, value:
43
82
  * Extracts the address from a resolved instruction account.
44
83
  *
45
84
  * 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.
85
+ * or any object exposing an `address` property — such as a {@link TransactionSigner}, an
86
+ * account meta, or a framework's address wrapper class (see {@link HasAddress}). This
87
+ * function extracts the underlying address from any of these types.
48
88
  *
49
89
  * @typeParam T - The address type, defaults to `string`.
50
90
  *
@@ -109,7 +149,8 @@ export function getResolvedInstructionAccountAsProgramDerivedAddress<T extends s
109
149
  /**
110
150
  * Extracts a {@link TransactionSigner} from a resolved instruction account.
111
151
  *
112
- * This function validates that the resolved account is a transaction signer and returns it.
152
+ * This function validates that the resolved account is a transaction signer or an
153
+ * {@link AccountSignerMeta} carrying one — and returns the signer.
113
154
  * Use this when you need the resolved account to be a signer.
114
155
  *
115
156
  * @typeParam T - The address type, defaults to `string`.
@@ -129,22 +170,29 @@ export function getResolvedInstructionAccountAsTransactionSigner<T extends strin
129
170
  inputName: string,
130
171
  value: ResolvedInstructionAccount<T>['value'] | undefined,
131
172
  ): TransactionSigner<T> {
132
- if (!isResolvedInstructionAccountSigner(value)) {
173
+ const signer = value && hasExplicitRole(value) && 'signer' in value ? value.signer : value;
174
+ if (!isResolvedInstructionAccountSigner<T>(signer)) {
133
175
  throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__UNEXPECTED_RESOLVED_INSTRUCTION_INPUT_TYPE, {
134
176
  expectedType: 'TransactionSigner',
135
177
  inputName,
136
178
  });
137
179
  }
138
- return value;
180
+ return signer;
139
181
  }
140
182
 
141
183
  /**
142
184
  * Represents a resolved account input for an instruction.
143
185
  *
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.
186
+ * During instruction building, account inputs are resolved to this type which captures
187
+ * the account value alongside the signer and writable flags declared by the program's IDL.
188
+ * The value can be any {@link InstructionAccountInput}, any {@link InstructionSignerInput},
189
+ * or `null` for optional accounts.
190
+ *
191
+ * The optional `isSigner` flag describes whether the IDL requires the account to sign the
192
+ * transaction — with `'either'` meaning the account may or may not be a signer, in which
193
+ * case providing a {@link TransactionSigner} value is what marks it as one. Omitting the
194
+ * flag — e.g. in program clients generated before its introduction — is equivalent to
195
+ * setting it to `'either'`.
148
196
  *
149
197
  * @typeParam TAddress - The address type, defaults to `string`.
150
198
  * @typeParam TValue - The type of the resolved value.
@@ -153,22 +201,139 @@ export function getResolvedInstructionAccountAsTransactionSigner<T extends strin
153
201
  * ```ts
154
202
  * const mintAccount: ResolvedInstructionAccount = {
155
203
  * value: mintAddress,
204
+ * isSigner: false,
156
205
  * isWritable: true,
157
206
  * };
158
207
  * ```
159
208
  */
160
209
  export type ResolvedInstructionAccount<
161
210
  TAddress extends string = string,
162
- TValue extends Address<TAddress> | ProgramDerivedAddress<TAddress> | TransactionSigner<TAddress> | null =
163
- | Address<TAddress>
164
- | ProgramDerivedAddress<TAddress>
165
- | TransactionSigner<TAddress>
211
+ TValue extends InstructionAccountInput<TAddress> | InstructionSignerInput<TAddress> | null =
212
+ | InstructionAccountInput<TAddress>
213
+ | InstructionSignerInput<TAddress>
166
214
  | null,
167
215
  > = {
216
+ isSigner?: boolean | 'either';
168
217
  isWritable: boolean;
169
218
  value: TValue;
170
219
  };
171
220
 
221
+ /**
222
+ * Extracts the address type parameter from an instruction account input.
223
+ *
224
+ * Given any {@link InstructionAccountInput} or {@link InstructionSignerInput} — e.g. an
225
+ * {@link Address}, an address-bearing object (see {@link HasAddress}), a
226
+ * {@link ProgramDerivedAddress} or an account meta — this type helper resolves to the
227
+ * branded address string it carries. This allows generated program clients to recover the
228
+ * address type parameter of an account from the caller's input type alone — e.g. via
229
+ * `InstructionAccountInputAddress<TInput['authority']>` — instead of declaring a dedicated
230
+ * address type parameter on the instruction builder.
231
+ *
232
+ * When given a union of inputs, the helper distributes over it, so a union whose members
233
+ * all share the same address brand resolves to that brand. Inputs carrying no brand
234
+ * resolve to `string`.
235
+ *
236
+ * @typeParam TInput - The type of the input provided by the caller for this account.
237
+ *
238
+ * @example
239
+ * ```ts
240
+ * type A = InstructionAccountInputAddress<Address<'1234'>>; // '1234'
241
+ * type B = InstructionAccountInputAddress<TransactionSigner<'1234'>>; // '1234'
242
+ * type C = InstructionAccountInputAddress<ProgramDerivedAddress<'1234'>>; // '1234'
243
+ * type D = InstructionAccountInputAddress<Address>; // string
244
+ * ```
245
+ *
246
+ * @see {@link ResolvedInstructionAccountMeta}
247
+ */
248
+ export type InstructionAccountInputAddress<TInput> =
249
+ TInput extends HasAddress<infer TAddress>
250
+ ? TAddress
251
+ : TInput extends ProgramDerivedAddress<infer TAddress>
252
+ ? TAddress
253
+ : TInput extends Address<infer TAddress>
254
+ ? TAddress
255
+ : string;
256
+
257
+ /**
258
+ * Computes the account meta type produced by an instruction account, based on the input
259
+ * provided by the caller.
260
+ *
261
+ * This type helper mirrors the runtime logic of {@link getAccountMetaFactory} so that
262
+ * generated program clients can accurately type the accounts of the instructions they
263
+ * return. Namely:
264
+ * - When the input carries an explicit `role` — i.e. it is an {@link AccountNonSignerMeta} or an
265
+ * {@link AccountSignerMeta} — the meta type preserves the input's role type: an inline
266
+ * `role: AccountRole.READONLY` override resolves to `ReadonlyAccount`, while a role only
267
+ * known at runtime widens to {@link AccountMeta}. If the input also carries a `signer`,
268
+ * {@link AccountSignerMeta} is used so the attached signer is reflected in the type.
269
+ * - When the input is a {@link TransactionSigner}, the meta type is `TSignerMeta` — e.g.
270
+ * `ReadonlySignerAccount<TAddress> & AccountSignerMeta<TAddress>` for accounts the IDL
271
+ * declares as signers. For non-signer accounts, `TSignerMeta` should be left to its
272
+ * default of `TAddress` so that signers merely act as address carriers.
273
+ * - Otherwise, the helper resolves to `TAddress` — the branded address string that generated
274
+ * instruction types map to the account meta declared by the program's IDL.
275
+ *
276
+ * Note that the checks are wrapped in tuples (`[TInput] extends [...]`) to prevent unions
277
+ * from distributing. If `TInput` is not narrowed to the caller's specific input type — e.g.
278
+ * when a declared input union is provided instead — the helper deterministically falls back
279
+ * to `TAddress`, matching the account meta declared by the program's IDL.
280
+ *
281
+ * @typeParam TInput - The type of the input provided by the caller for this account.
282
+ * @typeParam TAddress - The address type parameter of the account.
283
+ * @typeParam TSignerMeta - The meta type produced when a {@link TransactionSigner} is
284
+ * provided. Defaults to `TAddress`, which treats signers as plain address carriers.
285
+ *
286
+ * @example
287
+ * The instruction builder below captures the caller's input in a single `TInput` type
288
+ * parameter and recovers each account's address type parameter from it using
289
+ * {@link InstructionAccountInputAddress}.
290
+ * ```ts
291
+ * declare function getTransferInstruction<TInput extends TransferInput>(
292
+ * input: TInput,
293
+ * ): TransferInstruction<
294
+ * ResolvedInstructionAccountMeta<
295
+ * TInput['authority'],
296
+ * InstructionAccountInputAddress<TInput['authority']>,
297
+ * ReadonlySignerAccount<InstructionAccountInputAddress<TInput['authority']>> &
298
+ * AccountSignerMeta<InstructionAccountInputAddress<TInput['authority']>>
299
+ * >
300
+ * >;
301
+ * ```
302
+ *
303
+ * Alternatively, instruction builders may keep a dedicated address type parameter per
304
+ * account. In that case, the parameter below must intersect the concrete input type with
305
+ * the inferred `TInput` type parameter (`TransferInput<TAccountAuthority> & TInput`) —
306
+ * referencing the address type parameters only in `TInput`'s constraint makes their
307
+ * inference fall back to `string`. Defaulting `TInput` to the concrete input type keeps
308
+ * call sites with explicit type arguments working.
309
+ * ```ts
310
+ * declare function getTransferInstruction<
311
+ * TAccountAuthority extends string,
312
+ * TInput extends TransferInput<TAccountAuthority> = TransferInput<TAccountAuthority>,
313
+ * >(
314
+ * input: TransferInput<TAccountAuthority> & TInput,
315
+ * ): TransferInstruction<
316
+ * ResolvedInstructionAccountMeta<
317
+ * TInput['authority'],
318
+ * TAccountAuthority,
319
+ * ReadonlySignerAccount<TAccountAuthority> & AccountSignerMeta<TAccountAuthority>
320
+ * >
321
+ * >;
322
+ * ```
323
+ *
324
+ * @see {@link getAccountMetaFactory}
325
+ * @see {@link InstructionAccountInputAddress}
326
+ */
327
+ export type ResolvedInstructionAccountMeta<TInput, TAddress extends string, TSignerMeta = TAddress> = [TInput] extends [
328
+ { role: infer TRole extends AccountRole },
329
+ ]
330
+ ? ([TInput] extends [{ signer: TransactionSigner<TAddress> }]
331
+ ? AccountSignerMeta<TAddress>
332
+ : AccountMeta<TAddress>) & { readonly role: TRole }
333
+ : [TInput] extends [TransactionSigner<TAddress>]
334
+ ? TSignerMeta
335
+ : TAddress;
336
+
172
337
  /**
173
338
  * Creates a factory function that converts resolved instruction accounts to account metas.
174
339
  *
@@ -176,12 +341,26 @@ export type ResolvedInstructionAccount<
176
341
  * {@link AccountMeta} or {@link AccountSignerMeta} objects suitable for building instructions.
177
342
  * It also determines how to handle optional accounts based on the provided strategy.
178
343
  *
344
+ * The role of the resulting account meta is determined as follows, in order of precedence:
345
+ * 1. If the value carries an explicit `role` — i.e. it is an {@link AccountNonSignerMeta} or
346
+ * an {@link AccountSignerMeta} — that role is used as-is, regardless of the flags declared
347
+ * by the program's IDL.
348
+ * 2. Otherwise, if the value is a {@link TransactionSigner} and the account's `isSigner` flag
349
+ * is not `false`, the IDL's writable flag is upgraded to the corresponding signer role and
350
+ * the signer is attached to the meta. When `isSigner` is `false`, the signer merely acts
351
+ * as an address carrier and no upgrade occurs. Omitting the flag is equivalent to `'either'`.
352
+ * 3. Otherwise, the IDL's writable flag decides between the readonly and writable roles.
353
+ *
179
354
  * @param programAddress - The program address, used when optional accounts use the `programId` strategy.
180
355
  * @param optionalAccountStrategy - How to handle null account values:
181
356
  * - `'omitted'`: Optional accounts are excluded from the instruction entirely.
182
357
  * - `'programId'`: Optional accounts are replaced with the program address as a read-only account.
183
358
  * @returns A factory function that converts a resolved account to an account meta.
184
359
  *
360
+ * @throws Throws a {@link SolanaError} when the account's `isSigner` flag is `true` but the
361
+ * provided value is neither a {@link TransactionSigner} nor carries an explicit `role`. Use
362
+ * `createNoopSigner()` from `@solana/signers` if the account's signature is provided by other means.
363
+ *
185
364
  * @example
186
365
  * ```ts
187
366
  * const toAccountMeta = getAccountMetaFactory(programAddress, 'programId');
@@ -195,8 +374,26 @@ export function getAccountMetaFactory(programAddress: Address, optionalAccountSt
195
374
  return Object.freeze({ address: programAddress, role: AccountRole.READONLY });
196
375
  }
197
376
 
377
+ // Explicit roles always take precedence over the flags declared by the program's IDL.
378
+ if (hasExplicitRole(account.value)) {
379
+ return Object.freeze({
380
+ address: account.value.address,
381
+ role: account.value.role,
382
+ ...('signer' in account.value && account.value.signer ? { signer: account.value.signer } : {}),
383
+ });
384
+ }
385
+
386
+ // Only mark implicit values as signers when the IDL declares
387
+ // the account as a signer or lets the input decide (`'either'`).
388
+ const idlIsSigner = account.isSigner ?? 'either';
389
+ const isSigner = idlIsSigner !== false && isResolvedInstructionAccountSigner(account.value);
390
+ if (!isSigner && idlIsSigner === true) {
391
+ throw new SolanaError(SOLANA_ERROR__PROGRAM_CLIENTS__RESOLVED_INSTRUCTION_INPUT_MUST_BE_SIGNER, {
392
+ inputName,
393
+ });
394
+ }
395
+
198
396
  const writableRole = account.isWritable ? AccountRole.WRITABLE : AccountRole.READONLY;
199
- const isSigner = isResolvedInstructionAccountSigner(account.value);
200
397
  return Object.freeze({
201
398
  address: getAddressFromResolvedInstructionAccount(inputName, account.value),
202
399
  role: isSigner ? upgradeRoleToSigner(writableRole) : writableRole,
@@ -205,7 +402,23 @@ export function getAccountMetaFactory(programAddress: Address, optionalAccountSt
205
402
  };
206
403
  }
207
404
 
208
- function isResolvedInstructionAccountSigner(value: unknown): value is TransactionSigner {
405
+ /**
406
+ * Checks whether a resolved instruction account value carries an explicit account role,
407
+ * i.e. whether it is an {@link AccountNonSignerMeta} or an {@link AccountSignerMeta}.
408
+ *
409
+ * Since {@link AccountRole} is a numeric enum, requiring `role` to be a number prevents
410
+ * unrelated `role` properties on address-bearing objects from being mistaken for a role
411
+ * override.
412
+ */
413
+ function hasExplicitRole(
414
+ value: NonNullable<ResolvedInstructionAccount['value']>,
415
+ ): value is AccountNonSignerMeta | AccountSignerMeta {
416
+ return typeof value === 'object' && 'role' in value && typeof value.role === 'number';
417
+ }
418
+
419
+ function isResolvedInstructionAccountSigner<TAddress extends string = string>(
420
+ value: unknown,
421
+ ): value is TransactionSigner<TAddress> {
209
422
  return (
210
423
  !!value &&
211
424
  typeof value === 'object' &&