@getomnichain/omnichain 0.2.1 → 0.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.
Files changed (51) hide show
  1. package/dist/cjs/chain.base.js.map +1 -1
  2. package/dist/cjs/errors.js +55 -6
  3. package/dist/cjs/errors.js.map +1 -1
  4. package/dist/cjs/evm/evm_chain.js +421 -2
  5. package/dist/cjs/evm/evm_chain.js.map +1 -1
  6. package/dist/cjs/evm/evm_transaction_status.js +4 -0
  7. package/dist/cjs/evm/evm_transaction_status.js.map +1 -1
  8. package/dist/cjs/evm/unsigned_evm_transaction.js +12 -0
  9. package/dist/cjs/evm/unsigned_evm_transaction.js.map +1 -1
  10. package/dist/cjs/solana/solana_chain.js +618 -21
  11. package/dist/cjs/solana/solana_chain.js.map +1 -1
  12. package/dist/cjs/solana/unsigned_solana_transaction.js +33 -0
  13. package/dist/cjs/solana/unsigned_solana_transaction.js.map +1 -1
  14. package/dist/cjs/utxo/utxo_chain.js +94 -10
  15. package/dist/cjs/utxo/utxo_chain.js.map +1 -1
  16. package/dist/esm/chain.base.d.ts +38 -1
  17. package/dist/esm/chain.base.d.ts.map +1 -1
  18. package/dist/esm/chain.base.js.map +1 -1
  19. package/dist/esm/errors.d.ts +13 -0
  20. package/dist/esm/errors.d.ts.map +1 -1
  21. package/dist/esm/errors.js +51 -6
  22. package/dist/esm/errors.js.map +1 -1
  23. package/dist/esm/evm/evm_chain.d.ts +48 -2
  24. package/dist/esm/evm/evm_chain.d.ts.map +1 -1
  25. package/dist/esm/evm/evm_chain.js +423 -4
  26. package/dist/esm/evm/evm_chain.js.map +1 -1
  27. package/dist/esm/evm/evm_transaction_status.d.ts +4 -0
  28. package/dist/esm/evm/evm_transaction_status.d.ts.map +1 -1
  29. package/dist/esm/evm/evm_transaction_status.js +4 -0
  30. package/dist/esm/evm/evm_transaction_status.js.map +1 -1
  31. package/dist/esm/evm/unsigned_evm_transaction.d.ts +13 -0
  32. package/dist/esm/evm/unsigned_evm_transaction.d.ts.map +1 -1
  33. package/dist/esm/evm/unsigned_evm_transaction.js +12 -0
  34. package/dist/esm/evm/unsigned_evm_transaction.js.map +1 -1
  35. package/dist/esm/solana/solana_chain.d.ts +87 -3
  36. package/dist/esm/solana/solana_chain.d.ts.map +1 -1
  37. package/dist/esm/solana/solana_chain.js +618 -23
  38. package/dist/esm/solana/solana_chain.js.map +1 -1
  39. package/dist/esm/solana/unsigned_solana_transaction.d.ts +20 -1
  40. package/dist/esm/solana/unsigned_solana_transaction.d.ts.map +1 -1
  41. package/dist/esm/solana/unsigned_solana_transaction.js +33 -0
  42. package/dist/esm/solana/unsigned_solana_transaction.js.map +1 -1
  43. package/dist/esm/utxo/utxo_chain.d.ts +15 -2
  44. package/dist/esm/utxo/utxo_chain.d.ts.map +1 -1
  45. package/dist/esm/utxo/utxo_chain.js +96 -13
  46. package/dist/esm/utxo/utxo_chain.js.map +1 -1
  47. package/docs/CONNECTIONS.md +33 -0
  48. package/docs/EIP7702.md +77 -0
  49. package/docs/JITO.md +41 -0
  50. package/docs/MULTI_RPC.md +28 -0
  51. package/package.json +1 -1
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SolanaChain = void 0;
4
+ exports.signatureBase58FromBytes = signatureBase58FromBytes;
5
+ exports.bs58encode = bs58encode;
4
6
  const web3_js_1 = require("@solana/web3.js");
5
7
  const spl_token_1 = require("@solana/spl-token");
6
8
  const chain_base_ts_1 = require("../chain.base.js");
@@ -36,18 +38,26 @@ const SOL_PRIORITY_PERCENTILE = {
36
38
  class SolanaChain extends chain_base_ts_1.Chain {
37
39
  defaultRpcUrl;
38
40
  rpcUrl;
41
+ rpcUrls;
39
42
  legacyRpcEnvNames;
40
43
  explorerClusterSuffix;
41
44
  chainAgnosticGenesisHash;
45
+ jito;
42
46
  _nativeToken;
43
47
  _connection = null;
48
+ _rpcUrlIndex = 0;
44
49
  constructor(init) {
45
50
  super(init.chainId, init.name, network_type_ts_1.NetworkType.SOLANA, init.blockTimeSeconds, init.nativeSymbol, init.explorerBaseUrl);
46
51
  this.defaultRpcUrl = init.defaultRpcUrl;
47
52
  this.rpcUrl = init.rpcUrl;
53
+ this.rpcUrls = init.rpcUrls ?? [];
54
+ if (this.rpcUrls.length > 1) {
55
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `SolanaChainInit.rpcUrls accepts only ONE endpoint in 0.3.0 (got ${this.rpcUrls.length}). Automatic failover retry is deferred to a follow-up release; passing >1 endpoint would silently discard entries.`, { chainId: init.chainId });
56
+ }
48
57
  this.legacyRpcEnvNames = init.legacyRpcEnvNames ?? [];
49
58
  this.explorerClusterSuffix = init.explorerClusterSuffix ?? '';
50
59
  this.chainAgnosticGenesisHash = init.chainAgnosticGenesisHash;
60
+ this.jito = init.jito ?? null;
51
61
  this._nativeToken = solana_token_ts_1.SolanaToken.native(init.chainId, init.nativeSymbol, init.nativeDecimals ?? 9);
52
62
  (0, network_type_ts_1.registerNonEvmChain)(init.chainId, network_type_ts_1.NetworkType.SOLANA);
53
63
  }
@@ -106,9 +116,35 @@ class SolanaChain extends chain_base_ts_1.Chain {
106
116
  * the pre-rename `SOLANA_RPC_URL` here)
107
117
  * 5. `defaultRpcUrl` (never throws — public cluster)
108
118
  */
119
+ resolvedRpcUrlForRedaction() {
120
+ try {
121
+ return this.readRpcUrl();
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ async rpcWrap(fn, label) {
128
+ try {
129
+ return await fn();
130
+ }
131
+ catch (err) {
132
+ if (err instanceof errors_ts_1.ChainError)
133
+ throw err;
134
+ const rpc = this.resolvedRpcUrlForRedaction();
135
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Solana ${label} failed on ${this.name}`, rpc), { chainId: this.chainId }, (0, errors_ts_1.sanitizeCause)(err, rpc));
136
+ }
137
+ }
109
138
  readRpcUrl() {
110
139
  if (this.rpcUrl && this.rpcUrl.trim().length > 0)
111
140
  return this.rpcUrl.trim();
141
+ if (this.rpcUrls.length > 0) {
142
+ for (const candidate of this.rpcUrls) {
143
+ if (candidate && candidate.trim().length > 0)
144
+ return candidate.trim();
145
+ }
146
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcNotConfigured, `${this.name}: rpcUrls was supplied but every entry is blank; refusing to fall back to env or the public default cluster`, { chainId: this.chainId });
147
+ }
112
148
  const env = globalThis.process?.env;
113
149
  const candidates = [
114
150
  this.name.replace(/ /g, '_').toUpperCase() + '_RPC_URL',
@@ -272,19 +308,39 @@ class SolanaChain extends chain_base_ts_1.Chain {
272
308
  }
273
309
  instructions.push((0, spl_token_1.createTransferCheckedInstruction)(sourceAta, mintPk, destAta, fromPk, amountMr, decimals, [], programId));
274
310
  }
275
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('finalized');
276
- const message = web3_js_1.MessageV0.compile({
277
- payerKey: feePayerPk,
278
- instructions,
279
- recentBlockhash: blockhash,
280
- });
311
+ const { blockhash, lastValidBlockHeight } = await this.rpcWrap(() => connection.getLatestBlockhash('finalized'), 'getLatestBlockhash');
312
+ const alts = validateAltList(req.addressLookupTables, this.chainId);
313
+ let message;
314
+ try {
315
+ message = web3_js_1.MessageV0.compile({
316
+ payerKey: feePayerPk,
317
+ instructions,
318
+ recentBlockhash: blockhash,
319
+ addressLookupTableAccounts: alts,
320
+ });
321
+ }
322
+ catch (err) {
323
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Solana createTransferUnsignedTransaction: MessageV0.compile failed — instruction or ALT layout invalid`, { chainId: this.chainId }, err instanceof Error ? err : undefined);
324
+ }
325
+ const tx = new web3_js_1.VersionedTransaction(message);
326
+ let serializedSize;
327
+ try {
328
+ serializedSize = tx.serialize().length;
329
+ }
330
+ catch (err) {
331
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.TransactionTooLarge, `Compiled Solana tx exceeds the serialize buffer; supply addressLookupTables to compress the account list`, { chainId: this.chainId }, err instanceof Error ? err : undefined);
332
+ }
333
+ if (serializedSize > 1232) {
334
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.TransactionTooLarge, `Compiled Solana transfer exceeds 1232-byte wire limit; supply addressLookupTables to compress the account list`, { chainId: this.chainId });
335
+ }
281
336
  return new unsigned_solana_transaction_ts_1.UnsignedSolanaTransaction({
282
337
  chainId: this.chainId,
283
- transaction: new web3_js_1.VersionedTransaction(message),
338
+ transaction: tx,
284
339
  feePayer: feePayerAddress,
285
340
  recentBlockhash: blockhash,
286
341
  lastValidBlockHeight,
287
342
  instructions,
343
+ addressLookupTables: alts,
288
344
  });
289
345
  }
290
346
  /**
@@ -331,19 +387,39 @@ class SolanaChain extends chain_base_ts_1.Chain {
331
387
  for (const ix of req.instructions)
332
388
  allIxs.push(ix);
333
389
  const connection = this.getConnection();
334
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('finalized');
335
- const message = web3_js_1.MessageV0.compile({
336
- payerKey: feePayerPk,
337
- instructions: allIxs,
338
- recentBlockhash: blockhash,
339
- });
390
+ const { blockhash, lastValidBlockHeight } = await this.rpcWrap(() => connection.getLatestBlockhash('finalized'), 'getLatestBlockhash');
391
+ const alts = validateAltList(req.addressLookupTables, this.chainId);
392
+ let message;
393
+ try {
394
+ message = web3_js_1.MessageV0.compile({
395
+ payerKey: feePayerPk,
396
+ instructions: allIxs,
397
+ recentBlockhash: blockhash,
398
+ addressLookupTableAccounts: alts,
399
+ });
400
+ }
401
+ catch (err) {
402
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Solana createInstructionsUnsignedTransaction: MessageV0.compile failed — instruction or ALT layout invalid`, { chainId: this.chainId }, err instanceof Error ? err : undefined);
403
+ }
404
+ const tx = new web3_js_1.VersionedTransaction(message);
405
+ let serializedSize;
406
+ try {
407
+ serializedSize = tx.serialize().length;
408
+ }
409
+ catch (err) {
410
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.TransactionTooLarge, `Compiled Solana tx exceeds the serialize buffer; supply addressLookupTables to compress the account list`, { chainId: this.chainId }, err instanceof Error ? err : undefined);
411
+ }
412
+ if (serializedSize > 1232) {
413
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.TransactionTooLarge, `Compiled Solana tx exceeds 1232-byte wire limit; supply addressLookupTables to compress the account list`, { chainId: this.chainId });
414
+ }
340
415
  return new unsigned_solana_transaction_ts_1.UnsignedSolanaTransaction({
341
416
  chainId: this.chainId,
342
- transaction: new web3_js_1.VersionedTransaction(message),
417
+ transaction: tx,
343
418
  feePayer: feePayerAddress,
344
419
  recentBlockhash: blockhash,
345
420
  lastValidBlockHeight,
346
421
  instructions: allIxs,
422
+ addressLookupTables: alts,
347
423
  });
348
424
  }
349
425
  /**
@@ -355,13 +431,14 @@ class SolanaChain extends chain_base_ts_1.Chain {
355
431
  */
356
432
  async refreshBlockhash(unsigned) {
357
433
  const connection = this.getConnection();
358
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('finalized');
434
+ const { blockhash, lastValidBlockHeight } = await this.rpcWrap(() => connection.getLatestBlockhash('finalized'), 'getLatestBlockhash');
359
435
  if (blockhash === unsigned.recentBlockhash)
360
436
  return unsigned;
361
437
  const message = web3_js_1.MessageV0.compile({
362
438
  payerKey: unsigned.feePayerPubkey,
363
439
  instructions: unsigned.instructions,
364
440
  recentBlockhash: blockhash,
441
+ addressLookupTableAccounts: unsigned.addressLookupTables,
365
442
  });
366
443
  return new unsigned_solana_transaction_ts_1.UnsignedSolanaTransaction({
367
444
  chainId: unsigned.chainId,
@@ -370,6 +447,7 @@ class SolanaChain extends chain_base_ts_1.Chain {
370
447
  recentBlockhash: blockhash,
371
448
  lastValidBlockHeight,
372
449
  instructions: unsigned.instructions,
450
+ addressLookupTables: unsigned.addressLookupTables,
373
451
  });
374
452
  }
375
453
  /**
@@ -380,13 +458,13 @@ class SolanaChain extends chain_base_ts_1.Chain {
380
458
  */
381
459
  async estimateAndApplyCu(unsigned) {
382
460
  const connection = this.getConnection();
383
- const sim = await connection.simulateTransaction(unsigned.transaction, {
461
+ const sim = await this.rpcWrap(() => connection.simulateTransaction(unsigned.transaction, {
384
462
  sigVerify: false,
385
463
  replaceRecentBlockhash: true,
386
464
  commitment: 'confirmed',
387
- });
465
+ }), 'simulateTransaction');
388
466
  if (sim.value.err) {
389
- throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Solana simulation rejected the tx: ${JSON.stringify(sim.value.err)}`, { chainId: this.chainId });
467
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.SimulationFailed, `Solana simulation rejected the tx: ${JSON.stringify(sim.value.err)}`, { chainId: this.chainId });
390
468
  }
391
469
  const consumed = sim.value.unitsConsumed;
392
470
  if (!consumed)
@@ -406,6 +484,7 @@ class SolanaChain extends chain_base_ts_1.Chain {
406
484
  payerKey: unsigned.feePayerPubkey,
407
485
  instructions: newInstructions,
408
486
  recentBlockhash: unsigned.recentBlockhash,
487
+ addressLookupTableAccounts: unsigned.addressLookupTables,
409
488
  });
410
489
  return new unsigned_solana_transaction_ts_1.UnsignedSolanaTransaction({
411
490
  chainId: unsigned.chainId,
@@ -414,11 +493,12 @@ class SolanaChain extends chain_base_ts_1.Chain {
414
493
  recentBlockhash: unsigned.recentBlockhash,
415
494
  lastValidBlockHeight: unsigned.lastValidBlockHeight,
416
495
  instructions: newInstructions,
496
+ addressLookupTables: unsigned.addressLookupTables,
417
497
  });
418
498
  }
419
499
  /** Whether the given error came from a preflight simulation rejection (vs network/transport). */
420
500
  static isSimulationError(err) {
421
- if (err instanceof errors_ts_1.ChainError && err.kind === errors_ts_1.ChainErrorKinds.InvalidArgument)
501
+ if (err instanceof errors_ts_1.ChainError && err.kind === errors_ts_1.ChainErrorKinds.SimulationFailed)
422
502
  return true;
423
503
  const msg = err?.message ?? '';
424
504
  return /simulation failed|transaction simulation failed/i.test(msg);
@@ -428,7 +508,68 @@ class SolanaChain extends chain_base_ts_1.Chain {
428
508
  const msg = err?.message ?? '';
429
509
  return /blockhash not found|expired blockhash|blockhash.*expired/i.test(msg);
430
510
  }
431
- async getTransactionStatus(txHash) {
511
+ async getTransactionStatus(txHash, opts) {
512
+ if (Array.isArray(txHash)) {
513
+ return runSolanaBatchStatus(txHash, (h, batchSignal) => {
514
+ const composedSignal = opts?.signal
515
+ ? anySignal(opts.signal, batchSignal)
516
+ : batchSignal;
517
+ return this.getSingleSolanaStatus(h, { ...opts, signal: composedSignal });
518
+ });
519
+ }
520
+ return this.getSingleSolanaStatus(txHash, opts);
521
+ }
522
+ async getSingleSolanaStatus(txHash, opts) {
523
+ if (opts?.confirmations !== undefined && opts.confirmations > 1 && !opts.wait) {
524
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `getTransactionStatus: confirmations > 1 requires wait: true (a single status read cannot enforce finality)`, { chainId: this.chainId, txHash });
525
+ }
526
+ if (!opts?.wait)
527
+ return this.getSolanaStatusOnce(txHash);
528
+ if (opts.signal?.aborted) {
529
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `getTransactionStatus aborted before first poll`, { chainId: this.chainId, txHash });
530
+ }
531
+ const c = opts.confirmations ?? 1;
532
+ if (c !== 1 && c < 32) {
533
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Solana getTransactionStatus.confirmations must be 1 (confirmed) or >=32 (finalized); ${c} would ambiguously map otherwise.`, { chainId: this.chainId, txHash });
534
+ }
535
+ if (opts.timeoutMs === undefined) {
536
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `getTransactionStatus: wait: true requires an explicit timeoutMs. Unbounded polling would pin an RPC worker + connection forever if the tx is dropped.`, { chainId: this.chainId, txHash });
537
+ }
538
+ if (opts.timeoutMs < 0) {
539
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `getTransactionStatus: timeoutMs must be >= 0`, { chainId: this.chainId, txHash });
540
+ }
541
+ const deadline = Date.now() + opts.timeoutMs;
542
+ const pollMs = Math.max(400, this.blockTimeSeconds * 1000);
543
+ const wantFinalized = c >= 32;
544
+ let last;
545
+ while (true) {
546
+ last = await this.getSolanaStatusOnce(txHash);
547
+ if (last.status === 'Success' || last.status === 'Failed') {
548
+ if (!wantFinalized)
549
+ return last;
550
+ let sigStatus;
551
+ try {
552
+ sigStatus = await this.getConnection().getSignatureStatus(txHash, { searchTransactionHistory: true });
553
+ }
554
+ catch (err) {
555
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Failed to poll signature status for finalization on ${this.name}`, this.resolvedRpcUrlForRedaction()), { chainId: this.chainId, txHash }, (0, errors_ts_1.sanitizeCause)(err, this.resolvedRpcUrlForRedaction()));
556
+ }
557
+ if (sigStatus.value?.confirmationStatus === 'finalized')
558
+ return last;
559
+ if (Date.now() >= deadline) {
560
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, `getTransactionStatus timed out after ${opts.timeoutMs}ms before reaching finalized; last confirmation status: ${sigStatus.value?.confirmationStatus ?? 'unknown'}. Consumer must NOT credit as final.`, { chainId: this.chainId, txHash });
561
+ }
562
+ }
563
+ else if (Date.now() >= deadline) {
564
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, `getTransactionStatus timed out after ${opts.timeoutMs}ms; last observed status was ${last.status}. Consumer must NOT credit as final AND must NOT re-sign — the tx may still land. Re-broadcast the same signed bytes or continue polling with the same txHash.`, { chainId: this.chainId, txHash });
565
+ }
566
+ await solanaInterruptibleSleep(pollMs, opts.signal);
567
+ if (opts.signal?.aborted) {
568
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `getTransactionStatus aborted mid-poll`, { chainId: this.chainId, txHash });
569
+ }
570
+ }
571
+ }
572
+ async getSolanaStatusOnce(txHash) {
432
573
  const connection = this.getConnection();
433
574
  // Best-effort read of the current rpc URL for message/cause scrubbing.
434
575
  // readRpcUrl throws when nothing is configured; there'd be no
@@ -536,7 +677,342 @@ class SolanaChain extends chain_base_ts_1.Chain {
536
677
  });
537
678
  }
538
679
  async getChainTipHeight() {
539
- return this.getConnection().getSlot('confirmed');
680
+ return this.rpcWrap(() => this.getConnection().getSlot('confirmed'), 'getSlot');
681
+ }
682
+ async broadcast(signed, opts) {
683
+ if (opts && opts.signal !== undefined) {
684
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.FeatureNotSupported, `Solana broadcast: signal is not honored in 0.3.0 (silently ignoring would let a caller conclude 'not sent' while the tx still lands). Cancellation returns in 0.3.1.`, { chainId: this.chainId });
685
+ }
686
+ let bytes;
687
+ if (typeof signed === 'string') {
688
+ const stripped = signed.startsWith('0x') ? signed.slice(2) : signed;
689
+ if (!/^[0-9a-fA-F]+$/.test(stripped) || stripped.length % 2 !== 0 || stripped.length === 0) {
690
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Solana broadcast: signed must be Uint8Array or 0x-prefixed hex; got malformed string (len ${signed.length}). Base58/base64 encodings are NOT accepted — decode to bytes first.`, { chainId: this.chainId });
691
+ }
692
+ bytes = new Uint8Array(Buffer.from(stripped, 'hex'));
693
+ }
694
+ else {
695
+ bytes = signed;
696
+ }
697
+ if (bytes.length < 65) {
698
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Solana broadcast: signed bytes must be >= 65 bytes (1 sig-count + 64-byte signature), got ${bytes.length}`, { chainId: this.chainId });
699
+ }
700
+ if (bytes.length > 1232) {
701
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.TransactionTooLarge, `Solana broadcast: signed tx is ${bytes.length} bytes, exceeds 1232-byte wire limit`, { chainId: this.chainId });
702
+ }
703
+ if (opts?.via === 'jito') {
704
+ if (!this.jito) {
705
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.FeatureNotSupported, `Solana broadcast: via: 'jito' requires SolanaChain to be constructed with jito config`, { chainId: this.chainId });
706
+ }
707
+ if (opts.skipPreflight !== undefined || opts.maxRetries !== undefined) {
708
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Solana broadcast: skipPreflight/maxRetries do not apply to the Jito path (block-engine has its own retry semantics)`, { chainId: this.chainId });
709
+ }
710
+ const signature = signatureBase58FromBytes(bytes);
711
+ await this.submitJitoBundle([bytes]);
712
+ return signature;
713
+ }
714
+ try {
715
+ const sig = await this.getConnection().sendRawTransaction(bytes, {
716
+ skipPreflight: opts?.skipPreflight ?? false,
717
+ maxRetries: opts?.maxRetries,
718
+ });
719
+ return sig;
720
+ }
721
+ catch (err) {
722
+ const rawMsg = (err instanceof Error ? err.message : String(err)).toLowerCase();
723
+ if (rawMsg.includes('already been processed') || rawMsg.includes('already processed')) {
724
+ return signatureBase58FromBytes(bytes);
725
+ }
726
+ throw this.classifyBroadcastError(err);
727
+ }
728
+ }
729
+ async submitJitoBundle(signedTxs, opts) {
730
+ if (!this.jito) {
731
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.FeatureNotSupported, 'Jito bundle submission requires SolanaChain to be constructed with jito config', { chainId: this.chainId });
732
+ }
733
+ const body = {
734
+ jsonrpc: '2.0',
735
+ id: 1,
736
+ method: 'sendBundle',
737
+ params: [
738
+ signedTxs.map((b) => Buffer.from(b).toString('base64')),
739
+ { encoding: 'base64' },
740
+ ],
741
+ };
742
+ const jitoUrl = this.jito.url;
743
+ const signal = combineJitoSignal(opts?.signal, opts?.timeoutMs ?? 15_000);
744
+ let json;
745
+ try {
746
+ const resp = await fetch(jitoUrl, {
747
+ method: 'POST',
748
+ headers: {
749
+ 'Content-Type': 'application/json',
750
+ ...(this.jito.auth ? { Authorization: `Bearer ${this.jito.auth}` } : {}),
751
+ },
752
+ body: JSON.stringify(body),
753
+ signal,
754
+ });
755
+ if (!resp.ok) {
756
+ // 401/403 = jito auth config problem, not a bundle rejection.
757
+ // Treat as RpcError so consumer doesn't push the tx toward re-sign.
758
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Jito submitBundle HTTP ${resp.status} on ${this.name}`, jitoUrl), { chainId: this.chainId });
759
+ }
760
+ json = (await resp.json());
761
+ }
762
+ catch (err) {
763
+ if (err instanceof errors_ts_1.ChainError)
764
+ throw err;
765
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Jito submitBundle transport failed on ${this.name}`, jitoUrl), { chainId: this.chainId }, (0, errors_ts_1.sanitizeCause)(err, jitoUrl));
766
+ }
767
+ if (json.error || !json.result) {
768
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.BroadcastRejected, (0, errors_ts_1.sanitizeMessage)(`Jito submitBundle rejected on ${this.name}: ${json.error?.message ?? 'no bundle id returned'}`, jitoUrl), { chainId: this.chainId });
769
+ }
770
+ return json.result;
771
+ }
772
+ async getBundleStatus(bundleId, opts) {
773
+ if (!this.jito) {
774
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.FeatureNotSupported, 'Jito getBundleStatus requires SolanaChain to be constructed with jito config', { chainId: this.chainId });
775
+ }
776
+ const ids = Array.isArray(bundleId) ? bundleId : [bundleId];
777
+ const body = {
778
+ jsonrpc: '2.0',
779
+ id: 1,
780
+ method: 'getBundleStatuses',
781
+ params: [ids],
782
+ };
783
+ let json;
784
+ const jitoUrl = this.jito.url;
785
+ const signal = combineJitoSignal(opts?.signal, opts?.timeoutMs ?? 15_000);
786
+ try {
787
+ const resp = await fetch(jitoUrl, {
788
+ method: 'POST',
789
+ headers: {
790
+ 'Content-Type': 'application/json',
791
+ ...(this.jito.auth ? { Authorization: `Bearer ${this.jito.auth}` } : {}),
792
+ },
793
+ body: JSON.stringify(body),
794
+ signal,
795
+ });
796
+ if (!resp.ok) {
797
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Jito getBundleStatuses HTTP ${resp.status} on ${this.name}`, jitoUrl), { chainId: this.chainId });
798
+ }
799
+ json = (await resp.json());
800
+ }
801
+ catch (err) {
802
+ if (err instanceof errors_ts_1.ChainError)
803
+ throw err;
804
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Jito getBundleStatuses transport failed on ${this.name}`, jitoUrl), { chainId: this.chainId }, (0, errors_ts_1.sanitizeCause)(err, jitoUrl));
805
+ }
806
+ if (json.error) {
807
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Jito getBundleStatuses error on ${this.name}: ${json.error.message ?? 'unknown'}`, jitoUrl), { chainId: this.chainId });
808
+ }
809
+ const rawValue = json.result?.value;
810
+ if (rawValue !== undefined && rawValue !== null && !Array.isArray(rawValue)) {
811
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Jito getBundleStatuses returned malformed value (expected array or null)`, jitoUrl), { chainId: this.chainId });
812
+ }
813
+ const rows = Array.isArray(rawValue) ? rawValue : [];
814
+ const statuses = ids.map((id) => {
815
+ const row = rows.find((r) => r != null && r.bundle_id === id);
816
+ if (!row)
817
+ return { bundleId: id, state: 'Pending' };
818
+ // Jito's err field varies: null (success), { Ok: null } (success),
819
+ // { Err: <anything> } (failed), or a plain string (some proxies).
820
+ // Anything that's non-null AND not { Ok: null } means failure.
821
+ const rawErr = row.err;
822
+ const isOk = rawErr === null || rawErr === undefined
823
+ || (typeof rawErr === 'object' && 'Ok' in rawErr && rawErr.Ok === null && !('Err' in rawErr));
824
+ const errStr = isOk
825
+ ? undefined
826
+ : typeof rawErr === 'string'
827
+ ? rawErr
828
+ : typeof rawErr === 'object' && rawErr !== null && 'Err' in rawErr
829
+ ? String(rawErr.Err)
830
+ : JSON.stringify(rawErr);
831
+ const confStatus = row.confirmation_status;
832
+ const state = errStr !== undefined
833
+ ? 'Failed'
834
+ : (confStatus === 'finalized' || confStatus === 'confirmed' ? 'Landed' : 'Pending');
835
+ return { bundleId: id, state, slot: row.slot, err: errStr };
836
+ });
837
+ return Array.isArray(bundleId) ? statuses : statuses[0];
838
+ }
839
+ async createUnsignedTransaction(req) {
840
+ if (req && req.signal !== undefined) {
841
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.FeatureNotSupported, `Solana createUnsignedTransaction: signal is not honored in 0.3.0 (silently ignoring would let a caller conclude 'not built' while getLatestBlockhash continues). Cancellation returns in 0.3.1.`, { chainId: this.chainId });
842
+ }
843
+ if (!req.payer) {
844
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, 'CreateSolanaUnsignedTransactionRequest.payer is required', { chainId: this.chainId });
845
+ }
846
+ let payerKey;
847
+ try {
848
+ payerKey = new web3_js_1.PublicKey(req.payer);
849
+ }
850
+ catch (err) {
851
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Invalid Solana payer pubkey: ${req.payer}`, { chainId: this.chainId, address: req.payer }, err instanceof Error ? err : undefined);
852
+ }
853
+ if (!Array.isArray(req.instructions) || req.instructions.length === 0) {
854
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, 'CreateSolanaUnsignedTransactionRequest.instructions must be a non-empty array', { chainId: this.chainId });
855
+ }
856
+ const connection = this.getConnection();
857
+ let blockhash;
858
+ let lastValidBlockHeight;
859
+ try {
860
+ const bh = await connection.getLatestBlockhash('finalized');
861
+ blockhash = bh.blockhash;
862
+ lastValidBlockHeight = bh.lastValidBlockHeight;
863
+ }
864
+ catch (err) {
865
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Solana createUnsignedTransaction: getLatestBlockhash failed on ${this.name}`, this.resolvedRpcUrlForRedaction()), { chainId: this.chainId }, (0, errors_ts_1.sanitizeCause)(err, this.resolvedRpcUrlForRedaction()));
866
+ }
867
+ let message;
868
+ try {
869
+ message = web3_js_1.MessageV0.compile({
870
+ payerKey,
871
+ instructions: req.instructions,
872
+ recentBlockhash: blockhash,
873
+ addressLookupTableAccounts: req.addressLookupTables ?? [],
874
+ });
875
+ }
876
+ catch (err) {
877
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Solana createUnsignedTransaction: MessageV0.compile failed — instruction or ALT layout invalid`, { chainId: this.chainId }, err instanceof Error ? err : undefined);
878
+ }
879
+ const tx = new web3_js_1.VersionedTransaction(message);
880
+ let serializedSize;
881
+ try {
882
+ serializedSize = tx.serialize().length;
883
+ }
884
+ catch (err) {
885
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.TransactionTooLarge, `Compiled Solana tx exceeds the serialize buffer; supply addressLookupTables to compress the account list`, { chainId: this.chainId }, err instanceof Error ? err : undefined);
886
+ }
887
+ if (serializedSize > 1232) {
888
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.TransactionTooLarge, `Compiled Solana tx exceeds 1232-byte wire limit; supply addressLookupTables to reduce size`, { chainId: this.chainId });
889
+ }
890
+ return new unsigned_solana_transaction_ts_1.UnsignedSolanaTransaction({
891
+ chainId: this.chainId,
892
+ transaction: tx,
893
+ feePayer: req.payer,
894
+ recentBlockhash: blockhash,
895
+ lastValidBlockHeight,
896
+ instructions: req.instructions,
897
+ addressLookupTables: req.addressLookupTables,
898
+ });
899
+ }
900
+ async getAccountInfo(pubkey) {
901
+ const connection = this.getConnection();
902
+ const toPk = (raw) => {
903
+ try {
904
+ return new web3_js_1.PublicKey(raw);
905
+ }
906
+ catch (err) {
907
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Invalid Solana pubkey: ${raw}`, { chainId: this.chainId, address: raw }, err instanceof Error ? err : undefined);
908
+ }
909
+ };
910
+ if (Array.isArray(pubkey)) {
911
+ const pks = pubkey.map(toPk);
912
+ try {
913
+ const infos = await connection.getMultipleAccountsInfo(pks);
914
+ return infos.map((info) => (info === null ? null : toAccountInfoResult(info)));
915
+ }
916
+ catch (err) {
917
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Solana getMultipleAccountsInfo RPC failure on ${this.name}`, this.resolvedRpcUrlForRedaction()), { chainId: this.chainId }, (0, errors_ts_1.sanitizeCause)(err, this.resolvedRpcUrlForRedaction()));
918
+ }
919
+ }
920
+ try {
921
+ const info = await connection.getAccountInfo(toPk(pubkey));
922
+ return info === null ? null : toAccountInfoResult(info);
923
+ }
924
+ catch (err) {
925
+ if (err instanceof errors_ts_1.ChainError)
926
+ throw err;
927
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Solana getAccountInfo RPC failure on ${this.name}`, this.resolvedRpcUrlForRedaction()), { chainId: this.chainId, address: pubkey }, (0, errors_ts_1.sanitizeCause)(err, this.resolvedRpcUrlForRedaction()));
928
+ }
929
+ }
930
+ async getTokenAccount(owner, mint, opts) {
931
+ let ownerPk;
932
+ let mintPk;
933
+ try {
934
+ ownerPk = new web3_js_1.PublicKey(owner);
935
+ }
936
+ catch (err) {
937
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Invalid Solana owner pubkey: ${owner}`, { chainId: this.chainId, address: owner }, err instanceof Error ? err : undefined);
938
+ }
939
+ try {
940
+ mintPk = new web3_js_1.PublicKey(mint);
941
+ }
942
+ catch (err) {
943
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidTokenIdentifier, `Invalid Solana mint pubkey: ${mint}`, { chainId: this.chainId, identifier: mint }, err instanceof Error ? err : undefined);
944
+ }
945
+ let programId;
946
+ try {
947
+ programId = await this.resolveTokenProgramId(mintPk);
948
+ }
949
+ catch (err) {
950
+ if (err instanceof errors_ts_1.ChainError)
951
+ throw err;
952
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Solana getTokenAccount: mint-program lookup failed on ${this.name}`, this.resolvedRpcUrlForRedaction()), { chainId: this.chainId, identifier: mint }, (0, errors_ts_1.sanitizeCause)(err, this.resolvedRpcUrlForRedaction()));
953
+ }
954
+ let ataPk;
955
+ try {
956
+ ataPk = (0, spl_token_1.getAssociatedTokenAddressSync)(mintPk, ownerPk, opts?.allowOwnerOffCurve ?? false, programId);
957
+ }
958
+ catch (err) {
959
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `ATA derivation failed: ${err instanceof Error ? err.message : String(err)}`, { chainId: this.chainId, address: owner }, err instanceof Error ? err : undefined);
960
+ }
961
+ try {
962
+ const acc = await (0, spl_token_1.getAccount)(this.getConnection(), ataPk, undefined, programId);
963
+ return { ata: ataPk.toBase58(), exists: true, balanceMr: acc.amount };
964
+ }
965
+ catch (err) {
966
+ if (err instanceof spl_token_1.TokenAccountNotFoundError) {
967
+ return { ata: ataPk.toBase58(), exists: false, balanceMr: undefined };
968
+ }
969
+ if (err instanceof spl_token_1.TokenInvalidAccountOwnerError) {
970
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Solana getTokenAccount: account at ${ataPk.toBase58()} exists but is owned by an unexpected program (hostile squat or wrong mint program). Consumer should NOT create-ATA blindly.`, { chainId: this.chainId, address: ataPk.toBase58() }, err);
971
+ }
972
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Solana getTokenAccount RPC failure on ${this.name}`, this.resolvedRpcUrlForRedaction()), { chainId: this.chainId, address: ataPk.toBase58() }, (0, errors_ts_1.sanitizeCause)(err, this.resolvedRpcUrlForRedaction()));
973
+ }
974
+ }
975
+ async fetchAddressLookupTable(altAddress) {
976
+ let pk;
977
+ try {
978
+ pk = new web3_js_1.PublicKey(altAddress);
979
+ }
980
+ catch (err) {
981
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidAddress, `Invalid ALT pubkey: ${altAddress}`, { chainId: this.chainId, address: altAddress }, err instanceof Error ? err : undefined);
982
+ }
983
+ let res;
984
+ try {
985
+ res = await this.getConnection().getAddressLookupTable(pk);
986
+ }
987
+ catch (err) {
988
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Solana fetchAddressLookupTable RPC failure on ${this.name}`, this.resolvedRpcUrlForRedaction()), { chainId: this.chainId, address: altAddress }, (0, errors_ts_1.sanitizeCause)(err, this.resolvedRpcUrlForRedaction()));
989
+ }
990
+ if (!res.value) {
991
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Address lookup table ${altAddress} not found`, { chainId: this.chainId, address: altAddress });
992
+ }
993
+ return res.value;
994
+ }
995
+ classifyBroadcastError(err) {
996
+ const rpc = this.resolvedRpcUrlForRedaction();
997
+ const msg = err instanceof Error ? err.message.toLowerCase() : String(err).toLowerCase();
998
+ const safeCause = (0, errors_ts_1.sanitizeCause)(err, rpc);
999
+ const transportSignals = /econnreset|econnrefused|econnaborted|etimedout|enotfound|network request failed|fetch failed|socket hang up|too\s+many\s+requests|rate.?limit/;
1000
+ if (/blockhash\s+not\s+found|blockhash\s+expired|block\s+height\s+exceeded/.test(msg)) {
1001
+ return new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.BlockhashExpired, (0, errors_ts_1.sanitizeMessage)(`Solana broadcast rejected: blockhash expired on ${this.name}`, rpc), { chainId: this.chainId }, safeCause);
1002
+ }
1003
+ // Preflight/simulation MUST be checked before the size predicate — Solana
1004
+ // preflight errors routinely embed "exceeds <N>" for CU / account-count
1005
+ // limits, and would otherwise be misclassified as TransactionTooLarge.
1006
+ if (msg.includes('preflight') || msg.includes('simulation failed') || msg.includes('sendtransactionerror')) {
1007
+ return new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.SimulationFailed, (0, errors_ts_1.sanitizeMessage)(`Solana broadcast rejected: preflight simulation failed on ${this.name}`, rpc), { chainId: this.chainId }, safeCause);
1008
+ }
1009
+ if (/transaction\s+too\s+large|encoded\/raw transaction size exceeds|exceeds\s+the\s+maximum\s+transaction\s+size/.test(msg)) {
1010
+ return new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.TransactionTooLarge, (0, errors_ts_1.sanitizeMessage)(`Solana broadcast rejected: transaction exceeds 1232 bytes on ${this.name} (use ALT)`, rpc), { chainId: this.chainId }, safeCause);
1011
+ }
1012
+ if (transportSignals.test(msg)) {
1013
+ return new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.RpcError, (0, errors_ts_1.sanitizeMessage)(`Solana broadcast RPC transport failure on ${this.name}`, rpc), { chainId: this.chainId }, safeCause);
1014
+ }
1015
+ return new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.BroadcastRejected, (0, errors_ts_1.sanitizeMessage)(`Solana broadcast rejected on ${this.name}`, rpc), { chainId: this.chainId }, safeCause);
540
1016
  }
541
1017
  /**
542
1018
  * Resolves the token program owner for a given mint. Token-2022 mints are owned by
@@ -705,4 +1181,125 @@ class SolanaChain extends chain_base_ts_1.Chain {
705
1181
  }
706
1182
  }
707
1183
  exports.SolanaChain = SolanaChain;
1184
+ function validateAltList(input, chainId) {
1185
+ if (input === undefined || input === null)
1186
+ return [];
1187
+ if (!Array.isArray(input)) {
1188
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `addressLookupTables must be an array of AddressLookupTableAccount, got ${typeof input}`, { chainId });
1189
+ }
1190
+ for (let i = 0; i < input.length; i++) {
1191
+ const a = input[i];
1192
+ if (a === null || typeof a !== 'object' || !('key' in a) || !('state' in a)) {
1193
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `addressLookupTables[${i}] is not an AddressLookupTableAccount (missing key/state). Use SolanaChain.fetchAddressLookupTable to obtain valid instances.`, { chainId });
1194
+ }
1195
+ }
1196
+ return input;
1197
+ }
1198
+ function signatureBase58FromBytes(txBytes) {
1199
+ if (txBytes.length < 65) {
1200
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Solana broadcast: signed bytes too short (${txBytes.length}) to extract signature`, {});
1201
+ }
1202
+ const numSigs = txBytes[0];
1203
+ if (numSigs === 0 || txBytes.length < 1 + 64) {
1204
+ throw new errors_ts_1.ChainError(errors_ts_1.ChainErrorKinds.InvalidArgument, `Solana broadcast: no signatures in serialized transaction`, {});
1205
+ }
1206
+ const sigBytes = txBytes.subarray(1, 1 + 64);
1207
+ return bs58encode(sigBytes);
1208
+ }
1209
+ const BS58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
1210
+ function bs58encode(bytes) {
1211
+ let n = 0n;
1212
+ for (const b of bytes)
1213
+ n = (n << 8n) | BigInt(b);
1214
+ let out = '';
1215
+ while (n > 0n) {
1216
+ const rem = Number(n % 58n);
1217
+ out = BS58_ALPHABET[rem] + out;
1218
+ n = n / 58n;
1219
+ }
1220
+ for (const b of bytes) {
1221
+ if (b === 0)
1222
+ out = '1' + out;
1223
+ else
1224
+ break;
1225
+ }
1226
+ return out;
1227
+ }
1228
+ const SOLANA_BATCH_STATUS_CONCURRENCY = 8;
1229
+ async function runSolanaBatchStatus(items, fetchOne) {
1230
+ const results = new Array(items.length);
1231
+ const ac = new AbortController();
1232
+ let cursor = 0;
1233
+ const workers = [];
1234
+ const spawn = async () => {
1235
+ while (!ac.signal.aborted) {
1236
+ const idx = cursor++;
1237
+ if (idx >= items.length)
1238
+ return;
1239
+ try {
1240
+ results[idx] = await fetchOne(items[idx], ac.signal);
1241
+ }
1242
+ catch (err) {
1243
+ ac.abort();
1244
+ throw err;
1245
+ }
1246
+ }
1247
+ };
1248
+ const workerCount = Math.min(SOLANA_BATCH_STATUS_CONCURRENCY, items.length);
1249
+ for (let i = 0; i < workerCount; i++)
1250
+ workers.push(spawn());
1251
+ await Promise.all(workers);
1252
+ return results;
1253
+ }
1254
+ function anySignal(a, b) {
1255
+ if (typeof AbortSignal.any === 'function')
1256
+ return AbortSignal.any([a, b]);
1257
+ const ac = new AbortController();
1258
+ const onAbort = () => ac.abort();
1259
+ if (a.aborted || b.aborted)
1260
+ ac.abort();
1261
+ else {
1262
+ a.addEventListener('abort', onAbort, { once: true });
1263
+ b.addEventListener('abort', onAbort, { once: true });
1264
+ }
1265
+ return ac.signal;
1266
+ }
1267
+ function combineJitoSignal(consumerSignal, timeoutMs) {
1268
+ const timeout = AbortSignal.timeout(timeoutMs);
1269
+ if (!consumerSignal)
1270
+ return timeout;
1271
+ const ac = new AbortController();
1272
+ const onAbort = () => ac.abort();
1273
+ if (consumerSignal.aborted || timeout.aborted)
1274
+ ac.abort();
1275
+ else {
1276
+ consumerSignal.addEventListener('abort', onAbort, { once: true });
1277
+ timeout.addEventListener('abort', onAbort, { once: true });
1278
+ }
1279
+ return ac.signal;
1280
+ }
1281
+ async function solanaInterruptibleSleep(ms, signal) {
1282
+ if (signal?.aborted)
1283
+ return;
1284
+ await new Promise((resolve) => {
1285
+ const t = setTimeout(() => {
1286
+ signal?.removeEventListener('abort', onAbort);
1287
+ resolve();
1288
+ }, ms);
1289
+ const onAbort = () => {
1290
+ clearTimeout(t);
1291
+ resolve();
1292
+ };
1293
+ signal?.addEventListener('abort', onAbort, { once: true });
1294
+ });
1295
+ }
1296
+ function toAccountInfoResult(info) {
1297
+ return {
1298
+ owner: info.owner.toBase58(),
1299
+ lamports: BigInt(info.lamports),
1300
+ data: Uint8Array.from(info.data),
1301
+ executable: info.executable,
1302
+ rentEpoch: info.rentEpoch === undefined ? undefined : BigInt(info.rentEpoch),
1303
+ };
1304
+ }
708
1305
  //# sourceMappingURL=solana_chain.js.map