@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.
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 subscriptionCommitment = 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
- }, subscriptionCommitment);
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) {
@@ -5325,11 +5388,41 @@ class Connection {
5325
5388
  value
5326
5389
  } = response;
5327
5390
 
5391
+ if (value == null) {
5392
+ return;
5393
+ }
5394
+
5328
5395
  if (value !== null && value !== void 0 && value.err) {
5329
5396
  reject(value.err);
5330
- }
5397
+ } else {
5398
+ switch (commitment) {
5399
+ case 'confirmed':
5400
+ case 'single':
5401
+ case 'singleGossip':
5402
+ {
5403
+ if (value.confirmationStatus === 'processed') {
5404
+ return;
5405
+ }
5406
+
5407
+ break;
5408
+ }
5409
+
5410
+ case 'finalized':
5411
+ case 'max':
5412
+ case 'root':
5413
+ {
5414
+ if (value.confirmationStatus === 'processed' || value.confirmationStatus === 'confirmed') {
5415
+ return;
5416
+ }
5417
+
5418
+ break;
5419
+ }
5420
+ // exhaust enums to ensure full coverage
5421
+
5422
+ case 'processed':
5423
+ case 'recent':
5424
+ }
5331
5425
 
5332
- if (value) {
5333
5426
  done = true;
5334
5427
  resolve({
5335
5428
  __type: exports.TransactionStatus.PROCESSED,
@@ -5344,81 +5437,279 @@ class Connection {
5344
5437
  reject(err);
5345
5438
  }
5346
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;
5347
5467
  const expiryPromise = new Promise(resolve => {
5348
- if (typeof strategy === 'string') {
5349
- let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
5350
-
5351
- switch (subscriptionCommitment) {
5352
- case 'processed':
5353
- case 'recent':
5354
- case 'single':
5355
- case 'confirmed':
5356
- case 'singleGossip':
5357
- {
5358
- timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
5359
- break;
5360
- }
5468
+ const checkBlockHeight = async () => {
5469
+ try {
5470
+ const blockHeight = await this.getBlockHeight(commitment);
5471
+ return blockHeight;
5472
+ } catch (_e) {
5473
+ return -1;
5361
5474
  }
5475
+ };
5362
5476
 
5363
- timeoutId = setTimeout(() => resolve({
5364
- __type: exports.TransactionStatus.TIMED_OUT,
5365
- timeoutMs
5366
- }), 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;
5367
5507
  } else {
5368
- let config = strategy;
5508
+ throw new TransactionExpiredBlockheightExceededError(signature);
5509
+ }
5510
+ } finally {
5511
+ done = true;
5512
+ abortConfirmation();
5513
+ }
5369
5514
 
5370
- const checkBlockHeight = async () => {
5371
- try {
5372
- const blockHeight = await this.getBlockHeight(commitment);
5373
- return blockHeight;
5374
- } catch (_e) {
5375
- return -1;
5376
- }
5377
- };
5515
+ return result;
5516
+ }
5378
5517
 
5379
- (async () => {
5380
- let currentBlockHeight = await checkBlockHeight();
5381
- 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;
5382
5531
 
5383
- while (currentBlockHeight <= config.lastValidBlockHeight) {
5384
- await sleep(1000);
5385
- if (done) return;
5386
- currentBlockHeight = await checkBlockHeight();
5387
- 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;
5388
5562
  }
5389
5563
 
5390
- resolve({
5391
- __type: exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED
5392
- });
5393
- })();
5394
- }
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
5395
5577
  });
5396
5578
  let result;
5397
5579
 
5398
5580
  try {
5399
5581
  const outcome = await Promise.race([confirmationPromise, expiryPromise]);
5400
5582
 
5401
- switch (outcome.__type) {
5402
- case exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED:
5403
- 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;
5404
5590
 
5405
- case exports.TransactionStatus.PROCESSED:
5406
- result = outcome.response;
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
+ }
5605
+
5606
+ signatureStatus = status;
5407
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
+ }
5408
5622
 
5409
- case exports.TransactionStatus.TIMED_OUT:
5410
- throw new TransactionExpiredTimeoutError(rawSignature, outcome.timeoutMs / 1000);
5623
+ break;
5624
+
5625
+ case 'confirmed':
5626
+ case 'single':
5627
+ case 'singleGossip':
5628
+ if (confirmationStatus !== 'confirmed' && confirmationStatus !== 'finalized') {
5629
+ throw new TransactionExpiredNonceInvalidError(signature);
5630
+ }
5631
+
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
+ }
5411
5659
  }
5412
5660
  } finally {
5413
- clearTimeout(timeoutId);
5661
+ done = true;
5662
+ abortConfirmation();
5663
+ }
5414
5664
 
5415
- if (disposeSignatureSubscriptionStateChangeObserver) {
5416
- 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
+ }
5417
5686
  }
5418
5687
 
5419
- if (signatureSubscriptionId) {
5420
- 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);
5421
5709
  }
5710
+ } finally {
5711
+ clearTimeout(timeoutId);
5712
+ abortConfirmation();
5422
5713
  }
5423
5714
 
5424
5715
  return result;
@@ -6474,11 +6765,11 @@ class Connection {
6474
6765
  */
6475
6766
 
6476
6767
 
6477
- async getNonceAndContext(nonceAccount, commitment) {
6768
+ async getNonceAndContext(nonceAccount, commitmentOrConfig) {
6478
6769
  const {
6479
6770
  context,
6480
6771
  value: accountInfo
6481
- } = await this.getAccountInfoAndContext(nonceAccount, commitment);
6772
+ } = await this.getAccountInfoAndContext(nonceAccount, commitmentOrConfig);
6482
6773
  let value = null;
6483
6774
 
6484
6775
  if (accountInfo !== null) {
@@ -6495,8 +6786,8 @@ class Connection {
6495
6786
  */
6496
6787
 
6497
6788
 
6498
- async getNonce(nonceAccount, commitment) {
6499
- 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 => {
6500
6791
  throw new Error('failed to get nonce for account ' + nonceAccount.toBase58() + ': ' + e);
6501
6792
  });
6502
6793
  }
@@ -9911,6 +10202,9 @@ async function sendAndConfirmRawTransaction(connection, rawTransaction, confirma
9911
10202
  if (confirmationStrategyOrConfirmOptions && Object.prototype.hasOwnProperty.call(confirmationStrategyOrConfirmOptions, 'lastValidBlockHeight')) {
9912
10203
  confirmationStrategy = confirmationStrategyOrConfirmOptions;
9913
10204
  options = maybeConfirmOptions;
10205
+ } else if (confirmationStrategyOrConfirmOptions && Object.prototype.hasOwnProperty.call(confirmationStrategyOrConfirmOptions, 'nonceValue')) {
10206
+ confirmationStrategy = confirmationStrategyOrConfirmOptions;
10207
+ options = maybeConfirmOptions;
9914
10208
  } else {
9915
10209
  options = confirmationStrategyOrConfirmOptions;
9916
10210
  }
@@ -9995,6 +10289,7 @@ exports.SystemInstruction = SystemInstruction;
9995
10289
  exports.SystemProgram = SystemProgram;
9996
10290
  exports.Transaction = Transaction;
9997
10291
  exports.TransactionExpiredBlockheightExceededError = TransactionExpiredBlockheightExceededError;
10292
+ exports.TransactionExpiredNonceInvalidError = TransactionExpiredNonceInvalidError;
9998
10293
  exports.TransactionExpiredTimeoutError = TransactionExpiredTimeoutError;
9999
10294
  exports.TransactionInstruction = TransactionInstruction;
10000
10295
  exports.TransactionMessage = TransactionMessage;