@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solana/web3.js",
3
- "version": "1.41.9",
3
+ "version": "1.43.0",
4
4
  "description": "Solana Javascript API",
5
5
  "keywords": [
6
6
  "api",
@@ -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
- 'RequestUnits' | 'RequestHeapFrame';
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
- recentBlockhash: Blockhash | null;
2224
+ latestBlockhash: BlockhashWithExpiryBlockHeight | null;
2209
2225
  lastFetch: number;
2210
2226
  simulatedSignatures: Array<string>;
2211
2227
  transactionSignatures: Array<string>;
2212
2228
  } = {
2213
- recentBlockhash: null,
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
- * Confirm the transaction identified by the specified signature.
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
- signature: TransactionSignature,
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(signature);
2876
+ decodedSignature = bs58.decode(rawSignature);
2838
2877
  } catch (err) {
2839
- throw new Error('signature must be base58 encoded: ' + signature);
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 response: RpcResponseAndContext<SignatureResult> | null = null;
2849
- const confirmPromise = new Promise((resolve, reject) => {
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
- signature,
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
- resolve(null);
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
- let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
2869
- switch (subscriptionCommitment) {
2870
- case 'processed':
2871
- case 'recent':
2872
- case 'single':
2873
- case 'confirmed':
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
- // exhaust enums to ensure full coverage
2879
- case 'finalized':
2880
- case 'max':
2881
- case 'root':
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 promiseTimeout(confirmPromise, timeoutMs);
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<{blockhash: Blockhash, lastValidBlockHeight: number}>}
3330
+ * @return {Promise<BlockhashWithExpiryBlockHeight>}
3249
3331
  */
3250
3332
  async getLatestBlockhash(
3251
3333
  commitment?: Commitment,
3252
- ): Promise<{blockhash: Blockhash; lastValidBlockHeight: number}> {
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<{blockhash: Blockhash, lastValidBlockHeight: number}>}
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 _recentBlockhash(disableCache: boolean): Promise<Blockhash> {
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.recentBlockhash !== null && !expired) {
3924
- return this._blockhashInfo.recentBlockhash;
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<Blockhash> {
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 {blockhash} = await this.getRecentBlockhash('finalized');
4025
+ const latestBlockhash = await this.getLatestBlockhash('finalized');
3940
4026
 
3941
- if (this._blockhashInfo.recentBlockhash != blockhash) {
4027
+ if (cachedBlockhash !== latestBlockhash.blockhash) {
3942
4028
  this._blockhashInfo = {
3943
- recentBlockhash: blockhash,
4029
+ latestBlockhash,
3944
4030
  lastFetch: Date.now(),
3945
4031
  transactionSignatures: [],
3946
4032
  simulatedSignatures: [],
3947
4033
  };
3948
- return blockhash;
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
- recentBlockhash: originalTx.recentBlockhash,
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
- transaction.recentBlockhash = await this._recentBlockhash(disableCache);
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
- transaction.recentBlockhash = await this._recentBlockhash(disableCache);
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
@@ -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 TransactionCtorFields = {
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(opts?: TransactionCtorFields) {
219
- opts && Object.assign(this, 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
  /**