@solana/web3.js 1.38.0 → 1.40.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.38.0",
3
+ "version": "1.40.0",
4
4
  "description": "Solana Javascript API",
5
5
  "keywords": [
6
6
  "api",
@@ -79,7 +79,7 @@
79
79
  "@babel/preset-typescript": "^7.12.16",
80
80
  "@babel/register": "^7.12.13",
81
81
  "@commitlint/config-conventional": "^15.0.0",
82
- "@commitlint/travis-cli": "^15.0.0",
82
+ "@commitlint/travis-cli": "^16.2.3",
83
83
  "@rollup/plugin-alias": "^3.1.2",
84
84
  "@rollup/plugin-babel": "^5.2.3",
85
85
  "@rollup/plugin-commonjs": "^21.0.0",
@@ -95,7 +95,7 @@
95
95
  "@types/express-serve-static-core": "^4.17.21",
96
96
  "@types/mocha": "^9.0.0",
97
97
  "@types/mz": "^2.7.3",
98
- "@types/node": "^16.0.0",
98
+ "@types/node": "^17.0.24",
99
99
  "@types/secp256k1": "^4.0.1",
100
100
  "@types/sinon": "^10.0.0",
101
101
  "@typescript-eslint/eslint-plugin": "^4.14.2",
package/src/connection.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import bs58 from 'bs58';
2
2
  import {Buffer} from 'buffer';
3
- import fetch from 'cross-fetch';
3
+ import crossFetch from 'cross-fetch';
4
4
  import {
5
5
  type as pick,
6
6
  number,
@@ -814,9 +814,11 @@ function createRpcClient(
814
814
  url: string,
815
815
  useHttps: boolean,
816
816
  httpHeaders?: HttpHeaders,
817
+ customFetch?: typeof crossFetch,
817
818
  fetchMiddleware?: FetchMiddleware,
818
819
  disableRetryOnRateLimit?: boolean,
819
820
  ): RpcClient {
821
+ const fetch = customFetch ? customFetch : crossFetch;
820
822
  let agentManager: AgentManager | undefined;
821
823
  if (!process.env.BROWSER) {
822
824
  agentManager = new AgentManager(useHttps);
@@ -2108,6 +2110,8 @@ export type ConnectionConfig = {
2108
2110
  wsEndpoint?: string;
2109
2111
  /** Optional HTTP headers object */
2110
2112
  httpHeaders?: HttpHeaders;
2113
+ /** Optional custom fetch function */
2114
+ fetch?: typeof crossFetch;
2111
2115
  /** Optional fetch middleware callback */
2112
2116
  fetchMiddleware?: FetchMiddleware;
2113
2117
  /** Optional Disable retrying calls when server responds with HTTP 429 (Too Many Requests) */
@@ -2116,6 +2120,13 @@ export type ConnectionConfig = {
2116
2120
  confirmTransactionInitialTimeout?: number;
2117
2121
  };
2118
2122
 
2123
+ function createSubscriptionWarningMessage(id: number, label: string): string {
2124
+ return (
2125
+ 'Ignored unsubscribe request because an active subscription ' +
2126
+ `with id \`${id}\` for '${label}' events could not be found.`
2127
+ );
2128
+ }
2129
+
2119
2130
  /**
2120
2131
  * A connection to a fullnode JSON RPC endpoint
2121
2132
  */
@@ -2200,6 +2211,7 @@ export class Connection {
2200
2211
 
2201
2212
  let wsEndpoint;
2202
2213
  let httpHeaders;
2214
+ let fetch;
2203
2215
  let fetchMiddleware;
2204
2216
  let disableRetryOnRateLimit;
2205
2217
  if (commitmentOrConfig && typeof commitmentOrConfig === 'string') {
@@ -2210,6 +2222,7 @@ export class Connection {
2210
2222
  commitmentOrConfig.confirmTransactionInitialTimeout;
2211
2223
  wsEndpoint = commitmentOrConfig.wsEndpoint;
2212
2224
  httpHeaders = commitmentOrConfig.httpHeaders;
2225
+ fetch = commitmentOrConfig.fetch;
2213
2226
  fetchMiddleware = commitmentOrConfig.fetchMiddleware;
2214
2227
  disableRetryOnRateLimit = commitmentOrConfig.disableRetryOnRateLimit;
2215
2228
  }
@@ -2221,6 +2234,7 @@ export class Connection {
2221
2234
  url.toString(),
2222
2235
  useHttps,
2223
2236
  httpHeaders,
2237
+ fetch,
2224
2238
  fetchMiddleware,
2225
2239
  disableRetryOnRateLimit,
2226
2240
  );
@@ -3397,6 +3411,34 @@ export class Connection {
3397
3411
  return res;
3398
3412
  }
3399
3413
 
3414
+ /**
3415
+ * Fetch transaction details for a batch of confirmed transactions.
3416
+ * Similar to {@link getParsedTransactions} but returns a {@link TransactionResponse}.
3417
+ */
3418
+ async getTransactions(
3419
+ signatures: TransactionSignature[],
3420
+ commitment?: Finality,
3421
+ ): Promise<(TransactionResponse | null)[]> {
3422
+ const batch = signatures.map(signature => {
3423
+ const args = this._buildArgsAtLeastConfirmed([signature], commitment);
3424
+ return {
3425
+ methodName: 'getTransaction',
3426
+ args,
3427
+ };
3428
+ });
3429
+
3430
+ const unsafeRes = await this._rpcBatchRequest(batch);
3431
+ const res = unsafeRes.map((unsafeRes: any) => {
3432
+ const res = create(unsafeRes, GetTransactionRpcResult);
3433
+ if ('error' in res) {
3434
+ throw new Error('failed to get transactions: ' + res.error.message);
3435
+ }
3436
+ return res.result;
3437
+ });
3438
+
3439
+ return res;
3440
+ }
3441
+
3400
3442
  /**
3401
3443
  * Fetch a list of Transactions and transaction statuses from the cluster
3402
3444
  * for a confirmed block.
@@ -3888,6 +3930,8 @@ export class Connection {
3888
3930
  transaction.instructions = transactionOrMessage.instructions;
3889
3931
  } else {
3890
3932
  transaction = Transaction.populate(transactionOrMessage);
3933
+ // HACK: this function relies on mutating the populated transaction
3934
+ transaction._message = transaction._json = undefined;
3891
3935
  }
3892
3936
 
3893
3937
  if (transaction.nonceInfo && signers) {
@@ -4344,7 +4388,7 @@ export class Connection {
4344
4388
  await this._unsubscribe(subInfo, 'accountUnsubscribe');
4345
4389
  this._updateSubscriptions();
4346
4390
  } else {
4347
- throw new Error(`Unknown account change id: ${id}`);
4391
+ console.warn(createSubscriptionWarningMessage(id, 'account change'));
4348
4392
  }
4349
4393
  }
4350
4394
 
@@ -4408,7 +4452,9 @@ export class Connection {
4408
4452
  await this._unsubscribe(subInfo, 'programUnsubscribe');
4409
4453
  this._updateSubscriptions();
4410
4454
  } else {
4411
- throw new Error(`Unknown program account change id: ${id}`);
4455
+ console.warn(
4456
+ createSubscriptionWarningMessage(id, 'program account change'),
4457
+ );
4412
4458
  }
4413
4459
  }
4414
4460
 
@@ -4437,13 +4483,14 @@ export class Connection {
4437
4483
  * @param id subscription id to deregister.
4438
4484
  */
4439
4485
  async removeOnLogsListener(id: number): Promise<void> {
4440
- if (!this._logsSubscriptions[id]) {
4441
- throw new Error(`Unknown logs id: ${id}`);
4486
+ if (this._logsSubscriptions[id]) {
4487
+ const subInfo = this._logsSubscriptions[id];
4488
+ delete this._logsSubscriptions[id];
4489
+ await this._unsubscribe(subInfo, 'logsUnsubscribe');
4490
+ this._updateSubscriptions();
4491
+ } else {
4492
+ console.warn(createSubscriptionWarningMessage(id, 'logs'));
4442
4493
  }
4443
- const subInfo = this._logsSubscriptions[id];
4444
- delete this._logsSubscriptions[id];
4445
- await this._unsubscribe(subInfo, 'logsUnsubscribe');
4446
- this._updateSubscriptions();
4447
4494
  }
4448
4495
 
4449
4496
  /**
@@ -4502,7 +4549,7 @@ export class Connection {
4502
4549
  await this._unsubscribe(subInfo, 'slotUnsubscribe');
4503
4550
  this._updateSubscriptions();
4504
4551
  } else {
4505
- throw new Error(`Unknown slot change id: ${id}`);
4552
+ console.warn(createSubscriptionWarningMessage(id, 'slot change'));
4506
4553
  }
4507
4554
  }
4508
4555
 
@@ -4548,7 +4595,7 @@ export class Connection {
4548
4595
  await this._unsubscribe(subInfo, 'slotsUpdatesUnsubscribe');
4549
4596
  this._updateSubscriptions();
4550
4597
  } else {
4551
- throw new Error(`Unknown slot update id: ${id}`);
4598
+ console.warn(createSubscriptionWarningMessage(id, 'slot update'));
4552
4599
  }
4553
4600
  }
4554
4601
 
@@ -4693,7 +4740,7 @@ export class Connection {
4693
4740
  await this._unsubscribe(subInfo, 'signatureUnsubscribe');
4694
4741
  this._updateSubscriptions();
4695
4742
  } else {
4696
- throw new Error(`Unknown signature result id: ${id}`);
4743
+ console.warn(createSubscriptionWarningMessage(id, 'signature result'));
4697
4744
  }
4698
4745
  }
4699
4746
 
@@ -4738,7 +4785,7 @@ export class Connection {
4738
4785
  await this._unsubscribe(subInfo, 'rootUnsubscribe');
4739
4786
  this._updateSubscriptions();
4740
4787
  } else {
4741
- throw new Error(`Unknown root change id: ${id}`);
4788
+ console.warn(createSubscriptionWarningMessage(id, 'root change'));
4742
4789
  }
4743
4790
  }
4744
4791
  }
package/src/publickey.ts CHANGED
@@ -143,10 +143,10 @@ export class PublicKey extends Struct {
143
143
  * Derive a program address from seeds and a program ID.
144
144
  */
145
145
  /* eslint-disable require-await */
146
- static async createProgramAddress(
146
+ static createProgramAddressSync(
147
147
  seeds: Array<Buffer | Uint8Array>,
148
148
  programId: PublicKey,
149
- ): Promise<PublicKey> {
149
+ ): PublicKey {
150
150
  let buffer = Buffer.alloc(0);
151
151
  seeds.forEach(function (seed) {
152
152
  if (seed.length > MAX_SEED_LENGTH) {
@@ -167,6 +167,18 @@ export class PublicKey extends Struct {
167
167
  return new PublicKey(publicKeyBytes);
168
168
  }
169
169
 
170
+ /**
171
+ * Async version of createProgramAddressSync
172
+ * For backwards compatibility
173
+ */
174
+ /* eslint-disable require-await */
175
+ static async createProgramAddress(
176
+ seeds: Array<Buffer | Uint8Array>,
177
+ programId: PublicKey,
178
+ ): Promise<PublicKey> {
179
+ return this.createProgramAddressSync(seeds, programId);
180
+ }
181
+
170
182
  /**
171
183
  * Find a valid program address
172
184
  *
@@ -174,16 +186,16 @@ export class PublicKey extends Struct {
174
186
  * iterates a nonce until it finds one that when combined with the seeds
175
187
  * results in a valid program address.
176
188
  */
177
- static async findProgramAddress(
189
+ static findProgramAddressSync(
178
190
  seeds: Array<Buffer | Uint8Array>,
179
191
  programId: PublicKey,
180
- ): Promise<[PublicKey, number]> {
192
+ ): [PublicKey, number] {
181
193
  let nonce = 255;
182
194
  let address;
183
195
  while (nonce != 0) {
184
196
  try {
185
197
  const seedsWithNonce = seeds.concat(Buffer.from([nonce]));
186
- address = await this.createProgramAddress(seedsWithNonce, programId);
198
+ address = this.createProgramAddressSync(seedsWithNonce, programId);
187
199
  } catch (err) {
188
200
  if (err instanceof TypeError) {
189
201
  throw err;
@@ -196,11 +208,23 @@ export class PublicKey extends Struct {
196
208
  throw new Error(`Unable to find a viable program address nonce`);
197
209
  }
198
210
 
211
+ /**
212
+ * Async version of findProgramAddressSync
213
+ * For backwards compatibility
214
+ */
215
+ static async findProgramAddress(
216
+ seeds: Array<Buffer | Uint8Array>,
217
+ programId: PublicKey,
218
+ ): Promise<[PublicKey, number]> {
219
+ return this.findProgramAddressSync(seeds, programId);
220
+ }
221
+
199
222
  /**
200
223
  * Check that a pubkey is on the ed25519 curve.
201
224
  */
202
- static isOnCurve(pubkey: Uint8Array): boolean {
203
- return is_on_curve(pubkey) == 1;
225
+ static isOnCurve(pubkeyData: PublicKeyInitData): boolean {
226
+ const pubkey = new PublicKey(pubkeyData);
227
+ return is_on_curve(pubkey.toBytes()) == 1;
204
228
  }
205
229
  }
206
230
 
@@ -601,8 +601,8 @@ export class StakeProgram {
601
601
  * Max space of a Stake account
602
602
  *
603
603
  * This is generated from the solana-stake-program StakeState struct as
604
- * `std::mem::size_of::<StakeState>()`:
605
- * https://docs.rs/solana-stake-program/1.4.4/solana_stake_program/stake_state/enum.StakeState.html
604
+ * `StakeState::size_of()`:
605
+ * https://docs.rs/solana-stake-program/latest/solana_stake_program/stake_state/enum.StakeState.html
606
606
  */
607
607
  static space: number = 200;
608
608
 
@@ -66,6 +66,19 @@ export type SerializeConfig = {
66
66
  verifySignatures?: boolean;
67
67
  };
68
68
 
69
+ /**
70
+ * @internal
71
+ */
72
+ export interface TransactionInstructionJSON {
73
+ keys: {
74
+ pubkey: string;
75
+ isSigner: boolean;
76
+ isWritable: boolean;
77
+ }[];
78
+ programId: string;
79
+ data: number[];
80
+ }
81
+
69
82
  /**
70
83
  * Transaction Instruction class
71
84
  */
@@ -93,6 +106,21 @@ export class TransactionInstruction {
93
106
  this.data = opts.data;
94
107
  }
95
108
  }
109
+
110
+ /**
111
+ * @internal
112
+ */
113
+ toJSON(): TransactionInstructionJSON {
114
+ return {
115
+ keys: this.keys.map(({pubkey, isSigner, isWritable}) => ({
116
+ pubkey: pubkey.toJSON(),
117
+ isSigner,
118
+ isWritable,
119
+ })),
120
+ programId: this.programId.toJSON(),
121
+ data: [...this.data],
122
+ };
123
+ }
96
124
  }
97
125
 
98
126
  /**
@@ -128,6 +156,20 @@ export type NonceInformation = {
128
156
  nonceInstruction: TransactionInstruction;
129
157
  };
130
158
 
159
+ /**
160
+ * @internal
161
+ */
162
+ export interface TransactionJSON {
163
+ recentBlockhash: string | null;
164
+ feePayer: string | null;
165
+ nonceInfo: {
166
+ nonce: string;
167
+ nonceInstruction: TransactionInstructionJSON;
168
+ } | null;
169
+ instructions: TransactionInstructionJSON[];
170
+ signers: string[];
171
+ }
172
+
131
173
  /**
132
174
  * Transaction class
133
175
  */
@@ -169,6 +211,16 @@ export class Transaction {
169
211
  */
170
212
  nonceInfo?: NonceInformation;
171
213
 
214
+ /**
215
+ * @internal
216
+ */
217
+ _message?: Message;
218
+
219
+ /**
220
+ * @internal
221
+ */
222
+ _json?: TransactionJSON;
223
+
172
224
  /**
173
225
  * Construct an empty Transaction
174
226
  */
@@ -176,6 +228,26 @@ export class Transaction {
176
228
  opts && Object.assign(this, opts);
177
229
  }
178
230
 
231
+ /**
232
+ * @internal
233
+ */
234
+ toJSON(): TransactionJSON {
235
+ return {
236
+ recentBlockhash: this.recentBlockhash || null,
237
+ feePayer: this.feePayer ? this.feePayer.toJSON() : null,
238
+ nonceInfo: this.nonceInfo
239
+ ? {
240
+ nonce: this.nonceInfo.nonce,
241
+ nonceInstruction: this.nonceInfo.nonceInstruction.toJSON(),
242
+ }
243
+ : null,
244
+ instructions: this.instructions.map(instruction => instruction.toJSON()),
245
+ signers: this.signatures.map(({publicKey}) => {
246
+ return publicKey.toJSON();
247
+ }),
248
+ };
249
+ }
250
+
179
251
  /**
180
252
  * Add one or more instructions to this Transaction
181
253
  */
@@ -204,6 +276,15 @@ export class Transaction {
204
276
  * Compile transaction data
205
277
  */
206
278
  compileMessage(): Message {
279
+ if (this._message) {
280
+ if (JSON.stringify(this.toJSON()) !== JSON.stringify(this._json)) {
281
+ throw new Error(
282
+ 'Transaction message mutated after being populated from Message',
283
+ );
284
+ }
285
+ return this._message;
286
+ }
287
+
207
288
  const {nonceInfo} = this;
208
289
  if (nonceInfo && this.instructions[0] != nonceInfo.nonceInstruction) {
209
290
  this.recentBlockhash = nonceInfo.nonce;
@@ -719,6 +800,9 @@ export class Transaction {
719
800
  );
720
801
  });
721
802
 
803
+ transaction._message = message;
804
+ transaction._json = transaction.toJSON();
805
+
722
806
  return transaction;
723
807
  }
724
808
  }