@solana/web3.js 1.41.8 → 1.42.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.
@@ -2376,9 +2376,17 @@ function assert (condition, message) {
2376
2376
  }
2377
2377
  }
2378
2378
 
2379
+ exports.TransactionStatus = void 0;
2379
2380
  /**
2380
2381
  * Default (empty) signature
2381
2382
  */
2383
+
2384
+ (function (TransactionStatus) {
2385
+ TransactionStatus[TransactionStatus["BLOCKHEIGHT_EXCEEDED"] = 0] = "BLOCKHEIGHT_EXCEEDED";
2386
+ TransactionStatus[TransactionStatus["PROCESSED"] = 1] = "PROCESSED";
2387
+ TransactionStatus[TransactionStatus["TIMED_OUT"] = 2] = "TIMED_OUT";
2388
+ })(exports.TransactionStatus || (exports.TransactionStatus = {}));
2389
+
2382
2390
  const DEFAULT_SIGNATURE = buffer.Buffer.alloc(SIGNATURE_LENGTH_IN_BYTES).fill(0);
2383
2391
  /**
2384
2392
  * Account metadata used to define instructions
@@ -2469,10 +2477,23 @@ class Transaction {
2469
2477
  this.feePayer = void 0;
2470
2478
  this.instructions = [];
2471
2479
  this.recentBlockhash = void 0;
2480
+ this.lastValidBlockHeight = void 0;
2472
2481
  this.nonceInfo = void 0;
2473
2482
  this._message = void 0;
2474
2483
  this._json = void 0;
2475
- opts && Object.assign(this, opts);
2484
+
2485
+ if (!opts) {
2486
+ return;
2487
+ } else if (Object.prototype.hasOwnProperty.call(opts, 'lastValidBlockHeight')) {
2488
+ const newOpts = opts;
2489
+ Object.assign(this, newOpts);
2490
+ this.recentBlockhash = newOpts.blockhash;
2491
+ this.lastValidBlockHeight = newOpts.lastValidBlockHeight;
2492
+ } else {
2493
+ const oldOpts = opts;
2494
+ Object.assign(this, oldOpts);
2495
+ this.recentBlockhash = oldOpts.recentBlockhash;
2496
+ }
2476
2497
  }
2477
2498
  /**
2478
2499
  * @internal
@@ -3082,7 +3103,11 @@ async function sendAndConfirmTransaction(connection, transaction, signers, optio
3082
3103
  maxRetries: options.maxRetries
3083
3104
  };
3084
3105
  const signature = await connection.sendTransaction(transaction, signers, sendOptions);
3085
- const status = (await connection.confirmTransaction(signature, options && options.commitment)).value;
3106
+ const status = transaction.recentBlockhash != null && transaction.lastValidBlockHeight != null ? (await connection.confirmTransaction({
3107
+ signature: signature,
3108
+ blockhash: transaction.recentBlockhash,
3109
+ lastValidBlockHeight: transaction.lastValidBlockHeight
3110
+ }, options && options.commitment)).value : (await connection.confirmTransaction(signature, options && options.commitment)).value;
3086
3111
 
3087
3112
  if (status.err) {
3088
3113
  throw new Error(`Transaction ${signature} failed (${JSON.stringify(status)})`);
@@ -4994,16 +5019,28 @@ const NUM_SLOTS_PER_SECOND = NUM_TICKS_PER_SECOND / DEFAULT_TICKS_PER_SLOT;
4994
5019
 
4995
5020
  const MS_PER_SLOT = 1000 / NUM_SLOTS_PER_SECOND;
4996
5021
 
4997
- function promiseTimeout(promise, timeoutMs) {
4998
- let timeoutId;
4999
- const timeoutPromise = new Promise(resolve => {
5000
- timeoutId = setTimeout(() => resolve(null), timeoutMs);
5001
- });
5002
- return Promise.race([promise, timeoutPromise]).then(result => {
5003
- clearTimeout(timeoutId);
5004
- return result;
5005
- });
5022
+ class TransactionExpiredBlockheightExceededError extends Error {
5023
+ constructor(signature) {
5024
+ super(`Signature ${signature} has expired: block height exceeded.`);
5025
+ this.signature = void 0;
5026
+ this.signature = signature;
5027
+ }
5028
+
5029
+ }
5030
+ Object.defineProperty(TransactionExpiredBlockheightExceededError.prototype, 'name', {
5031
+ value: 'TransactionExpiredBlockheightExceededError'
5032
+ });
5033
+ class TransactionExpiredTimeoutError extends Error {
5034
+ constructor(signature, timeoutSeconds) {
5035
+ super(`Transaction was not confirmed in ${timeoutSeconds.toFixed(2)} seconds. It is ` + 'unknown if it succeeded or failed. Check signature ' + `${signature} using the Solana Explorer or CLI tools.`);
5036
+ this.signature = void 0;
5037
+ this.signature = signature;
5038
+ }
5039
+
5006
5040
  }
5041
+ Object.defineProperty(TransactionExpiredTimeoutError.prototype, 'name', {
5042
+ value: 'TransactionExpiredTimeoutError'
5043
+ });
5007
5044
 
5008
5045
  function makeWebsocketUrl(endpoint) {
5009
5046
  let url = new URL(endpoint);
@@ -5992,7 +6029,7 @@ class Connection {
5992
6029
  this._disableBlockhashCaching = false;
5993
6030
  this._pollingBlockhash = false;
5994
6031
  this._blockhashInfo = {
5995
- recentBlockhash: null,
6032
+ latestBlockhash: null,
5996
6033
  lastFetch: 0,
5997
6034
  transactionSignatures: [],
5998
6035
  simulatedSignatures: []
@@ -6473,67 +6510,124 @@ class Connection {
6473
6510
 
6474
6511
  return res.result;
6475
6512
  }
6476
- /**
6477
- * Confirm the transaction identified by the specified signature.
6478
- */
6479
6513
 
6514
+ // eslint-disable-next-line no-dupe-class-members
6515
+ async confirmTransaction(strategy, commitment) {
6516
+ let rawSignature;
6517
+
6518
+ if (typeof strategy == 'string') {
6519
+ rawSignature = strategy;
6520
+ } else {
6521
+ const config = strategy;
6522
+ rawSignature = config.signature;
6523
+ }
6480
6524
 
6481
- async confirmTransaction(signature, commitment) {
6482
6525
  let decodedSignature;
6483
6526
 
6484
6527
  try {
6485
- decodedSignature = bs58__default["default"].decode(signature);
6528
+ decodedSignature = bs58__default["default"].decode(rawSignature);
6486
6529
  } catch (err) {
6487
- throw new Error('signature must be base58 encoded: ' + signature);
6530
+ throw new Error('signature must be base58 encoded: ' + rawSignature);
6488
6531
  }
6489
6532
 
6490
6533
  assert(decodedSignature.length === 64, 'signature has invalid length');
6491
- const start = Date.now();
6492
6534
  const subscriptionCommitment = commitment || this.commitment;
6535
+ let timeoutId;
6493
6536
  let subscriptionId;
6494
- let response = null;
6495
- const confirmPromise = new Promise((resolve, reject) => {
6537
+ let done = false;
6538
+ const confirmationPromise = new Promise((resolve, reject) => {
6496
6539
  try {
6497
- subscriptionId = this.onSignature(signature, (result, context) => {
6540
+ subscriptionId = this.onSignature(rawSignature, (result, context) => {
6498
6541
  subscriptionId = undefined;
6499
- response = {
6542
+ const response = {
6500
6543
  context,
6501
6544
  value: result
6502
6545
  };
6503
- resolve(null);
6546
+ done = true;
6547
+ resolve({
6548
+ __type: exports.TransactionStatus.PROCESSED,
6549
+ response
6550
+ });
6504
6551
  }, subscriptionCommitment);
6505
6552
  } catch (err) {
6506
6553
  reject(err);
6507
6554
  }
6508
6555
  });
6509
- let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
6510
-
6511
- switch (subscriptionCommitment) {
6512
- case 'processed':
6513
- case 'recent':
6514
- case 'single':
6515
- case 'confirmed':
6516
- case 'singleGossip':
6517
- {
6518
- timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
6519
- break;
6556
+
6557
+ const checkBlockHeight = async () => {
6558
+ try {
6559
+ const blockHeight = await this.getBlockHeight(commitment);
6560
+ return blockHeight;
6561
+ } catch (_e) {
6562
+ return -1;
6563
+ }
6564
+ };
6565
+
6566
+ const expiryPromise = new Promise(resolve => {
6567
+ if (typeof strategy === 'string') {
6568
+ let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
6569
+
6570
+ switch (subscriptionCommitment) {
6571
+ case 'processed':
6572
+ case 'recent':
6573
+ case 'single':
6574
+ case 'confirmed':
6575
+ case 'singleGossip':
6576
+ {
6577
+ timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
6578
+ break;
6579
+ }
6520
6580
  }
6521
- }
6581
+
6582
+ timeoutId = setTimeout(() => resolve({
6583
+ __type: exports.TransactionStatus.TIMED_OUT,
6584
+ timeoutMs
6585
+ }), timeoutMs);
6586
+ } else {
6587
+ let config = strategy;
6588
+
6589
+ (async () => {
6590
+ let currentBlockHeight = await checkBlockHeight();
6591
+ if (done) return;
6592
+
6593
+ while (currentBlockHeight <= config.lastValidBlockHeight) {
6594
+ await sleep(1000);
6595
+ if (done) return;
6596
+ currentBlockHeight = await checkBlockHeight();
6597
+ if (done) return;
6598
+ }
6599
+
6600
+ resolve({
6601
+ __type: exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED
6602
+ });
6603
+ })();
6604
+ }
6605
+ });
6606
+ let result;
6522
6607
 
6523
6608
  try {
6524
- await promiseTimeout(confirmPromise, timeoutMs);
6609
+ const outcome = await Promise.race([confirmationPromise, expiryPromise]);
6610
+
6611
+ switch (outcome.__type) {
6612
+ case exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED:
6613
+ throw new TransactionExpiredBlockheightExceededError(rawSignature);
6614
+
6615
+ case exports.TransactionStatus.PROCESSED:
6616
+ result = outcome.response;
6617
+ break;
6618
+
6619
+ case exports.TransactionStatus.TIMED_OUT:
6620
+ throw new TransactionExpiredTimeoutError(rawSignature, outcome.timeoutMs / 1000);
6621
+ }
6525
6622
  } finally {
6623
+ clearTimeout(timeoutId);
6624
+
6526
6625
  if (subscriptionId) {
6527
6626
  this.removeSignatureListener(subscriptionId);
6528
6627
  }
6529
6628
  }
6530
6629
 
6531
- if (response === null) {
6532
- const duration = (Date.now() - start) / 1000;
6533
- throw new Error(`Transaction was not confirmed in ${duration.toFixed(2)} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`);
6534
- }
6535
-
6536
- return response;
6630
+ return result;
6537
6631
  }
6538
6632
  /**
6539
6633
  * Return the list of nodes that are currently participating in the cluster
@@ -6898,7 +6992,7 @@ class Connection {
6898
6992
  }
6899
6993
  /**
6900
6994
  * Fetch the latest blockhash from the cluster
6901
- * @return {Promise<{blockhash: Blockhash, lastValidBlockHeight: number}>}
6995
+ * @return {Promise<BlockhashWithExpiryBlockHeight>}
6902
6996
  */
6903
6997
 
6904
6998
 
@@ -6912,7 +7006,7 @@ class Connection {
6912
7006
  }
6913
7007
  /**
6914
7008
  * Fetch the latest blockhash from the cluster
6915
- * @return {Promise<{blockhash: Blockhash, lastValidBlockHeight: number}>}
7009
+ * @return {Promise<BlockhashWithExpiryBlockHeight>}
6916
7010
  */
6917
7011
 
6918
7012
 
@@ -7495,7 +7589,7 @@ class Connection {
7495
7589
  */
7496
7590
 
7497
7591
 
7498
- async _recentBlockhash(disableCache) {
7592
+ async _blockhashWithExpiryBlockHeight(disableCache) {
7499
7593
  if (!disableCache) {
7500
7594
  // Wait for polling to finish
7501
7595
  while (this._pollingBlockhash) {
@@ -7506,8 +7600,8 @@ class Connection {
7506
7600
 
7507
7601
  const expired = timeSinceFetch >= BLOCKHASH_CACHE_TIMEOUT_MS;
7508
7602
 
7509
- if (this._blockhashInfo.recentBlockhash !== null && !expired) {
7510
- return this._blockhashInfo.recentBlockhash;
7603
+ if (this._blockhashInfo.latestBlockhash !== null && !expired) {
7604
+ return this._blockhashInfo.latestBlockhash;
7511
7605
  }
7512
7606
  }
7513
7607
 
@@ -7523,20 +7617,20 @@ class Connection {
7523
7617
 
7524
7618
  try {
7525
7619
  const startTime = Date.now();
7620
+ const cachedLatestBlockhash = this._blockhashInfo.latestBlockhash;
7621
+ const cachedBlockhash = cachedLatestBlockhash ? cachedLatestBlockhash.blockhash : null;
7526
7622
 
7527
7623
  for (let i = 0; i < 50; i++) {
7528
- const {
7529
- blockhash
7530
- } = await this.getRecentBlockhash('finalized');
7624
+ const latestBlockhash = await this.getLatestBlockhash('finalized');
7531
7625
 
7532
- if (this._blockhashInfo.recentBlockhash != blockhash) {
7626
+ if (cachedBlockhash !== latestBlockhash.blockhash) {
7533
7627
  this._blockhashInfo = {
7534
- recentBlockhash: blockhash,
7628
+ latestBlockhash,
7535
7629
  lastFetch: Date.now(),
7536
7630
  transactionSignatures: [],
7537
7631
  simulatedSignatures: []
7538
7632
  };
7539
- return blockhash;
7633
+ return latestBlockhash;
7540
7634
  } // Sleep for approximately half a slot
7541
7635
 
7542
7636
 
@@ -7558,13 +7652,11 @@ class Connection {
7558
7652
 
7559
7653
  if (transactionOrMessage instanceof Transaction) {
7560
7654
  let originalTx = transactionOrMessage;
7561
- transaction = new Transaction({
7562
- recentBlockhash: originalTx.recentBlockhash,
7563
- nonceInfo: originalTx.nonceInfo,
7564
- feePayer: originalTx.feePayer,
7565
- signatures: [...originalTx.signatures]
7566
- });
7655
+ transaction = new Transaction();
7656
+ transaction.feePayer = originalTx.feePayer;
7567
7657
  transaction.instructions = transactionOrMessage.instructions;
7658
+ transaction.nonceInfo = originalTx.nonceInfo;
7659
+ transaction.signatures = originalTx.signatures;
7568
7660
  } else {
7569
7661
  transaction = Transaction.populate(transactionOrMessage); // HACK: this function relies on mutating the populated transaction
7570
7662
 
@@ -7577,7 +7669,9 @@ class Connection {
7577
7669
  let disableCache = this._disableBlockhashCaching;
7578
7670
 
7579
7671
  for (;;) {
7580
- transaction.recentBlockhash = await this._recentBlockhash(disableCache);
7672
+ const latestBlockhash = await this._blockhashWithExpiryBlockHeight(disableCache);
7673
+ transaction.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
7674
+ transaction.recentBlockhash = latestBlockhash.blockhash;
7581
7675
  if (!signers) break;
7582
7676
  transaction.sign(...signers);
7583
7677
 
@@ -7661,7 +7755,9 @@ class Connection {
7661
7755
  let disableCache = this._disableBlockhashCaching;
7662
7756
 
7663
7757
  for (;;) {
7664
- transaction.recentBlockhash = await this._recentBlockhash(disableCache);
7758
+ const latestBlockhash = await this._blockhashWithExpiryBlockHeight(disableCache);
7759
+ transaction.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
7760
+ transaction.recentBlockhash = latestBlockhash.blockhash;
7665
7761
  transaction.sign(...signers);
7666
7762
 
7667
7763
  if (!transaction.signature) {
@@ -7761,6 +7857,7 @@ class Connection {
7761
7857
 
7762
7858
 
7763
7859
  _wsOnError(err) {
7860
+ this._rpcWebSocketConnected = false;
7764
7861
  console.error('ws error:', err.message);
7765
7862
  }
7766
7863
  /**
@@ -7769,6 +7866,7 @@ class Connection {
7769
7866
 
7770
7867
 
7771
7868
  _wsOnClose(code) {
7869
+ this._rpcWebSocketConnected = false;
7772
7870
  this._rpcWebSocketGeneration++;
7773
7871
 
7774
7872
  if (this._rpcWebSocketHeartbeat) {
@@ -10100,16 +10198,36 @@ VoteProgram.space = 3731;
10100
10198
  *
10101
10199
  * @param {Connection} connection
10102
10200
  * @param {Buffer} rawTransaction
10201
+ * @param {BlockheightBasedTransactionConfimationStrategy} confirmationStrategy
10103
10202
  * @param {ConfirmOptions} [options]
10104
10203
  * @returns {Promise<TransactionSignature>}
10105
10204
  */
10106
- async function sendAndConfirmRawTransaction(connection, rawTransaction, options) {
10205
+
10206
+ /**
10207
+ * @deprecated Calling `sendAndConfirmRawTransaction()` without a `confirmationStrategy`
10208
+ * is no longer supported and will be removed in a future version.
10209
+ */
10210
+ // eslint-disable-next-line no-redeclare
10211
+ // eslint-disable-next-line no-redeclare
10212
+ async function sendAndConfirmRawTransaction(connection, rawTransaction, confirmationStrategyOrConfirmOptions, maybeConfirmOptions) {
10213
+ let confirmationStrategy;
10214
+ let options;
10215
+
10216
+ if (confirmationStrategyOrConfirmOptions && Object.prototype.hasOwnProperty.call(confirmationStrategyOrConfirmOptions, 'lastValidBlockHeight')) {
10217
+ confirmationStrategy = confirmationStrategyOrConfirmOptions;
10218
+ options = maybeConfirmOptions;
10219
+ } else {
10220
+ options = confirmationStrategyOrConfirmOptions;
10221
+ }
10222
+
10107
10223
  const sendOptions = options && {
10108
10224
  skipPreflight: options.skipPreflight,
10109
10225
  preflightCommitment: options.preflightCommitment || options.commitment
10110
10226
  };
10111
10227
  const signature = await connection.sendRawTransaction(rawTransaction, sendOptions);
10112
- const status = (await connection.confirmTransaction(signature, options && options.commitment)).value;
10228
+ const commitment = options && options.commitment;
10229
+ const confirmationPromise = confirmationStrategy ? connection.confirmTransaction(confirmationStrategy, commitment) : connection.confirmTransaction(signature, commitment);
10230
+ const status = (await confirmationPromise).value;
10113
10231
 
10114
10232
  if (status.err) {
10115
10233
  throw new Error(`Raw transaction ${signature} failed (${JSON.stringify(status)})`);