@solana/web3.js 1.66.4 → 1.67.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.
@@ -448,6 +448,17 @@ class TransactionExpiredTimeoutError extends Error {
448
448
  Object.defineProperty(TransactionExpiredTimeoutError.prototype, 'name', {
449
449
  value: 'TransactionExpiredTimeoutError'
450
450
  });
451
+ class TransactionExpiredNonceInvalidError extends Error {
452
+ constructor(signature) {
453
+ super(`Signature ${signature} has expired: the nonce is no longer valid.`);
454
+ this.signature = void 0;
455
+ this.signature = signature;
456
+ }
457
+
458
+ }
459
+ Object.defineProperty(TransactionExpiredNonceInvalidError.prototype, 'name', {
460
+ value: 'TransactionExpiredNonceInvalidError'
461
+ });
451
462
 
452
463
  class MessageAccountKeys {
453
464
  constructor(staticAccountKeys, accountKeysFromLookups) {
@@ -1285,6 +1296,7 @@ exports.TransactionStatus = void 0;
1285
1296
  TransactionStatus[TransactionStatus["BLOCKHEIGHT_EXCEEDED"] = 0] = "BLOCKHEIGHT_EXCEEDED";
1286
1297
  TransactionStatus[TransactionStatus["PROCESSED"] = 1] = "PROCESSED";
1287
1298
  TransactionStatus[TransactionStatus["TIMED_OUT"] = 2] = "TIMED_OUT";
1299
+ TransactionStatus[TransactionStatus["NONCE_INVALID"] = 3] = "NONCE_INVALID";
1288
1300
  })(exports.TransactionStatus || (exports.TransactionStatus = {}));
1289
1301
 
1290
1302
  const DEFAULT_SIGNATURE = buffer.Buffer.alloc(SIGNATURE_LENGTH_IN_BYTES).fill(0);
@@ -1379,6 +1391,7 @@ class Transaction {
1379
1391
  this.recentBlockhash = void 0;
1380
1392
  this.lastValidBlockHeight = void 0;
1381
1393
  this.nonceInfo = void 0;
1394
+ this.minNonceContextSlot = void 0;
1382
1395
  this._message = void 0;
1383
1396
  this._json = void 0;
1384
1397
 
@@ -1394,7 +1407,14 @@ class Transaction {
1394
1407
  this.signatures = opts.signatures;
1395
1408
  }
1396
1409
 
1397
- if (Object.prototype.hasOwnProperty.call(opts, 'lastValidBlockHeight')) {
1410
+ if (Object.prototype.hasOwnProperty.call(opts, 'nonceInfo')) {
1411
+ const {
1412
+ minContextSlot,
1413
+ nonceInfo
1414
+ } = opts;
1415
+ this.minNonceContextSlot = minContextSlot;
1416
+ this.nonceInfo = nonceInfo;
1417
+ } else if (Object.prototype.hasOwnProperty.call(opts, 'lastValidBlockHeight')) {
1398
1418
  const {
1399
1419
  blockhash,
1400
1420
  lastValidBlockHeight
@@ -2211,11 +2231,28 @@ async function sendAndConfirmTransaction(connection, transaction, signers, optio
2211
2231
  minContextSlot: options.minContextSlot
2212
2232
  };
2213
2233
  const signature = await connection.sendTransaction(transaction, signers, sendOptions);
2214
- const status = transaction.recentBlockhash != null && transaction.lastValidBlockHeight != null ? (await connection.confirmTransaction({
2215
- signature: signature,
2216
- blockhash: transaction.recentBlockhash,
2217
- lastValidBlockHeight: transaction.lastValidBlockHeight
2218
- }, options && options.commitment)).value : (await connection.confirmTransaction(signature, options && options.commitment)).value;
2234
+ let status;
2235
+
2236
+ if (transaction.recentBlockhash != null && transaction.lastValidBlockHeight != null) {
2237
+ status = (await connection.confirmTransaction({
2238
+ signature: signature,
2239
+ blockhash: transaction.recentBlockhash,
2240
+ lastValidBlockHeight: transaction.lastValidBlockHeight
2241
+ }, options && options.commitment)).value;
2242
+ } else if (transaction.minNonceContextSlot != null && transaction.nonceInfo != null) {
2243
+ const {
2244
+ nonceInstruction
2245
+ } = transaction.nonceInfo;
2246
+ const nonceAccountPubkey = nonceInstruction.keys[0].pubkey;
2247
+ status = (await connection.confirmTransaction({
2248
+ minContextSlot: transaction.minNonceContextSlot,
2249
+ nonceAccountPubkey,
2250
+ nonceValue: transaction.nonceInfo.nonce,
2251
+ signature
2252
+ }, options && options.commitment)).value;
2253
+ } else {
2254
+ status = (await connection.confirmTransaction(signature, options && options.commitment)).value;
2255
+ }
2219
2256
 
2220
2257
  if (status.err) {
2221
2258
  throw new Error(`Transaction ${signature} failed (${JSON.stringify(status)})`);
@@ -2284,6 +2321,9 @@ const FeeCalculatorLayout = BufferLayout__namespace.nu64('lamportsPerSignature')
2284
2321
 
2285
2322
  const NonceAccountLayout = BufferLayout__namespace.struct([BufferLayout__namespace.u32('version'), BufferLayout__namespace.u32('state'), publicKey('authorizedPubkey'), publicKey('nonce'), BufferLayout__namespace.struct([FeeCalculatorLayout], 'feeCalculator')]);
2286
2323
  const NONCE_ACCOUNT_LENGTH = NonceAccountLayout.span;
2324
+ /**
2325
+ * A durable nonce is a 32 byte value encoded as a base58 string.
2326
+ */
2287
2327
 
2288
2328
  /**
2289
2329
  * NonceAccount class
@@ -3646,10 +3686,13 @@ function extractCommitmentFromConfig(commitmentOrConfig) {
3646
3686
  };
3647
3687
  }
3648
3688
  /**
3649
- * @internal
3689
+ * A strategy for confirming durable nonce transactions.
3650
3690
  */
3651
3691
 
3652
3692
 
3693
+ /**
3694
+ * @internal
3695
+ */
3653
3696
  function createRpcResult(result) {
3654
3697
  return superstruct.union([superstruct.type({
3655
3698
  jsonrpc: superstruct.literal('2.0'),
@@ -5213,25 +5256,45 @@ class Connection {
5213
5256
  }
5214
5257
 
5215
5258
  assert(decodedSignature.length === 64, 'signature has invalid length');
5216
- const subscriptionCommitment = commitment || this.commitment;
5217
- let timeoutId;
5259
+
5260
+ if (typeof strategy === 'string') {
5261
+ return await this.confirmTransactionUsingLegacyTimeoutStrategy({
5262
+ commitment: commitment || this.commitment,
5263
+ signature: rawSignature
5264
+ });
5265
+ } else if ('lastValidBlockHeight' in strategy) {
5266
+ return await this.confirmTransactionUsingBlockHeightExceedanceStrategy({
5267
+ commitment: commitment || this.commitment,
5268
+ strategy
5269
+ });
5270
+ } else {
5271
+ return await this.confirmTransactionUsingDurableNonceStrategy({
5272
+ commitment: commitment || this.commitment,
5273
+ strategy
5274
+ });
5275
+ }
5276
+ }
5277
+
5278
+ getTransactionConfirmationPromise({
5279
+ commitment,
5280
+ signature
5281
+ }) {
5218
5282
  let signatureSubscriptionId;
5219
5283
  let disposeSignatureSubscriptionStateChangeObserver;
5220
5284
  let done = false;
5221
5285
  const confirmationPromise = new Promise((resolve, reject) => {
5222
5286
  try {
5223
- signatureSubscriptionId = this.onSignature(rawSignature, (result, context) => {
5287
+ signatureSubscriptionId = this.onSignature(signature, (result, context) => {
5224
5288
  signatureSubscriptionId = undefined;
5225
5289
  const response = {
5226
5290
  context,
5227
5291
  value: result
5228
5292
  };
5229
- done = true;
5230
5293
  resolve({
5231
5294
  __type: exports.TransactionStatus.PROCESSED,
5232
5295
  response
5233
5296
  });
5234
- }, subscriptionCommitment);
5297
+ }, commitment);
5235
5298
  const subscriptionSetupPromise = new Promise(resolveSubscriptionSetup => {
5236
5299
  if (signatureSubscriptionId == null) {
5237
5300
  resolveSubscriptionSetup();
@@ -5247,7 +5310,7 @@ class Connection {
5247
5310
  (async () => {
5248
5311
  await subscriptionSetupPromise;
5249
5312
  if (done) return;
5250
- const response = await this.getSignatureStatus(rawSignature);
5313
+ const response = await this.getSignatureStatus(signature);
5251
5314
  if (done) return;
5252
5315
 
5253
5316
  if (response == null) {
@@ -5259,11 +5322,41 @@ class Connection {
5259
5322
  value
5260
5323
  } = response;
5261
5324
 
5325
+ if (value == null) {
5326
+ return;
5327
+ }
5328
+
5262
5329
  if (value !== null && value !== void 0 && value.err) {
5263
5330
  reject(value.err);
5264
- }
5331
+ } else {
5332
+ switch (commitment) {
5333
+ case 'confirmed':
5334
+ case 'single':
5335
+ case 'singleGossip':
5336
+ {
5337
+ if (value.confirmationStatus === 'processed') {
5338
+ return;
5339
+ }
5340
+
5341
+ break;
5342
+ }
5343
+
5344
+ case 'finalized':
5345
+ case 'max':
5346
+ case 'root':
5347
+ {
5348
+ if (value.confirmationStatus === 'processed' || value.confirmationStatus === 'confirmed') {
5349
+ return;
5350
+ }
5351
+
5352
+ break;
5353
+ }
5354
+ // exhaust enums to ensure full coverage
5355
+
5356
+ case 'processed':
5357
+ case 'recent':
5358
+ }
5265
5359
 
5266
- if (value) {
5267
5360
  done = true;
5268
5361
  resolve({
5269
5362
  __type: exports.TransactionStatus.PROCESSED,
@@ -5278,81 +5371,279 @@ class Connection {
5278
5371
  reject(err);
5279
5372
  }
5280
5373
  });
5374
+
5375
+ const abortConfirmation = () => {
5376
+ if (disposeSignatureSubscriptionStateChangeObserver) {
5377
+ disposeSignatureSubscriptionStateChangeObserver();
5378
+ disposeSignatureSubscriptionStateChangeObserver = undefined;
5379
+ }
5380
+
5381
+ if (signatureSubscriptionId) {
5382
+ this.removeSignatureListener(signatureSubscriptionId);
5383
+ signatureSubscriptionId = undefined;
5384
+ }
5385
+ };
5386
+
5387
+ return {
5388
+ abortConfirmation,
5389
+ confirmationPromise
5390
+ };
5391
+ }
5392
+
5393
+ async confirmTransactionUsingBlockHeightExceedanceStrategy({
5394
+ commitment,
5395
+ strategy: {
5396
+ lastValidBlockHeight,
5397
+ signature
5398
+ }
5399
+ }) {
5400
+ let done = false;
5281
5401
  const expiryPromise = new Promise(resolve => {
5282
- if (typeof strategy === 'string') {
5283
- let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
5284
-
5285
- switch (subscriptionCommitment) {
5286
- case 'processed':
5287
- case 'recent':
5288
- case 'single':
5289
- case 'confirmed':
5290
- case 'singleGossip':
5291
- {
5292
- timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
5293
- break;
5294
- }
5402
+ const checkBlockHeight = async () => {
5403
+ try {
5404
+ const blockHeight = await this.getBlockHeight(commitment);
5405
+ return blockHeight;
5406
+ } catch (_e) {
5407
+ return -1;
5295
5408
  }
5409
+ };
5296
5410
 
5297
- timeoutId = setTimeout(() => resolve({
5298
- __type: exports.TransactionStatus.TIMED_OUT,
5299
- timeoutMs
5300
- }), timeoutMs);
5411
+ (async () => {
5412
+ let currentBlockHeight = await checkBlockHeight();
5413
+ if (done) return;
5414
+
5415
+ while (currentBlockHeight <= lastValidBlockHeight) {
5416
+ await sleep(1000);
5417
+ if (done) return;
5418
+ currentBlockHeight = await checkBlockHeight();
5419
+ if (done) return;
5420
+ }
5421
+
5422
+ resolve({
5423
+ __type: exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED
5424
+ });
5425
+ })();
5426
+ });
5427
+ const {
5428
+ abortConfirmation,
5429
+ confirmationPromise
5430
+ } = this.getTransactionConfirmationPromise({
5431
+ commitment,
5432
+ signature
5433
+ });
5434
+ let result;
5435
+
5436
+ try {
5437
+ const outcome = await Promise.race([confirmationPromise, expiryPromise]);
5438
+
5439
+ if (outcome.__type === exports.TransactionStatus.PROCESSED) {
5440
+ result = outcome.response;
5301
5441
  } else {
5302
- let config = strategy;
5442
+ throw new TransactionExpiredBlockheightExceededError(signature);
5443
+ }
5444
+ } finally {
5445
+ done = true;
5446
+ abortConfirmation();
5447
+ }
5303
5448
 
5304
- const checkBlockHeight = async () => {
5305
- try {
5306
- const blockHeight = await this.getBlockHeight(commitment);
5307
- return blockHeight;
5308
- } catch (_e) {
5309
- return -1;
5310
- }
5311
- };
5449
+ return result;
5450
+ }
5312
5451
 
5313
- (async () => {
5314
- let currentBlockHeight = await checkBlockHeight();
5315
- if (done) return;
5452
+ async confirmTransactionUsingDurableNonceStrategy({
5453
+ commitment,
5454
+ strategy: {
5455
+ minContextSlot,
5456
+ nonceAccountPubkey,
5457
+ nonceValue,
5458
+ signature
5459
+ }
5460
+ }) {
5461
+ let done = false;
5462
+ const expiryPromise = new Promise(resolve => {
5463
+ let currentNonceValue = nonceValue;
5464
+ let lastCheckedSlot = null;
5316
5465
 
5317
- while (currentBlockHeight <= config.lastValidBlockHeight) {
5318
- await sleep(1000);
5319
- if (done) return;
5320
- currentBlockHeight = await checkBlockHeight();
5321
- if (done) return;
5466
+ const getCurrentNonceValue = async () => {
5467
+ try {
5468
+ const {
5469
+ context,
5470
+ value: nonceAccount
5471
+ } = await this.getNonceAndContext(nonceAccountPubkey, {
5472
+ commitment,
5473
+ minContextSlot
5474
+ });
5475
+ lastCheckedSlot = context.slot;
5476
+ return nonceAccount === null || nonceAccount === void 0 ? void 0 : nonceAccount.nonce;
5477
+ } catch (e) {
5478
+ // If for whatever reason we can't reach/read the nonce
5479
+ // account, just keep using the last-known value.
5480
+ return currentNonceValue;
5481
+ }
5482
+ };
5483
+
5484
+ (async () => {
5485
+ currentNonceValue = await getCurrentNonceValue();
5486
+ if (done) return;
5487
+
5488
+ while (true // eslint-disable-line no-constant-condition
5489
+ ) {
5490
+ if (nonceValue !== currentNonceValue) {
5491
+ resolve({
5492
+ __type: exports.TransactionStatus.NONCE_INVALID,
5493
+ slotInWhichNonceDidAdvance: lastCheckedSlot
5494
+ });
5495
+ return;
5322
5496
  }
5323
5497
 
5324
- resolve({
5325
- __type: exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED
5326
- });
5327
- })();
5328
- }
5498
+ await sleep(2000);
5499
+ if (done) return;
5500
+ currentNonceValue = await getCurrentNonceValue();
5501
+ if (done) return;
5502
+ }
5503
+ })();
5504
+ });
5505
+ const {
5506
+ abortConfirmation,
5507
+ confirmationPromise
5508
+ } = this.getTransactionConfirmationPromise({
5509
+ commitment,
5510
+ signature
5329
5511
  });
5330
5512
  let result;
5331
5513
 
5332
5514
  try {
5333
5515
  const outcome = await Promise.race([confirmationPromise, expiryPromise]);
5334
5516
 
5335
- switch (outcome.__type) {
5336
- case exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED:
5337
- throw new TransactionExpiredBlockheightExceededError(rawSignature);
5517
+ if (outcome.__type === exports.TransactionStatus.PROCESSED) {
5518
+ result = outcome.response;
5519
+ } else {
5520
+ var _signatureStatus;
5521
+
5522
+ // Double check that the transaction is indeed unconfirmed.
5523
+ let signatureStatus;
5338
5524
 
5339
- case exports.TransactionStatus.PROCESSED:
5340
- result = outcome.response;
5525
+ while (true // eslint-disable-line no-constant-condition
5526
+ ) {
5527
+ var _outcome$slotInWhichN;
5528
+
5529
+ const status = await this.getSignatureStatus(signature);
5530
+
5531
+ if (status == null) {
5532
+ break;
5533
+ }
5534
+
5535
+ if (status.context.slot < ((_outcome$slotInWhichN = outcome.slotInWhichNonceDidAdvance) !== null && _outcome$slotInWhichN !== void 0 ? _outcome$slotInWhichN : minContextSlot)) {
5536
+ await sleep(400);
5537
+ continue;
5538
+ }
5539
+
5540
+ signatureStatus = status;
5341
5541
  break;
5542
+ }
5543
+
5544
+ if ((_signatureStatus = signatureStatus) !== null && _signatureStatus !== void 0 && _signatureStatus.value) {
5545
+ const commitmentForStatus = commitment || 'finalized';
5546
+ const {
5547
+ confirmationStatus
5548
+ } = signatureStatus.value;
5549
+
5550
+ switch (commitmentForStatus) {
5551
+ case 'processed':
5552
+ case 'recent':
5553
+ if (confirmationStatus !== 'processed' && confirmationStatus !== 'confirmed' && confirmationStatus !== 'finalized') {
5554
+ throw new TransactionExpiredNonceInvalidError(signature);
5555
+ }
5342
5556
 
5343
- case exports.TransactionStatus.TIMED_OUT:
5344
- throw new TransactionExpiredTimeoutError(rawSignature, outcome.timeoutMs / 1000);
5557
+ break;
5558
+
5559
+ case 'confirmed':
5560
+ case 'single':
5561
+ case 'singleGossip':
5562
+ if (confirmationStatus !== 'confirmed' && confirmationStatus !== 'finalized') {
5563
+ throw new TransactionExpiredNonceInvalidError(signature);
5564
+ }
5565
+
5566
+ break;
5567
+
5568
+ case 'finalized':
5569
+ case 'max':
5570
+ case 'root':
5571
+ if (confirmationStatus !== 'finalized') {
5572
+ throw new TransactionExpiredNonceInvalidError(signature);
5573
+ }
5574
+
5575
+ break;
5576
+
5577
+ default:
5578
+ // Exhaustive switch.
5579
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5580
+ (_ => {})(commitmentForStatus);
5581
+
5582
+ }
5583
+
5584
+ result = {
5585
+ context: signatureStatus.context,
5586
+ value: {
5587
+ err: signatureStatus.value.err
5588
+ }
5589
+ };
5590
+ } else {
5591
+ throw new TransactionExpiredNonceInvalidError(signature);
5592
+ }
5345
5593
  }
5346
5594
  } finally {
5347
- clearTimeout(timeoutId);
5595
+ done = true;
5596
+ abortConfirmation();
5597
+ }
5348
5598
 
5349
- if (disposeSignatureSubscriptionStateChangeObserver) {
5350
- disposeSignatureSubscriptionStateChangeObserver();
5599
+ return result;
5600
+ }
5601
+
5602
+ async confirmTransactionUsingLegacyTimeoutStrategy({
5603
+ commitment,
5604
+ signature
5605
+ }) {
5606
+ let timeoutId;
5607
+ const expiryPromise = new Promise(resolve => {
5608
+ let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
5609
+
5610
+ switch (commitment) {
5611
+ case 'processed':
5612
+ case 'recent':
5613
+ case 'single':
5614
+ case 'confirmed':
5615
+ case 'singleGossip':
5616
+ {
5617
+ timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
5618
+ break;
5619
+ }
5351
5620
  }
5352
5621
 
5353
- if (signatureSubscriptionId) {
5354
- this.removeSignatureListener(signatureSubscriptionId);
5622
+ timeoutId = setTimeout(() => resolve({
5623
+ __type: exports.TransactionStatus.TIMED_OUT,
5624
+ timeoutMs
5625
+ }), timeoutMs);
5626
+ });
5627
+ const {
5628
+ abortConfirmation,
5629
+ confirmationPromise
5630
+ } = this.getTransactionConfirmationPromise({
5631
+ commitment,
5632
+ signature
5633
+ });
5634
+ let result;
5635
+
5636
+ try {
5637
+ const outcome = await Promise.race([confirmationPromise, expiryPromise]);
5638
+
5639
+ if (outcome.__type === exports.TransactionStatus.PROCESSED) {
5640
+ result = outcome.response;
5641
+ } else {
5642
+ throw new TransactionExpiredTimeoutError(signature, outcome.timeoutMs / 1000);
5355
5643
  }
5644
+ } finally {
5645
+ clearTimeout(timeoutId);
5646
+ abortConfirmation();
5356
5647
  }
5357
5648
 
5358
5649
  return result;
@@ -6408,11 +6699,11 @@ class Connection {
6408
6699
  */
6409
6700
 
6410
6701
 
6411
- async getNonceAndContext(nonceAccount, commitment) {
6702
+ async getNonceAndContext(nonceAccount, commitmentOrConfig) {
6412
6703
  const {
6413
6704
  context,
6414
6705
  value: accountInfo
6415
- } = await this.getAccountInfoAndContext(nonceAccount, commitment);
6706
+ } = await this.getAccountInfoAndContext(nonceAccount, commitmentOrConfig);
6416
6707
  let value = null;
6417
6708
 
6418
6709
  if (accountInfo !== null) {
@@ -6429,8 +6720,8 @@ class Connection {
6429
6720
  */
6430
6721
 
6431
6722
 
6432
- async getNonce(nonceAccount, commitment) {
6433
- return await this.getNonceAndContext(nonceAccount, commitment).then(x => x.value).catch(e => {
6723
+ async getNonce(nonceAccount, commitmentOrConfig) {
6724
+ return await this.getNonceAndContext(nonceAccount, commitmentOrConfig).then(x => x.value).catch(e => {
6434
6725
  throw new Error('failed to get nonce for account ' + nonceAccount.toBase58() + ': ' + e);
6435
6726
  });
6436
6727
  }
@@ -9845,6 +10136,9 @@ async function sendAndConfirmRawTransaction(connection, rawTransaction, confirma
9845
10136
  if (confirmationStrategyOrConfirmOptions && Object.prototype.hasOwnProperty.call(confirmationStrategyOrConfirmOptions, 'lastValidBlockHeight')) {
9846
10137
  confirmationStrategy = confirmationStrategyOrConfirmOptions;
9847
10138
  options = maybeConfirmOptions;
10139
+ } else if (confirmationStrategyOrConfirmOptions && Object.prototype.hasOwnProperty.call(confirmationStrategyOrConfirmOptions, 'nonceValue')) {
10140
+ confirmationStrategy = confirmationStrategyOrConfirmOptions;
10141
+ options = maybeConfirmOptions;
9848
10142
  } else {
9849
10143
  options = confirmationStrategyOrConfirmOptions;
9850
10144
  }
@@ -9929,6 +10223,7 @@ exports.SystemInstruction = SystemInstruction;
9929
10223
  exports.SystemProgram = SystemProgram;
9930
10224
  exports.Transaction = Transaction;
9931
10225
  exports.TransactionExpiredBlockheightExceededError = TransactionExpiredBlockheightExceededError;
10226
+ exports.TransactionExpiredNonceInvalidError = TransactionExpiredNonceInvalidError;
9932
10227
  exports.TransactionExpiredTimeoutError = TransactionExpiredTimeoutError;
9933
10228
  exports.TransactionInstruction = TransactionInstruction;
9934
10229
  exports.TransactionMessage = TransactionMessage;