@solana/web3.js 1.41.9 → 1.41.11
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 +181 -60
- package/lib/index.browser.cjs.js.map +1 -1
- package/lib/index.browser.esm.js +182 -61
- package/lib/index.browser.esm.js.map +1 -1
- package/lib/index.cjs.js +181 -60
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +44 -10
- package/lib/index.esm.js +182 -61
- package/lib/index.esm.js.map +1 -1
- package/lib/index.iife.js +181 -60
- 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 +22 -22
- package/src/connection.ts +118 -41
- package/src/message.ts +9 -12
- package/src/transaction.ts +56 -8
- package/src/util/guarded-array-utils.ts +34 -0
- package/src/util/send-and-confirm-transaction.ts +19 -6
- package/src/util/tx-expiry-custom-errors.ts +35 -0
- package/src/validator-info.ts +5 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solana/web3.js",
|
|
3
|
-
"version": "1.41.
|
|
3
|
+
"version": "1.41.11",
|
|
4
4
|
"description": "Solana Javascript API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -35,26 +35,6 @@
|
|
|
35
35
|
"/lib",
|
|
36
36
|
"/src"
|
|
37
37
|
],
|
|
38
|
-
"scripts": {
|
|
39
|
-
"build": "npm run clean; cross-env NODE_ENV=production rollup -c; npm run type:gen",
|
|
40
|
-
"build:fixtures": "set -ex; ./test/fixtures/noop-program/build.sh",
|
|
41
|
-
"clean": "rimraf ./coverage ./lib",
|
|
42
|
-
"codecov": "set -ex; npm run test:cover; cat ./coverage/lcov.info | codecov",
|
|
43
|
-
"dev": "cross-env NODE_ENV=development rollup -c",
|
|
44
|
-
"doc": "set -ex; typedoc --treatWarningsAsErrors",
|
|
45
|
-
"type:gen": "./scripts/typegen.sh",
|
|
46
|
-
"lint": "set -ex; npm run pretty; eslint . --ext .js,.ts",
|
|
47
|
-
"lint:fix": "npm run pretty:fix && eslint . --fix --ext .js,.ts",
|
|
48
|
-
"type:check": "tsc -p tsconfig.json --noEmit",
|
|
49
|
-
"ok": "run-s lint test doc type:check",
|
|
50
|
-
"pretty": "prettier --check '{,{src,test}/**/}*.{j,t}s'",
|
|
51
|
-
"pretty:fix": "prettier --write '{,{src,test}/**/}*.{j,t}s'",
|
|
52
|
-
"re": "semantic-release --repository-url git@github.com:solana-labs/solana-web3.js.git",
|
|
53
|
-
"test": "cross-env TS_NODE_COMPILER_OPTIONS='{ \"module\": \"commonjs\" }' ts-mocha --require esm './test/**/*.test.ts'",
|
|
54
|
-
"test:cover": "nyc --reporter=lcov npm run test",
|
|
55
|
-
"test:live": "TEST_LIVE=1 npm run test",
|
|
56
|
-
"test:live-with-test-validator": "start-server-and-test 'solana-test-validator --reset --quiet' http://localhost:8899/health test:live"
|
|
57
|
-
},
|
|
58
38
|
"dependencies": {
|
|
59
39
|
"@babel/runtime": "^7.12.5",
|
|
60
40
|
"@ethersproject/sha2": "^5.5.0",
|
|
@@ -137,5 +117,25 @@
|
|
|
137
117
|
},
|
|
138
118
|
"engines": {
|
|
139
119
|
"node": ">=12.20.0"
|
|
120
|
+
},
|
|
121
|
+
"scripts": {
|
|
122
|
+
"build": "npm run clean; cross-env NODE_ENV=production rollup -c; npm run type:gen",
|
|
123
|
+
"build:fixtures": "set -ex; ./test/fixtures/noop-program/build.sh",
|
|
124
|
+
"clean": "rimraf ./coverage ./lib",
|
|
125
|
+
"codecov": "set -ex; npm run test:cover; cat ./coverage/lcov.info | codecov",
|
|
126
|
+
"dev": "cross-env NODE_ENV=development rollup -c",
|
|
127
|
+
"doc": "set -ex; typedoc --treatWarningsAsErrors",
|
|
128
|
+
"type:gen": "./scripts/typegen.sh",
|
|
129
|
+
"lint": "set -ex; npm run pretty; eslint . --ext .js,.ts",
|
|
130
|
+
"lint:fix": "npm run pretty:fix && eslint . --fix --ext .js,.ts",
|
|
131
|
+
"type:check": "tsc -p tsconfig.json --noEmit",
|
|
132
|
+
"ok": "run-s lint test doc type:check",
|
|
133
|
+
"pretty": "prettier --check '{,{src,test}/**/}*.{j,t}s'",
|
|
134
|
+
"pretty:fix": "prettier --write '{,{src,test}/**/}*.{j,t}s'",
|
|
135
|
+
"re": "semantic-release --repository-url git@github.com:solana-labs/solana-web3.js.git",
|
|
136
|
+
"test": "cross-env TS_NODE_COMPILER_OPTIONS='{ \"module\": \"commonjs\" }' ts-mocha --require esm './test/**/*.test.ts'",
|
|
137
|
+
"test:cover": "nyc --reporter=lcov npm run test",
|
|
138
|
+
"test:live": "TEST_LIVE=1 npm run test",
|
|
139
|
+
"test:live-with-test-validator": "start-server-and-test 'solana-test-validator --reset --quiet' http://localhost:8899/health test:live"
|
|
140
140
|
}
|
|
141
|
-
}
|
|
141
|
+
}
|
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,16 @@ export type RpcResponseAndContext<T> = {
|
|
|
281
284
|
value: T;
|
|
282
285
|
};
|
|
283
286
|
|
|
287
|
+
/**
|
|
288
|
+
* A strategy for confirming transactions that uses the last valid
|
|
289
|
+
* block height for a given blockhash to check for transaction expiration.
|
|
290
|
+
*/
|
|
291
|
+
export type BlockheightBasedTransactionConfimationStrategy = {
|
|
292
|
+
signature: TransactionSignature;
|
|
293
|
+
blockhash: Blockhash;
|
|
294
|
+
lastValidBlockHeight: number;
|
|
295
|
+
};
|
|
296
|
+
|
|
284
297
|
/**
|
|
285
298
|
* @internal
|
|
286
299
|
*/
|
|
@@ -2825,38 +2838,64 @@ export class Connection {
|
|
|
2825
2838
|
return res.result;
|
|
2826
2839
|
}
|
|
2827
2840
|
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2841
|
+
confirmTransaction(
|
|
2842
|
+
strategy: BlockheightBasedTransactionConfimationStrategy,
|
|
2843
|
+
commitment?: Commitment,
|
|
2844
|
+
): Promise<RpcResponseAndContext<SignatureResult>>;
|
|
2845
|
+
|
|
2846
|
+
/** @deprecated Instead, call `confirmTransaction` using a `TransactionConfirmationConfig` */
|
|
2847
|
+
// eslint-disable-next-line no-dupe-class-members
|
|
2848
|
+
confirmTransaction(
|
|
2849
|
+
strategy: TransactionSignature,
|
|
2850
|
+
commitment?: Commitment,
|
|
2851
|
+
): Promise<RpcResponseAndContext<SignatureResult>>;
|
|
2852
|
+
|
|
2853
|
+
// eslint-disable-next-line no-dupe-class-members
|
|
2831
2854
|
async confirmTransaction(
|
|
2832
|
-
|
|
2855
|
+
strategy:
|
|
2856
|
+
| BlockheightBasedTransactionConfimationStrategy
|
|
2857
|
+
| TransactionSignature,
|
|
2833
2858
|
commitment?: Commitment,
|
|
2834
2859
|
): Promise<RpcResponseAndContext<SignatureResult>> {
|
|
2860
|
+
let rawSignature: string;
|
|
2861
|
+
|
|
2862
|
+
if (typeof strategy == 'string') {
|
|
2863
|
+
rawSignature = strategy;
|
|
2864
|
+
} else {
|
|
2865
|
+
const config = strategy as BlockheightBasedTransactionConfimationStrategy;
|
|
2866
|
+
rawSignature = config.signature;
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2835
2869
|
let decodedSignature;
|
|
2870
|
+
|
|
2836
2871
|
try {
|
|
2837
|
-
decodedSignature = bs58.decode(
|
|
2872
|
+
decodedSignature = bs58.decode(rawSignature);
|
|
2838
2873
|
} catch (err) {
|
|
2839
|
-
throw new Error('signature must be base58 encoded: ' +
|
|
2874
|
+
throw new Error('signature must be base58 encoded: ' + rawSignature);
|
|
2840
2875
|
}
|
|
2841
2876
|
|
|
2842
2877
|
assert(decodedSignature.length === 64, 'signature has invalid length');
|
|
2843
2878
|
|
|
2844
|
-
const start = Date.now();
|
|
2845
2879
|
const subscriptionCommitment = commitment || this.commitment;
|
|
2846
|
-
|
|
2880
|
+
let timeoutId;
|
|
2847
2881
|
let subscriptionId;
|
|
2848
|
-
let
|
|
2849
|
-
|
|
2882
|
+
let done = false;
|
|
2883
|
+
|
|
2884
|
+
const confirmationPromise = new Promise<{
|
|
2885
|
+
__type: TransactionStatus.PROCESSED;
|
|
2886
|
+
response: RpcResponseAndContext<SignatureResult>;
|
|
2887
|
+
}>((resolve, reject) => {
|
|
2850
2888
|
try {
|
|
2851
2889
|
subscriptionId = this.onSignature(
|
|
2852
|
-
|
|
2890
|
+
rawSignature,
|
|
2853
2891
|
(result: SignatureResult, context: Context) => {
|
|
2854
2892
|
subscriptionId = undefined;
|
|
2855
|
-
response = {
|
|
2893
|
+
const response = {
|
|
2856
2894
|
context,
|
|
2857
2895
|
value: result,
|
|
2858
2896
|
};
|
|
2859
|
-
|
|
2897
|
+
done = true;
|
|
2898
|
+
resolve({__type: TransactionStatus.PROCESSED, response});
|
|
2860
2899
|
},
|
|
2861
2900
|
subscriptionCommitment,
|
|
2862
2901
|
);
|
|
@@ -2865,40 +2904,78 @@ export class Connection {
|
|
|
2865
2904
|
}
|
|
2866
2905
|
});
|
|
2867
2906
|
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
case 'singleGossip': {
|
|
2875
|
-
timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
|
|
2876
|
-
break;
|
|
2907
|
+
const checkBlockHeight = async () => {
|
|
2908
|
+
try {
|
|
2909
|
+
const blockHeight = await this.getBlockHeight(commitment);
|
|
2910
|
+
return blockHeight;
|
|
2911
|
+
} catch (_e) {
|
|
2912
|
+
return -1;
|
|
2877
2913
|
}
|
|
2878
|
-
|
|
2879
|
-
case 'finalized':
|
|
2880
|
-
case 'max':
|
|
2881
|
-
case 'root':
|
|
2882
|
-
}
|
|
2914
|
+
};
|
|
2883
2915
|
|
|
2916
|
+
const expiryPromise = new Promise<
|
|
2917
|
+
| {__type: TransactionStatus.BLOCKHEIGHT_EXCEEDED}
|
|
2918
|
+
| {__type: TransactionStatus.TIMED_OUT; timeoutMs: number}
|
|
2919
|
+
>(resolve => {
|
|
2920
|
+
if (typeof strategy === 'string') {
|
|
2921
|
+
let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
|
|
2922
|
+
switch (subscriptionCommitment) {
|
|
2923
|
+
case 'processed':
|
|
2924
|
+
case 'recent':
|
|
2925
|
+
case 'single':
|
|
2926
|
+
case 'confirmed':
|
|
2927
|
+
case 'singleGossip': {
|
|
2928
|
+
timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
|
|
2929
|
+
break;
|
|
2930
|
+
}
|
|
2931
|
+
// exhaust enums to ensure full coverage
|
|
2932
|
+
case 'finalized':
|
|
2933
|
+
case 'max':
|
|
2934
|
+
case 'root':
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
timeoutId = setTimeout(
|
|
2938
|
+
() => resolve({__type: TransactionStatus.TIMED_OUT, timeoutMs}),
|
|
2939
|
+
timeoutMs,
|
|
2940
|
+
);
|
|
2941
|
+
} else {
|
|
2942
|
+
let config = strategy as BlockheightBasedTransactionConfimationStrategy;
|
|
2943
|
+
(async () => {
|
|
2944
|
+
let currentBlockHeight = await checkBlockHeight();
|
|
2945
|
+
if (done) return;
|
|
2946
|
+
while (currentBlockHeight <= config.lastValidBlockHeight) {
|
|
2947
|
+
await sleep(1000);
|
|
2948
|
+
if (done) return;
|
|
2949
|
+
currentBlockHeight = await checkBlockHeight();
|
|
2950
|
+
if (done) return;
|
|
2951
|
+
}
|
|
2952
|
+
resolve({__type: TransactionStatus.BLOCKHEIGHT_EXCEEDED});
|
|
2953
|
+
})();
|
|
2954
|
+
}
|
|
2955
|
+
});
|
|
2956
|
+
|
|
2957
|
+
let result: RpcResponseAndContext<SignatureResult>;
|
|
2884
2958
|
try {
|
|
2885
|
-
await
|
|
2959
|
+
const outcome = await Promise.race([confirmationPromise, expiryPromise]);
|
|
2960
|
+
switch (outcome.__type) {
|
|
2961
|
+
case TransactionStatus.BLOCKHEIGHT_EXCEEDED:
|
|
2962
|
+
throw new TransactionExpiredBlockheightExceededError(rawSignature);
|
|
2963
|
+
case TransactionStatus.PROCESSED:
|
|
2964
|
+
result = outcome.response;
|
|
2965
|
+
break;
|
|
2966
|
+
case TransactionStatus.TIMED_OUT:
|
|
2967
|
+
throw new TransactionExpiredTimeoutError(
|
|
2968
|
+
rawSignature,
|
|
2969
|
+
outcome.timeoutMs / 1000,
|
|
2970
|
+
);
|
|
2971
|
+
}
|
|
2886
2972
|
} finally {
|
|
2973
|
+
clearTimeout(timeoutId);
|
|
2887
2974
|
if (subscriptionId) {
|
|
2888
2975
|
this.removeSignatureListener(subscriptionId);
|
|
2889
2976
|
}
|
|
2890
2977
|
}
|
|
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;
|
|
2978
|
+
return result;
|
|
2902
2979
|
}
|
|
2903
2980
|
|
|
2904
2981
|
/**
|
package/src/message.ts
CHANGED
|
@@ -8,6 +8,7 @@ import * as Layout from './layout';
|
|
|
8
8
|
import {PACKET_DATA_SIZE} from './transaction-constants';
|
|
9
9
|
import * as shortvec from './util/shortvec-encoding';
|
|
10
10
|
import {toBuffer} from './util/to-buffer';
|
|
11
|
+
import {guardedShift, guardedSplice} from './util/guarded-array-utils';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* The message header, identifying signed and read-only account
|
|
@@ -222,32 +223,28 @@ export class Message {
|
|
|
222
223
|
// Slice up wire data
|
|
223
224
|
let byteArray = [...buffer];
|
|
224
225
|
|
|
225
|
-
const numRequiredSignatures = byteArray
|
|
226
|
-
const numReadonlySignedAccounts = byteArray
|
|
227
|
-
const numReadonlyUnsignedAccounts = byteArray
|
|
226
|
+
const numRequiredSignatures = guardedShift(byteArray);
|
|
227
|
+
const numReadonlySignedAccounts = guardedShift(byteArray);
|
|
228
|
+
const numReadonlyUnsignedAccounts = guardedShift(byteArray);
|
|
228
229
|
|
|
229
230
|
const accountCount = shortvec.decodeLength(byteArray);
|
|
230
231
|
let accountKeys = [];
|
|
231
232
|
for (let i = 0; i < accountCount; i++) {
|
|
232
|
-
const account = byteArray
|
|
233
|
-
byteArray = byteArray.slice(PUBKEY_LENGTH);
|
|
233
|
+
const account = guardedSplice(byteArray, 0, PUBKEY_LENGTH);
|
|
234
234
|
accountKeys.push(bs58.encode(Buffer.from(account)));
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
const recentBlockhash = byteArray
|
|
238
|
-
byteArray = byteArray.slice(PUBKEY_LENGTH);
|
|
237
|
+
const recentBlockhash = guardedSplice(byteArray, 0, PUBKEY_LENGTH);
|
|
239
238
|
|
|
240
239
|
const instructionCount = shortvec.decodeLength(byteArray);
|
|
241
240
|
let instructions: CompiledInstruction[] = [];
|
|
242
241
|
for (let i = 0; i < instructionCount; i++) {
|
|
243
|
-
const programIdIndex = byteArray
|
|
242
|
+
const programIdIndex = guardedShift(byteArray);
|
|
244
243
|
const accountCount = shortvec.decodeLength(byteArray);
|
|
245
|
-
const accounts = byteArray
|
|
246
|
-
byteArray = byteArray.slice(accountCount);
|
|
244
|
+
const accounts = guardedSplice(byteArray, 0, accountCount);
|
|
247
245
|
const dataLength = shortvec.decodeLength(byteArray);
|
|
248
|
-
const dataSlice = byteArray
|
|
246
|
+
const dataSlice = guardedSplice(byteArray, 0, dataLength);
|
|
249
247
|
const data = bs58.encode(Buffer.from(dataSlice));
|
|
250
|
-
byteArray = byteArray.slice(dataLength);
|
|
251
248
|
instructions.push({
|
|
252
249
|
programIdIndex,
|
|
253
250
|
accounts,
|
package/src/transaction.ts
CHANGED
|
@@ -15,12 +15,19 @@ import invariant from './util/assert';
|
|
|
15
15
|
import type {Signer} from './keypair';
|
|
16
16
|
import type {Blockhash} from './blockhash';
|
|
17
17
|
import type {CompiledInstruction} from './message';
|
|
18
|
+
import {guardedSplice} from './util/guarded-array-utils';
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Transaction signature as base-58 encoded string
|
|
21
22
|
*/
|
|
22
23
|
export type TransactionSignature = string;
|
|
23
24
|
|
|
25
|
+
export const enum TransactionStatus {
|
|
26
|
+
BLOCKHEIGHT_EXCEEDED,
|
|
27
|
+
PROCESSED,
|
|
28
|
+
TIMED_OUT,
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
/**
|
|
25
32
|
* Default (empty) signature
|
|
26
33
|
*/
|
|
@@ -124,17 +131,30 @@ export type SignaturePubkeyPair = {
|
|
|
124
131
|
|
|
125
132
|
/**
|
|
126
133
|
* List of Transaction object fields that may be initialized at construction
|
|
127
|
-
*
|
|
128
134
|
*/
|
|
129
|
-
export type
|
|
130
|
-
/** A recent blockhash */
|
|
131
|
-
recentBlockhash?: Blockhash | null;
|
|
135
|
+
export type TransactionCtorFields_DEPRECATED = {
|
|
132
136
|
/** Optional nonce information used for offline nonce'd transactions */
|
|
133
137
|
nonceInfo?: NonceInformation | null;
|
|
134
138
|
/** The transaction fee payer */
|
|
135
139
|
feePayer?: PublicKey | null;
|
|
136
140
|
/** One or more signatures */
|
|
137
141
|
signatures?: Array<SignaturePubkeyPair>;
|
|
142
|
+
/** A recent blockhash */
|
|
143
|
+
recentBlockhash?: Blockhash;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* List of Transaction object fields that may be initialized at construction
|
|
148
|
+
*/
|
|
149
|
+
export type TransactionBlockhashCtor = {
|
|
150
|
+
/** The transaction fee payer */
|
|
151
|
+
feePayer?: PublicKey | null;
|
|
152
|
+
/** One or more signatures */
|
|
153
|
+
signatures?: Array<SignaturePubkeyPair>;
|
|
154
|
+
/** A recent blockhash */
|
|
155
|
+
blockhash: Blockhash;
|
|
156
|
+
/** the last block chain can advance to before tx is declared expired */
|
|
157
|
+
lastValidBlockHeight: number;
|
|
138
158
|
};
|
|
139
159
|
|
|
140
160
|
/**
|
|
@@ -196,6 +216,11 @@ export class Transaction {
|
|
|
196
216
|
*/
|
|
197
217
|
recentBlockhash?: Blockhash;
|
|
198
218
|
|
|
219
|
+
/**
|
|
220
|
+
* the last block chain can advance to before tx is declared expired
|
|
221
|
+
* */
|
|
222
|
+
lastValidBlockHeight?: number;
|
|
223
|
+
|
|
199
224
|
/**
|
|
200
225
|
* Optional Nonce information. If populated, transaction will use a durable
|
|
201
226
|
* Nonce hash instead of a recentBlockhash. Must be populated by the caller
|
|
@@ -212,11 +237,35 @@ export class Transaction {
|
|
|
212
237
|
*/
|
|
213
238
|
_json?: TransactionJSON;
|
|
214
239
|
|
|
240
|
+
// Construct a transaction with a blockhash and lastValidBlockHeight
|
|
241
|
+
constructor(opts?: TransactionBlockhashCtor);
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* @deprecated `TransactionCtorFields` has been deprecated and will be removed in a future version.
|
|
245
|
+
* Please supply a `TransactionBlockhashCtor` instead.
|
|
246
|
+
*/
|
|
247
|
+
constructor(opts?: TransactionCtorFields_DEPRECATED);
|
|
248
|
+
|
|
215
249
|
/**
|
|
216
250
|
* Construct an empty Transaction
|
|
217
251
|
*/
|
|
218
|
-
constructor(
|
|
219
|
-
opts
|
|
252
|
+
constructor(
|
|
253
|
+
opts?: TransactionBlockhashCtor | TransactionCtorFields_DEPRECATED,
|
|
254
|
+
) {
|
|
255
|
+
if (!opts) {
|
|
256
|
+
return;
|
|
257
|
+
} else if (
|
|
258
|
+
Object.prototype.hasOwnProperty.call(opts, 'lastValidBlockHeight')
|
|
259
|
+
) {
|
|
260
|
+
const newOpts = opts as TransactionBlockhashCtor;
|
|
261
|
+
Object.assign(this, newOpts);
|
|
262
|
+
this.recentBlockhash = newOpts.blockhash;
|
|
263
|
+
this.lastValidBlockHeight = newOpts.lastValidBlockHeight;
|
|
264
|
+
} else {
|
|
265
|
+
const oldOpts = opts as TransactionCtorFields_DEPRECATED;
|
|
266
|
+
Object.assign(this, oldOpts);
|
|
267
|
+
this.recentBlockhash = oldOpts.recentBlockhash;
|
|
268
|
+
}
|
|
220
269
|
}
|
|
221
270
|
|
|
222
271
|
/**
|
|
@@ -736,8 +785,7 @@ export class Transaction {
|
|
|
736
785
|
const signatureCount = shortvec.decodeLength(byteArray);
|
|
737
786
|
let signatures = [];
|
|
738
787
|
for (let i = 0; i < signatureCount; i++) {
|
|
739
|
-
const signature = byteArray
|
|
740
|
-
byteArray = byteArray.slice(SIGNATURE_LENGTH_IN_BYTES);
|
|
788
|
+
const signature = guardedSplice(byteArray, 0, SIGNATURE_LENGTH_IN_BYTES);
|
|
741
789
|
signatures.push(bs58.encode(Buffer.from(signature)));
|
|
742
790
|
}
|
|
743
791
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const END_OF_BUFFER_ERROR_MESSAGE = 'Reached end of buffer unexpectedly';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Delegates to `Array#shift`, but throws if the array is zero-length.
|
|
5
|
+
*/
|
|
6
|
+
export function guardedShift<T>(byteArray: T[]): T {
|
|
7
|
+
if (byteArray.length === 0) {
|
|
8
|
+
throw new Error(END_OF_BUFFER_ERROR_MESSAGE);
|
|
9
|
+
}
|
|
10
|
+
return byteArray.shift() as T;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Delegates to `Array#splice`, but throws if the section being spliced out extends past the end of
|
|
15
|
+
* the array.
|
|
16
|
+
*/
|
|
17
|
+
export function guardedSplice<T>(
|
|
18
|
+
byteArray: T[],
|
|
19
|
+
...args:
|
|
20
|
+
| [start: number, deleteCount?: number]
|
|
21
|
+
| [start: number, deleteCount: number, ...items: T[]]
|
|
22
|
+
): T[] {
|
|
23
|
+
const [start] = args;
|
|
24
|
+
if (
|
|
25
|
+
args.length === 2 // Implies that `deleteCount` was supplied
|
|
26
|
+
? start + (args[1] ?? 0) > byteArray.length
|
|
27
|
+
: start >= byteArray.length
|
|
28
|
+
) {
|
|
29
|
+
throw new Error(END_OF_BUFFER_ERROR_MESSAGE);
|
|
30
|
+
}
|
|
31
|
+
return byteArray.splice(
|
|
32
|
+
...(args as Parameters<typeof Array.prototype.splice>),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -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
|
+
});
|
package/src/validator-info.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import * as Layout from './layout';
|
|
10
10
|
import * as shortvec from './util/shortvec-encoding';
|
|
11
11
|
import {PublicKey} from './publickey';
|
|
12
|
+
import {guardedShift, guardedSplice} from './util/guarded-array-utils';
|
|
12
13
|
|
|
13
14
|
export const VALIDATOR_INFO_KEY = new PublicKey(
|
|
14
15
|
'Va1idator1nfo111111111111111111111111111111',
|
|
@@ -85,10 +86,10 @@ export class ValidatorInfo {
|
|
|
85
86
|
|
|
86
87
|
const configKeys: Array<ConfigKey> = [];
|
|
87
88
|
for (let i = 0; i < 2; i++) {
|
|
88
|
-
const publicKey = new PublicKey(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
const publicKey = new PublicKey(
|
|
90
|
+
guardedSplice(byteArray, 0, PUBKEY_LENGTH),
|
|
91
|
+
);
|
|
92
|
+
const isSigner = guardedShift(byteArray) === 1;
|
|
92
93
|
configKeys.push({publicKey, isSigner});
|
|
93
94
|
}
|
|
94
95
|
|