@solana/web3.js 1.41.1 → 1.41.4

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.cjs.js CHANGED
@@ -8,7 +8,7 @@ var BN = require('bn.js');
8
8
  var bs58 = require('bs58');
9
9
  var borsh = require('borsh');
10
10
  var BufferLayout = require('@solana/buffer-layout');
11
- var crossFetch = require('cross-fetch');
11
+ var fetch = require('cross-fetch');
12
12
  var superstruct = require('superstruct');
13
13
  var rpcWebsockets = require('rpc-websockets');
14
14
  var RpcClient = require('jayson/lib/client/browser');
@@ -41,7 +41,7 @@ var nacl__default = /*#__PURE__*/_interopDefaultLegacy(nacl);
41
41
  var BN__default = /*#__PURE__*/_interopDefaultLegacy(BN);
42
42
  var bs58__default = /*#__PURE__*/_interopDefaultLegacy(bs58);
43
43
  var BufferLayout__namespace = /*#__PURE__*/_interopNamespace(BufferLayout);
44
- var crossFetch__default = /*#__PURE__*/_interopDefaultLegacy(crossFetch);
44
+ var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
45
45
  var RpcClient__default = /*#__PURE__*/_interopDefaultLegacy(RpcClient);
46
46
  var http__default = /*#__PURE__*/_interopDefaultLegacy(http);
47
47
  var https__default = /*#__PURE__*/_interopDefaultLegacy(https);
@@ -3111,6 +3111,106 @@ function sleep(ms) {
3111
3111
  return new Promise(resolve => setTimeout(resolve, ms));
3112
3112
  }
3113
3113
 
3114
+ const encodeDecode = (layout) => {
3115
+ const decode = layout.decode.bind(layout);
3116
+ const encode = layout.encode.bind(layout);
3117
+ return { decode, encode };
3118
+ };
3119
+
3120
+ var node = {};
3121
+
3122
+ Object.defineProperty(node, "__esModule", { value: true });
3123
+ let converter;
3124
+ {
3125
+ try {
3126
+ converter = require('bindings')('bigint_buffer');
3127
+ }
3128
+ catch (e) {
3129
+ console.warn('bigint: Failed to load bindings, pure JS will be used (try npm run rebuild?)');
3130
+ }
3131
+ }
3132
+ /**
3133
+ * Convert a little-endian buffer into a BigInt.
3134
+ * @param buf The little-endian buffer to convert
3135
+ * @returns A BigInt with the little-endian representation of buf.
3136
+ */
3137
+ function toBigIntLE(buf) {
3138
+ if (converter === undefined) {
3139
+ const reversed = Buffer.from(buf);
3140
+ reversed.reverse();
3141
+ const hex = reversed.toString('hex');
3142
+ if (hex.length === 0) {
3143
+ return BigInt(0);
3144
+ }
3145
+ return BigInt(`0x${hex}`);
3146
+ }
3147
+ return converter.toBigInt(buf, false);
3148
+ }
3149
+ var toBigIntLE_1 = node.toBigIntLE = toBigIntLE;
3150
+ /**
3151
+ * Convert a big-endian buffer into a BigInt
3152
+ * @param buf The big-endian buffer to convert.
3153
+ * @returns A BigInt with the big-endian representation of buf.
3154
+ */
3155
+ function toBigIntBE(buf) {
3156
+ if (converter === undefined) {
3157
+ const hex = buf.toString('hex');
3158
+ if (hex.length === 0) {
3159
+ return BigInt(0);
3160
+ }
3161
+ return BigInt(`0x${hex}`);
3162
+ }
3163
+ return converter.toBigInt(buf, true);
3164
+ }
3165
+ node.toBigIntBE = toBigIntBE;
3166
+ /**
3167
+ * Convert a BigInt to a little-endian buffer.
3168
+ * @param num The BigInt to convert.
3169
+ * @param width The number of bytes that the resulting buffer should be.
3170
+ * @returns A little-endian buffer representation of num.
3171
+ */
3172
+ function toBufferLE(num, width) {
3173
+ if (converter === undefined) {
3174
+ const hex = num.toString(16);
3175
+ const buffer = Buffer.from(hex.padStart(width * 2, '0').slice(0, width * 2), 'hex');
3176
+ buffer.reverse();
3177
+ return buffer;
3178
+ }
3179
+ // Allocation is done here, since it is slower using napi in C
3180
+ return converter.fromBigInt(num, Buffer.allocUnsafe(width), false);
3181
+ }
3182
+ var toBufferLE_1 = node.toBufferLE = toBufferLE;
3183
+ /**
3184
+ * Convert a BigInt to a big-endian buffer.
3185
+ * @param num The BigInt to convert.
3186
+ * @param width The number of bytes that the resulting buffer should be.
3187
+ * @returns A big-endian buffer representation of num.
3188
+ */
3189
+ function toBufferBE(num, width) {
3190
+ if (converter === undefined) {
3191
+ const hex = num.toString(16);
3192
+ return Buffer.from(hex.padStart(width * 2, '0').slice(0, width * 2), 'hex');
3193
+ }
3194
+ return converter.fromBigInt(num, Buffer.allocUnsafe(width), true);
3195
+ }
3196
+ node.toBufferBE = toBufferBE;
3197
+
3198
+ const bigInt = (length) => (property) => {
3199
+ const layout = BufferLayout.blob(length, property);
3200
+ const { encode, decode } = encodeDecode(layout);
3201
+ const bigIntLayout = layout;
3202
+ bigIntLayout.decode = (buffer, offset) => {
3203
+ const src = decode(buffer, offset);
3204
+ return toBigIntLE_1(Buffer.from(src));
3205
+ };
3206
+ bigIntLayout.encode = (bigInt, buffer, offset) => {
3207
+ const src = toBufferLE_1(bigInt, length);
3208
+ return encode(src, buffer, offset);
3209
+ };
3210
+ return bigIntLayout;
3211
+ };
3212
+ const u64 = bigInt(8);
3213
+
3114
3214
  /**
3115
3215
  * Populate a buffer of instruction data using an InstructionType
3116
3216
  * @internal
@@ -3500,7 +3600,7 @@ const SYSTEM_INSTRUCTION_LAYOUTS = Object.freeze({
3500
3600
  },
3501
3601
  Transfer: {
3502
3602
  index: 2,
3503
- layout: BufferLayout__namespace.struct([BufferLayout__namespace.u32('instruction'), BufferLayout__namespace.ns64('lamports')])
3603
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u32('instruction'), u64('lamports')])
3504
3604
  },
3505
3605
  CreateWithSeed: {
3506
3606
  index: 3,
@@ -3536,7 +3636,7 @@ const SYSTEM_INSTRUCTION_LAYOUTS = Object.freeze({
3536
3636
  },
3537
3637
  TransferWithSeed: {
3538
3638
  index: 11,
3539
- layout: BufferLayout__namespace.struct([BufferLayout__namespace.u32('instruction'), BufferLayout__namespace.ns64('lamports'), rustString('seed'), publicKey('programId')])
3639
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u32('instruction'), u64('lamports'), rustString('seed'), publicKey('programId')])
3540
3640
  }
3541
3641
  });
3542
3642
  /**
@@ -3589,7 +3689,7 @@ class SystemProgram {
3589
3689
  if ('basePubkey' in params) {
3590
3690
  const type = SYSTEM_INSTRUCTION_LAYOUTS.TransferWithSeed;
3591
3691
  data = encodeData(type, {
3592
- lamports: params.lamports,
3692
+ lamports: BigInt(params.lamports),
3593
3693
  seed: params.seed,
3594
3694
  programId: toBuffer(params.programId.toBuffer())
3595
3695
  });
@@ -3609,7 +3709,7 @@ class SystemProgram {
3609
3709
  } else {
3610
3710
  const type = SYSTEM_INSTRUCTION_LAYOUTS.Transfer;
3611
3711
  data = encodeData(type, {
3612
- lamports: params.lamports
3712
+ lamports: BigInt(params.lamports)
3613
3713
  });
3614
3714
  keys = [{
3615
3715
  pubkey: params.fromPubkey,
@@ -4244,6 +4344,82 @@ class ComputeBudgetProgram {
4244
4344
  }
4245
4345
  ComputeBudgetProgram.programId = new PublicKey('ComputeBudget111111111111111111111111111111');
4246
4346
 
4347
+ var objToString = Object.prototype.toString;
4348
+ var objKeys = Object.keys || function(obj) {
4349
+ var keys = [];
4350
+ for (var name in obj) {
4351
+ keys.push(name);
4352
+ }
4353
+ return keys;
4354
+ };
4355
+
4356
+ function stringify(val, isArrayProp) {
4357
+ var i, max, str, keys, key, propVal, toStr;
4358
+ if (val === true) {
4359
+ return "true";
4360
+ }
4361
+ if (val === false) {
4362
+ return "false";
4363
+ }
4364
+ switch (typeof val) {
4365
+ case "object":
4366
+ if (val === null) {
4367
+ return null;
4368
+ } else if (val.toJSON && typeof val.toJSON === "function") {
4369
+ return stringify(val.toJSON(), isArrayProp);
4370
+ } else {
4371
+ toStr = objToString.call(val);
4372
+ if (toStr === "[object Array]") {
4373
+ str = '[';
4374
+ max = val.length - 1;
4375
+ for(i = 0; i < max; i++) {
4376
+ str += stringify(val[i], true) + ',';
4377
+ }
4378
+ if (max > -1) {
4379
+ str += stringify(val[i], true);
4380
+ }
4381
+ return str + ']';
4382
+ } else if (toStr === "[object Object]") {
4383
+ // only object is left
4384
+ keys = objKeys(val).sort();
4385
+ max = keys.length;
4386
+ str = "";
4387
+ i = 0;
4388
+ while (i < max) {
4389
+ key = keys[i];
4390
+ propVal = stringify(val[key], false);
4391
+ if (propVal !== undefined) {
4392
+ if (str) {
4393
+ str += ',';
4394
+ }
4395
+ str += JSON.stringify(key) + ':' + propVal;
4396
+ }
4397
+ i++;
4398
+ }
4399
+ return '{' + str + '}';
4400
+ } else {
4401
+ return JSON.stringify(val);
4402
+ }
4403
+ }
4404
+ case "function":
4405
+ case "undefined":
4406
+ return isArrayProp ? null : undefined;
4407
+ case "string":
4408
+ return JSON.stringify(val);
4409
+ default:
4410
+ return isFinite(val) ? val : null;
4411
+ }
4412
+ }
4413
+
4414
+ var fastStableStringify = function(val) {
4415
+ var returnVal = stringify(val, false);
4416
+ if (returnVal !== undefined) {
4417
+ return ''+ returnVal;
4418
+ }
4419
+ };
4420
+
4421
+ var fastStableStringify$1 = fastStableStringify;
4422
+
4247
4423
  const DESTROY_TIMEOUT_MS = 5000;
4248
4424
  class AgentManager {
4249
4425
  static _newAgent(useHttps) {
@@ -4459,6 +4635,12 @@ const BufferFromRawAccountData = superstruct.coerce(superstruct.instance(buffer.
4459
4635
  */
4460
4636
 
4461
4637
  const BLOCKHASH_CACHE_TIMEOUT_MS = 30 * 1000;
4638
+ /**
4639
+ * HACK.
4640
+ * Copied from rpc-websockets/dist/lib/client.
4641
+ * Otherwise, `yarn build` fails with:
4642
+ * https://gist.github.com/steveluscher/c057eca81d479ef705cdb53162f9971d
4643
+ */
4462
4644
 
4463
4645
  /**
4464
4646
  * @internal
@@ -4629,7 +4811,7 @@ const BlockProductionResponseStruct = jsonRpcResultAndContext(superstruct.type({
4629
4811
  */
4630
4812
 
4631
4813
  function createRpcClient(url, useHttps, httpHeaders, customFetch, fetchMiddleware, disableRetryOnRateLimit) {
4632
- const fetch = customFetch ? customFetch : crossFetch__default["default"];
4814
+ const fetch = customFetch ? customFetch : fetch__default["default"];
4633
4815
  let agentManager;
4634
4816
 
4635
4817
  {
@@ -5333,14 +5515,9 @@ const LogsNotificationResult = superstruct.type({
5333
5515
  * Filter for log subscriptions.
5334
5516
  */
5335
5517
 
5336
- function createSubscriptionWarningMessage(id, label) {
5337
- return 'Ignored unsubscribe request because an active subscription ' + `with id \`${id}\` for '${label}' events could not be found.`;
5338
- }
5339
5518
  /**
5340
5519
  * A connection to a fullnode JSON RPC endpoint
5341
5520
  */
5342
-
5343
-
5344
5521
  class Connection {
5345
5522
  /** @internal */
5346
5523
 
@@ -5364,21 +5541,13 @@ class Connection {
5364
5541
 
5365
5542
  /** @internal */
5366
5543
 
5367
- /** @internal */
5368
-
5369
- /** @internal */
5370
-
5371
- /** @internal */
5372
-
5373
- /** @internal */
5374
-
5375
- /** @internal */
5376
-
5377
- /** @internal */
5378
-
5379
- /** @internal */
5380
-
5381
- /** @internal */
5544
+ /** @internal
5545
+ * A number that we increment every time an active connection closes.
5546
+ * Used to determine whether the same socket connection that was open
5547
+ * when an async operation started is the same one that's active when
5548
+ * its continuation fires.
5549
+ *
5550
+ */
5382
5551
 
5383
5552
  /** @internal */
5384
5553
 
@@ -5394,7 +5563,19 @@ class Connection {
5394
5563
 
5395
5564
  /** @internal */
5396
5565
 
5397
- /** @internal */
5566
+ /**
5567
+ * Special case.
5568
+ * After a signature is processed, RPCs automatically dispose of the
5569
+ * subscription on the server side. We need to track which of these
5570
+ * subscriptions have been disposed in such a way, so that we know
5571
+ * whether the client is dealing with a not-yet-processed signature
5572
+ * (in which case we must tear down the server subscription) or an
5573
+ * already-processed signature (in which case the client can simply
5574
+ * clear out the subscription locally without telling the server).
5575
+ *
5576
+ * NOTE: There is a proposal to eliminate this special case, here:
5577
+ * https://github.com/solana-labs/solana/issues/18892
5578
+ */
5398
5579
 
5399
5580
  /** @internal */
5400
5581
 
@@ -5416,6 +5597,7 @@ class Connection {
5416
5597
  this._rpcWebSocketConnected = false;
5417
5598
  this._rpcWebSocketHeartbeat = null;
5418
5599
  this._rpcWebSocketIdleTimeout = null;
5600
+ this._rpcWebSocketGeneration = 0;
5419
5601
  this._disableBlockhashCaching = false;
5420
5602
  this._pollingBlockhash = false;
5421
5603
  this._blockhashInfo = {
@@ -5424,20 +5606,11 @@ class Connection {
5424
5606
  transactionSignatures: [],
5425
5607
  simulatedSignatures: []
5426
5608
  };
5427
- this._accountChangeSubscriptionCounter = 0;
5428
- this._accountChangeSubscriptions = {};
5429
- this._programAccountChangeSubscriptionCounter = 0;
5430
- this._programAccountChangeSubscriptions = {};
5431
- this._rootSubscriptionCounter = 0;
5432
- this._rootSubscriptions = {};
5433
- this._signatureSubscriptionCounter = 0;
5434
- this._signatureSubscriptions = {};
5435
- this._slotSubscriptionCounter = 0;
5436
- this._slotSubscriptions = {};
5437
- this._logsSubscriptionCounter = 0;
5438
- this._logsSubscriptions = {};
5439
- this._slotUpdateSubscriptionCounter = 0;
5440
- this._slotUpdateSubscriptions = {};
5609
+ this._nextClientSubscriptionId = 0;
5610
+ this._subscriptionDisposeFunctionsByClientSubscriptionId = {};
5611
+ this._subscriptionCallbacksByServerSubscriptionId = {};
5612
+ this._subscriptionsByHash = {};
5613
+ this._subscriptionsAutoDisposedByRpc = new Set();
5441
5614
  let url = new URL(endpoint);
5442
5615
  const useHttps = url.protocol === 'https:';
5443
5616
  let wsEndpoint;
@@ -7205,6 +7378,8 @@ class Connection {
7205
7378
 
7206
7379
 
7207
7380
  _wsOnClose(code) {
7381
+ this._rpcWebSocketGeneration++;
7382
+
7208
7383
  if (this._rpcWebSocketHeartbeat) {
7209
7384
  clearInterval(this._rpcWebSocketHeartbeat);
7210
7385
  this._rpcWebSocketHeartbeat = null;
@@ -7218,85 +7393,20 @@ class Connection {
7218
7393
  } // implicit close, prepare subscriptions for auto-reconnect
7219
7394
 
7220
7395
 
7221
- this._resetSubscriptions();
7222
- }
7223
- /**
7224
- * @internal
7225
- */
7226
-
7227
-
7228
- async _subscribe(sub, rpcMethod, rpcArgs) {
7229
- if (sub.subscriptionId == null) {
7230
- sub.subscriptionId = 'subscribing';
7231
-
7232
- try {
7233
- const id = await this._rpcWebSocket.call(rpcMethod, rpcArgs);
7234
-
7235
- if (typeof id === 'number' && sub.subscriptionId === 'subscribing') {
7236
- // eslint-disable-next-line require-atomic-updates
7237
- sub.subscriptionId = id;
7238
- }
7239
- } catch (err) {
7240
- if (sub.subscriptionId === 'subscribing') {
7241
- // eslint-disable-next-line require-atomic-updates
7242
- sub.subscriptionId = null;
7243
- }
7244
-
7245
- if (err instanceof Error) {
7246
- console.error(`${rpcMethod} error for argument`, rpcArgs, err.message);
7247
- }
7248
- }
7249
- }
7250
- }
7251
- /**
7252
- * @internal
7253
- */
7254
-
7255
-
7256
- async _unsubscribe(sub, rpcMethod) {
7257
- const subscriptionId = sub.subscriptionId;
7258
-
7259
- if (subscriptionId != null && typeof subscriptionId != 'string') {
7260
- const unsubscribeId = subscriptionId;
7261
-
7262
- try {
7263
- await this._rpcWebSocket.call(rpcMethod, [unsubscribeId]);
7264
- } catch (err) {
7265
- if (err instanceof Error) {
7266
- console.error(`${rpcMethod} error:`, err.message);
7267
- }
7268
- }
7269
- }
7270
- }
7271
- /**
7272
- * @internal
7273
- */
7274
-
7275
-
7276
- _resetSubscriptions() {
7277
- Object.values(this._accountChangeSubscriptions).forEach(s => s.subscriptionId = null);
7278
- Object.values(this._logsSubscriptions).forEach(s => s.subscriptionId = null);
7279
- Object.values(this._programAccountChangeSubscriptions).forEach(s => s.subscriptionId = null);
7280
- Object.values(this._rootSubscriptions).forEach(s => s.subscriptionId = null);
7281
- Object.values(this._signatureSubscriptions).forEach(s => s.subscriptionId = null);
7282
- Object.values(this._slotSubscriptions).forEach(s => s.subscriptionId = null);
7283
- Object.values(this._slotUpdateSubscriptions).forEach(s => s.subscriptionId = null);
7396
+ this._subscriptionCallbacksByServerSubscriptionId = {};
7397
+ Object.entries(this._subscriptionsByHash).forEach(([hash, subscription]) => {
7398
+ this._subscriptionsByHash[hash] = { ...subscription,
7399
+ state: 'pending'
7400
+ };
7401
+ });
7284
7402
  }
7285
7403
  /**
7286
7404
  * @internal
7287
7405
  */
7288
7406
 
7289
7407
 
7290
- _updateSubscriptions() {
7291
- const accountKeys = Object.keys(this._accountChangeSubscriptions).map(Number);
7292
- const programKeys = Object.keys(this._programAccountChangeSubscriptions).map(Number);
7293
- const slotKeys = Object.keys(this._slotSubscriptions).map(Number);
7294
- const slotUpdateKeys = Object.keys(this._slotUpdateSubscriptions).map(Number);
7295
- const signatureKeys = Object.keys(this._signatureSubscriptions).map(Number);
7296
- const rootKeys = Object.keys(this._rootSubscriptions).map(Number);
7297
- const logsKeys = Object.keys(this._logsSubscriptions).map(Number);
7298
-
7299
- if (accountKeys.length === 0 && programKeys.length === 0 && slotKeys.length === 0 && slotUpdateKeys.length === 0 && signatureKeys.length === 0 && rootKeys.length === 0 && logsKeys.length === 0) {
7408
+ async _updateSubscriptions() {
7409
+ if (Object.keys(this._subscriptionsByHash).length === 0) {
7300
7410
  if (this._rpcWebSocketConnected) {
7301
7411
  this._rpcWebSocketConnected = false;
7302
7412
  this._rpcWebSocketIdleTimeout = setTimeout(() => {
@@ -7328,60 +7438,167 @@ class Connection {
7328
7438
  return;
7329
7439
  }
7330
7440
 
7331
- for (let id of accountKeys) {
7332
- const sub = this._accountChangeSubscriptions[id];
7441
+ const activeWebSocketGeneration = this._rpcWebSocketGeneration;
7333
7442
 
7334
- this._subscribe(sub, 'accountSubscribe', this._buildArgs([sub.publicKey], sub.commitment, 'base64'));
7335
- }
7443
+ const isCurrentConnectionStillActive = () => {
7444
+ return activeWebSocketGeneration === this._rpcWebSocketGeneration;
7445
+ };
7336
7446
 
7337
- for (let id of programKeys) {
7338
- const sub = this._programAccountChangeSubscriptions[id];
7447
+ await Promise.all( // Don't be tempted to change this to `Object.entries`. We call
7448
+ // `_updateSubscriptions` recursively when processing the state,
7449
+ // so it's important that we look up the *current* version of
7450
+ // each subscription, every time we process a hash.
7451
+ Object.keys(this._subscriptionsByHash).map(async hash => {
7452
+ const subscription = this._subscriptionsByHash[hash];
7339
7453
 
7340
- this._subscribe(sub, 'programSubscribe', this._buildArgs([sub.programId], sub.commitment, 'base64', {
7341
- filters: sub.filters
7342
- }));
7343
- }
7344
-
7345
- for (let id of slotKeys) {
7346
- const sub = this._slotSubscriptions[id];
7454
+ if (subscription === undefined) {
7455
+ // This entry has since been deleted. Skip.
7456
+ return;
7457
+ }
7347
7458
 
7348
- this._subscribe(sub, 'slotSubscribe', []);
7349
- }
7459
+ switch (subscription.state) {
7460
+ case 'pending':
7461
+ case 'unsubscribed':
7462
+ if (subscription.callbacks.size === 0) {
7463
+ /**
7464
+ * You can end up here when:
7465
+ *
7466
+ * - a subscription has recently unsubscribed
7467
+ * without having new callbacks added to it
7468
+ * while the unsubscribe was in flight, or
7469
+ * - when a pending subscription has its
7470
+ * listeners removed before a request was
7471
+ * sent to the server.
7472
+ *
7473
+ * Being that nobody is interested in this
7474
+ * subscription any longer, delete it.
7475
+ */
7476
+ delete this._subscriptionsByHash[hash];
7477
+
7478
+ if (subscription.state === 'unsubscribed') {
7479
+ delete this._subscriptionCallbacksByServerSubscriptionId[subscription.serverSubscriptionId];
7480
+ }
7350
7481
 
7351
- for (let id of slotUpdateKeys) {
7352
- const sub = this._slotUpdateSubscriptions[id];
7482
+ await this._updateSubscriptions();
7483
+ return;
7484
+ }
7353
7485
 
7354
- this._subscribe(sub, 'slotsUpdatesSubscribe', []);
7355
- }
7486
+ await (async () => {
7487
+ const {
7488
+ args,
7489
+ method
7490
+ } = subscription;
7356
7491
 
7357
- for (let id of signatureKeys) {
7358
- const sub = this._signatureSubscriptions[id];
7359
- const args = [sub.signature];
7360
- if (sub.options) args.push(sub.options);
7492
+ try {
7493
+ this._subscriptionsByHash[hash] = { ...subscription,
7494
+ state: 'subscribing'
7495
+ };
7496
+ const serverSubscriptionId = await this._rpcWebSocket.call(method, args);
7497
+ this._subscriptionsByHash[hash] = { ...subscription,
7498
+ serverSubscriptionId,
7499
+ state: 'subscribed'
7500
+ };
7501
+ this._subscriptionCallbacksByServerSubscriptionId[serverSubscriptionId] = subscription.callbacks;
7502
+ await this._updateSubscriptions();
7503
+ } catch (e) {
7504
+ if (e instanceof Error) {
7505
+ console.error(`${method} error for argument`, args, e.message);
7506
+ }
7507
+
7508
+ if (!isCurrentConnectionStillActive()) {
7509
+ return;
7510
+ } // TODO: Maybe add an 'errored' state or a retry limit?
7361
7511
 
7362
- this._subscribe(sub, 'signatureSubscribe', args);
7363
- }
7364
7512
 
7365
- for (let id of rootKeys) {
7366
- const sub = this._rootSubscriptions[id];
7513
+ this._subscriptionsByHash[hash] = { ...subscription,
7514
+ state: 'pending'
7515
+ };
7516
+ await this._updateSubscriptions();
7517
+ }
7518
+ })();
7519
+ break;
7367
7520
 
7368
- this._subscribe(sub, 'rootSubscribe', []);
7369
- }
7521
+ case 'subscribed':
7522
+ if (subscription.callbacks.size === 0) {
7523
+ // By the time we successfully set up a subscription
7524
+ // with the server, the client stopped caring about it.
7525
+ // Tear it down now.
7526
+ await (async () => {
7527
+ const {
7528
+ serverSubscriptionId,
7529
+ unsubscribeMethod
7530
+ } = subscription;
7531
+
7532
+ if (this._subscriptionsAutoDisposedByRpc.has(serverSubscriptionId)) {
7533
+ /**
7534
+ * Special case.
7535
+ * If we're dealing with a subscription that has been auto-
7536
+ * disposed by the RPC, then we can skip the RPC call to
7537
+ * tear down the subscription here.
7538
+ *
7539
+ * NOTE: There is a proposal to eliminate this special case, here:
7540
+ * https://github.com/solana-labs/solana/issues/18892
7541
+ */
7542
+ this._subscriptionsAutoDisposedByRpc.delete(serverSubscriptionId);
7543
+ } else {
7544
+ this._subscriptionsByHash[hash] = { ...subscription,
7545
+ state: 'unsubscribing'
7546
+ };
7547
+
7548
+ try {
7549
+ await this._rpcWebSocket.call(unsubscribeMethod, [serverSubscriptionId]);
7550
+ } catch (e) {
7551
+ if (e instanceof Error) {
7552
+ console.error(`${unsubscribeMethod} error:`, e.message);
7553
+ }
7554
+
7555
+ if (!isCurrentConnectionStillActive()) {
7556
+ return;
7557
+ } // TODO: Maybe add an 'errored' state or a retry limit?
7558
+
7559
+
7560
+ this._subscriptionsByHash[hash] = { ...subscription,
7561
+ state: 'subscribed'
7562
+ };
7563
+ await this._updateSubscriptions();
7564
+ return;
7565
+ }
7566
+ }
7370
7567
 
7371
- for (let id of logsKeys) {
7372
- const sub = this._logsSubscriptions[id];
7373
- let filter;
7568
+ this._subscriptionsByHash[hash] = { ...subscription,
7569
+ state: 'unsubscribed'
7570
+ };
7571
+ await this._updateSubscriptions();
7572
+ })();
7573
+ }
7374
7574
 
7375
- if (typeof sub.filter === 'object') {
7376
- filter = {
7377
- mentions: [sub.filter.toString()]
7378
- };
7379
- } else {
7380
- filter = sub.filter;
7575
+ break;
7381
7576
  }
7577
+ }));
7578
+ }
7579
+ /**
7580
+ * @internal
7581
+ */
7582
+
7382
7583
 
7383
- this._subscribe(sub, 'logsSubscribe', this._buildArgs([filter], sub.commitment));
7584
+ _handleServerNotification(serverSubscriptionId, callbackArgs) {
7585
+ const callbacks = this._subscriptionCallbacksByServerSubscriptionId[serverSubscriptionId];
7586
+
7587
+ if (callbacks === undefined) {
7588
+ return;
7384
7589
  }
7590
+
7591
+ callbacks.forEach(cb => {
7592
+ try {
7593
+ cb( // I failed to find a way to convince TypeScript that `cb` is of type
7594
+ // `TCallback` which is certainly compatible with `Parameters<TCallback>`.
7595
+ // See https://github.com/microsoft/TypeScript/issues/47615
7596
+ // @ts-ignore
7597
+ ...callbackArgs);
7598
+ } catch (e) {
7599
+ console.error(e);
7600
+ }
7601
+ });
7385
7602
  }
7386
7603
  /**
7387
7604
  * @internal
@@ -7389,14 +7606,71 @@ class Connection {
7389
7606
 
7390
7607
 
7391
7608
  _wsOnAccountNotification(notification) {
7392
- const res = superstruct.create(notification, AccountNotificationResult);
7609
+ const {
7610
+ result,
7611
+ subscription
7612
+ } = superstruct.create(notification, AccountNotificationResult);
7393
7613
 
7394
- for (const sub of Object.values(this._accountChangeSubscriptions)) {
7395
- if (sub.subscriptionId === res.subscription) {
7396
- sub.callback(res.result.value, res.result.context);
7397
- return;
7398
- }
7614
+ this._handleServerNotification(subscription, [result.value, result.context]);
7615
+ }
7616
+ /**
7617
+ * @internal
7618
+ */
7619
+
7620
+
7621
+ _makeSubscription(subscriptionConfig,
7622
+ /**
7623
+ * When preparing `args` for a call to `_makeSubscription`, be sure
7624
+ * to carefully apply a default `commitment` property, if necessary.
7625
+ *
7626
+ * - If the user supplied a `commitment` use that.
7627
+ * - Otherwise, if the `Connection::commitment` is set, use that.
7628
+ * - Otherwise, set it to the RPC server default: `finalized`.
7629
+ *
7630
+ * This is extremely important to ensure that these two fundamentally
7631
+ * identical subscriptions produce the same identifying hash:
7632
+ *
7633
+ * - A subscription made without specifying a commitment.
7634
+ * - A subscription made where the commitment specified is the same
7635
+ * as the default applied to the subscription above.
7636
+ *
7637
+ * Example; these two subscriptions must produce the same hash:
7638
+ *
7639
+ * - An `accountSubscribe` subscription for `'PUBKEY'`
7640
+ * - An `accountSubscribe` subscription for `'PUBKEY'` with commitment
7641
+ * `'finalized'`.
7642
+ *
7643
+ * See the 'making a subscription with defaulted params omitted' test
7644
+ * in `connection-subscriptions.ts` for more.
7645
+ */
7646
+ args) {
7647
+ const clientSubscriptionId = this._nextClientSubscriptionId++;
7648
+ const hash = fastStableStringify$1([subscriptionConfig.method, args], true
7649
+ /* isArrayProp */
7650
+ );
7651
+ const existingSubscription = this._subscriptionsByHash[hash];
7652
+
7653
+ if (existingSubscription === undefined) {
7654
+ this._subscriptionsByHash[hash] = { ...subscriptionConfig,
7655
+ args,
7656
+ callbacks: new Set([subscriptionConfig.callback]),
7657
+ state: 'pending'
7658
+ };
7659
+ } else {
7660
+ existingSubscription.callbacks.add(subscriptionConfig.callback);
7399
7661
  }
7662
+
7663
+ this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId] = async () => {
7664
+ delete this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId];
7665
+ const subscription = this._subscriptionsByHash[hash];
7666
+ assert(subscription !== undefined, `Could not find a \`Subscription\` when tearing down client subscription #${clientSubscriptionId}`);
7667
+ subscription.callbacks.delete(subscriptionConfig.callback);
7668
+ await this._updateSubscriptions();
7669
+ };
7670
+
7671
+ this._updateSubscriptions();
7672
+
7673
+ return clientSubscriptionId;
7400
7674
  }
7401
7675
  /**
7402
7676
  * Register a callback to be invoked whenever the specified account changes
@@ -7409,35 +7683,24 @@ class Connection {
7409
7683
 
7410
7684
 
7411
7685
  onAccountChange(publicKey, callback, commitment) {
7412
- const id = ++this._accountChangeSubscriptionCounter;
7413
- this._accountChangeSubscriptions[id] = {
7414
- publicKey: publicKey.toBase58(),
7415
- callback,
7416
- commitment,
7417
- subscriptionId: null
7418
- };
7419
-
7420
- this._updateSubscriptions();
7686
+ const args = this._buildArgs([publicKey.toBase58()], commitment || this._commitment || 'finalized', // Apply connection/server default.
7687
+ 'base64');
7421
7688
 
7422
- return id;
7689
+ return this._makeSubscription({
7690
+ callback,
7691
+ method: 'accountSubscribe',
7692
+ unsubscribeMethod: 'accountUnsubscribe'
7693
+ }, args);
7423
7694
  }
7424
7695
  /**
7425
7696
  * Deregister an account notification callback
7426
7697
  *
7427
- * @param id subscription id to deregister
7698
+ * @param id client subscription id to deregister
7428
7699
  */
7429
7700
 
7430
7701
 
7431
- async removeAccountChangeListener(id) {
7432
- if (this._accountChangeSubscriptions[id]) {
7433
- const subInfo = this._accountChangeSubscriptions[id];
7434
- delete this._accountChangeSubscriptions[id];
7435
- await this._unsubscribe(subInfo, 'accountUnsubscribe');
7436
-
7437
- this._updateSubscriptions();
7438
- } else {
7439
- console.warn(createSubscriptionWarningMessage(id, 'account change'));
7440
- }
7702
+ async removeAccountChangeListener(clientSubscriptionId) {
7703
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'account change');
7441
7704
  }
7442
7705
  /**
7443
7706
  * @internal
@@ -7445,21 +7708,15 @@ class Connection {
7445
7708
 
7446
7709
 
7447
7710
  _wsOnProgramAccountNotification(notification) {
7448
- const res = superstruct.create(notification, ProgramAccountNotificationResult);
7711
+ const {
7712
+ result,
7713
+ subscription
7714
+ } = superstruct.create(notification, ProgramAccountNotificationResult);
7449
7715
 
7450
- for (const sub of Object.values(this._programAccountChangeSubscriptions)) {
7451
- if (sub.subscriptionId === res.subscription) {
7452
- const {
7453
- value,
7454
- context
7455
- } = res.result;
7456
- sub.callback({
7457
- accountId: value.pubkey,
7458
- accountInfo: value.account
7459
- }, context);
7460
- return;
7461
- }
7462
- }
7716
+ this._handleServerNotification(subscription, [{
7717
+ accountId: result.value.pubkey,
7718
+ accountInfo: result.value.account
7719
+ }, result.context]);
7463
7720
  }
7464
7721
  /**
7465
7722
  * Register a callback to be invoked whenever accounts owned by the
@@ -7474,36 +7731,30 @@ class Connection {
7474
7731
 
7475
7732
 
7476
7733
  onProgramAccountChange(programId, callback, commitment, filters) {
7477
- const id = ++this._programAccountChangeSubscriptionCounter;
7478
- this._programAccountChangeSubscriptions[id] = {
7479
- programId: programId.toBase58(),
7734
+ const args = this._buildArgs([programId.toBase58()], commitment || this._commitment || 'finalized', // Apply connection/server default.
7735
+ 'base64'
7736
+ /* encoding */
7737
+ , filters ? {
7738
+ filters: filters
7739
+ } : undefined
7740
+ /* extra */
7741
+ );
7742
+
7743
+ return this._makeSubscription({
7480
7744
  callback,
7481
- commitment,
7482
- subscriptionId: null,
7483
- filters
7484
- };
7485
-
7486
- this._updateSubscriptions();
7487
-
7488
- return id;
7745
+ method: 'programSubscribe',
7746
+ unsubscribeMethod: 'programUnsubscribe'
7747
+ }, args);
7489
7748
  }
7490
7749
  /**
7491
7750
  * Deregister an account notification callback
7492
7751
  *
7493
- * @param id subscription id to deregister
7752
+ * @param id client subscription id to deregister
7494
7753
  */
7495
7754
 
7496
7755
 
7497
- async removeProgramAccountChangeListener(id) {
7498
- if (this._programAccountChangeSubscriptions[id]) {
7499
- const subInfo = this._programAccountChangeSubscriptions[id];
7500
- delete this._programAccountChangeSubscriptions[id];
7501
- await this._unsubscribe(subInfo, 'programUnsubscribe');
7502
-
7503
- this._updateSubscriptions();
7504
- } else {
7505
- console.warn(createSubscriptionWarningMessage(id, 'program account change'));
7506
- }
7756
+ async removeProgramAccountChangeListener(clientSubscriptionId) {
7757
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'program account change');
7507
7758
  }
7508
7759
  /**
7509
7760
  * Registers a callback to be invoked whenever logs are emitted.
@@ -7511,35 +7762,26 @@ class Connection {
7511
7762
 
7512
7763
 
7513
7764
  onLogs(filter, callback, commitment) {
7514
- const id = ++this._logsSubscriptionCounter;
7515
- this._logsSubscriptions[id] = {
7516
- filter,
7517
- callback,
7518
- commitment,
7519
- subscriptionId: null
7520
- };
7521
-
7522
- this._updateSubscriptions();
7765
+ const args = this._buildArgs([typeof filter === 'object' ? {
7766
+ mentions: [filter.toString()]
7767
+ } : filter], commitment || this._commitment || 'finalized' // Apply connection/server default.
7768
+ );
7523
7769
 
7524
- return id;
7770
+ return this._makeSubscription({
7771
+ callback,
7772
+ method: 'logsSubscribe',
7773
+ unsubscribeMethod: 'logsUnsubscribe'
7774
+ }, args);
7525
7775
  }
7526
7776
  /**
7527
7777
  * Deregister a logs callback.
7528
7778
  *
7529
- * @param id subscription id to deregister.
7779
+ * @param id client subscription id to deregister.
7530
7780
  */
7531
7781
 
7532
7782
 
7533
- async removeOnLogsListener(id) {
7534
- if (this._logsSubscriptions[id]) {
7535
- const subInfo = this._logsSubscriptions[id];
7536
- delete this._logsSubscriptions[id];
7537
- await this._unsubscribe(subInfo, 'logsUnsubscribe');
7538
-
7539
- this._updateSubscriptions();
7540
- } else {
7541
- console.warn(createSubscriptionWarningMessage(id, 'logs'));
7542
- }
7783
+ async removeOnLogsListener(clientSubscriptionId) {
7784
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'logs');
7543
7785
  }
7544
7786
  /**
7545
7787
  * @internal
@@ -7547,17 +7789,12 @@ class Connection {
7547
7789
 
7548
7790
 
7549
7791
  _wsOnLogsNotification(notification) {
7550
- const res = superstruct.create(notification, LogsNotificationResult);
7551
- const keys = Object.keys(this._logsSubscriptions).map(Number);
7552
-
7553
- for (let id of keys) {
7554
- const sub = this._logsSubscriptions[id];
7792
+ const {
7793
+ result,
7794
+ subscription
7795
+ } = superstruct.create(notification, LogsNotificationResult);
7555
7796
 
7556
- if (sub.subscriptionId === res.subscription) {
7557
- sub.callback(res.result.value, res.result.context);
7558
- return;
7559
- }
7560
- }
7797
+ this._handleServerNotification(subscription, [result.value, result.context]);
7561
7798
  }
7562
7799
  /**
7563
7800
  * @internal
@@ -7565,14 +7802,12 @@ class Connection {
7565
7802
 
7566
7803
 
7567
7804
  _wsOnSlotNotification(notification) {
7568
- const res = superstruct.create(notification, SlotNotificationResult);
7805
+ const {
7806
+ result,
7807
+ subscription
7808
+ } = superstruct.create(notification, SlotNotificationResult);
7569
7809
 
7570
- for (const sub of Object.values(this._slotSubscriptions)) {
7571
- if (sub.subscriptionId === res.subscription) {
7572
- sub.callback(res.result);
7573
- return;
7574
- }
7575
- }
7810
+ this._handleServerNotification(subscription, [result]);
7576
7811
  }
7577
7812
  /**
7578
7813
  * Register a callback to be invoked upon slot changes
@@ -7583,33 +7818,23 @@ class Connection {
7583
7818
 
7584
7819
 
7585
7820
  onSlotChange(callback) {
7586
- const id = ++this._slotSubscriptionCounter;
7587
- this._slotSubscriptions[id] = {
7821
+ return this._makeSubscription({
7588
7822
  callback,
7589
- subscriptionId: null
7590
- };
7591
-
7592
- this._updateSubscriptions();
7593
-
7594
- return id;
7823
+ method: 'slotSubscribe',
7824
+ unsubscribeMethod: 'slotUnsubscribe'
7825
+ }, []
7826
+ /* args */
7827
+ );
7595
7828
  }
7596
7829
  /**
7597
7830
  * Deregister a slot notification callback
7598
7831
  *
7599
- * @param id subscription id to deregister
7832
+ * @param id client subscription id to deregister
7600
7833
  */
7601
7834
 
7602
7835
 
7603
- async removeSlotChangeListener(id) {
7604
- if (this._slotSubscriptions[id]) {
7605
- const subInfo = this._slotSubscriptions[id];
7606
- delete this._slotSubscriptions[id];
7607
- await this._unsubscribe(subInfo, 'slotUnsubscribe');
7608
-
7609
- this._updateSubscriptions();
7610
- } else {
7611
- console.warn(createSubscriptionWarningMessage(id, 'slot change'));
7612
- }
7836
+ async removeSlotChangeListener(clientSubscriptionId) {
7837
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'slot change');
7613
7838
  }
7614
7839
  /**
7615
7840
  * @internal
@@ -7617,14 +7842,12 @@ class Connection {
7617
7842
 
7618
7843
 
7619
7844
  _wsOnSlotUpdatesNotification(notification) {
7620
- const res = superstruct.create(notification, SlotUpdateNotificationResult);
7845
+ const {
7846
+ result,
7847
+ subscription
7848
+ } = superstruct.create(notification, SlotUpdateNotificationResult);
7621
7849
 
7622
- for (const sub of Object.values(this._slotUpdateSubscriptions)) {
7623
- if (sub.subscriptionId === res.subscription) {
7624
- sub.callback(res.result);
7625
- return;
7626
- }
7627
- }
7850
+ this._handleServerNotification(subscription, [result]);
7628
7851
  }
7629
7852
  /**
7630
7853
  * Register a callback to be invoked upon slot updates. {@link SlotUpdate}'s
@@ -7636,32 +7859,36 @@ class Connection {
7636
7859
 
7637
7860
 
7638
7861
  onSlotUpdate(callback) {
7639
- const id = ++this._slotUpdateSubscriptionCounter;
7640
- this._slotUpdateSubscriptions[id] = {
7862
+ return this._makeSubscription({
7641
7863
  callback,
7642
- subscriptionId: null
7643
- };
7644
-
7645
- this._updateSubscriptions();
7646
-
7647
- return id;
7864
+ method: 'slotsUpdatesSubscribe',
7865
+ unsubscribeMethod: 'slotsUpdatesUnsubscribe'
7866
+ }, []
7867
+ /* args */
7868
+ );
7648
7869
  }
7649
7870
  /**
7650
7871
  * Deregister a slot update notification callback
7651
7872
  *
7652
- * @param id subscription id to deregister
7873
+ * @param id client subscription id to deregister
7653
7874
  */
7654
7875
 
7655
7876
 
7656
- async removeSlotUpdateListener(id) {
7657
- if (this._slotUpdateSubscriptions[id]) {
7658
- const subInfo = this._slotUpdateSubscriptions[id];
7659
- delete this._slotUpdateSubscriptions[id];
7660
- await this._unsubscribe(subInfo, 'slotsUpdatesUnsubscribe');
7877
+ async removeSlotUpdateListener(clientSubscriptionId) {
7878
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'slot update');
7879
+ }
7880
+ /**
7881
+ * @internal
7882
+ */
7661
7883
 
7662
- this._updateSubscriptions();
7884
+
7885
+ async _unsubscribeClientSubscription(clientSubscriptionId, subscriptionName) {
7886
+ const dispose = this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId];
7887
+
7888
+ if (dispose) {
7889
+ await dispose();
7663
7890
  } else {
7664
- console.warn(createSubscriptionWarningMessage(id, 'slot update'));
7891
+ console.warn('Ignored unsubscribe request because an active subscription with id ' + `\`${clientSubscriptionId}\` for '${subscriptionName}' events ` + 'could not be found.');
7665
7892
  }
7666
7893
  }
7667
7894
 
@@ -7708,30 +7935,34 @@ class Connection {
7708
7935
 
7709
7936
 
7710
7937
  _wsOnSignatureNotification(notification) {
7711
- const res = superstruct.create(notification, SignatureNotificationResult);
7712
-
7713
- for (const [id, sub] of Object.entries(this._signatureSubscriptions)) {
7714
- if (sub.subscriptionId === res.subscription) {
7715
- if (res.result.value === 'receivedSignature') {
7716
- sub.callback({
7717
- type: 'received'
7718
- }, res.result.context);
7719
- } else {
7720
- // Signatures subscriptions are auto-removed by the RPC service so
7721
- // no need to explicitly send an unsubscribe message
7722
- delete this._signatureSubscriptions[Number(id)];
7723
-
7724
- this._updateSubscriptions();
7725
-
7726
- sub.callback({
7727
- type: 'status',
7728
- result: res.result.value
7729
- }, res.result.context);
7730
- }
7731
-
7732
- return;
7733
- }
7734
- }
7938
+ const {
7939
+ result,
7940
+ subscription
7941
+ } = superstruct.create(notification, SignatureNotificationResult);
7942
+
7943
+ if (result.value !== 'receivedSignature') {
7944
+ /**
7945
+ * Special case.
7946
+ * After a signature is processed, RPCs automatically dispose of the
7947
+ * subscription on the server side. We need to track which of these
7948
+ * subscriptions have been disposed in such a way, so that we know
7949
+ * whether the client is dealing with a not-yet-processed signature
7950
+ * (in which case we must tear down the server subscription) or an
7951
+ * already-processed signature (in which case the client can simply
7952
+ * clear out the subscription locally without telling the server).
7953
+ *
7954
+ * NOTE: There is a proposal to eliminate this special case, here:
7955
+ * https://github.com/solana-labs/solana/issues/18892
7956
+ */
7957
+ this._subscriptionsAutoDisposedByRpc.add(subscription);
7958
+ }
7959
+
7960
+ this._handleServerNotification(subscription, result.value === 'receivedSignature' ? [{
7961
+ type: 'received'
7962
+ }, result.context] : [{
7963
+ type: 'status',
7964
+ result: result.value
7965
+ }, result.context]);
7735
7966
  }
7736
7967
  /**
7737
7968
  * Register a callback to be invoked upon signature updates
@@ -7744,23 +7975,26 @@ class Connection {
7744
7975
 
7745
7976
 
7746
7977
  onSignature(signature, callback, commitment) {
7747
- const id = ++this._signatureSubscriptionCounter;
7748
- this._signatureSubscriptions[id] = {
7749
- signature,
7978
+ const args = this._buildArgs([signature], commitment || this._commitment || 'finalized' // Apply connection/server default.
7979
+ );
7980
+
7981
+ const clientSubscriptionId = this._makeSubscription({
7750
7982
  callback: (notification, context) => {
7751
7983
  if (notification.type === 'status') {
7752
- callback(notification.result, context);
7984
+ callback(notification.result, context); // Signatures subscriptions are auto-removed by the RPC service
7985
+ // so no need to explicitly send an unsubscribe message.
7986
+
7987
+ try {
7988
+ this.removeSignatureListener(clientSubscriptionId); // eslint-disable-next-line no-empty
7989
+ } catch {// Already removed.
7990
+ }
7753
7991
  }
7754
7992
  },
7755
- options: {
7756
- commitment
7757
- },
7758
- subscriptionId: null
7759
- };
7760
-
7761
- this._updateSubscriptions();
7993
+ method: 'signatureSubscribe',
7994
+ unsubscribeMethod: 'signatureUnsubscribe'
7995
+ }, args);
7762
7996
 
7763
- return id;
7997
+ return clientSubscriptionId;
7764
7998
  }
7765
7999
  /**
7766
8000
  * Register a callback to be invoked when a transaction is
@@ -7775,35 +8009,43 @@ class Connection {
7775
8009
 
7776
8010
 
7777
8011
  onSignatureWithOptions(signature, callback, options) {
7778
- const id = ++this._signatureSubscriptionCounter;
7779
- this._signatureSubscriptions[id] = {
7780
- signature,
7781
- callback,
7782
- options,
7783
- subscriptionId: null
8012
+ const {
8013
+ commitment,
8014
+ ...extra
8015
+ } = { ...options,
8016
+ commitment: options && options.commitment || this._commitment || 'finalized' // Apply connection/server default.
8017
+
7784
8018
  };
7785
8019
 
7786
- this._updateSubscriptions();
8020
+ const args = this._buildArgs([signature], commitment, undefined
8021
+ /* encoding */
8022
+ , extra);
8023
+
8024
+ const clientSubscriptionId = this._makeSubscription({
8025
+ callback: (notification, context) => {
8026
+ callback(notification, context); // Signatures subscriptions are auto-removed by the RPC service
8027
+ // so no need to explicitly send an unsubscribe message.
8028
+
8029
+ try {
8030
+ this.removeSignatureListener(clientSubscriptionId); // eslint-disable-next-line no-empty
8031
+ } catch {// Already removed.
8032
+ }
8033
+ },
8034
+ method: 'signatureSubscribe',
8035
+ unsubscribeMethod: 'signatureUnsubscribe'
8036
+ }, args);
7787
8037
 
7788
- return id;
8038
+ return clientSubscriptionId;
7789
8039
  }
7790
8040
  /**
7791
8041
  * Deregister a signature notification callback
7792
8042
  *
7793
- * @param id subscription id to deregister
8043
+ * @param id client subscription id to deregister
7794
8044
  */
7795
8045
 
7796
8046
 
7797
- async removeSignatureListener(id) {
7798
- if (this._signatureSubscriptions[id]) {
7799
- const subInfo = this._signatureSubscriptions[id];
7800
- delete this._signatureSubscriptions[id];
7801
- await this._unsubscribe(subInfo, 'signatureUnsubscribe');
7802
-
7803
- this._updateSubscriptions();
7804
- } else {
7805
- console.warn(createSubscriptionWarningMessage(id, 'signature result'));
7806
- }
8047
+ async removeSignatureListener(clientSubscriptionId) {
8048
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'signature result');
7807
8049
  }
7808
8050
  /**
7809
8051
  * @internal
@@ -7811,14 +8053,12 @@ class Connection {
7811
8053
 
7812
8054
 
7813
8055
  _wsOnRootNotification(notification) {
7814
- const res = superstruct.create(notification, RootNotificationResult);
8056
+ const {
8057
+ result,
8058
+ subscription
8059
+ } = superstruct.create(notification, RootNotificationResult);
7815
8060
 
7816
- for (const sub of Object.values(this._rootSubscriptions)) {
7817
- if (sub.subscriptionId === res.subscription) {
7818
- sub.callback(res.result);
7819
- return;
7820
- }
7821
- }
8061
+ this._handleServerNotification(subscription, [result]);
7822
8062
  }
7823
8063
  /**
7824
8064
  * Register a callback to be invoked upon root changes
@@ -7829,33 +8069,23 @@ class Connection {
7829
8069
 
7830
8070
 
7831
8071
  onRootChange(callback) {
7832
- const id = ++this._rootSubscriptionCounter;
7833
- this._rootSubscriptions[id] = {
8072
+ return this._makeSubscription({
7834
8073
  callback,
7835
- subscriptionId: null
7836
- };
7837
-
7838
- this._updateSubscriptions();
7839
-
7840
- return id;
8074
+ method: 'rootSubscribe',
8075
+ unsubscribeMethod: 'rootUnsubscribe'
8076
+ }, []
8077
+ /* args */
8078
+ );
7841
8079
  }
7842
8080
  /**
7843
8081
  * Deregister a root notification callback
7844
8082
  *
7845
- * @param id subscription id to deregister
8083
+ * @param id client subscription id to deregister
7846
8084
  */
7847
8085
 
7848
8086
 
7849
- async removeRootChangeListener(id) {
7850
- if (this._rootSubscriptions[id]) {
7851
- const subInfo = this._rootSubscriptions[id];
7852
- delete this._rootSubscriptions[id];
7853
- await this._unsubscribe(subInfo, 'rootUnsubscribe');
7854
-
7855
- this._updateSubscriptions();
7856
- } else {
7857
- console.warn(createSubscriptionWarningMessage(id, 'root change'));
7858
- }
8087
+ async removeRootChangeListener(clientSubscriptionId) {
8088
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'root change');
7859
8089
  }
7860
8090
 
7861
8091
  }
@@ -9557,7 +9787,9 @@ exports.MAX_SEED_LENGTH = MAX_SEED_LENGTH;
9557
9787
  exports.Message = Message;
9558
9788
  exports.NONCE_ACCOUNT_LENGTH = NONCE_ACCOUNT_LENGTH;
9559
9789
  exports.NonceAccount = NonceAccount;
9790
+ exports.PACKET_DATA_SIZE = PACKET_DATA_SIZE;
9560
9791
  exports.PublicKey = PublicKey;
9792
+ exports.SIGNATURE_LENGTH_IN_BYTES = SIGNATURE_LENGTH_IN_BYTES;
9561
9793
  exports.SOLANA_SCHEMA = SOLANA_SCHEMA;
9562
9794
  exports.STAKE_CONFIG_ID = STAKE_CONFIG_ID;
9563
9795
  exports.STAKE_INSTRUCTION_LAYOUTS = STAKE_INSTRUCTION_LAYOUTS;