@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.
package/lib/index.cjs.js CHANGED
@@ -2388,9 +2388,17 @@ function assert (condition, message) {
2388
2388
  }
2389
2389
  }
2390
2390
 
2391
+ exports.TransactionStatus = void 0;
2391
2392
  /**
2392
2393
  * Default (empty) signature
2393
2394
  */
2395
+
2396
+ (function (TransactionStatus) {
2397
+ TransactionStatus[TransactionStatus["BLOCKHEIGHT_EXCEEDED"] = 0] = "BLOCKHEIGHT_EXCEEDED";
2398
+ TransactionStatus[TransactionStatus["PROCESSED"] = 1] = "PROCESSED";
2399
+ TransactionStatus[TransactionStatus["TIMED_OUT"] = 2] = "TIMED_OUT";
2400
+ })(exports.TransactionStatus || (exports.TransactionStatus = {}));
2401
+
2394
2402
  const DEFAULT_SIGNATURE = buffer.Buffer.alloc(SIGNATURE_LENGTH_IN_BYTES).fill(0);
2395
2403
  /**
2396
2404
  * Account metadata used to define instructions
@@ -2481,10 +2489,23 @@ class Transaction {
2481
2489
  this.feePayer = void 0;
2482
2490
  this.instructions = [];
2483
2491
  this.recentBlockhash = void 0;
2492
+ this.lastValidBlockHeight = void 0;
2484
2493
  this.nonceInfo = void 0;
2485
2494
  this._message = void 0;
2486
2495
  this._json = void 0;
2487
- opts && Object.assign(this, opts);
2496
+
2497
+ if (!opts) {
2498
+ return;
2499
+ } else if (Object.prototype.hasOwnProperty.call(opts, 'lastValidBlockHeight')) {
2500
+ const newOpts = opts;
2501
+ Object.assign(this, newOpts);
2502
+ this.recentBlockhash = newOpts.blockhash;
2503
+ this.lastValidBlockHeight = newOpts.lastValidBlockHeight;
2504
+ } else {
2505
+ const oldOpts = opts;
2506
+ Object.assign(this, oldOpts);
2507
+ this.recentBlockhash = oldOpts.recentBlockhash;
2508
+ }
2488
2509
  }
2489
2510
  /**
2490
2511
  * @internal
@@ -3094,7 +3115,11 @@ async function sendAndConfirmTransaction(connection, transaction, signers, optio
3094
3115
  maxRetries: options.maxRetries
3095
3116
  };
3096
3117
  const signature = await connection.sendTransaction(transaction, signers, sendOptions);
3097
- const status = (await connection.confirmTransaction(signature, options && options.commitment)).value;
3118
+ const status = transaction.recentBlockhash != null && transaction.lastValidBlockHeight != null ? (await connection.confirmTransaction({
3119
+ signature: signature,
3120
+ blockhash: transaction.recentBlockhash,
3121
+ lastValidBlockHeight: transaction.lastValidBlockHeight
3122
+ }, options && options.commitment)).value : (await connection.confirmTransaction(signature, options && options.commitment)).value;
3098
3123
 
3099
3124
  if (status.err) {
3100
3125
  throw new Error(`Transaction ${signature} failed (${JSON.stringify(status)})`);
@@ -4494,16 +4519,28 @@ const NUM_SLOTS_PER_SECOND = NUM_TICKS_PER_SECOND / DEFAULT_TICKS_PER_SLOT;
4494
4519
 
4495
4520
  const MS_PER_SLOT = 1000 / NUM_SLOTS_PER_SECOND;
4496
4521
 
4497
- function promiseTimeout(promise, timeoutMs) {
4498
- let timeoutId;
4499
- const timeoutPromise = new Promise(resolve => {
4500
- timeoutId = setTimeout(() => resolve(null), timeoutMs);
4501
- });
4502
- return Promise.race([promise, timeoutPromise]).then(result => {
4503
- clearTimeout(timeoutId);
4504
- return result;
4505
- });
4522
+ class TransactionExpiredBlockheightExceededError extends Error {
4523
+ constructor(signature) {
4524
+ super(`Signature ${signature} has expired: block height exceeded.`);
4525
+ this.signature = void 0;
4526
+ this.signature = signature;
4527
+ }
4528
+
4529
+ }
4530
+ Object.defineProperty(TransactionExpiredBlockheightExceededError.prototype, 'name', {
4531
+ value: 'TransactionExpiredBlockheightExceededError'
4532
+ });
4533
+ class TransactionExpiredTimeoutError extends Error {
4534
+ constructor(signature, timeoutSeconds) {
4535
+ 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.`);
4536
+ this.signature = void 0;
4537
+ this.signature = signature;
4538
+ }
4539
+
4506
4540
  }
4541
+ Object.defineProperty(TransactionExpiredTimeoutError.prototype, 'name', {
4542
+ value: 'TransactionExpiredTimeoutError'
4543
+ });
4507
4544
 
4508
4545
  function makeWebsocketUrl(endpoint) {
4509
4546
  let url = new URL(endpoint);
@@ -5498,7 +5535,7 @@ class Connection {
5498
5535
  this._disableBlockhashCaching = false;
5499
5536
  this._pollingBlockhash = false;
5500
5537
  this._blockhashInfo = {
5501
- recentBlockhash: null,
5538
+ latestBlockhash: null,
5502
5539
  lastFetch: 0,
5503
5540
  transactionSignatures: [],
5504
5541
  simulatedSignatures: []
@@ -5979,67 +6016,124 @@ class Connection {
5979
6016
 
5980
6017
  return res.result;
5981
6018
  }
5982
- /**
5983
- * Confirm the transaction identified by the specified signature.
5984
- */
5985
6019
 
6020
+ // eslint-disable-next-line no-dupe-class-members
6021
+ async confirmTransaction(strategy, commitment) {
6022
+ let rawSignature;
6023
+
6024
+ if (typeof strategy == 'string') {
6025
+ rawSignature = strategy;
6026
+ } else {
6027
+ const config = strategy;
6028
+ rawSignature = config.signature;
6029
+ }
5986
6030
 
5987
- async confirmTransaction(signature, commitment) {
5988
6031
  let decodedSignature;
5989
6032
 
5990
6033
  try {
5991
- decodedSignature = bs58__default["default"].decode(signature);
6034
+ decodedSignature = bs58__default["default"].decode(rawSignature);
5992
6035
  } catch (err) {
5993
- throw new Error('signature must be base58 encoded: ' + signature);
6036
+ throw new Error('signature must be base58 encoded: ' + rawSignature);
5994
6037
  }
5995
6038
 
5996
6039
  assert(decodedSignature.length === 64, 'signature has invalid length');
5997
- const start = Date.now();
5998
6040
  const subscriptionCommitment = commitment || this.commitment;
6041
+ let timeoutId;
5999
6042
  let subscriptionId;
6000
- let response = null;
6001
- const confirmPromise = new Promise((resolve, reject) => {
6043
+ let done = false;
6044
+ const confirmationPromise = new Promise((resolve, reject) => {
6002
6045
  try {
6003
- subscriptionId = this.onSignature(signature, (result, context) => {
6046
+ subscriptionId = this.onSignature(rawSignature, (result, context) => {
6004
6047
  subscriptionId = undefined;
6005
- response = {
6048
+ const response = {
6006
6049
  context,
6007
6050
  value: result
6008
6051
  };
6009
- resolve(null);
6052
+ done = true;
6053
+ resolve({
6054
+ __type: exports.TransactionStatus.PROCESSED,
6055
+ response
6056
+ });
6010
6057
  }, subscriptionCommitment);
6011
6058
  } catch (err) {
6012
6059
  reject(err);
6013
6060
  }
6014
6061
  });
6015
- let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
6016
-
6017
- switch (subscriptionCommitment) {
6018
- case 'processed':
6019
- case 'recent':
6020
- case 'single':
6021
- case 'confirmed':
6022
- case 'singleGossip':
6023
- {
6024
- timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
6025
- break;
6062
+
6063
+ const checkBlockHeight = async () => {
6064
+ try {
6065
+ const blockHeight = await this.getBlockHeight(commitment);
6066
+ return blockHeight;
6067
+ } catch (_e) {
6068
+ return -1;
6069
+ }
6070
+ };
6071
+
6072
+ const expiryPromise = new Promise(resolve => {
6073
+ if (typeof strategy === 'string') {
6074
+ let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
6075
+
6076
+ switch (subscriptionCommitment) {
6077
+ case 'processed':
6078
+ case 'recent':
6079
+ case 'single':
6080
+ case 'confirmed':
6081
+ case 'singleGossip':
6082
+ {
6083
+ timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
6084
+ break;
6085
+ }
6026
6086
  }
6027
- }
6087
+
6088
+ timeoutId = setTimeout(() => resolve({
6089
+ __type: exports.TransactionStatus.TIMED_OUT,
6090
+ timeoutMs
6091
+ }), timeoutMs);
6092
+ } else {
6093
+ let config = strategy;
6094
+
6095
+ (async () => {
6096
+ let currentBlockHeight = await checkBlockHeight();
6097
+ if (done) return;
6098
+
6099
+ while (currentBlockHeight <= config.lastValidBlockHeight) {
6100
+ await sleep(1000);
6101
+ if (done) return;
6102
+ currentBlockHeight = await checkBlockHeight();
6103
+ if (done) return;
6104
+ }
6105
+
6106
+ resolve({
6107
+ __type: exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED
6108
+ });
6109
+ })();
6110
+ }
6111
+ });
6112
+ let result;
6028
6113
 
6029
6114
  try {
6030
- await promiseTimeout(confirmPromise, timeoutMs);
6115
+ const outcome = await Promise.race([confirmationPromise, expiryPromise]);
6116
+
6117
+ switch (outcome.__type) {
6118
+ case exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED:
6119
+ throw new TransactionExpiredBlockheightExceededError(rawSignature);
6120
+
6121
+ case exports.TransactionStatus.PROCESSED:
6122
+ result = outcome.response;
6123
+ break;
6124
+
6125
+ case exports.TransactionStatus.TIMED_OUT:
6126
+ throw new TransactionExpiredTimeoutError(rawSignature, outcome.timeoutMs / 1000);
6127
+ }
6031
6128
  } finally {
6129
+ clearTimeout(timeoutId);
6130
+
6032
6131
  if (subscriptionId) {
6033
6132
  this.removeSignatureListener(subscriptionId);
6034
6133
  }
6035
6134
  }
6036
6135
 
6037
- if (response === null) {
6038
- const duration = (Date.now() - start) / 1000;
6039
- 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.`);
6040
- }
6041
-
6042
- return response;
6136
+ return result;
6043
6137
  }
6044
6138
  /**
6045
6139
  * Return the list of nodes that are currently participating in the cluster
@@ -6404,7 +6498,7 @@ class Connection {
6404
6498
  }
6405
6499
  /**
6406
6500
  * Fetch the latest blockhash from the cluster
6407
- * @return {Promise<{blockhash: Blockhash, lastValidBlockHeight: number}>}
6501
+ * @return {Promise<BlockhashWithExpiryBlockHeight>}
6408
6502
  */
6409
6503
 
6410
6504
 
@@ -6418,7 +6512,7 @@ class Connection {
6418
6512
  }
6419
6513
  /**
6420
6514
  * Fetch the latest blockhash from the cluster
6421
- * @return {Promise<{blockhash: Blockhash, lastValidBlockHeight: number}>}
6515
+ * @return {Promise<BlockhashWithExpiryBlockHeight>}
6422
6516
  */
6423
6517
 
6424
6518
 
@@ -7001,7 +7095,7 @@ class Connection {
7001
7095
  */
7002
7096
 
7003
7097
 
7004
- async _recentBlockhash(disableCache) {
7098
+ async _blockhashWithExpiryBlockHeight(disableCache) {
7005
7099
  if (!disableCache) {
7006
7100
  // Wait for polling to finish
7007
7101
  while (this._pollingBlockhash) {
@@ -7012,8 +7106,8 @@ class Connection {
7012
7106
 
7013
7107
  const expired = timeSinceFetch >= BLOCKHASH_CACHE_TIMEOUT_MS;
7014
7108
 
7015
- if (this._blockhashInfo.recentBlockhash !== null && !expired) {
7016
- return this._blockhashInfo.recentBlockhash;
7109
+ if (this._blockhashInfo.latestBlockhash !== null && !expired) {
7110
+ return this._blockhashInfo.latestBlockhash;
7017
7111
  }
7018
7112
  }
7019
7113
 
@@ -7029,20 +7123,20 @@ class Connection {
7029
7123
 
7030
7124
  try {
7031
7125
  const startTime = Date.now();
7126
+ const cachedLatestBlockhash = this._blockhashInfo.latestBlockhash;
7127
+ const cachedBlockhash = cachedLatestBlockhash ? cachedLatestBlockhash.blockhash : null;
7032
7128
 
7033
7129
  for (let i = 0; i < 50; i++) {
7034
- const {
7035
- blockhash
7036
- } = await this.getRecentBlockhash('finalized');
7130
+ const latestBlockhash = await this.getLatestBlockhash('finalized');
7037
7131
 
7038
- if (this._blockhashInfo.recentBlockhash != blockhash) {
7132
+ if (cachedBlockhash !== latestBlockhash.blockhash) {
7039
7133
  this._blockhashInfo = {
7040
- recentBlockhash: blockhash,
7134
+ latestBlockhash,
7041
7135
  lastFetch: Date.now(),
7042
7136
  transactionSignatures: [],
7043
7137
  simulatedSignatures: []
7044
7138
  };
7045
- return blockhash;
7139
+ return latestBlockhash;
7046
7140
  } // Sleep for approximately half a slot
7047
7141
 
7048
7142
 
@@ -7064,13 +7158,11 @@ class Connection {
7064
7158
 
7065
7159
  if (transactionOrMessage instanceof Transaction) {
7066
7160
  let originalTx = transactionOrMessage;
7067
- transaction = new Transaction({
7068
- recentBlockhash: originalTx.recentBlockhash,
7069
- nonceInfo: originalTx.nonceInfo,
7070
- feePayer: originalTx.feePayer,
7071
- signatures: [...originalTx.signatures]
7072
- });
7161
+ transaction = new Transaction();
7162
+ transaction.feePayer = originalTx.feePayer;
7073
7163
  transaction.instructions = transactionOrMessage.instructions;
7164
+ transaction.nonceInfo = originalTx.nonceInfo;
7165
+ transaction.signatures = originalTx.signatures;
7074
7166
  } else {
7075
7167
  transaction = Transaction.populate(transactionOrMessage); // HACK: this function relies on mutating the populated transaction
7076
7168
 
@@ -7083,7 +7175,9 @@ class Connection {
7083
7175
  let disableCache = this._disableBlockhashCaching;
7084
7176
 
7085
7177
  for (;;) {
7086
- transaction.recentBlockhash = await this._recentBlockhash(disableCache);
7178
+ const latestBlockhash = await this._blockhashWithExpiryBlockHeight(disableCache);
7179
+ transaction.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
7180
+ transaction.recentBlockhash = latestBlockhash.blockhash;
7087
7181
  if (!signers) break;
7088
7182
  transaction.sign(...signers);
7089
7183
 
@@ -7167,7 +7261,9 @@ class Connection {
7167
7261
  let disableCache = this._disableBlockhashCaching;
7168
7262
 
7169
7263
  for (;;) {
7170
- transaction.recentBlockhash = await this._recentBlockhash(disableCache);
7264
+ const latestBlockhash = await this._blockhashWithExpiryBlockHeight(disableCache);
7265
+ transaction.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
7266
+ transaction.recentBlockhash = latestBlockhash.blockhash;
7171
7267
  transaction.sign(...signers);
7172
7268
 
7173
7269
  if (!transaction.signature) {
@@ -7267,6 +7363,7 @@ class Connection {
7267
7363
 
7268
7364
 
7269
7365
  _wsOnError(err) {
7366
+ this._rpcWebSocketConnected = false;
7270
7367
  console.error('ws error:', err.message);
7271
7368
  }
7272
7369
  /**
@@ -7275,6 +7372,7 @@ class Connection {
7275
7372
 
7276
7373
 
7277
7374
  _wsOnClose(code) {
7375
+ this._rpcWebSocketConnected = false;
7278
7376
  this._rpcWebSocketGeneration++;
7279
7377
 
7280
7378
  if (this._rpcWebSocketHeartbeat) {
@@ -9606,16 +9704,36 @@ VoteProgram.space = 3731;
9606
9704
  *
9607
9705
  * @param {Connection} connection
9608
9706
  * @param {Buffer} rawTransaction
9707
+ * @param {BlockheightBasedTransactionConfimationStrategy} confirmationStrategy
9609
9708
  * @param {ConfirmOptions} [options]
9610
9709
  * @returns {Promise<TransactionSignature>}
9611
9710
  */
9612
- async function sendAndConfirmRawTransaction(connection, rawTransaction, options) {
9711
+
9712
+ /**
9713
+ * @deprecated Calling `sendAndConfirmRawTransaction()` without a `confirmationStrategy`
9714
+ * is no longer supported and will be removed in a future version.
9715
+ */
9716
+ // eslint-disable-next-line no-redeclare
9717
+ // eslint-disable-next-line no-redeclare
9718
+ async function sendAndConfirmRawTransaction(connection, rawTransaction, confirmationStrategyOrConfirmOptions, maybeConfirmOptions) {
9719
+ let confirmationStrategy;
9720
+ let options;
9721
+
9722
+ if (confirmationStrategyOrConfirmOptions && Object.prototype.hasOwnProperty.call(confirmationStrategyOrConfirmOptions, 'lastValidBlockHeight')) {
9723
+ confirmationStrategy = confirmationStrategyOrConfirmOptions;
9724
+ options = maybeConfirmOptions;
9725
+ } else {
9726
+ options = confirmationStrategyOrConfirmOptions;
9727
+ }
9728
+
9613
9729
  const sendOptions = options && {
9614
9730
  skipPreflight: options.skipPreflight,
9615
9731
  preflightCommitment: options.preflightCommitment || options.commitment
9616
9732
  };
9617
9733
  const signature = await connection.sendRawTransaction(rawTransaction, sendOptions);
9618
- const status = (await connection.confirmTransaction(signature, options && options.commitment)).value;
9734
+ const commitment = options && options.commitment;
9735
+ const confirmationPromise = confirmationStrategy ? connection.confirmTransaction(confirmationStrategy, commitment) : connection.confirmTransaction(signature, commitment);
9736
+ const status = (await confirmationPromise).value;
9619
9737
 
9620
9738
  if (status.err) {
9621
9739
  throw new Error(`Raw transaction ${signature} failed (${JSON.stringify(status)})`);