@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solana/web3.js",
3
- "version": "1.41.9",
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
- * Confirm the transaction identified by the specified signature.
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
- signature: TransactionSignature,
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(signature);
2872
+ decodedSignature = bs58.decode(rawSignature);
2838
2873
  } catch (err) {
2839
- throw new Error('signature must be base58 encoded: ' + signature);
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 response: RpcResponseAndContext<SignatureResult> | null = null;
2849
- const confirmPromise = new Promise((resolve, reject) => {
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
- signature,
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
- resolve(null);
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
- 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;
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
- // exhaust enums to ensure full coverage
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 promiseTimeout(confirmPromise, timeoutMs);
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.shift() as number;
226
- const numReadonlySignedAccounts = byteArray.shift() as number;
227
- const numReadonlyUnsignedAccounts = byteArray.shift() as number;
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.slice(0, PUBKEY_LENGTH);
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.slice(0, PUBKEY_LENGTH);
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.shift() as number;
242
+ const programIdIndex = guardedShift(byteArray);
244
243
  const accountCount = shortvec.decodeLength(byteArray);
245
- const accounts = byteArray.slice(0, accountCount);
246
- byteArray = byteArray.slice(accountCount);
244
+ const accounts = guardedSplice(byteArray, 0, accountCount);
247
245
  const dataLength = shortvec.decodeLength(byteArray);
248
- const dataSlice = byteArray.slice(0, dataLength);
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,
@@ -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 TransactionCtorFields = {
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(opts?: TransactionCtorFields) {
219
- opts && Object.assign(this, 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.slice(0, SIGNATURE_LENGTH_IN_BYTES);
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
- await connection.confirmTransaction(
38
- signature,
39
- options && options.commitment,
40
- )
41
- ).value;
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
+ });
@@ -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(byteArray.slice(0, PUBKEY_LENGTH));
89
- byteArray = byteArray.slice(PUBKEY_LENGTH);
90
- const isSigner = byteArray.slice(0, 1)[0] === 1;
91
- byteArray = byteArray.slice(1);
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