@vue-solana/core 3.0.1 → 4.0.0-rc.1

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.
Files changed (50) hide show
  1. package/README.md +39 -2
  2. package/dist/action.d.ts +10 -3
  3. package/dist/action.mjs +4 -4
  4. package/dist/address.d.ts +7 -2
  5. package/dist/address.mjs +8 -2
  6. package/dist/clusters.d.ts +1 -1
  7. package/dist/clusters.mjs +10 -1
  8. package/dist/errors.d.ts +1 -1
  9. package/dist/errors.mjs +4 -1
  10. package/dist/index.d.ts +4 -11
  11. package/dist/index.mjs +2 -8
  12. package/dist/ios-wallet.d.ts +1 -2
  13. package/dist/ios-wallet.mjs +675 -1
  14. package/dist/kit.d.ts +1 -1
  15. package/dist/kit.mjs +22 -9
  16. package/dist/mobile-wallet.d.ts +12 -2
  17. package/dist/mobile-wallet.mjs +4 -1
  18. package/dist/rpc.d.ts +1 -1
  19. package/dist/shared/{core.DaUlAmcN.mjs → core.A4I__4IR.mjs} +86 -6
  20. package/dist/shared/{core.C9vxHRXl.d.mts → core.BhF1J_tt.d.ts} +40 -2
  21. package/dist/timeout.d.ts +22 -1
  22. package/dist/timeout.mjs +53 -17
  23. package/dist/token-accounts.d.ts +1 -1
  24. package/dist/token-accounts.mjs +31 -2
  25. package/dist/transaction.d.ts +9 -1
  26. package/dist/transaction.mjs +52 -10
  27. package/dist/types.d.ts +1 -1
  28. package/dist/wallet-standard.d.ts +24 -4
  29. package/dist/wallet-standard.mjs +1 -1
  30. package/dist/wallet.d.ts +1 -1
  31. package/package.json +13 -5
  32. package/dist/action.d.mts +0 -28
  33. package/dist/address.d.mts +0 -16
  34. package/dist/buffer-polyfill.d.mts +0 -7
  35. package/dist/clusters.d.mts +0 -10
  36. package/dist/errors.d.mts +0 -17
  37. package/dist/index.d.mts +0 -21
  38. package/dist/ios-wallet.d.mts +0 -62
  39. package/dist/kit.d.mts +0 -3
  40. package/dist/mobile-wallet-support.d.mts +0 -3
  41. package/dist/mobile-wallet.d.mts +0 -20
  42. package/dist/rpc.d.mts +0 -7
  43. package/dist/shared/core.C9vxHRXl.d.ts +0 -193
  44. package/dist/shared/core.DJsPYgx_.mjs +0 -625
  45. package/dist/timeout.d.mts +0 -4
  46. package/dist/token-accounts.d.mts +0 -36
  47. package/dist/transaction.d.mts +0 -18
  48. package/dist/types.d.mts +0 -3
  49. package/dist/wallet-standard.d.mts +0 -22
  50. package/dist/wallet.d.mts +0 -25
package/dist/kit.mjs CHANGED
@@ -1,25 +1,38 @@
1
- import { getBase64Encoder, getBase58Decoder, signBytes, createClient, extendClient, createKeyPairFromBytes, isSolanaError, SOLANA_ERROR__KEYS__PUBLIC_KEY_MUST_MATCH_PRIVATE_KEY } from '@solana/kit';
1
+ import { getBase64Encoder, getBase58Decoder, signBytes, createClient, extendClient, createRpc, createDefaultRpcTransport, createSolanaRpcApi, DEFAULT_RPC_CONFIG, createSubscriptionRpc, createDefaultRpcSubscriptionsTransport, createDefaultSolanaRpcSubscriptionsChannelCreator, createSolanaRpcSubscriptionsApi, DEFAULT_RPC_SUBSCRIPTIONS_CONFIG, createKeyPairFromBytes, isSolanaError, SOLANA_ERROR__KEYS__PUBLIC_KEY_MUST_MATCH_PRIVATE_KEY } from '@solana/kit';
2
2
  export * from '@solana/kit';
3
- import { solanaRpc, rpcAirdrop } from '@solana/kit-plugin-rpc';
3
+ import { solanaRpc, rpcAirdrop, rpcGetMinimumBalance, rpcTransactionPlanner, rpcTransactionPlanSigningExecutor, rpcTransactionPlanSendingExecutor } from '@solana/kit-plugin-rpc';
4
4
  import { DEFAULT_CLUSTER, getClusterEndpoint, getWebSocketEndpoint, getClusterWebSocketEndpoint } from './clusters.mjs';
5
5
 
6
- function buildSolanaClient(endpoint, wsEndpoint, payer) {
6
+ function buildSolanaClient(endpoint, wsEndpoint, payer, commitment) {
7
7
  const client = createClient().use(
8
8
  (current) => extendClient(current, payer ? { payer } : {})
9
9
  );
10
- return client.use(
11
- solanaRpc({
12
- rpcUrl: endpoint,
13
- rpcSubscriptionsUrl: wsEndpoint
10
+ const rpc = commitment ? createRpc({
11
+ api: createSolanaRpcApi({ ...DEFAULT_RPC_CONFIG, defaultCommitment: commitment }),
12
+ transport: createDefaultRpcTransport({ url: endpoint })
13
+ }) : void 0;
14
+ const rpcSubscriptions = commitment ? createSubscriptionRpc({
15
+ api: createSolanaRpcSubscriptionsApi({
16
+ ...DEFAULT_RPC_SUBSCRIPTIONS_CONFIG,
17
+ defaultCommitment: commitment
18
+ }),
19
+ transport: createDefaultRpcSubscriptionsTransport({
20
+ createChannel: createDefaultSolanaRpcSubscriptionsChannelCreator({
21
+ url: wsEndpoint
22
+ })
14
23
  })
15
- ).use(rpcAirdrop());
24
+ }) : void 0;
25
+ if (!rpc || !rpcSubscriptions) {
26
+ return client.use(solanaRpc({ rpcUrl: endpoint, rpcSubscriptionsUrl: wsEndpoint })).use(rpcAirdrop());
27
+ }
28
+ return client.use((current) => extendClient(current, { rpc, rpcSubscriptions })).use(rpcGetMinimumBalance()).use(rpcTransactionPlanner()).use(rpcTransactionPlanSigningExecutor()).use(rpcTransactionPlanSendingExecutor()).use(rpcAirdrop());
16
29
  }
17
30
  function createSolanaClient(config = {}) {
18
31
  const cluster = config.cluster ?? DEFAULT_CLUSTER;
19
32
  const endpoint = config.endpoint ?? getClusterEndpoint(cluster);
20
33
  const wsEndpoint = config.wsEndpoint ?? (config.endpoint ? getWebSocketEndpoint(endpoint) : getClusterWebSocketEndpoint(cluster));
21
34
  const payer = config.payer ?? resolvePayerFromSecretKey(config.payerSecretKey);
22
- return buildSolanaClient(endpoint, wsEndpoint, payer);
35
+ return buildSolanaClient(endpoint, wsEndpoint, payer, config.commitment);
23
36
  }
24
37
  function resolvePayerFromSecretKey(payerSecretKey) {
25
38
  if (!payerSecretKey) {
@@ -1,5 +1,5 @@
1
1
  export { isSolanaMobileWalletSupported } from './mobile-wallet-support.js';
2
- import { a as SolanaChain } from './shared/core.C9vxHRXl.js';
2
+ import { a as SolanaChain } from './shared/core.BhF1J_tt.js';
3
3
  import '@solana/kit-plugin-rpc';
4
4
  import '@solana/kit';
5
5
 
@@ -14,7 +14,17 @@ interface RegisterSolanaMobileWalletOptions {
14
14
  remoteHostAuthority?: string;
15
15
  }
16
16
  declare function registerSolanaMobileWallet(options?: RegisterSolanaMobileWalletOptions): boolean;
17
+ /**
18
+ * Clears this package's local record of which Mobile Wallet Adapter
19
+ * registration keys have been applied. This only affects the deduplication guard
20
+ * inside `registerSolanaMobileWallet()`; it does not (and cannot) unregister an
21
+ * MWA wallet already installed into the host's Wallet Standard registry, because
22
+ * `@solana-mobile/wallet-standard-mobile` provides no unregister API. Calling
23
+ * `registerSolanaMobileWallet()` after this may therefore install a duplicate
24
+ * host-side registration. Intended for test/HMR reset only.
25
+ */
26
+ declare function unregisterSolanaMobileWallet(): void;
17
27
  declare function getDefaultMobileWalletAppIdentity(): SolanaMobileWalletAppIdentity;
18
28
 
19
- export { getDefaultMobileWalletAppIdentity, registerSolanaMobileWallet };
29
+ export { getDefaultMobileWalletAppIdentity, registerSolanaMobileWallet, unregisterSolanaMobileWallet };
20
30
  export type { RegisterSolanaMobileWalletOptions, SolanaMobileWalletAppIdentity };
@@ -28,6 +28,9 @@ function registerSolanaMobileWallet(options = {}) {
28
28
  registeredMobileWalletKeys.add(registrationKey);
29
29
  return true;
30
30
  }
31
+ function unregisterSolanaMobileWallet() {
32
+ registeredMobileWalletKeys.clear();
33
+ }
31
34
  function getDefaultMobileWalletAppIdentity() {
32
35
  if (typeof window === "undefined") {
33
36
  return {
@@ -54,4 +57,4 @@ function getDocumentIconPath() {
54
57
  return icon.href;
55
58
  }
56
59
 
57
- export { getDefaultMobileWalletAppIdentity, isSolanaMobileWalletSupported, registerSolanaMobileWallet };
60
+ export { getDefaultMobileWalletAppIdentity, isSolanaMobileWalletSupported, registerSolanaMobileWallet, unregisterSolanaMobileWallet };
package/dist/rpc.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { d as SolanaConfig, e as SolanaContext } from './shared/core.C9vxHRXl.js';
1
+ import { d as SolanaConfig, e as SolanaContext } from './shared/core.BhF1J_tt.js';
2
2
  import '@solana/kit-plugin-rpc';
3
3
  import '@solana/kit';
4
4
 
@@ -1,8 +1,42 @@
1
1
  import { StandardEvents, StandardDisconnect, StandardConnect } from '@wallet-standard/features';
2
2
  import { SolanaSignTransaction, SolanaSignIn, SolanaSignAndSendTransaction, SolanaSignMessage } from '@solana/wallet-standard-features';
3
- import bs58 from 'bs58';
3
+ import { isSignatureBytes, verifySignature, getBase58Decoder } from '@solana/kit';
4
+ import { createSolanaError } from '../errors.mjs';
4
5
  import { getWallets } from '@wallet-standard/app';
5
6
 
7
+ async function assertWalletSignature(publicKey, signedMessage, signature, label) {
8
+ if (!isSignatureBytes(signature)) {
9
+ throw createSolanaError(
10
+ "INVALID_SIGNATURE",
11
+ `The wallet returned a malformed ${label} signature.`
12
+ );
13
+ }
14
+ let key;
15
+ try {
16
+ key = await globalThis.crypto.subtle.importKey(
17
+ "raw",
18
+ publicKey,
19
+ "Ed25519",
20
+ true,
21
+ ["verify"]
22
+ );
23
+ } catch {
24
+ return;
25
+ }
26
+ let valid;
27
+ try {
28
+ valid = await verifySignature(key, signature, signedMessage);
29
+ } catch {
30
+ return;
31
+ }
32
+ if (!valid) {
33
+ throw createSolanaError(
34
+ "INVALID_SIGNATURE",
35
+ `The wallet returned an invalid ${label} signature.`
36
+ );
37
+ }
38
+ }
39
+
6
40
  const SOLANA_CHAINS = [
7
41
  "solana:mainnet",
8
42
  "solana:testnet",
@@ -42,16 +76,20 @@ function adaptSolanaStandardWallet(walletInfo, options = {}) {
42
76
  let disconnecting = false;
43
77
  let manuallyDisconnected = false;
44
78
  const eventsFeature = wallet.features[StandardEvents];
45
- eventsFeature?.on("change", (properties) => {
79
+ const unsubscribeChange = eventsFeature?.on("change", (properties) => {
46
80
  if (properties.accounts) {
47
81
  accounts = properties.accounts;
48
82
  account = allowAccountUpdates && !manuallyDisconnected ? getSolanaAccount(accounts, options.chain) : void 0;
49
83
  options.onChange?.();
50
84
  }
51
85
  });
86
+ function dispose() {
87
+ unsubscribeChange?.();
88
+ }
52
89
  return {
53
90
  platform: walletInfo.platform,
54
91
  source: walletInfo.source,
92
+ dispose,
55
93
  get publicKey() {
56
94
  return account ? account.address : null;
57
95
  },
@@ -110,7 +148,30 @@ function adaptSolanaStandardWallet(walletInfo, options = {}) {
110
148
  if (!result) {
111
149
  throw new Error("Solana wallet did not return a sign-in result");
112
150
  }
113
- return toSolanaSignInResult(result);
151
+ const signInResult = toSolanaSignInResult(result);
152
+ const requestedAddress = input?.address ?? account?.address;
153
+ const signInAccount = accounts.find(
154
+ (candidate) => candidate.address === signInResult.account.address
155
+ );
156
+ if (!signInAccount) {
157
+ throw createSolanaError(
158
+ "INVALID_SIGNATURE",
159
+ "The wallet returned a sign-in account it does not expose."
160
+ );
161
+ }
162
+ if (requestedAddress !== void 0 && requestedAddress !== signInAccount.address) {
163
+ throw createSolanaError(
164
+ "INVALID_SIGNATURE",
165
+ `The wallet signed in as ${signInResult.account.address} instead of the requested ${requestedAddress}.`
166
+ );
167
+ }
168
+ await assertWalletSignature(
169
+ signInAccount.publicKey,
170
+ signInResult.signedMessage,
171
+ signInResult.signature,
172
+ "sign-in"
173
+ );
174
+ return signInResult;
114
175
  } : void 0,
115
176
  signMessage: hasSignMessage(wallet) ? async (message) => {
116
177
  const activeAccount = getActiveAccount(account);
@@ -121,6 +182,12 @@ function adaptSolanaStandardWallet(walletInfo, options = {}) {
121
182
  if (!result) {
122
183
  throw new Error("Solana wallet did not return a message signature");
123
184
  }
185
+ await assertWalletSignature(
186
+ activeAccount.publicKey,
187
+ result.signedMessage,
188
+ result.signature,
189
+ "message"
190
+ );
124
191
  return result;
125
192
  } : void 0,
126
193
  signTransaction: hasSignTransaction(wallet) ? async (transaction) => {
@@ -158,7 +225,7 @@ function adaptSolanaStandardWallet(walletInfo, options = {}) {
158
225
  if (!result) {
159
226
  throw new Error("Solana wallet did not return a transaction signature");
160
227
  }
161
- return { signature: bs58.encode(result.signature) };
228
+ return { signature: getBase58Decoder().decode(result.signature) };
162
229
  } : void 0,
163
230
  signAndSendTransactions: hasSignAndSendTransaction(wallet) ? async (transactions, sendOptions) => {
164
231
  const activeAccount = getActiveAccount(account);
@@ -179,11 +246,24 @@ function adaptSolanaStandardWallet(walletInfo, options = {}) {
179
246
  if (!result) {
180
247
  throw new Error("Solana wallet did not return a transaction signature");
181
248
  }
182
- return bs58.encode(result.signature);
249
+ return getBase58Decoder().decode(result.signature);
183
250
  });
184
251
  } : void 0
185
252
  };
186
253
  }
254
+ async function disconnectSolanaStandardWallet(walletInfo) {
255
+ const wallet = walletInfo.wallet;
256
+ if (typeof wallet !== "object" && typeof wallet !== "function") {
257
+ return false;
258
+ }
259
+ const features = wallet?.features;
260
+ const feature = features?.[StandardDisconnect];
261
+ if (typeof feature?.disconnect !== "function") {
262
+ return false;
263
+ }
264
+ await feature.disconnect();
265
+ return true;
266
+ }
187
267
  async function signAllThroughFeature(wallet, getAccount, transactions, chain) {
188
268
  const activeAccount = getAccount();
189
269
  const results = await wallet.features[SolanaSignTransaction].signTransaction(
@@ -301,4 +381,4 @@ function getWalletUrl(wallet) {
301
381
  return "url" in wallet && typeof wallet.url === "string" ? wallet.url : void 0;
302
382
  }
303
383
 
304
- export { SOLANA_CHAINS as S, SOLANA_MOBILE_WALLET_ADAPTER_WALLET_NAME as a, adaptSolanaStandardWallet as b, getSolanaChain as c, getRegisteredSolanaWallets as g, isSolanaStandardWallet as i, subscribeSolanaWallets as s };
384
+ export { SOLANA_CHAINS as S, SOLANA_MOBILE_WALLET_ADAPTER_WALLET_NAME as a, adaptSolanaStandardWallet as b, getSolanaChain as c, disconnectSolanaStandardWallet as d, getRegisteredSolanaWallets as g, isSolanaStandardWallet as i, subscribeSolanaWallets as s };
@@ -1,6 +1,6 @@
1
1
  import * as _solana_kit_plugin_rpc from '@solana/kit-plugin-rpc';
2
2
  import * as _solana_kit from '@solana/kit';
3
- import { Commitment, Slot, TransactionSigner, Address, ReadonlyUint8Array, Signature } from '@solana/kit';
3
+ import { Address, ReadonlyUint8Array, Slot, Commitment, TransactionSigner, Signature } from '@solana/kit';
4
4
 
5
5
  /**
6
6
  * Solana cluster names. `mainnet` is Solana's official mainnet cluster name.
@@ -82,9 +82,21 @@ interface SolanaWallet {
82
82
  source?: SolanaWalletInfo["source"];
83
83
  connect: () => Promise<void>;
84
84
  disconnect: () => Promise<void>;
85
+ /**
86
+ * Releases wallet-event subscriptions captured at adapt time (e.g. the
87
+ * Wallet Standard `change` listener). The wallet stays connected; only
88
+ * event delivery stops. Idempotent. Present on adapted wallets; plain
89
+ * caller-supplied wallets typically omit it.
90
+ */
91
+ dispose?: () => void;
85
92
  signMessage?: (message: Uint8Array) => Promise<SolanaSignMessageResult>;
86
93
  signIn?: (input?: SolanaSignInInput) => Promise<SolanaSignInResult>;
87
94
  signTransaction?: (transaction: SolanaTransaction) => Promise<SolanaTransaction>;
95
+ /**
96
+ * @deprecated Prefer `signTransactions` (identical signature). `signTransactions`
97
+ * is tried first in `useSignTransactions()`; `signAllTransactions` remains as
98
+ * the legacy Wallet Standard naming.
99
+ */
88
100
  signAllTransactions?: (transactions: SolanaTransaction[]) => Promise<SolanaTransaction[]>;
89
101
  signTransactions?: (transactions: SolanaTransaction[]) => Promise<SolanaTransaction[]>;
90
102
  signAndSendTransaction?: (transaction: SolanaTransaction, options?: SendTransactionOptions) => Promise<{
@@ -128,6 +140,26 @@ interface SendTransactionOptions {
128
140
  interface ConfirmTransactionOptions {
129
141
  commitment?: Commitment;
130
142
  timeoutMs?: number;
143
+ /**
144
+ * Milliseconds between `getSignatureStatuses` polls. Defaults to 1500.
145
+ * Must be greater than 0: with no delay between polls an absent signature
146
+ * would hammer the RPC until the timeout expires.
147
+ */
148
+ pollIntervalMs?: number;
149
+ /**
150
+ * Rejects without further polling when the caller's own `AbortSignal`
151
+ * fires. An aborted confirmation rejects with the signal's reason (an
152
+ * `AbortError` by default) rather than a timeout error.
153
+ */
154
+ abortSignal?: AbortSignal;
155
+ /**
156
+ * Pass `true` to search transaction history when the signature may have
157
+ * left the recent status cache. Defaults to `false`: an older signature
158
+ * never appears in `getSignatureStatuses` results, so confirming one
159
+ * without this flag resolves to `TRANSACTION_NOT_FOUND` after the timeout
160
+ * rather than confirming.
161
+ */
162
+ searchTransactionHistory?: boolean;
131
163
  }
132
164
  interface TransactionStatus {
133
165
  slot: Slot;
@@ -141,9 +173,10 @@ interface TransactionConfirmation {
141
173
  status: TransactionStatus;
142
174
  }
143
175
 
144
- declare function buildSolanaClient(endpoint: string, wsEndpoint: string, payer: TransactionSigner | undefined): _solana_kit.Client<{
176
+ declare function buildSolanaClient(endpoint: string, wsEndpoint: string, payer: TransactionSigner | undefined, commitment: Commitment | undefined): _solana_kit.Client<{
145
177
  signTransactions: (input: _solana_kit.InstructionPlanInput | _solana_kit.TransactionPlanInput, config?: {
146
178
  abortSignal?: AbortSignal;
179
+ maxInstructionsPerTransaction?: number;
147
180
  }) => Promise<_solana_kit.TransactionPlanResult<_solana_kit_plugin_rpc.RpcSignContext>>;
148
181
  payer: TransactionSigner;
149
182
  rpc: _solana_kit.Rpc<_solana_kit.RequestAirdropApi & _solana_kit.GetAccountInfoApi & _solana_kit.GetAgGenesisCertApi & _solana_kit.GetBalanceApi & _solana_kit.GetBlockApi & _solana_kit.GetBlockCommitmentApi & _solana_kit.GetBlockHeightApi & _solana_kit.GetBlockProductionApi & _solana_kit.GetBlocksApi & _solana_kit.GetBlocksWithLimitApi & _solana_kit.GetBlockTimeApi & _solana_kit.GetClusterNodesApi & _solana_kit.GetEpochInfoApi & _solana_kit.GetEpochScheduleApi & _solana_kit.GetFeeForMessageApi & _solana_kit.GetFirstAvailableBlockApi & _solana_kit.GetGenesisHashApi & _solana_kit.GetHealthApi & _solana_kit.GetHighestSnapshotSlotApi & _solana_kit.GetIdentityApi & _solana_kit.GetInflationGovernorApi & _solana_kit.GetInflationRateApi & _solana_kit.GetInflationRewardApi & _solana_kit.GetLargestAccountsApi & _solana_kit.GetLatestBlockhashApi & _solana_kit.GetLeaderScheduleApi & _solana_kit.GetMaxRetransmitSlotApi & _solana_kit.GetMaxShredInsertSlotApi & _solana_kit.GetMinimumBalanceForRentExemptionApi & _solana_kit.GetMultipleAccountsApi & _solana_kit.GetProgramAccountsApi & _solana_kit.GetRecentPerformanceSamplesApi & _solana_kit.GetRecentPrioritizationFeesApi & _solana_kit.GetSignaturesForAddressApi & _solana_kit.GetSignatureStatusesApi & _solana_kit.GetSlotApi & _solana_kit.GetSlotLeaderApi & _solana_kit.GetSlotLeadersApi & _solana_kit.GetStakeMinimumDelegationApi & _solana_kit.GetSupplyApi & _solana_kit.GetTokenAccountBalanceApi & _solana_kit.GetTokenAccountsByDelegateApi & _solana_kit.GetTokenAccountsByOwnerApi & _solana_kit.GetTokenLargestAccountsApi & _solana_kit.GetTokenSupplyApi & _solana_kit.GetTransactionApi & _solana_kit.GetTransactionCountApi & _solana_kit.GetTransactionsForAddressApi & _solana_kit.GetVersionApi & _solana_kit.GetVoteAccountsApi & _solana_kit.IsBlockhashValidApi & _solana_kit.MinimumLedgerSlotApi & _solana_kit.SendTransactionApi & _solana_kit.SimulateTransactionApi>;
@@ -152,9 +185,11 @@ declare function buildSolanaClient(endpoint: string, wsEndpoint: string, payer:
152
185
  transactionPlanner: _solana_kit.TransactionPlanner;
153
186
  planTransaction: (input: _solana_kit.InstructionPlanInput, config?: {
154
187
  abortSignal?: AbortSignal;
188
+ maxInstructionsPerTransaction?: number;
155
189
  }) => Promise<_solana_kit.SingleTransactionPlan["message"]>;
156
190
  planTransactions: (input: _solana_kit.InstructionPlanInput, config?: {
157
191
  abortSignal?: AbortSignal;
192
+ maxInstructionsPerTransaction?: number;
158
193
  }) => Promise<_solana_kit.TransactionPlan>;
159
194
  signTransaction: (input: _solana_kit.InstructionPlanInput | (_solana_kit.TransactionMessage & _solana_kit.TransactionMessageWithFeePayer<string>) | Readonly<{
160
195
  kind: "single";
@@ -162,6 +197,7 @@ declare function buildSolanaClient(endpoint: string, wsEndpoint: string, payer:
162
197
  planType: "transactionPlan";
163
198
  }>, config?: {
164
199
  abortSignal?: AbortSignal;
200
+ maxInstructionsPerTransaction?: number;
165
201
  }) => Promise<_solana_kit.SuccessfulSingleTransactionPlanResult<_solana_kit_plugin_rpc.RpcSignContext>>;
166
202
  transactionPlanExecutor: _solana_kit.TransactionPlanExecutor<_solana_kit_plugin_rpc.RpcSendContext>;
167
203
  sendTransaction: (input: _solana_kit.InstructionPlanInput | (_solana_kit.TransactionMessage & _solana_kit.TransactionMessageWithFeePayer<string>) | Readonly<{
@@ -170,9 +206,11 @@ declare function buildSolanaClient(endpoint: string, wsEndpoint: string, payer:
170
206
  planType: "transactionPlan";
171
207
  }>, config?: {
172
208
  abortSignal?: AbortSignal;
209
+ maxInstructionsPerTransaction?: number;
173
210
  }) => Promise<_solana_kit.SuccessfulSingleTransactionPlanResult<_solana_kit_plugin_rpc.RpcSendContext>>;
174
211
  sendTransactions: (input: _solana_kit.InstructionPlanInput | _solana_kit.TransactionPlanInput, config?: {
175
212
  abortSignal?: AbortSignal;
213
+ maxInstructionsPerTransaction?: number;
176
214
  }) => Promise<_solana_kit.TransactionPlanResult<_solana_kit_plugin_rpc.RpcSendContext>>;
177
215
  airdrop: (address: Address, amount: _solana_kit.Lamports, abortSignal?: AbortSignal) => Promise<_solana_kit.Signature | undefined>;
178
216
  }>;
package/dist/timeout.d.ts CHANGED
@@ -1,4 +1,25 @@
1
- declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number | undefined, createError: () => unknown): Promise<T>;
1
+ import { SolanaErrorCode } from './errors.js';
2
+
3
+ interface WithTimeoutOptions {
4
+ /**
5
+ * Rejects without racing when the caller's own `AbortSignal` fires first.
6
+ * An aborted wait rejects with the signal's reason (an `AbortError` by
7
+ * default) rather than the timeout error.
8
+ */
9
+ signal?: AbortSignal;
10
+ /**
11
+ * Error code for the timeout rejection when `createError` is not given.
12
+ * Defaults to `"TRANSACTION_TIMEOUT"`.
13
+ */
14
+ code?: SolanaErrorCode;
15
+ /**
16
+ * Message for the timeout rejection when `createError` is not given.
17
+ */
18
+ message?: string;
19
+ }
20
+ declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number | undefined, createError?: () => unknown, options?: WithTimeoutOptions): Promise<T>;
21
+ declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number | undefined, options?: WithTimeoutOptions): Promise<T>;
2
22
  declare function withSolanaTimeout<T>(promise: Promise<T>, timeoutMs: number | undefined, message: string): Promise<T>;
3
23
 
4
24
  export { withSolanaTimeout, withTimeout };
25
+ export type { WithTimeoutOptions };
package/dist/timeout.mjs CHANGED
@@ -1,28 +1,64 @@
1
1
  import { createSolanaError } from './errors.mjs';
2
2
 
3
- function withTimeout(promise, timeoutMs, createError) {
3
+ function withTimeout(promise, timeoutMs, createErrorOrOptions, maybeOptions) {
4
+ const createError = typeof createErrorOrOptions === "function" ? createErrorOrOptions : void 0;
5
+ const {
6
+ signal,
7
+ code = "TRANSACTION_TIMEOUT",
8
+ message
9
+ } = (typeof createErrorOrOptions === "function" ? maybeOptions : createErrorOrOptions) ?? {};
4
10
  if (timeoutMs === void 0) {
5
- return promise;
11
+ return signal ? raceWithSignal(promise, signal) : promise;
6
12
  }
13
+ if (typeof timeoutMs !== "number" || Number.isNaN(timeoutMs) || timeoutMs < 0) {
14
+ throw createSolanaError(
15
+ "INVALID_INPUT",
16
+ `Invalid timeout: expected a non-negative number of milliseconds, received ${String(timeoutMs)}`
17
+ );
18
+ }
19
+ signal?.throwIfAborted();
7
20
  let timeoutId;
8
- return Promise.race([
9
- promise,
10
- // ponytail: the try/catch looks redundant but is not. `createError()` runs
11
- // in the `setTimeout` callback, not the executor body, so a throwing
12
- // factory would be an uncaught exception rather than a rejection.
13
- new Promise((_, reject) => {
14
- timeoutId = setTimeout(() => {
15
- try {
16
- reject(createError());
17
- } catch (error) {
18
- reject(error);
19
- }
20
- }, timeoutMs);
21
- })
22
- ]).finally(() => {
21
+ let onAbort;
22
+ const timeoutPromise = new Promise((_, reject) => {
23
+ timeoutId = setTimeout(() => {
24
+ try {
25
+ reject(createError?.() ?? createSolanaError(code, message ?? "Operation timed out"));
26
+ } catch (error) {
27
+ reject(error);
28
+ }
29
+ }, timeoutMs);
30
+ if (signal) {
31
+ onAbort = () => reject(signal.reason);
32
+ signal.addEventListener("abort", onAbort, { once: true });
33
+ }
34
+ });
35
+ const raced = signal ? raceWithSignal(promise, signal) : promise;
36
+ return Promise.race([raced, timeoutPromise]).finally(() => {
23
37
  if (timeoutId) {
24
38
  clearTimeout(timeoutId);
25
39
  }
40
+ if (signal && onAbort) {
41
+ signal.removeEventListener("abort", onAbort);
42
+ }
43
+ });
44
+ }
45
+ function raceWithSignal(promise, signal) {
46
+ if (signal.aborted) {
47
+ return Promise.reject(signal.reason);
48
+ }
49
+ return new Promise((resolve, reject) => {
50
+ const onAbort = () => reject(signal.reason);
51
+ signal.addEventListener("abort", onAbort, { once: true });
52
+ promise.then(
53
+ (value) => {
54
+ signal.removeEventListener("abort", onAbort);
55
+ resolve(value);
56
+ },
57
+ (cause) => {
58
+ signal.removeEventListener("abort", onAbort);
59
+ reject(cause);
60
+ }
61
+ );
26
62
  });
27
63
  }
28
64
  function withSolanaTimeout(promise, timeoutMs, message) {
@@ -1,5 +1,5 @@
1
1
  import { Address, Commitment } from '@solana/kit';
2
- import { b as SolanaClient } from './shared/core.C9vxHRXl.js';
2
+ import { b as SolanaClient } from './shared/core.BhF1J_tt.js';
3
3
  import '@solana/kit-plugin-rpc';
4
4
 
5
5
  interface TokenAccountInfo {
@@ -1,5 +1,5 @@
1
- import { address } from '@solana/kit';
2
- import { normalizeSolanaError } from './errors.mjs';
1
+ import { address, isAddress } from '@solana/kit';
2
+ import { normalizeSolanaError, createSolanaError } from './errors.mjs';
3
3
 
4
4
  const TOKEN_PROGRAM_ADDRESS = address("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
5
5
  const TOKEN_2022_PROGRAM_ADDRESS = address("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
@@ -54,6 +54,13 @@ function tokenAccountInfoFromParsed(address2, data) {
54
54
  if (!parsed) {
55
55
  throw new Error(`Token account ${address2} has no parsed data`);
56
56
  }
57
+ const mismatches = validateParsedTokenAccount(parsed);
58
+ if (mismatches.length > 0) {
59
+ throw createSolanaError(
60
+ "RPC_FAILURE",
61
+ `Token account ${address2} returned malformed data (${mismatches.join(", ")})`
62
+ );
63
+ }
57
64
  return {
58
65
  address: address2,
59
66
  mint: parsed.mint,
@@ -64,5 +71,27 @@ function tokenAccountInfoFromParsed(address2, data) {
64
71
  isNative: parsed.isNative
65
72
  };
66
73
  }
74
+ function validateParsedTokenAccount(parsed) {
75
+ const mismatches = [];
76
+ if (typeof parsed.mint !== "string" || !isAddress(parsed.mint)) {
77
+ mismatches.push("mint");
78
+ }
79
+ if (typeof parsed.owner !== "string" || !isAddress(parsed.owner)) {
80
+ mismatches.push("owner");
81
+ }
82
+ if (typeof parsed.tokenAmount?.amount !== "string" || !/^\d+$/.test(parsed.tokenAmount.amount)) {
83
+ mismatches.push("tokenAmount.amount");
84
+ }
85
+ if (typeof parsed.tokenAmount?.decimals !== "number" || !Number.isInteger(parsed.tokenAmount.decimals) || parsed.tokenAmount.decimals < 0) {
86
+ mismatches.push("tokenAmount.decimals");
87
+ }
88
+ if (parsed.state !== "frozen" && parsed.state !== "initialized" && parsed.state !== "uninitialized") {
89
+ mismatches.push("state");
90
+ }
91
+ if (typeof parsed.isNative !== "boolean") {
92
+ mismatches.push("isNative");
93
+ }
94
+ return mismatches;
95
+ }
67
96
 
68
97
  export { getTokenAccount, getTokenAccountsByOwner, getTokenBalance };
@@ -1,5 +1,5 @@
1
1
  import { Signature } from '@solana/kit';
2
- import { b as SolanaClient, C as ConfirmTransactionOptions, T as TransactionConfirmation, k as SolanaWallet, j as SolanaTransaction, S as SendTransactionOptions } from './shared/core.C9vxHRXl.js';
2
+ import { b as SolanaClient, C as ConfirmTransactionOptions, T as TransactionConfirmation, k as SolanaWallet, j as SolanaTransaction, S as SendTransactionOptions } from './shared/core.BhF1J_tt.js';
3
3
  import '@solana/kit-plugin-rpc';
4
4
 
5
5
  /**
@@ -12,6 +12,14 @@ declare function signAndSendTransaction(client: SolanaClient, wallet: SolanaWall
12
12
  /**
13
13
  * Confirm a transaction signature against the kit RPC until it reaches the
14
14
  * requested commitment.
15
+ *
16
+ * Kit's `sendAndConfirmTransaction` cannot back this: it needs the whole
17
+ * transaction plus a `rpcSubscriptions` websocket and sends the transaction
18
+ * itself, whereas this confirms a signature a wallet already submitted.
19
+ *
20
+ * Kit's `getSignatureStatuses` takes no commitment parameter — the commitment
21
+ * check below is client-side. A missing status means the signature is unknown
22
+ * (not found), distinct from a failed transaction or an expired wait.
15
23
  */
16
24
  declare function confirmTransactionSignature(client: SolanaClient, signature: Signature, options?: ConfirmTransactionOptions): Promise<TransactionConfirmation>;
17
25
 
@@ -31,17 +31,36 @@ async function signAndSendTransaction(client, wallet, transaction, options) {
31
31
  async function confirmTransactionSignature(client, signature, options = {}) {
32
32
  const commitment = options.commitment ?? DEFAULT_CONFIRMATION_COMMITMENT;
33
33
  const timeoutMs = options.timeoutMs ?? DEFAULT_CONFIRMATION_TIMEOUT_MS;
34
- let elapsedMs = 0;
34
+ const pollIntervalMs = options.pollIntervalMs ?? SIGNATURE_STATUS_POLL_INTERVAL_MS;
35
+ const { abortSignal, searchTransactionHistory = false } = options;
36
+ if (typeof timeoutMs !== "number" || Number.isNaN(timeoutMs) || timeoutMs < 0) {
37
+ throw createSolanaError(
38
+ "INVALID_INPUT",
39
+ `Invalid confirmation timeout: expected a non-negative number of milliseconds, received ${String(timeoutMs)}`
40
+ );
41
+ }
42
+ if (typeof pollIntervalMs !== "number" || Number.isNaN(pollIntervalMs) || pollIntervalMs <= 0) {
43
+ throw createSolanaError(
44
+ "INVALID_INPUT",
45
+ `Invalid confirmation poll interval: expected a positive number of milliseconds, received ${String(pollIntervalMs)}`
46
+ );
47
+ }
48
+ abortSignal?.throwIfAborted();
49
+ const deadline = Date.now() + timeoutMs;
50
+ let sawStatus = false;
35
51
  try {
36
- while (elapsedMs < timeoutMs) {
37
- const response = await client.rpc.getSignatureStatuses([signature]).send();
52
+ while (Date.now() < deadline) {
53
+ abortSignal?.throwIfAborted();
54
+ const response = await client.rpc.getSignatureStatuses([signature], { searchTransactionHistory }).send({ abortSignal });
38
55
  const [status] = response.value;
39
56
  if (!status) {
40
- throw new Error(`Transaction ${signature} was not found.`);
57
+ await wait(pollIntervalMs, abortSignal);
58
+ continue;
41
59
  }
60
+ sawStatus = true;
42
61
  if (status.err) {
43
62
  throw createSolanaError(
44
- "RPC_FAILURE",
63
+ "TRANSACTION_FAILED",
45
64
  `Transaction ${signature} failed to reach ${commitment} commitment.`,
46
65
  { cause: status.err }
47
66
  );
@@ -49,14 +68,22 @@ async function confirmTransactionSignature(client, signature, options = {}) {
49
68
  if (hasReachedCommitment(status.confirmationStatus, commitment)) {
50
69
  return { signature, commitment, status };
51
70
  }
52
- await wait(SIGNATURE_STATUS_POLL_INTERVAL_MS);
53
- elapsedMs += SIGNATURE_STATUS_POLL_INTERVAL_MS;
71
+ await wait(pollIntervalMs, abortSignal);
72
+ }
73
+ if (!sawStatus) {
74
+ throw createSolanaError("TRANSACTION_NOT_FOUND", `Transaction ${signature} was not found.`);
54
75
  }
55
76
  throw createSolanaError(
56
77
  "TRANSACTION_TIMEOUT",
57
78
  `Timed out waiting for transaction ${signature} to reach ${commitment} commitment.`
58
79
  );
59
80
  } catch (cause) {
81
+ if (abortSignal?.aborted) {
82
+ throw abortSignal.reason ?? cause;
83
+ }
84
+ if (isAbortErrorLike(cause)) {
85
+ throw cause;
86
+ }
60
87
  throw normalizeSolanaError(cause, "RPC_FAILURE");
61
88
  }
62
89
  }
@@ -69,10 +96,25 @@ function hasReachedCommitment(actualStatus, targetCommitment) {
69
96
  }
70
97
  return (COMMITMENT_RANK[actualStatus] ?? 0) >= (COMMITMENT_RANK[targetCommitment] ?? 0);
71
98
  }
72
- function wait(ms) {
73
- return new Promise((resolve) => {
74
- setTimeout(resolve, ms);
99
+ function wait(ms, signal) {
100
+ if (ms <= 0) {
101
+ signal?.throwIfAborted();
102
+ return Promise.resolve();
103
+ }
104
+ return new Promise((resolve, reject) => {
105
+ const timeoutId = setTimeout(() => {
106
+ signal?.removeEventListener("abort", onAbort);
107
+ resolve();
108
+ }, ms);
109
+ function onAbort() {
110
+ clearTimeout(timeoutId);
111
+ reject(signal.reason);
112
+ }
113
+ signal?.addEventListener("abort", onAbort, { once: true });
75
114
  });
76
115
  }
116
+ function isAbortErrorLike(cause) {
117
+ return typeof cause === "object" && cause !== null && cause.name === "AbortError";
118
+ }
77
119
 
78
120
  export { confirmTransactionSignature, signAndSendTransaction };
package/dist/types.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import '@solana/kit';
2
- export { C as ConfirmTransactionOptions, S as SendTransactionOptions, a as SolanaChain, c as SolanaCluster, d as SolanaConfig, e as SolanaContext, f as SolanaSignInAccount, g as SolanaSignInInput, h as SolanaSignInResult, i as SolanaSignMessageResult, j as SolanaTransaction, k as SolanaWallet, l as SolanaWalletInfo, T as TransactionConfirmation, m as TransactionStatus } from './shared/core.C9vxHRXl.js';
2
+ export { C as ConfirmTransactionOptions, S as SendTransactionOptions, a as SolanaChain, c as SolanaCluster, d as SolanaConfig, e as SolanaContext, f as SolanaSignInAccount, g as SolanaSignInInput, h as SolanaSignInResult, i as SolanaSignMessageResult, j as SolanaTransaction, k as SolanaWallet, l as SolanaWalletInfo, T as TransactionConfirmation, m as TransactionStatus } from './shared/core.BhF1J_tt.js';
3
3
  import '@solana/kit-plugin-rpc';