@solana/web3.js 1.39.0 → 1.39.2

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.39.0",
3
+ "version": "1.39.2",
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": "mocha -r ts-node/register './test/**/*.test.ts'",
54
- "test:cover": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' nyc --reporter=lcov mocha -r ts-node/register './test/**/*.test.ts'",
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",
@@ -133,5 +113,25 @@
133
113
  },
134
114
  "engines": {
135
115
  "node": ">=12.20.0"
116
+ },
117
+ "scripts": {
118
+ "build": "npm run clean; cross-env NODE_ENV=production rollup -c; npm run type:gen",
119
+ "build:fixtures": "set -ex; ./test/fixtures/noop-program/build.sh",
120
+ "clean": "rimraf ./coverage ./lib",
121
+ "codecov": "set -ex; npm run test:cover; cat ./coverage/lcov.info | codecov",
122
+ "dev": "cross-env NODE_ENV=development rollup -c",
123
+ "doc": "set -ex; typedoc --treatWarningsAsErrors",
124
+ "type:gen": "./scripts/typegen.sh",
125
+ "lint": "set -ex; npm run pretty; eslint . --ext .js,.ts",
126
+ "lint:fix": "npm run pretty:fix && eslint . --fix --ext .js,.ts",
127
+ "type:check": "tsc -p tsconfig.json --noEmit",
128
+ "ok": "run-s lint test doc type:check",
129
+ "pretty": "prettier --check '{,{src,test}/**/}*.{j,t}s'",
130
+ "pretty:fix": "prettier --write '{,{src,test}/**/}*.{j,t}s'",
131
+ "re": "semantic-release --repository-url git@github.com:solana-labs/solana-web3.js.git",
132
+ "test": "mocha -r ts-node/register './test/**/*.test.ts'",
133
+ "test:cover": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' nyc --reporter=lcov mocha -r ts-node/register './test/**/*.test.ts'",
134
+ "test:live": "TEST_LIVE=1 npm run test",
135
+ "test:live-with-test-validator": "start-server-and-test 'solana-test-validator --reset --quiet' http://localhost:8899/health test:live"
136
136
  }
137
- }
137
+ }
package/src/connection.ts CHANGED
@@ -3895,6 +3895,8 @@ export class Connection {
3895
3895
  transaction.instructions = transactionOrMessage.instructions;
3896
3896
  } else {
3897
3897
  transaction = Transaction.populate(transactionOrMessage);
3898
+ // HACK: this function relies on mutating the populated transaction
3899
+ transaction._message = transaction._json = undefined;
3898
3900
  }
3899
3901
 
3900
3902
  if (transaction.nonceInfo && signers) {
package/src/message.ts CHANGED
@@ -8,6 +8,7 @@ import * as Layout from './layout';
8
8
  import {PACKET_DATA_SIZE} from './transaction';
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,
@@ -11,6 +11,7 @@ import invariant from './util/assert';
11
11
  import type {Signer} from './keypair';
12
12
  import type {Blockhash} from './blockhash';
13
13
  import type {CompiledInstruction} from './message';
14
+ import {guardedSplice} from './util/guarded-array-utils';
14
15
 
15
16
  /**
16
17
  * Transaction signature as base-58 encoded string
@@ -66,6 +67,19 @@ export type SerializeConfig = {
66
67
  verifySignatures?: boolean;
67
68
  };
68
69
 
70
+ /**
71
+ * @internal
72
+ */
73
+ export interface TransactionInstructionJSON {
74
+ keys: {
75
+ pubkey: string;
76
+ isSigner: boolean;
77
+ isWritable: boolean;
78
+ }[];
79
+ programId: string;
80
+ data: number[];
81
+ }
82
+
69
83
  /**
70
84
  * Transaction Instruction class
71
85
  */
@@ -93,6 +107,21 @@ export class TransactionInstruction {
93
107
  this.data = opts.data;
94
108
  }
95
109
  }
110
+
111
+ /**
112
+ * @internal
113
+ */
114
+ toJSON(): TransactionInstructionJSON {
115
+ return {
116
+ keys: this.keys.map(({pubkey, isSigner, isWritable}) => ({
117
+ pubkey: pubkey.toJSON(),
118
+ isSigner,
119
+ isWritable,
120
+ })),
121
+ programId: this.programId.toJSON(),
122
+ data: [...this.data],
123
+ };
124
+ }
96
125
  }
97
126
 
98
127
  /**
@@ -128,6 +157,20 @@ export type NonceInformation = {
128
157
  nonceInstruction: TransactionInstruction;
129
158
  };
130
159
 
160
+ /**
161
+ * @internal
162
+ */
163
+ export interface TransactionJSON {
164
+ recentBlockhash: string | null;
165
+ feePayer: string | null;
166
+ nonceInfo: {
167
+ nonce: string;
168
+ nonceInstruction: TransactionInstructionJSON;
169
+ } | null;
170
+ instructions: TransactionInstructionJSON[];
171
+ signatures: {publicKey: string; signature: number[] | null}[];
172
+ }
173
+
131
174
  /**
132
175
  * Transaction class
133
176
  */
@@ -169,6 +212,16 @@ export class Transaction {
169
212
  */
170
213
  nonceInfo?: NonceInformation;
171
214
 
215
+ /**
216
+ * @internal
217
+ */
218
+ _message?: Message;
219
+
220
+ /**
221
+ * @internal
222
+ */
223
+ _json?: TransactionJSON;
224
+
172
225
  /**
173
226
  * Construct an empty Transaction
174
227
  */
@@ -176,6 +229,27 @@ export class Transaction {
176
229
  opts && Object.assign(this, opts);
177
230
  }
178
231
 
232
+ /**
233
+ * @internal
234
+ */
235
+ toJSON(): TransactionJSON {
236
+ return {
237
+ recentBlockhash: this.recentBlockhash || null,
238
+ feePayer: this.feePayer ? this.feePayer.toJSON() : null,
239
+ nonceInfo: this.nonceInfo
240
+ ? {
241
+ nonce: this.nonceInfo.nonce,
242
+ nonceInstruction: this.nonceInfo.nonceInstruction.toJSON(),
243
+ }
244
+ : null,
245
+ instructions: this.instructions.map(instruction => instruction.toJSON()),
246
+ signatures: this.signatures.map(({publicKey, signature}) => ({
247
+ publicKey: publicKey.toJSON(),
248
+ signature: signature ? [...signature] : null,
249
+ })),
250
+ };
251
+ }
252
+
179
253
  /**
180
254
  * Add one or more instructions to this Transaction
181
255
  */
@@ -204,6 +278,15 @@ export class Transaction {
204
278
  * Compile transaction data
205
279
  */
206
280
  compileMessage(): Message {
281
+ if (this._message) {
282
+ if (JSON.stringify(this.toJSON()) !== JSON.stringify(this._json)) {
283
+ throw new Error(
284
+ 'Transaction mutated after being populated from Message',
285
+ );
286
+ }
287
+ return this._message;
288
+ }
289
+
207
290
  const {nonceInfo} = this;
208
291
  if (nonceInfo && this.instructions[0] != nonceInfo.nonceInstruction) {
209
292
  this.recentBlockhash = nonceInfo.nonce;
@@ -666,8 +749,7 @@ export class Transaction {
666
749
  const signatureCount = shortvec.decodeLength(byteArray);
667
750
  let signatures = [];
668
751
  for (let i = 0; i < signatureCount; i++) {
669
- const signature = byteArray.slice(0, SIGNATURE_LENGTH);
670
- byteArray = byteArray.slice(SIGNATURE_LENGTH);
752
+ const signature = guardedSplice(byteArray, 0, SIGNATURE_LENGTH);
671
753
  signatures.push(bs58.encode(Buffer.from(signature)));
672
754
  }
673
755
 
@@ -719,6 +801,9 @@ export class Transaction {
719
801
  );
720
802
  });
721
803
 
804
+ transaction._message = message;
805
+ transaction._json = transaction.toJSON();
806
+
722
807
  return transaction;
723
808
  }
724
809
  }
@@ -0,0 +1,37 @@
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
+ var _args$;
24
+ const [start] = args;
25
+ if (
26
+ args.length === 2 // Implies that `deleteCount` was supplied
27
+ ? start +
28
+ ((_args$ = args[1]) !== null && _args$ !== void 0 ? _args$ : 0) >
29
+ byteArray.length
30
+ : start >= byteArray.length
31
+ ) {
32
+ throw new Error(END_OF_BUFFER_ERROR_MESSAGE);
33
+ }
34
+ return byteArray.splice(
35
+ ...(args as Parameters<typeof Array.prototype.splice>),
36
+ );
37
+ }
@@ -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