@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.
@@ -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 confirmationCommitment = 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
- }, confirmationCommitment);
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) {
@@ -5266,7 +5329,7 @@ class Connection {
5266
5329
  if (value !== null && value !== void 0 && value.err) {
5267
5330
  reject(value.err);
5268
5331
  } else {
5269
- switch (confirmationCommitment) {
5332
+ switch (commitment) {
5270
5333
  case 'confirmed':
5271
5334
  case 'single':
5272
5335
  case 'singleGossip':
@@ -5308,81 +5371,279 @@ class Connection {
5308
5371
  reject(err);
5309
5372
  }
5310
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;
5311
5401
  const expiryPromise = new Promise(resolve => {
5312
- if (typeof strategy === 'string') {
5313
- let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
5314
-
5315
- switch (confirmationCommitment) {
5316
- case 'processed':
5317
- case 'recent':
5318
- case 'single':
5319
- case 'confirmed':
5320
- case 'singleGossip':
5321
- {
5322
- timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
5323
- break;
5324
- }
5402
+ const checkBlockHeight = async () => {
5403
+ try {
5404
+ const blockHeight = await this.getBlockHeight(commitment);
5405
+ return blockHeight;
5406
+ } catch (_e) {
5407
+ return -1;
5325
5408
  }
5409
+ };
5326
5410
 
5327
- timeoutId = setTimeout(() => resolve({
5328
- __type: exports.TransactionStatus.TIMED_OUT,
5329
- timeoutMs
5330
- }), 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;
5331
5441
  } else {
5332
- let config = strategy;
5442
+ throw new TransactionExpiredBlockheightExceededError(signature);
5443
+ }
5444
+ } finally {
5445
+ done = true;
5446
+ abortConfirmation();
5447
+ }
5333
5448
 
5334
- const checkBlockHeight = async () => {
5335
- try {
5336
- const blockHeight = await this.getBlockHeight(commitment);
5337
- return blockHeight;
5338
- } catch (_e) {
5339
- return -1;
5340
- }
5341
- };
5449
+ return result;
5450
+ }
5342
5451
 
5343
- (async () => {
5344
- let currentBlockHeight = await checkBlockHeight();
5345
- 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;
5346
5465
 
5347
- while (currentBlockHeight <= config.lastValidBlockHeight) {
5348
- await sleep(1000);
5349
- if (done) return;
5350
- currentBlockHeight = await checkBlockHeight();
5351
- 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;
5352
5496
  }
5353
5497
 
5354
- resolve({
5355
- __type: exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED
5356
- });
5357
- })();
5358
- }
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
5359
5511
  });
5360
5512
  let result;
5361
5513
 
5362
5514
  try {
5363
5515
  const outcome = await Promise.race([confirmationPromise, expiryPromise]);
5364
5516
 
5365
- switch (outcome.__type) {
5366
- case exports.TransactionStatus.BLOCKHEIGHT_EXCEEDED:
5367
- 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;
5524
+
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
+ }
5368
5539
 
5369
- case exports.TransactionStatus.PROCESSED:
5370
- result = outcome.response;
5540
+ signatureStatus = status;
5371
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
+ }
5556
+
5557
+ break;
5558
+
5559
+ case 'confirmed':
5560
+ case 'single':
5561
+ case 'singleGossip':
5562
+ if (confirmationStatus !== 'confirmed' && confirmationStatus !== 'finalized') {
5563
+ throw new TransactionExpiredNonceInvalidError(signature);
5564
+ }
5372
5565
 
5373
- case exports.TransactionStatus.TIMED_OUT:
5374
- throw new TransactionExpiredTimeoutError(rawSignature, outcome.timeoutMs / 1000);
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
+ }
5375
5593
  }
5376
5594
  } finally {
5377
- clearTimeout(timeoutId);
5595
+ done = true;
5596
+ abortConfirmation();
5597
+ }
5378
5598
 
5379
- if (disposeSignatureSubscriptionStateChangeObserver) {
5380
- 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
+ }
5381
5620
  }
5382
5621
 
5383
- if (signatureSubscriptionId) {
5384
- 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);
5385
5643
  }
5644
+ } finally {
5645
+ clearTimeout(timeoutId);
5646
+ abortConfirmation();
5386
5647
  }
5387
5648
 
5388
5649
  return result;
@@ -6438,11 +6699,11 @@ class Connection {
6438
6699
  */
6439
6700
 
6440
6701
 
6441
- async getNonceAndContext(nonceAccount, commitment) {
6702
+ async getNonceAndContext(nonceAccount, commitmentOrConfig) {
6442
6703
  const {
6443
6704
  context,
6444
6705
  value: accountInfo
6445
- } = await this.getAccountInfoAndContext(nonceAccount, commitment);
6706
+ } = await this.getAccountInfoAndContext(nonceAccount, commitmentOrConfig);
6446
6707
  let value = null;
6447
6708
 
6448
6709
  if (accountInfo !== null) {
@@ -6459,8 +6720,8 @@ class Connection {
6459
6720
  */
6460
6721
 
6461
6722
 
6462
- async getNonce(nonceAccount, commitment) {
6463
- 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 => {
6464
6725
  throw new Error('failed to get nonce for account ' + nonceAccount.toBase58() + ': ' + e);
6465
6726
  });
6466
6727
  }
@@ -9875,6 +10136,9 @@ async function sendAndConfirmRawTransaction(connection, rawTransaction, confirma
9875
10136
  if (confirmationStrategyOrConfirmOptions && Object.prototype.hasOwnProperty.call(confirmationStrategyOrConfirmOptions, 'lastValidBlockHeight')) {
9876
10137
  confirmationStrategy = confirmationStrategyOrConfirmOptions;
9877
10138
  options = maybeConfirmOptions;
10139
+ } else if (confirmationStrategyOrConfirmOptions && Object.prototype.hasOwnProperty.call(confirmationStrategyOrConfirmOptions, 'nonceValue')) {
10140
+ confirmationStrategy = confirmationStrategyOrConfirmOptions;
10141
+ options = maybeConfirmOptions;
9878
10142
  } else {
9879
10143
  options = confirmationStrategyOrConfirmOptions;
9880
10144
  }
@@ -9959,6 +10223,7 @@ exports.SystemInstruction = SystemInstruction;
9959
10223
  exports.SystemProgram = SystemProgram;
9960
10224
  exports.Transaction = Transaction;
9961
10225
  exports.TransactionExpiredBlockheightExceededError = TransactionExpiredBlockheightExceededError;
10226
+ exports.TransactionExpiredNonceInvalidError = TransactionExpiredNonceInvalidError;
9962
10227
  exports.TransactionExpiredTimeoutError = TransactionExpiredTimeoutError;
9963
10228
  exports.TransactionInstruction = TransactionInstruction;
9964
10229
  exports.TransactionMessage = TransactionMessage;