@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.browser.cjs.js +365 -70
- package/lib/index.browser.cjs.js.map +1 -1
- package/lib/index.browser.esm.js +365 -71
- package/lib/index.browser.esm.js.map +1 -1
- package/lib/index.cjs.js +365 -70
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +81 -5
- package/lib/index.esm.js +365 -71
- package/lib/index.esm.js.map +1 -1
- package/lib/index.iife.js +365 -70
- package/lib/index.iife.js.map +1 -1
- package/lib/index.iife.min.js +3 -3
- package/lib/index.iife.min.js.map +1 -1
- package/lib/index.native.js +365 -70
- package/lib/index.native.js.map +1 -1
- package/package.json +1 -1
- package/src/connection.ts +347 -77
- package/src/nonce-account.ts +7 -3
- package/src/transaction/expiry-custom-errors.ts +13 -0
- package/src/transaction/legacy.ts +39 -3
- package/src/utils/send-and-confirm-raw-transaction.ts +13 -0
- package/src/utils/send-and-confirm-transaction.ts +39 -18
package/package.json
CHANGED
package/src/connection.ts
CHANGED
|
@@ -28,7 +28,7 @@ import {AgentManager} from './agent-manager';
|
|
|
28
28
|
import {EpochSchedule} from './epoch-schedule';
|
|
29
29
|
import {SendTransactionError, SolanaJSONRPCError} from './errors';
|
|
30
30
|
import fetchImpl, {Response} from './fetch-impl';
|
|
31
|
-
import {NonceAccount} from './nonce-account';
|
|
31
|
+
import {DurableNonce, NonceAccount} from './nonce-account';
|
|
32
32
|
import {PublicKey} from './publickey';
|
|
33
33
|
import {Signer} from './keypair';
|
|
34
34
|
import {MS_PER_SLOT} from './timing';
|
|
@@ -45,6 +45,7 @@ import {sleep} from './utils/sleep';
|
|
|
45
45
|
import {toBuffer} from './utils/to-buffer';
|
|
46
46
|
import {
|
|
47
47
|
TransactionExpiredBlockheightExceededError,
|
|
48
|
+
TransactionExpiredNonceInvalidError,
|
|
48
49
|
TransactionExpiredTimeoutError,
|
|
49
50
|
} from './transaction/expiry-custom-errors';
|
|
50
51
|
import {makeWebsocketUrl} from './utils/makeWebsocketUrl';
|
|
@@ -338,6 +339,28 @@ function extractCommitmentFromConfig<TConfig>(
|
|
|
338
339
|
return {commitment, config};
|
|
339
340
|
}
|
|
340
341
|
|
|
342
|
+
/**
|
|
343
|
+
* A strategy for confirming durable nonce transactions.
|
|
344
|
+
*/
|
|
345
|
+
export type DurableNonceTransactionConfirmationStrategy = {
|
|
346
|
+
/**
|
|
347
|
+
* The lowest slot at which to fetch the nonce value from the
|
|
348
|
+
* nonce account. This should be no lower than the slot at
|
|
349
|
+
* which the last-known value of the nonce was fetched.
|
|
350
|
+
*/
|
|
351
|
+
minContextSlot: number;
|
|
352
|
+
/**
|
|
353
|
+
* The account where the current value of the nonce is stored.
|
|
354
|
+
*/
|
|
355
|
+
nonceAccountPubkey: PublicKey;
|
|
356
|
+
/**
|
|
357
|
+
* The nonce value that was used to sign the transaction
|
|
358
|
+
* for which confirmation is being sought.
|
|
359
|
+
*/
|
|
360
|
+
nonceValue: DurableNonce;
|
|
361
|
+
signature: TransactionSignature;
|
|
362
|
+
};
|
|
363
|
+
|
|
341
364
|
/**
|
|
342
365
|
* @internal
|
|
343
366
|
*/
|
|
@@ -2438,6 +2461,26 @@ export type GetTransactionCountConfig = {
|
|
|
2438
2461
|
minContextSlot?: number;
|
|
2439
2462
|
};
|
|
2440
2463
|
|
|
2464
|
+
/**
|
|
2465
|
+
* Configuration object for `getNonce`
|
|
2466
|
+
*/
|
|
2467
|
+
export type GetNonceConfig = {
|
|
2468
|
+
/** Optional commitment level */
|
|
2469
|
+
commitment?: Commitment;
|
|
2470
|
+
/** The minimum slot that the request can be evaluated at */
|
|
2471
|
+
minContextSlot?: number;
|
|
2472
|
+
};
|
|
2473
|
+
|
|
2474
|
+
/**
|
|
2475
|
+
* Configuration object for `getNonceAndContext`
|
|
2476
|
+
*/
|
|
2477
|
+
export type GetNonceAndContextConfig = {
|
|
2478
|
+
/** Optional commitment level */
|
|
2479
|
+
commitment?: Commitment;
|
|
2480
|
+
/** The minimum slot that the request can be evaluated at */
|
|
2481
|
+
minContextSlot?: number;
|
|
2482
|
+
};
|
|
2483
|
+
|
|
2441
2484
|
/**
|
|
2442
2485
|
* Information describing an account
|
|
2443
2486
|
*/
|
|
@@ -3348,7 +3391,9 @@ export class Connection {
|
|
|
3348
3391
|
}
|
|
3349
3392
|
|
|
3350
3393
|
confirmTransaction(
|
|
3351
|
-
strategy:
|
|
3394
|
+
strategy:
|
|
3395
|
+
| BlockheightBasedTransactionConfirmationStrategy
|
|
3396
|
+
| DurableNonceTransactionConfirmationStrategy,
|
|
3352
3397
|
commitment?: Commitment,
|
|
3353
3398
|
): Promise<RpcResponseAndContext<SignatureResult>>;
|
|
3354
3399
|
|
|
@@ -3363,6 +3408,7 @@ export class Connection {
|
|
|
3363
3408
|
async confirmTransaction(
|
|
3364
3409
|
strategy:
|
|
3365
3410
|
| BlockheightBasedTransactionConfirmationStrategy
|
|
3411
|
+
| DurableNonceTransactionConfirmationStrategy
|
|
3366
3412
|
| TransactionSignature,
|
|
3367
3413
|
commitment?: Commitment,
|
|
3368
3414
|
): Promise<RpcResponseAndContext<SignatureResult>> {
|
|
@@ -3371,8 +3417,9 @@ export class Connection {
|
|
|
3371
3417
|
if (typeof strategy == 'string') {
|
|
3372
3418
|
rawSignature = strategy;
|
|
3373
3419
|
} else {
|
|
3374
|
-
const config =
|
|
3375
|
-
|
|
3420
|
+
const config = strategy as
|
|
3421
|
+
| BlockheightBasedTransactionConfirmationStrategy
|
|
3422
|
+
| DurableNonceTransactionConfirmationStrategy;
|
|
3376
3423
|
rawSignature = config.signature;
|
|
3377
3424
|
}
|
|
3378
3425
|
|
|
@@ -3386,31 +3433,58 @@ export class Connection {
|
|
|
3386
3433
|
|
|
3387
3434
|
assert(decodedSignature.length === 64, 'signature has invalid length');
|
|
3388
3435
|
|
|
3389
|
-
|
|
3390
|
-
|
|
3436
|
+
if (typeof strategy === 'string') {
|
|
3437
|
+
return await this.confirmTransactionUsingLegacyTimeoutStrategy({
|
|
3438
|
+
commitment: commitment || this.commitment,
|
|
3439
|
+
signature: rawSignature,
|
|
3440
|
+
});
|
|
3441
|
+
} else if ('lastValidBlockHeight' in strategy) {
|
|
3442
|
+
return await this.confirmTransactionUsingBlockHeightExceedanceStrategy({
|
|
3443
|
+
commitment: commitment || this.commitment,
|
|
3444
|
+
strategy,
|
|
3445
|
+
});
|
|
3446
|
+
} else {
|
|
3447
|
+
return await this.confirmTransactionUsingDurableNonceStrategy({
|
|
3448
|
+
commitment: commitment || this.commitment,
|
|
3449
|
+
strategy,
|
|
3450
|
+
});
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
|
|
3454
|
+
private getTransactionConfirmationPromise({
|
|
3455
|
+
commitment,
|
|
3456
|
+
signature,
|
|
3457
|
+
}: {
|
|
3458
|
+
commitment?: Commitment;
|
|
3459
|
+
signature: string;
|
|
3460
|
+
}): {
|
|
3461
|
+
abortConfirmation(): void;
|
|
3462
|
+
confirmationPromise: Promise<{
|
|
3463
|
+
__type: TransactionStatus.PROCESSED;
|
|
3464
|
+
response: RpcResponseAndContext<SignatureResult>;
|
|
3465
|
+
}>;
|
|
3466
|
+
} {
|
|
3391
3467
|
let signatureSubscriptionId: number | undefined;
|
|
3392
3468
|
let disposeSignatureSubscriptionStateChangeObserver:
|
|
3393
3469
|
| SubscriptionStateChangeDisposeFn
|
|
3394
3470
|
| undefined;
|
|
3395
3471
|
let done = false;
|
|
3396
|
-
|
|
3397
3472
|
const confirmationPromise = new Promise<{
|
|
3398
3473
|
__type: TransactionStatus.PROCESSED;
|
|
3399
3474
|
response: RpcResponseAndContext<SignatureResult>;
|
|
3400
3475
|
}>((resolve, reject) => {
|
|
3401
3476
|
try {
|
|
3402
3477
|
signatureSubscriptionId = this.onSignature(
|
|
3403
|
-
|
|
3478
|
+
signature,
|
|
3404
3479
|
(result: SignatureResult, context: Context) => {
|
|
3405
3480
|
signatureSubscriptionId = undefined;
|
|
3406
3481
|
const response = {
|
|
3407
3482
|
context,
|
|
3408
3483
|
value: result,
|
|
3409
3484
|
};
|
|
3410
|
-
done = true;
|
|
3411
3485
|
resolve({__type: TransactionStatus.PROCESSED, response});
|
|
3412
3486
|
},
|
|
3413
|
-
|
|
3487
|
+
commitment,
|
|
3414
3488
|
);
|
|
3415
3489
|
const subscriptionSetupPromise = new Promise<void>(
|
|
3416
3490
|
resolveSubscriptionSetup => {
|
|
@@ -3432,16 +3506,42 @@ export class Connection {
|
|
|
3432
3506
|
(async () => {
|
|
3433
3507
|
await subscriptionSetupPromise;
|
|
3434
3508
|
if (done) return;
|
|
3435
|
-
const response = await this.getSignatureStatus(
|
|
3509
|
+
const response = await this.getSignatureStatus(signature);
|
|
3436
3510
|
if (done) return;
|
|
3437
3511
|
if (response == null) {
|
|
3438
3512
|
return;
|
|
3439
3513
|
}
|
|
3440
3514
|
const {context, value} = response;
|
|
3515
|
+
if (value == null) {
|
|
3516
|
+
return;
|
|
3517
|
+
}
|
|
3441
3518
|
if (value?.err) {
|
|
3442
3519
|
reject(value.err);
|
|
3443
|
-
}
|
|
3444
|
-
|
|
3520
|
+
} else {
|
|
3521
|
+
switch (commitment) {
|
|
3522
|
+
case 'confirmed':
|
|
3523
|
+
case 'single':
|
|
3524
|
+
case 'singleGossip': {
|
|
3525
|
+
if (value.confirmationStatus === 'processed') {
|
|
3526
|
+
return;
|
|
3527
|
+
}
|
|
3528
|
+
break;
|
|
3529
|
+
}
|
|
3530
|
+
case 'finalized':
|
|
3531
|
+
case 'max':
|
|
3532
|
+
case 'root': {
|
|
3533
|
+
if (
|
|
3534
|
+
value.confirmationStatus === 'processed' ||
|
|
3535
|
+
value.confirmationStatus === 'confirmed'
|
|
3536
|
+
) {
|
|
3537
|
+
return;
|
|
3538
|
+
}
|
|
3539
|
+
break;
|
|
3540
|
+
}
|
|
3541
|
+
// exhaust enums to ensure full coverage
|
|
3542
|
+
case 'processed':
|
|
3543
|
+
case 'recent':
|
|
3544
|
+
}
|
|
3445
3545
|
done = true;
|
|
3446
3546
|
resolve({
|
|
3447
3547
|
__type: TransactionStatus.PROCESSED,
|
|
@@ -3456,80 +3556,250 @@ export class Connection {
|
|
|
3456
3556
|
reject(err);
|
|
3457
3557
|
}
|
|
3458
3558
|
});
|
|
3559
|
+
const abortConfirmation = () => {
|
|
3560
|
+
if (disposeSignatureSubscriptionStateChangeObserver) {
|
|
3561
|
+
disposeSignatureSubscriptionStateChangeObserver();
|
|
3562
|
+
disposeSignatureSubscriptionStateChangeObserver = undefined;
|
|
3563
|
+
}
|
|
3564
|
+
if (signatureSubscriptionId) {
|
|
3565
|
+
this.removeSignatureListener(signatureSubscriptionId);
|
|
3566
|
+
signatureSubscriptionId = undefined;
|
|
3567
|
+
}
|
|
3568
|
+
};
|
|
3569
|
+
return {abortConfirmation, confirmationPromise};
|
|
3570
|
+
}
|
|
3459
3571
|
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
case 'max':
|
|
3478
|
-
case 'root':
|
|
3572
|
+
private async confirmTransactionUsingBlockHeightExceedanceStrategy({
|
|
3573
|
+
commitment,
|
|
3574
|
+
strategy: {lastValidBlockHeight, signature},
|
|
3575
|
+
}: {
|
|
3576
|
+
commitment?: Commitment;
|
|
3577
|
+
strategy: BlockheightBasedTransactionConfirmationStrategy;
|
|
3578
|
+
}) {
|
|
3579
|
+
let done: boolean = false;
|
|
3580
|
+
const expiryPromise = new Promise<{
|
|
3581
|
+
__type: TransactionStatus.BLOCKHEIGHT_EXCEEDED;
|
|
3582
|
+
}>(resolve => {
|
|
3583
|
+
const checkBlockHeight = async () => {
|
|
3584
|
+
try {
|
|
3585
|
+
const blockHeight = await this.getBlockHeight(commitment);
|
|
3586
|
+
return blockHeight;
|
|
3587
|
+
} catch (_e) {
|
|
3588
|
+
return -1;
|
|
3479
3589
|
}
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
)
|
|
3590
|
+
};
|
|
3591
|
+
(async () => {
|
|
3592
|
+
let currentBlockHeight = await checkBlockHeight();
|
|
3593
|
+
if (done) return;
|
|
3594
|
+
while (currentBlockHeight <= lastValidBlockHeight) {
|
|
3595
|
+
await sleep(1000);
|
|
3596
|
+
if (done) return;
|
|
3597
|
+
currentBlockHeight = await checkBlockHeight();
|
|
3598
|
+
if (done) return;
|
|
3599
|
+
}
|
|
3600
|
+
resolve({__type: TransactionStatus.BLOCKHEIGHT_EXCEEDED});
|
|
3601
|
+
})();
|
|
3602
|
+
});
|
|
3603
|
+
const {abortConfirmation, confirmationPromise} =
|
|
3604
|
+
this.getTransactionConfirmationPromise({commitment, signature});
|
|
3605
|
+
let result: RpcResponseAndContext<SignatureResult>;
|
|
3606
|
+
try {
|
|
3607
|
+
const outcome = await Promise.race([confirmationPromise, expiryPromise]);
|
|
3608
|
+
if (outcome.__type === TransactionStatus.PROCESSED) {
|
|
3609
|
+
result = outcome.response;
|
|
3485
3610
|
} else {
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3611
|
+
throw new TransactionExpiredBlockheightExceededError(signature);
|
|
3612
|
+
}
|
|
3613
|
+
} finally {
|
|
3614
|
+
done = true;
|
|
3615
|
+
abortConfirmation();
|
|
3616
|
+
}
|
|
3617
|
+
return result;
|
|
3618
|
+
}
|
|
3619
|
+
|
|
3620
|
+
private async confirmTransactionUsingDurableNonceStrategy({
|
|
3621
|
+
commitment,
|
|
3622
|
+
strategy: {minContextSlot, nonceAccountPubkey, nonceValue, signature},
|
|
3623
|
+
}: {
|
|
3624
|
+
commitment?: Commitment;
|
|
3625
|
+
strategy: DurableNonceTransactionConfirmationStrategy;
|
|
3626
|
+
}) {
|
|
3627
|
+
let done: boolean = false;
|
|
3628
|
+
const expiryPromise = new Promise<{
|
|
3629
|
+
__type: TransactionStatus.NONCE_INVALID;
|
|
3630
|
+
slotInWhichNonceDidAdvance: number | null;
|
|
3631
|
+
}>(resolve => {
|
|
3632
|
+
let currentNonceValue: string | undefined = nonceValue;
|
|
3633
|
+
let lastCheckedSlot: number | null = null;
|
|
3634
|
+
const getCurrentNonceValue = async () => {
|
|
3635
|
+
try {
|
|
3636
|
+
const {context, value: nonceAccount} = await this.getNonceAndContext(
|
|
3637
|
+
nonceAccountPubkey,
|
|
3638
|
+
{
|
|
3639
|
+
commitment,
|
|
3640
|
+
minContextSlot,
|
|
3641
|
+
},
|
|
3642
|
+
);
|
|
3643
|
+
lastCheckedSlot = context.slot;
|
|
3644
|
+
return nonceAccount?.nonce;
|
|
3645
|
+
} catch (e) {
|
|
3646
|
+
// If for whatever reason we can't reach/read the nonce
|
|
3647
|
+
// account, just keep using the last-known value.
|
|
3648
|
+
return currentNonceValue;
|
|
3649
|
+
}
|
|
3650
|
+
};
|
|
3651
|
+
(async () => {
|
|
3652
|
+
currentNonceValue = await getCurrentNonceValue();
|
|
3653
|
+
if (done) return;
|
|
3654
|
+
while (
|
|
3655
|
+
true // eslint-disable-line no-constant-condition
|
|
3656
|
+
) {
|
|
3657
|
+
if (nonceValue !== currentNonceValue) {
|
|
3658
|
+
resolve({
|
|
3659
|
+
__type: TransactionStatus.NONCE_INVALID,
|
|
3660
|
+
slotInWhichNonceDidAdvance: lastCheckedSlot,
|
|
3661
|
+
});
|
|
3662
|
+
return;
|
|
3494
3663
|
}
|
|
3495
|
-
|
|
3496
|
-
(async () => {
|
|
3497
|
-
let currentBlockHeight = await checkBlockHeight();
|
|
3664
|
+
await sleep(2000);
|
|
3498
3665
|
if (done) return;
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
if (done) return;
|
|
3504
|
-
}
|
|
3505
|
-
resolve({__type: TransactionStatus.BLOCKHEIGHT_EXCEEDED});
|
|
3506
|
-
})();
|
|
3507
|
-
}
|
|
3666
|
+
currentNonceValue = await getCurrentNonceValue();
|
|
3667
|
+
if (done) return;
|
|
3668
|
+
}
|
|
3669
|
+
})();
|
|
3508
3670
|
});
|
|
3509
|
-
|
|
3671
|
+
const {abortConfirmation, confirmationPromise} =
|
|
3672
|
+
this.getTransactionConfirmationPromise({commitment, signature});
|
|
3510
3673
|
let result: RpcResponseAndContext<SignatureResult>;
|
|
3511
3674
|
try {
|
|
3512
3675
|
const outcome = await Promise.race([confirmationPromise, expiryPromise]);
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3676
|
+
if (outcome.__type === TransactionStatus.PROCESSED) {
|
|
3677
|
+
result = outcome.response;
|
|
3678
|
+
} else {
|
|
3679
|
+
// Double check that the transaction is indeed unconfirmed.
|
|
3680
|
+
let signatureStatus:
|
|
3681
|
+
| RpcResponseAndContext<SignatureStatus | null>
|
|
3682
|
+
| null
|
|
3683
|
+
| undefined;
|
|
3684
|
+
while (
|
|
3685
|
+
true // eslint-disable-line no-constant-condition
|
|
3686
|
+
) {
|
|
3687
|
+
const status = await this.getSignatureStatus(signature);
|
|
3688
|
+
if (status == null) {
|
|
3689
|
+
break;
|
|
3690
|
+
}
|
|
3691
|
+
if (
|
|
3692
|
+
status.context.slot <
|
|
3693
|
+
(outcome.slotInWhichNonceDidAdvance ?? minContextSlot)
|
|
3694
|
+
) {
|
|
3695
|
+
await sleep(400);
|
|
3696
|
+
continue;
|
|
3697
|
+
}
|
|
3698
|
+
signatureStatus = status;
|
|
3518
3699
|
break;
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
)
|
|
3700
|
+
}
|
|
3701
|
+
if (signatureStatus?.value) {
|
|
3702
|
+
const commitmentForStatus = commitment || 'finalized';
|
|
3703
|
+
const {confirmationStatus} = signatureStatus.value;
|
|
3704
|
+
switch (commitmentForStatus) {
|
|
3705
|
+
case 'processed':
|
|
3706
|
+
case 'recent':
|
|
3707
|
+
if (
|
|
3708
|
+
confirmationStatus !== 'processed' &&
|
|
3709
|
+
confirmationStatus !== 'confirmed' &&
|
|
3710
|
+
confirmationStatus !== 'finalized'
|
|
3711
|
+
) {
|
|
3712
|
+
throw new TransactionExpiredNonceInvalidError(signature);
|
|
3713
|
+
}
|
|
3714
|
+
break;
|
|
3715
|
+
case 'confirmed':
|
|
3716
|
+
case 'single':
|
|
3717
|
+
case 'singleGossip':
|
|
3718
|
+
if (
|
|
3719
|
+
confirmationStatus !== 'confirmed' &&
|
|
3720
|
+
confirmationStatus !== 'finalized'
|
|
3721
|
+
) {
|
|
3722
|
+
throw new TransactionExpiredNonceInvalidError(signature);
|
|
3723
|
+
}
|
|
3724
|
+
break;
|
|
3725
|
+
case 'finalized':
|
|
3726
|
+
case 'max':
|
|
3727
|
+
case 'root':
|
|
3728
|
+
if (confirmationStatus !== 'finalized') {
|
|
3729
|
+
throw new TransactionExpiredNonceInvalidError(signature);
|
|
3730
|
+
}
|
|
3731
|
+
break;
|
|
3732
|
+
default:
|
|
3733
|
+
// Exhaustive switch.
|
|
3734
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3735
|
+
((_: never) => {})(commitmentForStatus);
|
|
3736
|
+
}
|
|
3737
|
+
result = {
|
|
3738
|
+
context: signatureStatus.context,
|
|
3739
|
+
value: {err: signatureStatus.value.err},
|
|
3740
|
+
};
|
|
3741
|
+
} else {
|
|
3742
|
+
throw new TransactionExpiredNonceInvalidError(signature);
|
|
3743
|
+
}
|
|
3524
3744
|
}
|
|
3525
3745
|
} finally {
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3746
|
+
done = true;
|
|
3747
|
+
abortConfirmation();
|
|
3748
|
+
}
|
|
3749
|
+
return result;
|
|
3750
|
+
}
|
|
3751
|
+
|
|
3752
|
+
private async confirmTransactionUsingLegacyTimeoutStrategy({
|
|
3753
|
+
commitment,
|
|
3754
|
+
signature,
|
|
3755
|
+
}: {
|
|
3756
|
+
commitment?: Commitment;
|
|
3757
|
+
signature: string;
|
|
3758
|
+
}) {
|
|
3759
|
+
let timeoutId;
|
|
3760
|
+
const expiryPromise = new Promise<{
|
|
3761
|
+
__type: TransactionStatus.TIMED_OUT;
|
|
3762
|
+
timeoutMs: number;
|
|
3763
|
+
}>(resolve => {
|
|
3764
|
+
let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
|
|
3765
|
+
switch (commitment) {
|
|
3766
|
+
case 'processed':
|
|
3767
|
+
case 'recent':
|
|
3768
|
+
case 'single':
|
|
3769
|
+
case 'confirmed':
|
|
3770
|
+
case 'singleGossip': {
|
|
3771
|
+
timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
|
|
3772
|
+
break;
|
|
3773
|
+
}
|
|
3774
|
+
// exhaust enums to ensure full coverage
|
|
3775
|
+
case 'finalized':
|
|
3776
|
+
case 'max':
|
|
3777
|
+
case 'root':
|
|
3529
3778
|
}
|
|
3530
|
-
|
|
3531
|
-
|
|
3779
|
+
timeoutId = setTimeout(
|
|
3780
|
+
() => resolve({__type: TransactionStatus.TIMED_OUT, timeoutMs}),
|
|
3781
|
+
timeoutMs,
|
|
3782
|
+
);
|
|
3783
|
+
});
|
|
3784
|
+
const {abortConfirmation, confirmationPromise} =
|
|
3785
|
+
this.getTransactionConfirmationPromise({
|
|
3786
|
+
commitment,
|
|
3787
|
+
signature,
|
|
3788
|
+
});
|
|
3789
|
+
let result: RpcResponseAndContext<SignatureResult>;
|
|
3790
|
+
try {
|
|
3791
|
+
const outcome = await Promise.race([confirmationPromise, expiryPromise]);
|
|
3792
|
+
if (outcome.__type === TransactionStatus.PROCESSED) {
|
|
3793
|
+
result = outcome.response;
|
|
3794
|
+
} else {
|
|
3795
|
+
throw new TransactionExpiredTimeoutError(
|
|
3796
|
+
signature,
|
|
3797
|
+
outcome.timeoutMs / 1000,
|
|
3798
|
+
);
|
|
3532
3799
|
}
|
|
3800
|
+
} finally {
|
|
3801
|
+
clearTimeout(timeoutId);
|
|
3802
|
+
abortConfirmation();
|
|
3533
3803
|
}
|
|
3534
3804
|
return result;
|
|
3535
3805
|
}
|
|
@@ -4682,11 +4952,11 @@ export class Connection {
|
|
|
4682
4952
|
*/
|
|
4683
4953
|
async getNonceAndContext(
|
|
4684
4954
|
nonceAccount: PublicKey,
|
|
4685
|
-
|
|
4955
|
+
commitmentOrConfig?: Commitment | GetNonceAndContextConfig,
|
|
4686
4956
|
): Promise<RpcResponseAndContext<NonceAccount | null>> {
|
|
4687
4957
|
const {context, value: accountInfo} = await this.getAccountInfoAndContext(
|
|
4688
4958
|
nonceAccount,
|
|
4689
|
-
|
|
4959
|
+
commitmentOrConfig,
|
|
4690
4960
|
);
|
|
4691
4961
|
|
|
4692
4962
|
let value = null;
|
|
@@ -4705,9 +4975,9 @@ export class Connection {
|
|
|
4705
4975
|
*/
|
|
4706
4976
|
async getNonce(
|
|
4707
4977
|
nonceAccount: PublicKey,
|
|
4708
|
-
|
|
4978
|
+
commitmentOrConfig?: Commitment | GetNonceConfig,
|
|
4709
4979
|
): Promise<NonceAccount | null> {
|
|
4710
|
-
return await this.getNonceAndContext(nonceAccount,
|
|
4980
|
+
return await this.getNonceAndContext(nonceAccount, commitmentOrConfig)
|
|
4711
4981
|
.then(x => x.value)
|
|
4712
4982
|
.catch(e => {
|
|
4713
4983
|
throw new Error(
|
package/src/nonce-account.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as BufferLayout from '@solana/buffer-layout';
|
|
2
2
|
import {Buffer} from 'buffer';
|
|
3
3
|
|
|
4
|
-
import type {Blockhash} from './blockhash';
|
|
5
4
|
import * as Layout from './layout';
|
|
6
5
|
import {PublicKey} from './publickey';
|
|
7
6
|
import type {FeeCalculator} from './fee-calculator';
|
|
@@ -36,9 +35,14 @@ const NonceAccountLayout = BufferLayout.struct<
|
|
|
36
35
|
|
|
37
36
|
export const NONCE_ACCOUNT_LENGTH = NonceAccountLayout.span;
|
|
38
37
|
|
|
38
|
+
/**
|
|
39
|
+
* A durable nonce is a 32 byte value encoded as a base58 string.
|
|
40
|
+
*/
|
|
41
|
+
export type DurableNonce = string;
|
|
42
|
+
|
|
39
43
|
type NonceAccountArgs = {
|
|
40
44
|
authorizedPubkey: PublicKey;
|
|
41
|
-
nonce:
|
|
45
|
+
nonce: DurableNonce;
|
|
42
46
|
feeCalculator: FeeCalculator;
|
|
43
47
|
};
|
|
44
48
|
|
|
@@ -47,7 +51,7 @@ type NonceAccountArgs = {
|
|
|
47
51
|
*/
|
|
48
52
|
export class NonceAccount {
|
|
49
53
|
authorizedPubkey: PublicKey;
|
|
50
|
-
nonce:
|
|
54
|
+
nonce: DurableNonce;
|
|
51
55
|
feeCalculator: FeeCalculator;
|
|
52
56
|
|
|
53
57
|
/**
|
|
@@ -33,3 +33,16 @@ export class TransactionExpiredTimeoutError extends Error {
|
|
|
33
33
|
Object.defineProperty(TransactionExpiredTimeoutError.prototype, 'name', {
|
|
34
34
|
value: 'TransactionExpiredTimeoutError',
|
|
35
35
|
});
|
|
36
|
+
|
|
37
|
+
export class TransactionExpiredNonceInvalidError extends Error {
|
|
38
|
+
signature: string;
|
|
39
|
+
|
|
40
|
+
constructor(signature: string) {
|
|
41
|
+
super(`Signature ${signature} has expired: the nonce is no longer valid.`);
|
|
42
|
+
this.signature = signature;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
Object.defineProperty(TransactionExpiredNonceInvalidError.prototype, 'name', {
|
|
47
|
+
value: 'TransactionExpiredNonceInvalidError',
|
|
48
|
+
});
|