@solana/web3.js 1.41.8 → 1.42.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 +182 -64
- package/lib/index.browser.cjs.js.map +1 -1
- package/lib/index.browser.esm.js +183 -65
- package/lib/index.browser.esm.js.map +1 -1
- package/lib/index.cjs.js +182 -64
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +65 -22
- package/lib/index.esm.js +183 -65
- package/lib/index.esm.js.map +1 -1
- package/lib/index.iife.js +182 -64
- package/lib/index.iife.js.map +1 -1
- package/lib/index.iife.min.js +2 -2
- package/lib/index.iife.min.js.map +1 -1
- package/package.json +2 -2
- package/src/connection.ts +157 -65
- package/src/transaction.ts +54 -6
- package/src/util/send-and-confirm-raw-transaction.ts +52 -7
- package/src/util/send-and-confirm-transaction.ts +19 -6
- package/src/util/tx-expiry-custom-errors.ts +35 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solana/web3.js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.0",
|
|
4
4
|
"description": "Solana Javascript API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
"sinon": "^13.0.2",
|
|
130
130
|
"sinon-chai": "^3.7.0",
|
|
131
131
|
"start-server-and-test": "^1.12.0",
|
|
132
|
-
"ts-mocha": "^
|
|
132
|
+
"ts-mocha": "^10.0.0",
|
|
133
133
|
"ts-node": "^10.0.0",
|
|
134
134
|
"tslib": "^2.1.0",
|
|
135
135
|
"typedoc": "^0.22.2",
|
package/src/connection.ts
CHANGED
|
@@ -32,12 +32,15 @@ import {NonceAccount} from './nonce-account';
|
|
|
32
32
|
import {PublicKey} from './publickey';
|
|
33
33
|
import {Signer} from './keypair';
|
|
34
34
|
import {MS_PER_SLOT} from './timing';
|
|
35
|
-
import {Transaction} from './transaction';
|
|
35
|
+
import {Transaction, TransactionStatus} from './transaction';
|
|
36
36
|
import {Message} from './message';
|
|
37
37
|
import assert from './util/assert';
|
|
38
38
|
import {sleep} from './util/sleep';
|
|
39
|
-
import {promiseTimeout} from './util/promise-timeout';
|
|
40
39
|
import {toBuffer} from './util/to-buffer';
|
|
40
|
+
import {
|
|
41
|
+
TransactionExpiredBlockheightExceededError,
|
|
42
|
+
TransactionExpiredTimeoutError,
|
|
43
|
+
} from './util/tx-expiry-custom-errors';
|
|
41
44
|
import {makeWebsocketUrl} from './util/url';
|
|
42
45
|
import type {Blockhash} from './blockhash';
|
|
43
46
|
import type {FeeCalculator} from './fee-calculator';
|
|
@@ -281,6 +284,19 @@ export type RpcResponseAndContext<T> = {
|
|
|
281
284
|
value: T;
|
|
282
285
|
};
|
|
283
286
|
|
|
287
|
+
export type BlockhashWithExpiryBlockHeight = Readonly<{
|
|
288
|
+
blockhash: Blockhash;
|
|
289
|
+
lastValidBlockHeight: number;
|
|
290
|
+
}>;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* A strategy for confirming transactions that uses the last valid
|
|
294
|
+
* block height for a given blockhash to check for transaction expiration.
|
|
295
|
+
*/
|
|
296
|
+
export type BlockheightBasedTransactionConfimationStrategy = {
|
|
297
|
+
signature: TransactionSignature;
|
|
298
|
+
} & BlockhashWithExpiryBlockHeight;
|
|
299
|
+
|
|
284
300
|
/**
|
|
285
301
|
* @internal
|
|
286
302
|
*/
|
|
@@ -2205,12 +2221,12 @@ export class Connection {
|
|
|
2205
2221
|
/** @internal */ _disableBlockhashCaching: boolean = false;
|
|
2206
2222
|
/** @internal */ _pollingBlockhash: boolean = false;
|
|
2207
2223
|
/** @internal */ _blockhashInfo: {
|
|
2208
|
-
|
|
2224
|
+
latestBlockhash: BlockhashWithExpiryBlockHeight | null;
|
|
2209
2225
|
lastFetch: number;
|
|
2210
2226
|
simulatedSignatures: Array<string>;
|
|
2211
2227
|
transactionSignatures: Array<string>;
|
|
2212
2228
|
} = {
|
|
2213
|
-
|
|
2229
|
+
latestBlockhash: null,
|
|
2214
2230
|
lastFetch: 0,
|
|
2215
2231
|
transactionSignatures: [],
|
|
2216
2232
|
simulatedSignatures: [],
|
|
@@ -2825,38 +2841,64 @@ export class Connection {
|
|
|
2825
2841
|
return res.result;
|
|
2826
2842
|
}
|
|
2827
2843
|
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2844
|
+
confirmTransaction(
|
|
2845
|
+
strategy: BlockheightBasedTransactionConfimationStrategy,
|
|
2846
|
+
commitment?: Commitment,
|
|
2847
|
+
): Promise<RpcResponseAndContext<SignatureResult>>;
|
|
2848
|
+
|
|
2849
|
+
/** @deprecated Instead, call `confirmTransaction` using a `TransactionConfirmationConfig` */
|
|
2850
|
+
// eslint-disable-next-line no-dupe-class-members
|
|
2851
|
+
confirmTransaction(
|
|
2852
|
+
strategy: TransactionSignature,
|
|
2853
|
+
commitment?: Commitment,
|
|
2854
|
+
): Promise<RpcResponseAndContext<SignatureResult>>;
|
|
2855
|
+
|
|
2856
|
+
// eslint-disable-next-line no-dupe-class-members
|
|
2831
2857
|
async confirmTransaction(
|
|
2832
|
-
|
|
2858
|
+
strategy:
|
|
2859
|
+
| BlockheightBasedTransactionConfimationStrategy
|
|
2860
|
+
| TransactionSignature,
|
|
2833
2861
|
commitment?: Commitment,
|
|
2834
2862
|
): Promise<RpcResponseAndContext<SignatureResult>> {
|
|
2863
|
+
let rawSignature: string;
|
|
2864
|
+
|
|
2865
|
+
if (typeof strategy == 'string') {
|
|
2866
|
+
rawSignature = strategy;
|
|
2867
|
+
} else {
|
|
2868
|
+
const config = strategy as BlockheightBasedTransactionConfimationStrategy;
|
|
2869
|
+
rawSignature = config.signature;
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2835
2872
|
let decodedSignature;
|
|
2873
|
+
|
|
2836
2874
|
try {
|
|
2837
|
-
decodedSignature = bs58.decode(
|
|
2875
|
+
decodedSignature = bs58.decode(rawSignature);
|
|
2838
2876
|
} catch (err) {
|
|
2839
|
-
throw new Error('signature must be base58 encoded: ' +
|
|
2877
|
+
throw new Error('signature must be base58 encoded: ' + rawSignature);
|
|
2840
2878
|
}
|
|
2841
2879
|
|
|
2842
2880
|
assert(decodedSignature.length === 64, 'signature has invalid length');
|
|
2843
2881
|
|
|
2844
|
-
const start = Date.now();
|
|
2845
2882
|
const subscriptionCommitment = commitment || this.commitment;
|
|
2846
|
-
|
|
2883
|
+
let timeoutId;
|
|
2847
2884
|
let subscriptionId;
|
|
2848
|
-
let
|
|
2849
|
-
|
|
2885
|
+
let done = false;
|
|
2886
|
+
|
|
2887
|
+
const confirmationPromise = new Promise<{
|
|
2888
|
+
__type: TransactionStatus.PROCESSED;
|
|
2889
|
+
response: RpcResponseAndContext<SignatureResult>;
|
|
2890
|
+
}>((resolve, reject) => {
|
|
2850
2891
|
try {
|
|
2851
2892
|
subscriptionId = this.onSignature(
|
|
2852
|
-
|
|
2893
|
+
rawSignature,
|
|
2853
2894
|
(result: SignatureResult, context: Context) => {
|
|
2854
2895
|
subscriptionId = undefined;
|
|
2855
|
-
response = {
|
|
2896
|
+
const response = {
|
|
2856
2897
|
context,
|
|
2857
2898
|
value: result,
|
|
2858
2899
|
};
|
|
2859
|
-
|
|
2900
|
+
done = true;
|
|
2901
|
+
resolve({__type: TransactionStatus.PROCESSED, response});
|
|
2860
2902
|
},
|
|
2861
2903
|
subscriptionCommitment,
|
|
2862
2904
|
);
|
|
@@ -2865,40 +2907,78 @@ export class Connection {
|
|
|
2865
2907
|
}
|
|
2866
2908
|
});
|
|
2867
2909
|
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
case 'singleGossip': {
|
|
2875
|
-
timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
|
|
2876
|
-
break;
|
|
2910
|
+
const checkBlockHeight = async () => {
|
|
2911
|
+
try {
|
|
2912
|
+
const blockHeight = await this.getBlockHeight(commitment);
|
|
2913
|
+
return blockHeight;
|
|
2914
|
+
} catch (_e) {
|
|
2915
|
+
return -1;
|
|
2877
2916
|
}
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2917
|
+
};
|
|
2918
|
+
|
|
2919
|
+
const expiryPromise = new Promise<
|
|
2920
|
+
| {__type: TransactionStatus.BLOCKHEIGHT_EXCEEDED}
|
|
2921
|
+
| {__type: TransactionStatus.TIMED_OUT; timeoutMs: number}
|
|
2922
|
+
>(resolve => {
|
|
2923
|
+
if (typeof strategy === 'string') {
|
|
2924
|
+
let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
|
|
2925
|
+
switch (subscriptionCommitment) {
|
|
2926
|
+
case 'processed':
|
|
2927
|
+
case 'recent':
|
|
2928
|
+
case 'single':
|
|
2929
|
+
case 'confirmed':
|
|
2930
|
+
case 'singleGossip': {
|
|
2931
|
+
timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
|
|
2932
|
+
break;
|
|
2933
|
+
}
|
|
2934
|
+
// exhaust enums to ensure full coverage
|
|
2935
|
+
case 'finalized':
|
|
2936
|
+
case 'max':
|
|
2937
|
+
case 'root':
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
timeoutId = setTimeout(
|
|
2941
|
+
() => resolve({__type: TransactionStatus.TIMED_OUT, timeoutMs}),
|
|
2942
|
+
timeoutMs,
|
|
2943
|
+
);
|
|
2944
|
+
} else {
|
|
2945
|
+
let config = strategy as BlockheightBasedTransactionConfimationStrategy;
|
|
2946
|
+
(async () => {
|
|
2947
|
+
let currentBlockHeight = await checkBlockHeight();
|
|
2948
|
+
if (done) return;
|
|
2949
|
+
while (currentBlockHeight <= config.lastValidBlockHeight) {
|
|
2950
|
+
await sleep(1000);
|
|
2951
|
+
if (done) return;
|
|
2952
|
+
currentBlockHeight = await checkBlockHeight();
|
|
2953
|
+
if (done) return;
|
|
2954
|
+
}
|
|
2955
|
+
resolve({__type: TransactionStatus.BLOCKHEIGHT_EXCEEDED});
|
|
2956
|
+
})();
|
|
2957
|
+
}
|
|
2958
|
+
});
|
|
2883
2959
|
|
|
2960
|
+
let result: RpcResponseAndContext<SignatureResult>;
|
|
2884
2961
|
try {
|
|
2885
|
-
await
|
|
2962
|
+
const outcome = await Promise.race([confirmationPromise, expiryPromise]);
|
|
2963
|
+
switch (outcome.__type) {
|
|
2964
|
+
case TransactionStatus.BLOCKHEIGHT_EXCEEDED:
|
|
2965
|
+
throw new TransactionExpiredBlockheightExceededError(rawSignature);
|
|
2966
|
+
case TransactionStatus.PROCESSED:
|
|
2967
|
+
result = outcome.response;
|
|
2968
|
+
break;
|
|
2969
|
+
case TransactionStatus.TIMED_OUT:
|
|
2970
|
+
throw new TransactionExpiredTimeoutError(
|
|
2971
|
+
rawSignature,
|
|
2972
|
+
outcome.timeoutMs / 1000,
|
|
2973
|
+
);
|
|
2974
|
+
}
|
|
2886
2975
|
} finally {
|
|
2976
|
+
clearTimeout(timeoutId);
|
|
2887
2977
|
if (subscriptionId) {
|
|
2888
2978
|
this.removeSignatureListener(subscriptionId);
|
|
2889
2979
|
}
|
|
2890
2980
|
}
|
|
2891
|
-
|
|
2892
|
-
if (response === null) {
|
|
2893
|
-
const duration = (Date.now() - start) / 1000;
|
|
2894
|
-
throw new Error(
|
|
2895
|
-
`Transaction was not confirmed in ${duration.toFixed(
|
|
2896
|
-
2,
|
|
2897
|
-
)} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`,
|
|
2898
|
-
);
|
|
2899
|
-
}
|
|
2900
|
-
|
|
2901
|
-
return response;
|
|
2981
|
+
return result;
|
|
2902
2982
|
}
|
|
2903
2983
|
|
|
2904
2984
|
/**
|
|
@@ -3245,11 +3325,11 @@ export class Connection {
|
|
|
3245
3325
|
|
|
3246
3326
|
/**
|
|
3247
3327
|
* Fetch the latest blockhash from the cluster
|
|
3248
|
-
* @return {Promise<
|
|
3328
|
+
* @return {Promise<BlockhashWithExpiryBlockHeight>}
|
|
3249
3329
|
*/
|
|
3250
3330
|
async getLatestBlockhash(
|
|
3251
3331
|
commitment?: Commitment,
|
|
3252
|
-
): Promise<
|
|
3332
|
+
): Promise<BlockhashWithExpiryBlockHeight> {
|
|
3253
3333
|
try {
|
|
3254
3334
|
const res = await this.getLatestBlockhashAndContext(commitment);
|
|
3255
3335
|
return res.value;
|
|
@@ -3260,13 +3340,11 @@ export class Connection {
|
|
|
3260
3340
|
|
|
3261
3341
|
/**
|
|
3262
3342
|
* Fetch the latest blockhash from the cluster
|
|
3263
|
-
* @return {Promise<
|
|
3343
|
+
* @return {Promise<BlockhashWithExpiryBlockHeight>}
|
|
3264
3344
|
*/
|
|
3265
3345
|
async getLatestBlockhashAndContext(
|
|
3266
3346
|
commitment?: Commitment,
|
|
3267
|
-
): Promise<
|
|
3268
|
-
RpcResponseAndContext<{blockhash: Blockhash; lastValidBlockHeight: number}>
|
|
3269
|
-
> {
|
|
3347
|
+
): Promise<RpcResponseAndContext<BlockhashWithExpiryBlockHeight>> {
|
|
3270
3348
|
const args = this._buildArgs([], commitment);
|
|
3271
3349
|
const unsafeRes = await this._rpcRequest('getLatestBlockhash', args);
|
|
3272
3350
|
const res = create(unsafeRes, GetLatestBlockhashRpcResult);
|
|
@@ -3912,7 +3990,9 @@ export class Connection {
|
|
|
3912
3990
|
/**
|
|
3913
3991
|
* @internal
|
|
3914
3992
|
*/
|
|
3915
|
-
async
|
|
3993
|
+
async _blockhashWithExpiryBlockHeight(
|
|
3994
|
+
disableCache: boolean,
|
|
3995
|
+
): Promise<BlockhashWithExpiryBlockHeight> {
|
|
3916
3996
|
if (!disableCache) {
|
|
3917
3997
|
// Wait for polling to finish
|
|
3918
3998
|
while (this._pollingBlockhash) {
|
|
@@ -3920,8 +4000,8 @@ export class Connection {
|
|
|
3920
4000
|
}
|
|
3921
4001
|
const timeSinceFetch = Date.now() - this._blockhashInfo.lastFetch;
|
|
3922
4002
|
const expired = timeSinceFetch >= BLOCKHASH_CACHE_TIMEOUT_MS;
|
|
3923
|
-
if (this._blockhashInfo.
|
|
3924
|
-
return this._blockhashInfo.
|
|
4003
|
+
if (this._blockhashInfo.latestBlockhash !== null && !expired) {
|
|
4004
|
+
return this._blockhashInfo.latestBlockhash;
|
|
3925
4005
|
}
|
|
3926
4006
|
}
|
|
3927
4007
|
|
|
@@ -3931,21 +4011,25 @@ export class Connection {
|
|
|
3931
4011
|
/**
|
|
3932
4012
|
* @internal
|
|
3933
4013
|
*/
|
|
3934
|
-
async _pollNewBlockhash(): Promise<
|
|
4014
|
+
async _pollNewBlockhash(): Promise<BlockhashWithExpiryBlockHeight> {
|
|
3935
4015
|
this._pollingBlockhash = true;
|
|
3936
4016
|
try {
|
|
3937
4017
|
const startTime = Date.now();
|
|
4018
|
+
const cachedLatestBlockhash = this._blockhashInfo.latestBlockhash;
|
|
4019
|
+
const cachedBlockhash = cachedLatestBlockhash
|
|
4020
|
+
? cachedLatestBlockhash.blockhash
|
|
4021
|
+
: null;
|
|
3938
4022
|
for (let i = 0; i < 50; i++) {
|
|
3939
|
-
const
|
|
4023
|
+
const latestBlockhash = await this.getLatestBlockhash('finalized');
|
|
3940
4024
|
|
|
3941
|
-
if (
|
|
4025
|
+
if (cachedBlockhash !== latestBlockhash.blockhash) {
|
|
3942
4026
|
this._blockhashInfo = {
|
|
3943
|
-
|
|
4027
|
+
latestBlockhash,
|
|
3944
4028
|
lastFetch: Date.now(),
|
|
3945
4029
|
transactionSignatures: [],
|
|
3946
4030
|
simulatedSignatures: [],
|
|
3947
4031
|
};
|
|
3948
|
-
return
|
|
4032
|
+
return latestBlockhash;
|
|
3949
4033
|
}
|
|
3950
4034
|
|
|
3951
4035
|
// Sleep for approximately half a slot
|
|
@@ -3971,13 +4055,11 @@ export class Connection {
|
|
|
3971
4055
|
let transaction;
|
|
3972
4056
|
if (transactionOrMessage instanceof Transaction) {
|
|
3973
4057
|
let originalTx: Transaction = transactionOrMessage;
|
|
3974
|
-
transaction = new Transaction(
|
|
3975
|
-
|
|
3976
|
-
nonceInfo: originalTx.nonceInfo,
|
|
3977
|
-
feePayer: originalTx.feePayer,
|
|
3978
|
-
signatures: [...originalTx.signatures],
|
|
3979
|
-
});
|
|
4058
|
+
transaction = new Transaction();
|
|
4059
|
+
transaction.feePayer = originalTx.feePayer;
|
|
3980
4060
|
transaction.instructions = transactionOrMessage.instructions;
|
|
4061
|
+
transaction.nonceInfo = originalTx.nonceInfo;
|
|
4062
|
+
transaction.signatures = originalTx.signatures;
|
|
3981
4063
|
} else {
|
|
3982
4064
|
transaction = Transaction.populate(transactionOrMessage);
|
|
3983
4065
|
// HACK: this function relies on mutating the populated transaction
|
|
@@ -3989,7 +4071,11 @@ export class Connection {
|
|
|
3989
4071
|
} else {
|
|
3990
4072
|
let disableCache = this._disableBlockhashCaching;
|
|
3991
4073
|
for (;;) {
|
|
3992
|
-
|
|
4074
|
+
const latestBlockhash = await this._blockhashWithExpiryBlockHeight(
|
|
4075
|
+
disableCache,
|
|
4076
|
+
);
|
|
4077
|
+
transaction.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
|
|
4078
|
+
transaction.recentBlockhash = latestBlockhash.blockhash;
|
|
3993
4079
|
|
|
3994
4080
|
if (!signers) break;
|
|
3995
4081
|
|
|
@@ -4077,7 +4163,11 @@ export class Connection {
|
|
|
4077
4163
|
} else {
|
|
4078
4164
|
let disableCache = this._disableBlockhashCaching;
|
|
4079
4165
|
for (;;) {
|
|
4080
|
-
|
|
4166
|
+
const latestBlockhash = await this._blockhashWithExpiryBlockHeight(
|
|
4167
|
+
disableCache,
|
|
4168
|
+
);
|
|
4169
|
+
transaction.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
|
|
4170
|
+
transaction.recentBlockhash = latestBlockhash.blockhash;
|
|
4081
4171
|
transaction.sign(...signers);
|
|
4082
4172
|
if (!transaction.signature) {
|
|
4083
4173
|
throw new Error('!signature'); // should never happen
|
|
@@ -4174,6 +4264,7 @@ export class Connection {
|
|
|
4174
4264
|
* @internal
|
|
4175
4265
|
*/
|
|
4176
4266
|
_wsOnError(err: Error) {
|
|
4267
|
+
this._rpcWebSocketConnected = false;
|
|
4177
4268
|
console.error('ws error:', err.message);
|
|
4178
4269
|
}
|
|
4179
4270
|
|
|
@@ -4181,6 +4272,7 @@ export class Connection {
|
|
|
4181
4272
|
* @internal
|
|
4182
4273
|
*/
|
|
4183
4274
|
_wsOnClose(code: number) {
|
|
4275
|
+
this._rpcWebSocketConnected = false;
|
|
4184
4276
|
this._rpcWebSocketGeneration++;
|
|
4185
4277
|
if (this._rpcWebSocketHeartbeat) {
|
|
4186
4278
|
clearInterval(this._rpcWebSocketHeartbeat);
|
package/src/transaction.ts
CHANGED
|
@@ -21,6 +21,12 @@ import type {CompiledInstruction} from './message';
|
|
|
21
21
|
*/
|
|
22
22
|
export type TransactionSignature = string;
|
|
23
23
|
|
|
24
|
+
export const enum TransactionStatus {
|
|
25
|
+
BLOCKHEIGHT_EXCEEDED,
|
|
26
|
+
PROCESSED,
|
|
27
|
+
TIMED_OUT,
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
/**
|
|
25
31
|
* Default (empty) signature
|
|
26
32
|
*/
|
|
@@ -124,17 +130,30 @@ export type SignaturePubkeyPair = {
|
|
|
124
130
|
|
|
125
131
|
/**
|
|
126
132
|
* List of Transaction object fields that may be initialized at construction
|
|
127
|
-
*
|
|
128
133
|
*/
|
|
129
|
-
export type
|
|
130
|
-
/** A recent blockhash */
|
|
131
|
-
recentBlockhash?: Blockhash | null;
|
|
134
|
+
export type TransactionCtorFields_DEPRECATED = {
|
|
132
135
|
/** Optional nonce information used for offline nonce'd transactions */
|
|
133
136
|
nonceInfo?: NonceInformation | null;
|
|
134
137
|
/** The transaction fee payer */
|
|
135
138
|
feePayer?: PublicKey | null;
|
|
136
139
|
/** One or more signatures */
|
|
137
140
|
signatures?: Array<SignaturePubkeyPair>;
|
|
141
|
+
/** A recent blockhash */
|
|
142
|
+
recentBlockhash?: Blockhash;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* List of Transaction object fields that may be initialized at construction
|
|
147
|
+
*/
|
|
148
|
+
export type TransactionBlockhashCtor = {
|
|
149
|
+
/** The transaction fee payer */
|
|
150
|
+
feePayer?: PublicKey | null;
|
|
151
|
+
/** One or more signatures */
|
|
152
|
+
signatures?: Array<SignaturePubkeyPair>;
|
|
153
|
+
/** A recent blockhash */
|
|
154
|
+
blockhash: Blockhash;
|
|
155
|
+
/** the last block chain can advance to before tx is declared expired */
|
|
156
|
+
lastValidBlockHeight: number;
|
|
138
157
|
};
|
|
139
158
|
|
|
140
159
|
/**
|
|
@@ -196,6 +215,11 @@ export class Transaction {
|
|
|
196
215
|
*/
|
|
197
216
|
recentBlockhash?: Blockhash;
|
|
198
217
|
|
|
218
|
+
/**
|
|
219
|
+
* the last block chain can advance to before tx is declared expired
|
|
220
|
+
* */
|
|
221
|
+
lastValidBlockHeight?: number;
|
|
222
|
+
|
|
199
223
|
/**
|
|
200
224
|
* Optional Nonce information. If populated, transaction will use a durable
|
|
201
225
|
* Nonce hash instead of a recentBlockhash. Must be populated by the caller
|
|
@@ -212,11 +236,35 @@ export class Transaction {
|
|
|
212
236
|
*/
|
|
213
237
|
_json?: TransactionJSON;
|
|
214
238
|
|
|
239
|
+
// Construct a transaction with a blockhash and lastValidBlockHeight
|
|
240
|
+
constructor(opts?: TransactionBlockhashCtor);
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @deprecated `TransactionCtorFields` has been deprecated and will be removed in a future version.
|
|
244
|
+
* Please supply a `TransactionBlockhashCtor` instead.
|
|
245
|
+
*/
|
|
246
|
+
constructor(opts?: TransactionCtorFields_DEPRECATED);
|
|
247
|
+
|
|
215
248
|
/**
|
|
216
249
|
* Construct an empty Transaction
|
|
217
250
|
*/
|
|
218
|
-
constructor(
|
|
219
|
-
opts
|
|
251
|
+
constructor(
|
|
252
|
+
opts?: TransactionBlockhashCtor | TransactionCtorFields_DEPRECATED,
|
|
253
|
+
) {
|
|
254
|
+
if (!opts) {
|
|
255
|
+
return;
|
|
256
|
+
} else if (
|
|
257
|
+
Object.prototype.hasOwnProperty.call(opts, 'lastValidBlockHeight')
|
|
258
|
+
) {
|
|
259
|
+
const newOpts = opts as TransactionBlockhashCtor;
|
|
260
|
+
Object.assign(this, newOpts);
|
|
261
|
+
this.recentBlockhash = newOpts.blockhash;
|
|
262
|
+
this.lastValidBlockHeight = newOpts.lastValidBlockHeight;
|
|
263
|
+
} else {
|
|
264
|
+
const oldOpts = opts as TransactionCtorFields_DEPRECATED;
|
|
265
|
+
Object.assign(this, oldOpts);
|
|
266
|
+
this.recentBlockhash = oldOpts.recentBlockhash;
|
|
267
|
+
}
|
|
220
268
|
}
|
|
221
269
|
|
|
222
270
|
/**
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type {Buffer} from 'buffer';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
BlockheightBasedTransactionConfimationStrategy,
|
|
5
|
+
Connection,
|
|
6
|
+
} from '../connection';
|
|
4
7
|
import type {TransactionSignature} from '../transaction';
|
|
5
8
|
import type {ConfirmOptions} from '../connection';
|
|
6
9
|
|
|
@@ -11,14 +14,57 @@ import type {ConfirmOptions} from '../connection';
|
|
|
11
14
|
*
|
|
12
15
|
* @param {Connection} connection
|
|
13
16
|
* @param {Buffer} rawTransaction
|
|
17
|
+
* @param {BlockheightBasedTransactionConfimationStrategy} confirmationStrategy
|
|
14
18
|
* @param {ConfirmOptions} [options]
|
|
15
19
|
* @returns {Promise<TransactionSignature>}
|
|
16
20
|
*/
|
|
17
21
|
export async function sendAndConfirmRawTransaction(
|
|
18
22
|
connection: Connection,
|
|
19
23
|
rawTransaction: Buffer,
|
|
24
|
+
confirmationStrategy: BlockheightBasedTransactionConfimationStrategy,
|
|
20
25
|
options?: ConfirmOptions,
|
|
26
|
+
): Promise<TransactionSignature>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated Calling `sendAndConfirmRawTransaction()` without a `confirmationStrategy`
|
|
30
|
+
* is no longer supported and will be removed in a future version.
|
|
31
|
+
*/
|
|
32
|
+
// eslint-disable-next-line no-redeclare
|
|
33
|
+
export async function sendAndConfirmRawTransaction(
|
|
34
|
+
connection: Connection,
|
|
35
|
+
rawTransaction: Buffer,
|
|
36
|
+
options?: ConfirmOptions,
|
|
37
|
+
): Promise<TransactionSignature>;
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line no-redeclare
|
|
40
|
+
export async function sendAndConfirmRawTransaction(
|
|
41
|
+
connection: Connection,
|
|
42
|
+
rawTransaction: Buffer,
|
|
43
|
+
confirmationStrategyOrConfirmOptions:
|
|
44
|
+
| BlockheightBasedTransactionConfimationStrategy
|
|
45
|
+
| ConfirmOptions
|
|
46
|
+
| undefined,
|
|
47
|
+
maybeConfirmOptions?: ConfirmOptions,
|
|
21
48
|
): Promise<TransactionSignature> {
|
|
49
|
+
let confirmationStrategy:
|
|
50
|
+
| BlockheightBasedTransactionConfimationStrategy
|
|
51
|
+
| undefined;
|
|
52
|
+
let options: ConfirmOptions | undefined;
|
|
53
|
+
if (
|
|
54
|
+
confirmationStrategyOrConfirmOptions &&
|
|
55
|
+
Object.prototype.hasOwnProperty.call(
|
|
56
|
+
confirmationStrategyOrConfirmOptions,
|
|
57
|
+
'lastValidBlockHeight',
|
|
58
|
+
)
|
|
59
|
+
) {
|
|
60
|
+
confirmationStrategy =
|
|
61
|
+
confirmationStrategyOrConfirmOptions as BlockheightBasedTransactionConfimationStrategy;
|
|
62
|
+
options = maybeConfirmOptions;
|
|
63
|
+
} else {
|
|
64
|
+
options = confirmationStrategyOrConfirmOptions as
|
|
65
|
+
| ConfirmOptions
|
|
66
|
+
| undefined;
|
|
67
|
+
}
|
|
22
68
|
const sendOptions = options && {
|
|
23
69
|
skipPreflight: options.skipPreflight,
|
|
24
70
|
preflightCommitment: options.preflightCommitment || options.commitment,
|
|
@@ -29,12 +75,11 @@ export async function sendAndConfirmRawTransaction(
|
|
|
29
75
|
sendOptions,
|
|
30
76
|
);
|
|
31
77
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
).value;
|
|
78
|
+
const commitment = options && options.commitment;
|
|
79
|
+
const confirmationPromise = confirmationStrategy
|
|
80
|
+
? connection.confirmTransaction(confirmationStrategy, commitment)
|
|
81
|
+
: connection.confirmTransaction(signature, commitment);
|
|
82
|
+
const status = (await confirmationPromise).value;
|
|
38
83
|
|
|
39
84
|
if (status.err) {
|
|
40
85
|
throw new Error(
|
|
@@ -33,12 +33,25 @@ export async function sendAndConfirmTransaction(
|
|
|
33
33
|
sendOptions,
|
|
34
34
|
);
|
|
35
35
|
|
|
36
|
-
const status =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
const status =
|
|
37
|
+
transaction.recentBlockhash != null &&
|
|
38
|
+
transaction.lastValidBlockHeight != null
|
|
39
|
+
? (
|
|
40
|
+
await connection.confirmTransaction(
|
|
41
|
+
{
|
|
42
|
+
signature: signature,
|
|
43
|
+
blockhash: transaction.recentBlockhash,
|
|
44
|
+
lastValidBlockHeight: transaction.lastValidBlockHeight,
|
|
45
|
+
},
|
|
46
|
+
options && options.commitment,
|
|
47
|
+
)
|
|
48
|
+
).value
|
|
49
|
+
: (
|
|
50
|
+
await connection.confirmTransaction(
|
|
51
|
+
signature,
|
|
52
|
+
options && options.commitment,
|
|
53
|
+
)
|
|
54
|
+
).value;
|
|
42
55
|
|
|
43
56
|
if (status.err) {
|
|
44
57
|
throw new Error(
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export class TransactionExpiredBlockheightExceededError extends Error {
|
|
2
|
+
signature: string;
|
|
3
|
+
|
|
4
|
+
constructor(signature: string) {
|
|
5
|
+
super(`Signature ${signature} has expired: block height exceeded.`);
|
|
6
|
+
this.signature = signature;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
Object.defineProperty(
|
|
11
|
+
TransactionExpiredBlockheightExceededError.prototype,
|
|
12
|
+
'name',
|
|
13
|
+
{
|
|
14
|
+
value: 'TransactionExpiredBlockheightExceededError',
|
|
15
|
+
},
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export class TransactionExpiredTimeoutError extends Error {
|
|
19
|
+
signature: string;
|
|
20
|
+
|
|
21
|
+
constructor(signature: string, timeoutSeconds: number) {
|
|
22
|
+
super(
|
|
23
|
+
`Transaction was not confirmed in ${timeoutSeconds.toFixed(
|
|
24
|
+
2,
|
|
25
|
+
)} seconds. It is ` +
|
|
26
|
+
'unknown if it succeeded or failed. Check signature ' +
|
|
27
|
+
`${signature} using the Solana Explorer or CLI tools.`,
|
|
28
|
+
);
|
|
29
|
+
this.signature = signature;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Object.defineProperty(TransactionExpiredTimeoutError.prototype, 'name', {
|
|
34
|
+
value: 'TransactionExpiredTimeoutError',
|
|
35
|
+
});
|