@solana/web3.js 1.41.9 → 1.43.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 +238 -66
- package/lib/index.browser.cjs.js.map +1 -1
- package/lib/index.browser.esm.js +239 -67
- package/lib/index.browser.esm.js.map +1 -1
- package/lib/index.cjs.js +238 -66
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +101 -24
- package/lib/index.esm.js +239 -67
- package/lib/index.esm.js.map +1 -1
- package/lib/index.iife.js +238 -66
- 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 +1 -1
- package/src/compute-budget.ts +92 -3
- package/src/connection.ts +157 -65
- package/src/transaction.ts +54 -7
- 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
package/src/compute-budget.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as BufferLayout from '@solana/buffer-layout';
|
|
2
|
+
import {u64} from '@solana/buffer-layout-utils';
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
encodeData,
|
|
@@ -76,6 +77,34 @@ export class ComputeBudgetInstruction {
|
|
|
76
77
|
return {bytes};
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Decode set compute unit limit compute budget instruction and retrieve the instruction params.
|
|
82
|
+
*/
|
|
83
|
+
static decodeSetComputeUnitLimit(
|
|
84
|
+
instruction: TransactionInstruction,
|
|
85
|
+
): SetComputeUnitLimitParams {
|
|
86
|
+
this.checkProgramId(instruction.programId);
|
|
87
|
+
const {units} = decodeData(
|
|
88
|
+
COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.SetComputeUnitLimit,
|
|
89
|
+
instruction.data,
|
|
90
|
+
);
|
|
91
|
+
return {units};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Decode set compute unit price compute budget instruction and retrieve the instruction params.
|
|
96
|
+
*/
|
|
97
|
+
static decodeSetComputeUnitPrice(
|
|
98
|
+
instruction: TransactionInstruction,
|
|
99
|
+
): SetComputeUnitPriceParams {
|
|
100
|
+
this.checkProgramId(instruction.programId);
|
|
101
|
+
const {microLamports} = decodeData(
|
|
102
|
+
COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.SetComputeUnitPrice,
|
|
103
|
+
instruction.data,
|
|
104
|
+
);
|
|
105
|
+
return {microLamports};
|
|
106
|
+
}
|
|
107
|
+
|
|
79
108
|
/**
|
|
80
109
|
* @internal
|
|
81
110
|
*/
|
|
@@ -96,11 +125,18 @@ export type ComputeBudgetInstructionType =
|
|
|
96
125
|
// It would be preferable for this type to be `keyof ComputeBudgetInstructionInputData`
|
|
97
126
|
// but Typedoc does not transpile `keyof` expressions.
|
|
98
127
|
// See https://github.com/TypeStrong/typedoc/issues/1894
|
|
99
|
-
|
|
128
|
+
| 'RequestUnits'
|
|
129
|
+
| 'RequestHeapFrame'
|
|
130
|
+
| 'SetComputeUnitLimit'
|
|
131
|
+
| 'SetComputeUnitPrice';
|
|
100
132
|
|
|
101
133
|
type ComputeBudgetInstructionInputData = {
|
|
102
134
|
RequestUnits: IInstructionInputData & Readonly<RequestUnitsParams>;
|
|
103
135
|
RequestHeapFrame: IInstructionInputData & Readonly<RequestHeapFrameParams>;
|
|
136
|
+
SetComputeUnitLimit: IInstructionInputData &
|
|
137
|
+
Readonly<SetComputeUnitLimitParams>;
|
|
138
|
+
SetComputeUnitPrice: IInstructionInputData &
|
|
139
|
+
Readonly<SetComputeUnitPriceParams>;
|
|
104
140
|
};
|
|
105
141
|
|
|
106
142
|
/**
|
|
@@ -109,8 +145,7 @@ type ComputeBudgetInstructionInputData = {
|
|
|
109
145
|
export interface RequestUnitsParams {
|
|
110
146
|
/** Units to request for transaction-wide compute */
|
|
111
147
|
units: number;
|
|
112
|
-
|
|
113
|
-
/** Additional fee to pay */
|
|
148
|
+
/** Prioritization fee lamports */
|
|
114
149
|
additionalFee: number;
|
|
115
150
|
}
|
|
116
151
|
|
|
@@ -122,6 +157,22 @@ export type RequestHeapFrameParams = {
|
|
|
122
157
|
bytes: number;
|
|
123
158
|
};
|
|
124
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Set compute unit limit instruction params
|
|
162
|
+
*/
|
|
163
|
+
export interface SetComputeUnitLimitParams {
|
|
164
|
+
/** Transaction-wide compute unit limit */
|
|
165
|
+
units: number;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Set compute unit price instruction params
|
|
170
|
+
*/
|
|
171
|
+
export interface SetComputeUnitPriceParams {
|
|
172
|
+
/** Transaction compute unit price used for prioritization fees */
|
|
173
|
+
microLamports: number | bigint;
|
|
174
|
+
}
|
|
175
|
+
|
|
125
176
|
/**
|
|
126
177
|
* An enumeration of valid ComputeBudget InstructionType's
|
|
127
178
|
* @internal
|
|
@@ -147,6 +198,18 @@ export const COMPUTE_BUDGET_INSTRUCTION_LAYOUTS = Object.freeze<{
|
|
|
147
198
|
ComputeBudgetInstructionInputData['RequestHeapFrame']
|
|
148
199
|
>([BufferLayout.u8('instruction'), BufferLayout.u32('bytes')]),
|
|
149
200
|
},
|
|
201
|
+
SetComputeUnitLimit: {
|
|
202
|
+
index: 2,
|
|
203
|
+
layout: BufferLayout.struct<
|
|
204
|
+
ComputeBudgetInstructionInputData['SetComputeUnitLimit']
|
|
205
|
+
>([BufferLayout.u8('instruction'), BufferLayout.u32('units')]),
|
|
206
|
+
},
|
|
207
|
+
SetComputeUnitPrice: {
|
|
208
|
+
index: 3,
|
|
209
|
+
layout: BufferLayout.struct<
|
|
210
|
+
ComputeBudgetInstructionInputData['SetComputeUnitPrice']
|
|
211
|
+
>([BufferLayout.u8('instruction'), u64('microLamports')]),
|
|
212
|
+
},
|
|
150
213
|
});
|
|
151
214
|
|
|
152
215
|
/**
|
|
@@ -186,4 +249,30 @@ export class ComputeBudgetProgram {
|
|
|
186
249
|
data,
|
|
187
250
|
});
|
|
188
251
|
}
|
|
252
|
+
|
|
253
|
+
static setComputeUnitLimit(
|
|
254
|
+
params: SetComputeUnitLimitParams,
|
|
255
|
+
): TransactionInstruction {
|
|
256
|
+
const type = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.SetComputeUnitLimit;
|
|
257
|
+
const data = encodeData(type, params);
|
|
258
|
+
return new TransactionInstruction({
|
|
259
|
+
keys: [],
|
|
260
|
+
programId: this.programId,
|
|
261
|
+
data,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
static setComputeUnitPrice(
|
|
266
|
+
params: SetComputeUnitPriceParams,
|
|
267
|
+
): TransactionInstruction {
|
|
268
|
+
const type = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.SetComputeUnitPrice;
|
|
269
|
+
const data = encodeData(type, {
|
|
270
|
+
microLamports: BigInt(params.microLamports),
|
|
271
|
+
});
|
|
272
|
+
return new TransactionInstruction({
|
|
273
|
+
keys: [],
|
|
274
|
+
programId: this.programId,
|
|
275
|
+
data,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
189
278
|
}
|
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 BlockheightBasedTransactionConfirmationStrategy = {
|
|
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,65 @@ export class Connection {
|
|
|
2825
2841
|
return res.result;
|
|
2826
2842
|
}
|
|
2827
2843
|
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2844
|
+
confirmTransaction(
|
|
2845
|
+
strategy: BlockheightBasedTransactionConfirmationStrategy,
|
|
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
|
+
| BlockheightBasedTransactionConfirmationStrategy
|
|
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 =
|
|
2869
|
+
strategy as BlockheightBasedTransactionConfirmationStrategy;
|
|
2870
|
+
rawSignature = config.signature;
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2835
2873
|
let decodedSignature;
|
|
2874
|
+
|
|
2836
2875
|
try {
|
|
2837
|
-
decodedSignature = bs58.decode(
|
|
2876
|
+
decodedSignature = bs58.decode(rawSignature);
|
|
2838
2877
|
} catch (err) {
|
|
2839
|
-
throw new Error('signature must be base58 encoded: ' +
|
|
2878
|
+
throw new Error('signature must be base58 encoded: ' + rawSignature);
|
|
2840
2879
|
}
|
|
2841
2880
|
|
|
2842
2881
|
assert(decodedSignature.length === 64, 'signature has invalid length');
|
|
2843
2882
|
|
|
2844
|
-
const start = Date.now();
|
|
2845
2883
|
const subscriptionCommitment = commitment || this.commitment;
|
|
2846
|
-
|
|
2884
|
+
let timeoutId;
|
|
2847
2885
|
let subscriptionId;
|
|
2848
|
-
let
|
|
2849
|
-
|
|
2886
|
+
let done = false;
|
|
2887
|
+
|
|
2888
|
+
const confirmationPromise = new Promise<{
|
|
2889
|
+
__type: TransactionStatus.PROCESSED;
|
|
2890
|
+
response: RpcResponseAndContext<SignatureResult>;
|
|
2891
|
+
}>((resolve, reject) => {
|
|
2850
2892
|
try {
|
|
2851
2893
|
subscriptionId = this.onSignature(
|
|
2852
|
-
|
|
2894
|
+
rawSignature,
|
|
2853
2895
|
(result: SignatureResult, context: Context) => {
|
|
2854
2896
|
subscriptionId = undefined;
|
|
2855
|
-
response = {
|
|
2897
|
+
const response = {
|
|
2856
2898
|
context,
|
|
2857
2899
|
value: result,
|
|
2858
2900
|
};
|
|
2859
|
-
|
|
2901
|
+
done = true;
|
|
2902
|
+
resolve({__type: TransactionStatus.PROCESSED, response});
|
|
2860
2903
|
},
|
|
2861
2904
|
subscriptionCommitment,
|
|
2862
2905
|
);
|
|
@@ -2865,40 +2908,79 @@ export class Connection {
|
|
|
2865
2908
|
}
|
|
2866
2909
|
});
|
|
2867
2910
|
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
case 'singleGossip': {
|
|
2875
|
-
timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
|
|
2876
|
-
break;
|
|
2911
|
+
const checkBlockHeight = async () => {
|
|
2912
|
+
try {
|
|
2913
|
+
const blockHeight = await this.getBlockHeight(commitment);
|
|
2914
|
+
return blockHeight;
|
|
2915
|
+
} catch (_e) {
|
|
2916
|
+
return -1;
|
|
2877
2917
|
}
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2918
|
+
};
|
|
2919
|
+
|
|
2920
|
+
const expiryPromise = new Promise<
|
|
2921
|
+
| {__type: TransactionStatus.BLOCKHEIGHT_EXCEEDED}
|
|
2922
|
+
| {__type: TransactionStatus.TIMED_OUT; timeoutMs: number}
|
|
2923
|
+
>(resolve => {
|
|
2924
|
+
if (typeof strategy === 'string') {
|
|
2925
|
+
let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
|
|
2926
|
+
switch (subscriptionCommitment) {
|
|
2927
|
+
case 'processed':
|
|
2928
|
+
case 'recent':
|
|
2929
|
+
case 'single':
|
|
2930
|
+
case 'confirmed':
|
|
2931
|
+
case 'singleGossip': {
|
|
2932
|
+
timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
|
|
2933
|
+
break;
|
|
2934
|
+
}
|
|
2935
|
+
// exhaust enums to ensure full coverage
|
|
2936
|
+
case 'finalized':
|
|
2937
|
+
case 'max':
|
|
2938
|
+
case 'root':
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
timeoutId = setTimeout(
|
|
2942
|
+
() => resolve({__type: TransactionStatus.TIMED_OUT, timeoutMs}),
|
|
2943
|
+
timeoutMs,
|
|
2944
|
+
);
|
|
2945
|
+
} else {
|
|
2946
|
+
let config =
|
|
2947
|
+
strategy as BlockheightBasedTransactionConfirmationStrategy;
|
|
2948
|
+
(async () => {
|
|
2949
|
+
let currentBlockHeight = await checkBlockHeight();
|
|
2950
|
+
if (done) return;
|
|
2951
|
+
while (currentBlockHeight <= config.lastValidBlockHeight) {
|
|
2952
|
+
await sleep(1000);
|
|
2953
|
+
if (done) return;
|
|
2954
|
+
currentBlockHeight = await checkBlockHeight();
|
|
2955
|
+
if (done) return;
|
|
2956
|
+
}
|
|
2957
|
+
resolve({__type: TransactionStatus.BLOCKHEIGHT_EXCEEDED});
|
|
2958
|
+
})();
|
|
2959
|
+
}
|
|
2960
|
+
});
|
|
2883
2961
|
|
|
2962
|
+
let result: RpcResponseAndContext<SignatureResult>;
|
|
2884
2963
|
try {
|
|
2885
|
-
await
|
|
2964
|
+
const outcome = await Promise.race([confirmationPromise, expiryPromise]);
|
|
2965
|
+
switch (outcome.__type) {
|
|
2966
|
+
case TransactionStatus.BLOCKHEIGHT_EXCEEDED:
|
|
2967
|
+
throw new TransactionExpiredBlockheightExceededError(rawSignature);
|
|
2968
|
+
case TransactionStatus.PROCESSED:
|
|
2969
|
+
result = outcome.response;
|
|
2970
|
+
break;
|
|
2971
|
+
case TransactionStatus.TIMED_OUT:
|
|
2972
|
+
throw new TransactionExpiredTimeoutError(
|
|
2973
|
+
rawSignature,
|
|
2974
|
+
outcome.timeoutMs / 1000,
|
|
2975
|
+
);
|
|
2976
|
+
}
|
|
2886
2977
|
} finally {
|
|
2978
|
+
clearTimeout(timeoutId);
|
|
2887
2979
|
if (subscriptionId) {
|
|
2888
2980
|
this.removeSignatureListener(subscriptionId);
|
|
2889
2981
|
}
|
|
2890
2982
|
}
|
|
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;
|
|
2983
|
+
return result;
|
|
2902
2984
|
}
|
|
2903
2985
|
|
|
2904
2986
|
/**
|
|
@@ -3245,11 +3327,11 @@ export class Connection {
|
|
|
3245
3327
|
|
|
3246
3328
|
/**
|
|
3247
3329
|
* Fetch the latest blockhash from the cluster
|
|
3248
|
-
* @return {Promise<
|
|
3330
|
+
* @return {Promise<BlockhashWithExpiryBlockHeight>}
|
|
3249
3331
|
*/
|
|
3250
3332
|
async getLatestBlockhash(
|
|
3251
3333
|
commitment?: Commitment,
|
|
3252
|
-
): Promise<
|
|
3334
|
+
): Promise<BlockhashWithExpiryBlockHeight> {
|
|
3253
3335
|
try {
|
|
3254
3336
|
const res = await this.getLatestBlockhashAndContext(commitment);
|
|
3255
3337
|
return res.value;
|
|
@@ -3260,13 +3342,11 @@ export class Connection {
|
|
|
3260
3342
|
|
|
3261
3343
|
/**
|
|
3262
3344
|
* Fetch the latest blockhash from the cluster
|
|
3263
|
-
* @return {Promise<
|
|
3345
|
+
* @return {Promise<BlockhashWithExpiryBlockHeight>}
|
|
3264
3346
|
*/
|
|
3265
3347
|
async getLatestBlockhashAndContext(
|
|
3266
3348
|
commitment?: Commitment,
|
|
3267
|
-
): Promise<
|
|
3268
|
-
RpcResponseAndContext<{blockhash: Blockhash; lastValidBlockHeight: number}>
|
|
3269
|
-
> {
|
|
3349
|
+
): Promise<RpcResponseAndContext<BlockhashWithExpiryBlockHeight>> {
|
|
3270
3350
|
const args = this._buildArgs([], commitment);
|
|
3271
3351
|
const unsafeRes = await this._rpcRequest('getLatestBlockhash', args);
|
|
3272
3352
|
const res = create(unsafeRes, GetLatestBlockhashRpcResult);
|
|
@@ -3912,7 +3992,9 @@ export class Connection {
|
|
|
3912
3992
|
/**
|
|
3913
3993
|
* @internal
|
|
3914
3994
|
*/
|
|
3915
|
-
async
|
|
3995
|
+
async _blockhashWithExpiryBlockHeight(
|
|
3996
|
+
disableCache: boolean,
|
|
3997
|
+
): Promise<BlockhashWithExpiryBlockHeight> {
|
|
3916
3998
|
if (!disableCache) {
|
|
3917
3999
|
// Wait for polling to finish
|
|
3918
4000
|
while (this._pollingBlockhash) {
|
|
@@ -3920,8 +4002,8 @@ export class Connection {
|
|
|
3920
4002
|
}
|
|
3921
4003
|
const timeSinceFetch = Date.now() - this._blockhashInfo.lastFetch;
|
|
3922
4004
|
const expired = timeSinceFetch >= BLOCKHASH_CACHE_TIMEOUT_MS;
|
|
3923
|
-
if (this._blockhashInfo.
|
|
3924
|
-
return this._blockhashInfo.
|
|
4005
|
+
if (this._blockhashInfo.latestBlockhash !== null && !expired) {
|
|
4006
|
+
return this._blockhashInfo.latestBlockhash;
|
|
3925
4007
|
}
|
|
3926
4008
|
}
|
|
3927
4009
|
|
|
@@ -3931,21 +4013,25 @@ export class Connection {
|
|
|
3931
4013
|
/**
|
|
3932
4014
|
* @internal
|
|
3933
4015
|
*/
|
|
3934
|
-
async _pollNewBlockhash(): Promise<
|
|
4016
|
+
async _pollNewBlockhash(): Promise<BlockhashWithExpiryBlockHeight> {
|
|
3935
4017
|
this._pollingBlockhash = true;
|
|
3936
4018
|
try {
|
|
3937
4019
|
const startTime = Date.now();
|
|
4020
|
+
const cachedLatestBlockhash = this._blockhashInfo.latestBlockhash;
|
|
4021
|
+
const cachedBlockhash = cachedLatestBlockhash
|
|
4022
|
+
? cachedLatestBlockhash.blockhash
|
|
4023
|
+
: null;
|
|
3938
4024
|
for (let i = 0; i < 50; i++) {
|
|
3939
|
-
const
|
|
4025
|
+
const latestBlockhash = await this.getLatestBlockhash('finalized');
|
|
3940
4026
|
|
|
3941
|
-
if (
|
|
4027
|
+
if (cachedBlockhash !== latestBlockhash.blockhash) {
|
|
3942
4028
|
this._blockhashInfo = {
|
|
3943
|
-
|
|
4029
|
+
latestBlockhash,
|
|
3944
4030
|
lastFetch: Date.now(),
|
|
3945
4031
|
transactionSignatures: [],
|
|
3946
4032
|
simulatedSignatures: [],
|
|
3947
4033
|
};
|
|
3948
|
-
return
|
|
4034
|
+
return latestBlockhash;
|
|
3949
4035
|
}
|
|
3950
4036
|
|
|
3951
4037
|
// Sleep for approximately half a slot
|
|
@@ -3971,13 +4057,11 @@ export class Connection {
|
|
|
3971
4057
|
let transaction;
|
|
3972
4058
|
if (transactionOrMessage instanceof Transaction) {
|
|
3973
4059
|
let originalTx: Transaction = transactionOrMessage;
|
|
3974
|
-
transaction = new Transaction(
|
|
3975
|
-
|
|
3976
|
-
nonceInfo: originalTx.nonceInfo,
|
|
3977
|
-
feePayer: originalTx.feePayer,
|
|
3978
|
-
signatures: [...originalTx.signatures],
|
|
3979
|
-
});
|
|
4060
|
+
transaction = new Transaction();
|
|
4061
|
+
transaction.feePayer = originalTx.feePayer;
|
|
3980
4062
|
transaction.instructions = transactionOrMessage.instructions;
|
|
4063
|
+
transaction.nonceInfo = originalTx.nonceInfo;
|
|
4064
|
+
transaction.signatures = originalTx.signatures;
|
|
3981
4065
|
} else {
|
|
3982
4066
|
transaction = Transaction.populate(transactionOrMessage);
|
|
3983
4067
|
// HACK: this function relies on mutating the populated transaction
|
|
@@ -3989,7 +4073,11 @@ export class Connection {
|
|
|
3989
4073
|
} else {
|
|
3990
4074
|
let disableCache = this._disableBlockhashCaching;
|
|
3991
4075
|
for (;;) {
|
|
3992
|
-
|
|
4076
|
+
const latestBlockhash = await this._blockhashWithExpiryBlockHeight(
|
|
4077
|
+
disableCache,
|
|
4078
|
+
);
|
|
4079
|
+
transaction.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
|
|
4080
|
+
transaction.recentBlockhash = latestBlockhash.blockhash;
|
|
3993
4081
|
|
|
3994
4082
|
if (!signers) break;
|
|
3995
4083
|
|
|
@@ -4077,7 +4165,11 @@ export class Connection {
|
|
|
4077
4165
|
} else {
|
|
4078
4166
|
let disableCache = this._disableBlockhashCaching;
|
|
4079
4167
|
for (;;) {
|
|
4080
|
-
|
|
4168
|
+
const latestBlockhash = await this._blockhashWithExpiryBlockHeight(
|
|
4169
|
+
disableCache,
|
|
4170
|
+
);
|
|
4171
|
+
transaction.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
|
|
4172
|
+
transaction.recentBlockhash = latestBlockhash.blockhash;
|
|
4081
4173
|
transaction.sign(...signers);
|
|
4082
4174
|
if (!transaction.signature) {
|
|
4083
4175
|
throw new Error('!signature'); // should never happen
|
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
|
/**
|
|
@@ -551,7 +599,6 @@ export class Transaction {
|
|
|
551
599
|
|
|
552
600
|
const message = this._compile();
|
|
553
601
|
this._partialSign(message, ...uniqueSigners);
|
|
554
|
-
this._verifySignatures(message.serialize(), true);
|
|
555
602
|
}
|
|
556
603
|
|
|
557
604
|
/**
|