@solana/web3.js 1.66.5 → 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.
package/lib/index.cjs.js CHANGED
@@ -454,6 +454,17 @@ class TransactionExpiredTimeoutError extends Error {
454
454
  Object.defineProperty(TransactionExpiredTimeoutError.prototype, 'name', {
455
455
  value: 'TransactionExpiredTimeoutError'
456
456
  });
457
+ class TransactionExpiredNonceInvalidError extends Error {
458
+ constructor(signature) {
459
+ super(`Signature ${signature} has expired: the nonce is no longer valid.`);
460
+ this.signature = void 0;
461
+ this.signature = signature;
462
+ }
463
+
464
+ }
465
+ Object.defineProperty(TransactionExpiredNonceInvalidError.prototype, 'name', {
466
+ value: 'TransactionExpiredNonceInvalidError'
467
+ });
457
468
 
458
469
  class MessageAccountKeys {
459
470
  constructor(staticAccountKeys, accountKeysFromLookups) {
@@ -1291,6 +1302,7 @@ exports.TransactionStatus = void 0;
1291
1302
  TransactionStatus[TransactionStatus["BLOCKHEIGHT_EXCEEDED"] = 0] = "BLOCKHEIGHT_EXCEEDED";
1292
1303
  TransactionStatus[TransactionStatus["PROCESSED"] = 1] = "PROCESSED";
1293
1304
  TransactionStatus[TransactionStatus["TIMED_OUT"] = 2] = "TIMED_OUT";
1305
+ TransactionStatus[TransactionStatus["NONCE_INVALID"] = 3] = "NONCE_INVALID";
1294
1306
  })(exports.TransactionStatus || (exports.TransactionStatus = {}));
1295
1307
 
1296
1308
  const DEFAULT_SIGNATURE = buffer.Buffer.alloc(SIGNATURE_LENGTH_IN_BYTES).fill(0);
@@ -1385,6 +1397,7 @@ class Transaction {
1385
1397
  this.recentBlockhash = void 0;
1386
1398
  this.lastValidBlockHeight = void 0;
1387
1399
  this.nonceInfo = void 0;
1400
+ this.minNonceContextSlot = void 0;
1388
1401
  this._message = void 0;
1389
1402
  this._json = void 0;
1390
1403
 
@@ -1400,7 +1413,14 @@ class Transaction {
1400
1413
  this.signatures = opts.signatures;
1401
1414
  }
1402
1415
 
1403
- if (Object.prototype.hasOwnProperty.call(opts, 'lastValidBlockHeight')) {
1416
+ if (Object.prototype.hasOwnProperty.call(opts, 'nonceInfo')) {
1417
+ const {
1418
+ minContextSlot,
1419
+ nonceInfo
1420
+ } = opts;
1421
+ this.minNonceContextSlot = minContextSlot;
1422
+ this.nonceInfo = nonceInfo;
1423
+ } else if (Object.prototype.hasOwnProperty.call(opts, 'lastValidBlockHeight')) {
1404
1424
  const {
1405
1425
  blockhash,
1406
1426
  lastValidBlockHeight
@@ -2217,11 +2237,28 @@ async function sendAndConfirmTransaction(connection, transaction, signers, optio
2217
2237
  minContextSlot: options.minContextSlot
2218
2238
  };
2219
2239
  const signature = await connection.sendTransaction(transaction, signers, sendOptions);
2220
- const status = transaction.recentBlockhash != null && transaction.lastValidBlockHeight != null ? (await connection.confirmTransaction({
2221
- signature: signature,
2222
- blockhash: transaction.recentBlockhash,
2223
- lastValidBlockHeight: transaction.lastValidBlockHeight
2224
- }, options && options.commitment)).value : (await connection.confirmTransaction(signature, options && options.commitment)).value;
2240
+ let status;
2241
+
2242
+ if (transaction.recentBlockhash != null && transaction.lastValidBlockHeight != null) {
2243
+ status = (await connection.confirmTransaction({
2244
+ signature: signature,
2245
+ blockhash: transaction.recentBlockhash,
2246
+ lastValidBlockHeight: transaction.lastValidBlockHeight
2247
+ }, options && options.commitment)).value;
2248
+ } else if (transaction.minNonceContextSlot != null && transaction.nonceInfo != null) {
2249
+ const {
2250
+ nonceInstruction
2251
+ } = transaction.nonceInfo;
2252
+ const nonceAccountPubkey = nonceInstruction.keys[0].pubkey;
2253
+ status = (await connection.confirmTransaction({
2254
+ minContextSlot: transaction.minNonceContextSlot,
2255
+ nonceAccountPubkey,
2256
+ nonceValue: transaction.nonceInfo.nonce,
2257
+ signature
2258
+ }, options && options.commitment)).value;
2259
+ } else {
2260
+ status = (await connection.confirmTransaction(signature, options && options.commitment)).value;
2261
+ }
2225
2262
 
2226
2263
  if (status.err) {
2227
2264
  throw new Error(`Transaction ${signature} failed (${JSON.stringify(status)})`);
@@ -2290,6 +2327,9 @@ const FeeCalculatorLayout = BufferLayout__namespace.nu64('lamportsPerSignature')
2290
2327
 
2291
2328
  const NonceAccountLayout = BufferLayout__namespace.struct([BufferLayout__namespace.u32('version'), BufferLayout__namespace.u32('state'), publicKey('authorizedPubkey'), publicKey('nonce'), BufferLayout__namespace.struct([FeeCalculatorLayout], 'feeCalculator')]);
2292
2329
  const NONCE_ACCOUNT_LENGTH = NonceAccountLayout.span;
2330
+ /**
2331
+ * A durable nonce is a 32 byte value encoded as a base58 string.
2332
+ */
2293
2333
 
2294
2334
  /**
2295
2335
  * NonceAccount class
@@ -3704,10 +3744,13 @@ function extractCommitmentFromConfig(commitmentOrConfig) {
3704
3744
  };
3705
3745
  }
3706
3746
  /**
3707
- * @internal
3747
+ * A strategy for confirming durable nonce transactions.
3708
3748
  */
3709
3749
 
3710
3750
 
3751
+ /**
3752
+ * @internal
3753
+ */
3711
3754
  function createRpcResult(result) {
3712
3755
  return superstruct.union([superstruct.type({
3713
3756
  jsonrpc: superstruct.literal('2.0'),
@@ -5279,25 +5322,45 @@ class Connection {
5279
5322
  }
5280
5323
 
5281
5324
  assert(decodedSignature.length === 64, 'signature has invalid length');
5282
- const confirmationCommitment = commitment || this.commitment;
5283
- let timeoutId;
5325
+
5326
+ if (typeof strategy === 'string') {
5327
+ return await this.confirmTransactionUsingLegacyTimeoutStrategy({
5328
+ commitment: commitment || this.commitment,
5329
+ signature: rawSignature
5330
+ });
5331
+ } else if ('lastValidBlockHeight' in strategy) {
5332
+ return await this.confirmTransactionUsingBlockHeightExceedanceStrategy({
5333
+ commitment: commitment || this.commitment,
5334
+ strategy
5335
+ });
5336
+ } else {
5337
+ return await this.confirmTransactionUsingDurableNonceStrategy({
5338
+ commitment: commitment || this.commitment,
5339
+ strategy
5340
+ });
5341
+ }
5342
+ }
5343
+
5344
+ getTransactionConfirmationPromise({
5345
+ commitment,
5346
+ signature
5347
+ }) {
5284
5348
  let signatureSubscriptionId;
5285
5349
  let disposeSignatureSubscriptionStateChangeObserver;
5286
5350
  let done = false;
5287
5351
  const confirmationPromise = new Promise((resolve, reject) => {
5288
5352
  try {
5289
- signatureSubscriptionId = this.onSignature(rawSignature, (result, context) => {
5353
+ signatureSubscriptionId = this.onSignature(signature, (result, context) => {
5290
5354
  signatureSubscriptionId = undefined;
5291
5355
  const response = {
5292
5356
  context,
5293
5357
  value: result
5294
5358
  };
5295
- done = true;
5296
5359
  resolve({
5297
5360
  __type: exports.TransactionStatus.PROCESSED,
5298
5361
  response
5299
5362
  });
5300
- }, confirmationCommitment);
5363
+ }, commitment);
5301
5364
  const subscriptionSetupPromise = new Promise(resolveSubscriptionSetup => {
5302
5365
  if (signatureSubscriptionId == null) {
5303
5366
  resolveSubscriptionSetup();
@@ -5313,7 +5376,7 @@ class Connection {
5313
5376
  (async () => {
5314
5377
  await subscriptionSetupPromise;
5315
5378
  if (done) return;
5316
- const response = await this.getSignatureStatus(rawSignature);
5379
+ const response = await this.getSignatureStatus(signature);
5317
5380
  if (done) return;
5318
5381
 
5319
5382
  if (response == null) {
@@ -5332,7 +5395,7 @@ class Connection {
5332
5395
  if (value !== null && value !== void 0 && value.err) {
5333
5396
  reject(value.err);
5334
5397
  } else {
5335
- switch (confirmationCommitment) {
5398
+ switch (commitment) {
5336
5399
  case 'confirmed':
5337
5400
  case 'single':
5338
5401
  case 'singleGossip':
@@ -5374,81 +5437,279 @@ class Connection {
5374
5437
  reject(err);
5375
5438
  }
5376
5439
  });
5440
+
5441
+ const abortConfirmation = () => {
5442
+ if (disposeSignatureSubscriptionStateChangeObserver) {
5443
+ disposeSignatureSubscriptionStateChangeObserver();
5444
+ disposeSignatureSubscriptionStateChangeObserver = undefined;
5445
+ }
5446
+
5447
+ if (signatureSubscriptionId) {
5448
+ this.removeSignatureListener(signatureSubscriptionId);
5449
+ signatureSubscriptionId = undefined;
5450
+ }
5451
+ };
5452
+
5453
+ return {
5454
+ abortConfirmation,
5455
+ confirmationPromise
5456
+ };
5457
+ }
5458
+
5459
+ async confirmTransactionUsingBlockHeightExceedanceStrategy({
5460
+ commitment,
5461
+ strategy: {
5462
+ lastValidBlockHeight,
5463
+ signature
5464
+ }
5465
+ }) {
5466
+ let done = false;
5377
5467
  const expiryPromise = new Promise(resolve => {
5378
- if (typeof strategy === 'string') {
5379
- let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
5380
-
5381
- switch (confirmationCommitment) {
5382
- case 'processed':
5383
- case 'recent':
5384
- case 'single':
5385
- case 'confirmed':
5386
- case 'singleGossip':
5387
- {
5388
- timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
5389
- break;
5390
- }
5468
+ const checkBlockHeight = async () => {
5469
+ try {
5470
+ const blockHeight = await this.getBlockHeight(commitment);
5471
+ return blockHeight;
5472
+ } catch (_e) {
5473
+ return -1;
5391
5474
  }
5475
+ };
5392
5476
 
5393
- timeoutId = setTimeout(() => resolve({
5394
- __type: exports.TransactionStatus.TIMED_OUT,
5395
- timeoutMs
5396
- }), timeoutMs);
5477
+ (async () => {
5478
+ let currentBlockHeight = await checkBlockHeight();
5479
+ if (done) return;
5480
+
5481
+ while (currentBlockHeight <= lastValidBlockHeight) {
5482
+ await sleep(1000);
5483
+ if (done) return;
5484
+ currentBlockHeight = await checkBlockHeight();
5485
+ if (done) return;
5486
+ }
5487
+
5488
+ resolve({
5489
+ __type: exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED
5490
+ });
5491
+ })();
5492
+ });
5493
+ const {
5494
+ abortConfirmation,
5495
+ confirmationPromise
5496
+ } = this.getTransactionConfirmationPromise({
5497
+ commitment,
5498
+ signature
5499
+ });
5500
+ let result;
5501
+
5502
+ try {
5503
+ const outcome = await Promise.race([confirmationPromise, expiryPromise]);
5504
+
5505
+ if (outcome.__type === exports.TransactionStatus.PROCESSED) {
5506
+ result = outcome.response;
5397
5507
  } else {
5398
- let config = strategy;
5508
+ throw new TransactionExpiredBlockheightExceededError(signature);
5509
+ }
5510
+ } finally {
5511
+ done = true;
5512
+ abortConfirmation();
5513
+ }
5399
5514
 
5400
- const checkBlockHeight = async () => {
5401
- try {
5402
- const blockHeight = await this.getBlockHeight(commitment);
5403
- return blockHeight;
5404
- } catch (_e) {
5405
- return -1;
5406
- }
5407
- };
5515
+ return result;
5516
+ }
5408
5517
 
5409
- (async () => {
5410
- let currentBlockHeight = await checkBlockHeight();
5411
- if (done) return;
5518
+ async confirmTransactionUsingDurableNonceStrategy({
5519
+ commitment,
5520
+ strategy: {
5521
+ minContextSlot,
5522
+ nonceAccountPubkey,
5523
+ nonceValue,
5524
+ signature
5525
+ }
5526
+ }) {
5527
+ let done = false;
5528
+ const expiryPromise = new Promise(resolve => {
5529
+ let currentNonceValue = nonceValue;
5530
+ let lastCheckedSlot = null;
5412
5531
 
5413
- while (currentBlockHeight <= config.lastValidBlockHeight) {
5414
- await sleep(1000);
5415
- if (done) return;
5416
- currentBlockHeight = await checkBlockHeight();
5417
- if (done) return;
5532
+ const getCurrentNonceValue = async () => {
5533
+ try {
5534
+ const {
5535
+ context,
5536
+ value: nonceAccount
5537
+ } = await this.getNonceAndContext(nonceAccountPubkey, {
5538
+ commitment,
5539
+ minContextSlot
5540
+ });
5541
+ lastCheckedSlot = context.slot;
5542
+ return nonceAccount === null || nonceAccount === void 0 ? void 0 : nonceAccount.nonce;
5543
+ } catch (e) {
5544
+ // If for whatever reason we can't reach/read the nonce
5545
+ // account, just keep using the last-known value.
5546
+ return currentNonceValue;
5547
+ }
5548
+ };
5549
+
5550
+ (async () => {
5551
+ currentNonceValue = await getCurrentNonceValue();
5552
+ if (done) return;
5553
+
5554
+ while (true // eslint-disable-line no-constant-condition
5555
+ ) {
5556
+ if (nonceValue !== currentNonceValue) {
5557
+ resolve({
5558
+ __type: exports.TransactionStatus.NONCE_INVALID,
5559
+ slotInWhichNonceDidAdvance: lastCheckedSlot
5560
+ });
5561
+ return;
5418
5562
  }
5419
5563
 
5420
- resolve({
5421
- __type: exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED
5422
- });
5423
- })();
5424
- }
5564
+ await sleep(2000);
5565
+ if (done) return;
5566
+ currentNonceValue = await getCurrentNonceValue();
5567
+ if (done) return;
5568
+ }
5569
+ })();
5570
+ });
5571
+ const {
5572
+ abortConfirmation,
5573
+ confirmationPromise
5574
+ } = this.getTransactionConfirmationPromise({
5575
+ commitment,
5576
+ signature
5425
5577
  });
5426
5578
  let result;
5427
5579
 
5428
5580
  try {
5429
5581
  const outcome = await Promise.race([confirmationPromise, expiryPromise]);
5430
5582
 
5431
- switch (outcome.__type) {
5432
- case exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED:
5433
- throw new TransactionExpiredBlockheightExceededError(rawSignature);
5583
+ if (outcome.__type === exports.TransactionStatus.PROCESSED) {
5584
+ result = outcome.response;
5585
+ } else {
5586
+ var _signatureStatus;
5587
+
5588
+ // Double check that the transaction is indeed unconfirmed.
5589
+ let signatureStatus;
5590
+
5591
+ while (true // eslint-disable-line no-constant-condition
5592
+ ) {
5593
+ var _outcome$slotInWhichN;
5594
+
5595
+ const status = await this.getSignatureStatus(signature);
5596
+
5597
+ if (status == null) {
5598
+ break;
5599
+ }
5600
+
5601
+ if (status.context.slot < ((_outcome$slotInWhichN = outcome.slotInWhichNonceDidAdvance) !== null && _outcome$slotInWhichN !== void 0 ? _outcome$slotInWhichN : minContextSlot)) {
5602
+ await sleep(400);
5603
+ continue;
5604
+ }
5434
5605
 
5435
- case exports.TransactionStatus.PROCESSED:
5436
- result = outcome.response;
5606
+ signatureStatus = status;
5437
5607
  break;
5608
+ }
5609
+
5610
+ if ((_signatureStatus = signatureStatus) !== null && _signatureStatus !== void 0 && _signatureStatus.value) {
5611
+ const commitmentForStatus = commitment || 'finalized';
5612
+ const {
5613
+ confirmationStatus
5614
+ } = signatureStatus.value;
5615
+
5616
+ switch (commitmentForStatus) {
5617
+ case 'processed':
5618
+ case 'recent':
5619
+ if (confirmationStatus !== 'processed' && confirmationStatus !== 'confirmed' && confirmationStatus !== 'finalized') {
5620
+ throw new TransactionExpiredNonceInvalidError(signature);
5621
+ }
5622
+
5623
+ break;
5624
+
5625
+ case 'confirmed':
5626
+ case 'single':
5627
+ case 'singleGossip':
5628
+ if (confirmationStatus !== 'confirmed' && confirmationStatus !== 'finalized') {
5629
+ throw new TransactionExpiredNonceInvalidError(signature);
5630
+ }
5438
5631
 
5439
- case exports.TransactionStatus.TIMED_OUT:
5440
- throw new TransactionExpiredTimeoutError(rawSignature, outcome.timeoutMs / 1000);
5632
+ break;
5633
+
5634
+ case 'finalized':
5635
+ case 'max':
5636
+ case 'root':
5637
+ if (confirmationStatus !== 'finalized') {
5638
+ throw new TransactionExpiredNonceInvalidError(signature);
5639
+ }
5640
+
5641
+ break;
5642
+
5643
+ default:
5644
+ // Exhaustive switch.
5645
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5646
+ (_ => {})(commitmentForStatus);
5647
+
5648
+ }
5649
+
5650
+ result = {
5651
+ context: signatureStatus.context,
5652
+ value: {
5653
+ err: signatureStatus.value.err
5654
+ }
5655
+ };
5656
+ } else {
5657
+ throw new TransactionExpiredNonceInvalidError(signature);
5658
+ }
5441
5659
  }
5442
5660
  } finally {
5443
- clearTimeout(timeoutId);
5661
+ done = true;
5662
+ abortConfirmation();
5663
+ }
5444
5664
 
5445
- if (disposeSignatureSubscriptionStateChangeObserver) {
5446
- disposeSignatureSubscriptionStateChangeObserver();
5665
+ return result;
5666
+ }
5667
+
5668
+ async confirmTransactionUsingLegacyTimeoutStrategy({
5669
+ commitment,
5670
+ signature
5671
+ }) {
5672
+ let timeoutId;
5673
+ const expiryPromise = new Promise(resolve => {
5674
+ let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
5675
+
5676
+ switch (commitment) {
5677
+ case 'processed':
5678
+ case 'recent':
5679
+ case 'single':
5680
+ case 'confirmed':
5681
+ case 'singleGossip':
5682
+ {
5683
+ timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
5684
+ break;
5685
+ }
5447
5686
  }
5448
5687
 
5449
- if (signatureSubscriptionId) {
5450
- this.removeSignatureListener(signatureSubscriptionId);
5688
+ timeoutId = setTimeout(() => resolve({
5689
+ __type: exports.TransactionStatus.TIMED_OUT,
5690
+ timeoutMs
5691
+ }), timeoutMs);
5692
+ });
5693
+ const {
5694
+ abortConfirmation,
5695
+ confirmationPromise
5696
+ } = this.getTransactionConfirmationPromise({
5697
+ commitment,
5698
+ signature
5699
+ });
5700
+ let result;
5701
+
5702
+ try {
5703
+ const outcome = await Promise.race([confirmationPromise, expiryPromise]);
5704
+
5705
+ if (outcome.__type === exports.TransactionStatus.PROCESSED) {
5706
+ result = outcome.response;
5707
+ } else {
5708
+ throw new TransactionExpiredTimeoutError(signature, outcome.timeoutMs / 1000);
5451
5709
  }
5710
+ } finally {
5711
+ clearTimeout(timeoutId);
5712
+ abortConfirmation();
5452
5713
  }
5453
5714
 
5454
5715
  return result;
@@ -6504,11 +6765,11 @@ class Connection {
6504
6765
  */
6505
6766
 
6506
6767
 
6507
- async getNonceAndContext(nonceAccount, commitment) {
6768
+ async getNonceAndContext(nonceAccount, commitmentOrConfig) {
6508
6769
  const {
6509
6770
  context,
6510
6771
  value: accountInfo
6511
- } = await this.getAccountInfoAndContext(nonceAccount, commitment);
6772
+ } = await this.getAccountInfoAndContext(nonceAccount, commitmentOrConfig);
6512
6773
  let value = null;
6513
6774
 
6514
6775
  if (accountInfo !== null) {
@@ -6525,8 +6786,8 @@ class Connection {
6525
6786
  */
6526
6787
 
6527
6788
 
6528
- async getNonce(nonceAccount, commitment) {
6529
- return await this.getNonceAndContext(nonceAccount, commitment).then(x => x.value).catch(e => {
6789
+ async getNonce(nonceAccount, commitmentOrConfig) {
6790
+ return await this.getNonceAndContext(nonceAccount, commitmentOrConfig).then(x => x.value).catch(e => {
6530
6791
  throw new Error('failed to get nonce for account ' + nonceAccount.toBase58() + ': ' + e);
6531
6792
  });
6532
6793
  }
@@ -9941,6 +10202,9 @@ async function sendAndConfirmRawTransaction(connection, rawTransaction, confirma
9941
10202
  if (confirmationStrategyOrConfirmOptions && Object.prototype.hasOwnProperty.call(confirmationStrategyOrConfirmOptions, 'lastValidBlockHeight')) {
9942
10203
  confirmationStrategy = confirmationStrategyOrConfirmOptions;
9943
10204
  options = maybeConfirmOptions;
10205
+ } else if (confirmationStrategyOrConfirmOptions && Object.prototype.hasOwnProperty.call(confirmationStrategyOrConfirmOptions, 'nonceValue')) {
10206
+ confirmationStrategy = confirmationStrategyOrConfirmOptions;
10207
+ options = maybeConfirmOptions;
9944
10208
  } else {
9945
10209
  options = confirmationStrategyOrConfirmOptions;
9946
10210
  }
@@ -10025,6 +10289,7 @@ exports.SystemInstruction = SystemInstruction;
10025
10289
  exports.SystemProgram = SystemProgram;
10026
10290
  exports.Transaction = Transaction;
10027
10291
  exports.TransactionExpiredBlockheightExceededError = TransactionExpiredBlockheightExceededError;
10292
+ exports.TransactionExpiredNonceInvalidError = TransactionExpiredNonceInvalidError;
10028
10293
  exports.TransactionExpiredTimeoutError = TransactionExpiredTimeoutError;
10029
10294
  exports.TransactionInstruction = TransactionInstruction;
10030
10295
  exports.TransactionMessage = TransactionMessage;