@solana/web3.js 1.40.1 → 1.41.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/lib/index.esm.js CHANGED
@@ -2085,6 +2085,16 @@ class Account {
2085
2085
 
2086
2086
  const BPF_LOADER_DEPRECATED_PROGRAM_ID = new PublicKey('BPFLoader1111111111111111111111111111111111');
2087
2087
 
2088
+ /**
2089
+ * Maximum over-the-wire size of a Transaction
2090
+ *
2091
+ * 1280 is IPv6 minimum MTU
2092
+ * 40 bytes is the size of the IPv6 header
2093
+ * 8 bytes is the size of the fragment header
2094
+ */
2095
+ const PACKET_DATA_SIZE = 1280 - 40 - 8;
2096
+ const SIGNATURE_LENGTH_IN_BYTES = 64;
2097
+
2088
2098
  /**
2089
2099
  * Layout for a public key
2090
2100
  */
@@ -2344,20 +2354,8 @@ function assert (condition, message) {
2344
2354
 
2345
2355
  /**
2346
2356
  * Default (empty) signature
2347
- *
2348
- * Signatures are 64 bytes in length
2349
- */
2350
- const DEFAULT_SIGNATURE = Buffer.alloc(64).fill(0);
2351
- /**
2352
- * Maximum over-the-wire size of a Transaction
2353
- *
2354
- * 1280 is IPv6 minimum MTU
2355
- * 40 bytes is the size of the IPv6 header
2356
- * 8 bytes is the size of the fragment header
2357
2357
  */
2358
-
2359
- const PACKET_DATA_SIZE = 1280 - 40 - 8;
2360
- const SIGNATURE_LENGTH = 64;
2358
+ const DEFAULT_SIGNATURE = Buffer.alloc(SIGNATURE_LENGTH_IN_BYTES).fill(0);
2361
2359
  /**
2362
2360
  * Account metadata used to define instructions
2363
2361
  */
@@ -2987,8 +2985,8 @@ class Transaction {
2987
2985
  let signatures = [];
2988
2986
 
2989
2987
  for (let i = 0; i < signatureCount; i++) {
2990
- const signature = byteArray.slice(0, SIGNATURE_LENGTH);
2991
- byteArray = byteArray.slice(SIGNATURE_LENGTH);
2988
+ const signature = byteArray.slice(0, SIGNATURE_LENGTH_IN_BYTES);
2989
+ byteArray = byteArray.slice(SIGNATURE_LENGTH_IN_BYTES);
2992
2990
  signatures.push(bs58.encode(Buffer.from(signature)));
2993
2991
  }
2994
2992
 
@@ -3877,11 +3875,11 @@ class SystemProgram {
3877
3875
  }
3878
3876
  SystemProgram.programId = new PublicKey('11111111111111111111111111111111');
3879
3877
 
3880
- // Keep program chunks under PACKET_DATA_SIZE, leaving enough room for the
3881
3878
  // rest of the Transaction fields
3882
3879
  //
3883
3880
  // TODO: replace 300 with a proper constant for the size of the other
3884
3881
  // Transaction fields
3882
+
3885
3883
  const CHUNK_SIZE = PACKET_DATA_SIZE - 300;
3886
3884
  /**
3887
3885
  * Program loader interface
@@ -4081,6 +4079,212 @@ class BpfLoader {
4081
4079
 
4082
4080
  }
4083
4081
 
4082
+ /**
4083
+ * Compute Budget Instruction class
4084
+ */
4085
+
4086
+ class ComputeBudgetInstruction {
4087
+ /**
4088
+ * @internal
4089
+ */
4090
+ constructor() {}
4091
+ /**
4092
+ * Decode a compute budget instruction and retrieve the instruction type.
4093
+ */
4094
+
4095
+
4096
+ static decodeInstructionType(instruction) {
4097
+ this.checkProgramId(instruction.programId);
4098
+ const instructionTypeLayout = BufferLayout.u8('instruction');
4099
+ const typeIndex = instructionTypeLayout.decode(instruction.data);
4100
+ let type;
4101
+
4102
+ for (const [ixType, layout] of Object.entries(COMPUTE_BUDGET_INSTRUCTION_LAYOUTS)) {
4103
+ if (layout.index == typeIndex) {
4104
+ type = ixType;
4105
+ break;
4106
+ }
4107
+ }
4108
+
4109
+ if (!type) {
4110
+ throw new Error('Instruction type incorrect; not a ComputeBudgetInstruction');
4111
+ }
4112
+
4113
+ return type;
4114
+ }
4115
+ /**
4116
+ * Decode request units compute budget instruction and retrieve the instruction params.
4117
+ */
4118
+
4119
+
4120
+ static decodeRequestUnits(instruction) {
4121
+ this.checkProgramId(instruction.programId);
4122
+ const {
4123
+ units,
4124
+ additionalFee
4125
+ } = decodeData(COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestUnits, instruction.data);
4126
+ return {
4127
+ units,
4128
+ additionalFee
4129
+ };
4130
+ }
4131
+ /**
4132
+ * Decode request heap frame compute budget instruction and retrieve the instruction params.
4133
+ */
4134
+
4135
+
4136
+ static decodeRequestHeapFrame(instruction) {
4137
+ this.checkProgramId(instruction.programId);
4138
+ const {
4139
+ bytes
4140
+ } = decodeData(COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestHeapFrame, instruction.data);
4141
+ return {
4142
+ bytes
4143
+ };
4144
+ }
4145
+ /**
4146
+ * @internal
4147
+ */
4148
+
4149
+
4150
+ static checkProgramId(programId) {
4151
+ if (!programId.equals(ComputeBudgetProgram.programId)) {
4152
+ throw new Error('invalid instruction; programId is not ComputeBudgetProgram');
4153
+ }
4154
+ }
4155
+
4156
+ }
4157
+ /**
4158
+ * An enumeration of valid ComputeBudgetInstructionType's
4159
+ */
4160
+
4161
+ /**
4162
+ * An enumeration of valid ComputeBudget InstructionType's
4163
+ * @internal
4164
+ */
4165
+ const COMPUTE_BUDGET_INSTRUCTION_LAYOUTS = Object.freeze({
4166
+ RequestUnits: {
4167
+ index: 0,
4168
+ layout: BufferLayout.struct([BufferLayout.u8('instruction'), BufferLayout.u32('units'), BufferLayout.u32('additionalFee')])
4169
+ },
4170
+ RequestHeapFrame: {
4171
+ index: 1,
4172
+ layout: BufferLayout.struct([BufferLayout.u8('instruction'), BufferLayout.u32('bytes')])
4173
+ }
4174
+ });
4175
+ /**
4176
+ * Factory class for transaction instructions to interact with the Compute Budget program
4177
+ */
4178
+
4179
+ class ComputeBudgetProgram {
4180
+ /**
4181
+ * @internal
4182
+ */
4183
+ constructor() {}
4184
+ /**
4185
+ * Public key that identifies the Compute Budget program
4186
+ */
4187
+
4188
+
4189
+ static requestUnits(params) {
4190
+ const type = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestUnits;
4191
+ const data = encodeData(type, params);
4192
+ return new TransactionInstruction({
4193
+ keys: [],
4194
+ programId: this.programId,
4195
+ data
4196
+ });
4197
+ }
4198
+
4199
+ static requestHeapFrame(params) {
4200
+ const type = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestHeapFrame;
4201
+ const data = encodeData(type, params);
4202
+ return new TransactionInstruction({
4203
+ keys: [],
4204
+ programId: this.programId,
4205
+ data
4206
+ });
4207
+ }
4208
+
4209
+ }
4210
+ ComputeBudgetProgram.programId = new PublicKey('ComputeBudget111111111111111111111111111111');
4211
+
4212
+ var objToString = Object.prototype.toString;
4213
+ var objKeys = Object.keys || function(obj) {
4214
+ var keys = [];
4215
+ for (var name in obj) {
4216
+ keys.push(name);
4217
+ }
4218
+ return keys;
4219
+ };
4220
+
4221
+ function stringify(val, isArrayProp) {
4222
+ var i, max, str, keys, key, propVal, toStr;
4223
+ if (val === true) {
4224
+ return "true";
4225
+ }
4226
+ if (val === false) {
4227
+ return "false";
4228
+ }
4229
+ switch (typeof val) {
4230
+ case "object":
4231
+ if (val === null) {
4232
+ return null;
4233
+ } else if (val.toJSON && typeof val.toJSON === "function") {
4234
+ return stringify(val.toJSON(), isArrayProp);
4235
+ } else {
4236
+ toStr = objToString.call(val);
4237
+ if (toStr === "[object Array]") {
4238
+ str = '[';
4239
+ max = val.length - 1;
4240
+ for(i = 0; i < max; i++) {
4241
+ str += stringify(val[i], true) + ',';
4242
+ }
4243
+ if (max > -1) {
4244
+ str += stringify(val[i], true);
4245
+ }
4246
+ return str + ']';
4247
+ } else if (toStr === "[object Object]") {
4248
+ // only object is left
4249
+ keys = objKeys(val).sort();
4250
+ max = keys.length;
4251
+ str = "";
4252
+ i = 0;
4253
+ while (i < max) {
4254
+ key = keys[i];
4255
+ propVal = stringify(val[key], false);
4256
+ if (propVal !== undefined) {
4257
+ if (str) {
4258
+ str += ',';
4259
+ }
4260
+ str += JSON.stringify(key) + ':' + propVal;
4261
+ }
4262
+ i++;
4263
+ }
4264
+ return '{' + str + '}';
4265
+ } else {
4266
+ return JSON.stringify(val);
4267
+ }
4268
+ }
4269
+ case "function":
4270
+ case "undefined":
4271
+ return isArrayProp ? null : undefined;
4272
+ case "string":
4273
+ return JSON.stringify(val);
4274
+ default:
4275
+ return isFinite(val) ? val : null;
4276
+ }
4277
+ }
4278
+
4279
+ var fastStableStringify = function(val) {
4280
+ var returnVal = stringify(val, false);
4281
+ if (returnVal !== undefined) {
4282
+ return ''+ returnVal;
4283
+ }
4284
+ };
4285
+
4286
+ var fastStableStringify$1 = fastStableStringify;
4287
+
4084
4288
  const DESTROY_TIMEOUT_MS = 5000;
4085
4289
  class AgentManager {
4086
4290
  static _newAgent(useHttps) {
@@ -4296,6 +4500,12 @@ const BufferFromRawAccountData = coerce(instance(Buffer), RawAccountDataResult,
4296
4500
  */
4297
4501
 
4298
4502
  const BLOCKHASH_CACHE_TIMEOUT_MS = 30 * 1000;
4503
+ /**
4504
+ * HACK.
4505
+ * Copied from rpc-websockets/dist/lib/client.
4506
+ * Otherwise, `yarn build` fails with:
4507
+ * https://gist.github.com/steveluscher/c057eca81d479ef705cdb53162f9971d
4508
+ */
4299
4509
 
4300
4510
  /**
4301
4511
  * @internal
@@ -5170,14 +5380,9 @@ const LogsNotificationResult = type({
5170
5380
  * Filter for log subscriptions.
5171
5381
  */
5172
5382
 
5173
- function createSubscriptionWarningMessage(id, label) {
5174
- return 'Ignored unsubscribe request because an active subscription ' + `with id \`${id}\` for '${label}' events could not be found.`;
5175
- }
5176
5383
  /**
5177
5384
  * A connection to a fullnode JSON RPC endpoint
5178
5385
  */
5179
-
5180
-
5181
5386
  class Connection {
5182
5387
  /** @internal */
5183
5388
 
@@ -5201,21 +5406,13 @@ class Connection {
5201
5406
 
5202
5407
  /** @internal */
5203
5408
 
5204
- /** @internal */
5205
-
5206
- /** @internal */
5207
-
5208
- /** @internal */
5209
-
5210
- /** @internal */
5211
-
5212
- /** @internal */
5213
-
5214
- /** @internal */
5215
-
5216
- /** @internal */
5217
-
5218
- /** @internal */
5409
+ /** @internal
5410
+ * A number that we increment every time an active connection closes.
5411
+ * Used to determine whether the same socket connection that was open
5412
+ * when an async operation started is the same one that's active when
5413
+ * its continuation fires.
5414
+ *
5415
+ */
5219
5416
 
5220
5417
  /** @internal */
5221
5418
 
@@ -5231,7 +5428,19 @@ class Connection {
5231
5428
 
5232
5429
  /** @internal */
5233
5430
 
5234
- /** @internal */
5431
+ /**
5432
+ * Special case.
5433
+ * After a signature is processed, RPCs automatically dispose of the
5434
+ * subscription on the server side. We need to track which of these
5435
+ * subscriptions have been disposed in such a way, so that we know
5436
+ * whether the client is dealing with a not-yet-processed signature
5437
+ * (in which case we must tear down the server subscription) or an
5438
+ * already-processed signature (in which case the client can simply
5439
+ * clear out the subscription locally without telling the server).
5440
+ *
5441
+ * NOTE: There is a proposal to eliminate this special case, here:
5442
+ * https://github.com/solana-labs/solana/issues/18892
5443
+ */
5235
5444
 
5236
5445
  /** @internal */
5237
5446
 
@@ -5253,6 +5462,7 @@ class Connection {
5253
5462
  this._rpcWebSocketConnected = false;
5254
5463
  this._rpcWebSocketHeartbeat = null;
5255
5464
  this._rpcWebSocketIdleTimeout = null;
5465
+ this._rpcWebSocketGeneration = 0;
5256
5466
  this._disableBlockhashCaching = false;
5257
5467
  this._pollingBlockhash = false;
5258
5468
  this._blockhashInfo = {
@@ -5261,20 +5471,11 @@ class Connection {
5261
5471
  transactionSignatures: [],
5262
5472
  simulatedSignatures: []
5263
5473
  };
5264
- this._accountChangeSubscriptionCounter = 0;
5265
- this._accountChangeSubscriptions = {};
5266
- this._programAccountChangeSubscriptionCounter = 0;
5267
- this._programAccountChangeSubscriptions = {};
5268
- this._rootSubscriptionCounter = 0;
5269
- this._rootSubscriptions = {};
5270
- this._signatureSubscriptionCounter = 0;
5271
- this._signatureSubscriptions = {};
5272
- this._slotSubscriptionCounter = 0;
5273
- this._slotSubscriptions = {};
5274
- this._logsSubscriptionCounter = 0;
5275
- this._logsSubscriptions = {};
5276
- this._slotUpdateSubscriptionCounter = 0;
5277
- this._slotUpdateSubscriptions = {};
5474
+ this._nextClientSubscriptionId = 0;
5475
+ this._subscriptionDisposeFunctionsByClientSubscriptionId = {};
5476
+ this._subscriptionCallbacksByServerSubscriptionId = {};
5477
+ this._subscriptionsByHash = {};
5478
+ this._subscriptionsAutoDisposedByRpc = new Set();
5278
5479
  let url = new URL(endpoint);
5279
5480
  const useHttps = url.protocol === 'https:';
5280
5481
  let wsEndpoint;
@@ -7042,6 +7243,8 @@ class Connection {
7042
7243
 
7043
7244
 
7044
7245
  _wsOnClose(code) {
7246
+ this._rpcWebSocketGeneration++;
7247
+
7045
7248
  if (this._rpcWebSocketHeartbeat) {
7046
7249
  clearInterval(this._rpcWebSocketHeartbeat);
7047
7250
  this._rpcWebSocketHeartbeat = null;
@@ -7055,85 +7258,20 @@ class Connection {
7055
7258
  } // implicit close, prepare subscriptions for auto-reconnect
7056
7259
 
7057
7260
 
7058
- this._resetSubscriptions();
7059
- }
7060
- /**
7061
- * @internal
7062
- */
7063
-
7064
-
7065
- async _subscribe(sub, rpcMethod, rpcArgs) {
7066
- if (sub.subscriptionId == null) {
7067
- sub.subscriptionId = 'subscribing';
7068
-
7069
- try {
7070
- const id = await this._rpcWebSocket.call(rpcMethod, rpcArgs);
7071
-
7072
- if (typeof id === 'number' && sub.subscriptionId === 'subscribing') {
7073
- // eslint-disable-next-line require-atomic-updates
7074
- sub.subscriptionId = id;
7075
- }
7076
- } catch (err) {
7077
- if (sub.subscriptionId === 'subscribing') {
7078
- // eslint-disable-next-line require-atomic-updates
7079
- sub.subscriptionId = null;
7080
- }
7081
-
7082
- if (err instanceof Error) {
7083
- console.error(`${rpcMethod} error for argument`, rpcArgs, err.message);
7084
- }
7085
- }
7086
- }
7087
- }
7088
- /**
7089
- * @internal
7090
- */
7091
-
7092
-
7093
- async _unsubscribe(sub, rpcMethod) {
7094
- const subscriptionId = sub.subscriptionId;
7095
-
7096
- if (subscriptionId != null && typeof subscriptionId != 'string') {
7097
- const unsubscribeId = subscriptionId;
7098
-
7099
- try {
7100
- await this._rpcWebSocket.call(rpcMethod, [unsubscribeId]);
7101
- } catch (err) {
7102
- if (err instanceof Error) {
7103
- console.error(`${rpcMethod} error:`, err.message);
7104
- }
7105
- }
7106
- }
7107
- }
7108
- /**
7109
- * @internal
7110
- */
7111
-
7112
-
7113
- _resetSubscriptions() {
7114
- Object.values(this._accountChangeSubscriptions).forEach(s => s.subscriptionId = null);
7115
- Object.values(this._logsSubscriptions).forEach(s => s.subscriptionId = null);
7116
- Object.values(this._programAccountChangeSubscriptions).forEach(s => s.subscriptionId = null);
7117
- Object.values(this._rootSubscriptions).forEach(s => s.subscriptionId = null);
7118
- Object.values(this._signatureSubscriptions).forEach(s => s.subscriptionId = null);
7119
- Object.values(this._slotSubscriptions).forEach(s => s.subscriptionId = null);
7120
- Object.values(this._slotUpdateSubscriptions).forEach(s => s.subscriptionId = null);
7261
+ this._subscriptionCallbacksByServerSubscriptionId = {};
7262
+ Object.entries(this._subscriptionsByHash).forEach(([hash, subscription]) => {
7263
+ this._subscriptionsByHash[hash] = { ...subscription,
7264
+ state: 'pending'
7265
+ };
7266
+ });
7121
7267
  }
7122
7268
  /**
7123
7269
  * @internal
7124
7270
  */
7125
7271
 
7126
7272
 
7127
- _updateSubscriptions() {
7128
- const accountKeys = Object.keys(this._accountChangeSubscriptions).map(Number);
7129
- const programKeys = Object.keys(this._programAccountChangeSubscriptions).map(Number);
7130
- const slotKeys = Object.keys(this._slotSubscriptions).map(Number);
7131
- const slotUpdateKeys = Object.keys(this._slotUpdateSubscriptions).map(Number);
7132
- const signatureKeys = Object.keys(this._signatureSubscriptions).map(Number);
7133
- const rootKeys = Object.keys(this._rootSubscriptions).map(Number);
7134
- const logsKeys = Object.keys(this._logsSubscriptions).map(Number);
7135
-
7136
- if (accountKeys.length === 0 && programKeys.length === 0 && slotKeys.length === 0 && slotUpdateKeys.length === 0 && signatureKeys.length === 0 && rootKeys.length === 0 && logsKeys.length === 0) {
7273
+ async _updateSubscriptions() {
7274
+ if (Object.keys(this._subscriptionsByHash).length === 0) {
7137
7275
  if (this._rpcWebSocketConnected) {
7138
7276
  this._rpcWebSocketConnected = false;
7139
7277
  this._rpcWebSocketIdleTimeout = setTimeout(() => {
@@ -7165,60 +7303,167 @@ class Connection {
7165
7303
  return;
7166
7304
  }
7167
7305
 
7168
- for (let id of accountKeys) {
7169
- const sub = this._accountChangeSubscriptions[id];
7306
+ const activeWebSocketGeneration = this._rpcWebSocketGeneration;
7170
7307
 
7171
- this._subscribe(sub, 'accountSubscribe', this._buildArgs([sub.publicKey], sub.commitment, 'base64'));
7172
- }
7173
-
7174
- for (let id of programKeys) {
7175
- const sub = this._programAccountChangeSubscriptions[id];
7308
+ const isCurrentConnectionStillActive = () => {
7309
+ return activeWebSocketGeneration === this._rpcWebSocketGeneration;
7310
+ };
7176
7311
 
7177
- this._subscribe(sub, 'programSubscribe', this._buildArgs([sub.programId], sub.commitment, 'base64', {
7178
- filters: sub.filters
7179
- }));
7180
- }
7312
+ await Promise.all( // Don't be tempted to change this to `Object.entries`. We call
7313
+ // `_updateSubscriptions` recursively when processing the state,
7314
+ // so it's important that we look up the *current* version of
7315
+ // each subscription, every time we process a hash.
7316
+ Object.keys(this._subscriptionsByHash).map(async hash => {
7317
+ const subscription = this._subscriptionsByHash[hash];
7181
7318
 
7182
- for (let id of slotKeys) {
7183
- const sub = this._slotSubscriptions[id];
7319
+ if (subscription === undefined) {
7320
+ // This entry has since been deleted. Skip.
7321
+ return;
7322
+ }
7184
7323
 
7185
- this._subscribe(sub, 'slotSubscribe', []);
7186
- }
7324
+ switch (subscription.state) {
7325
+ case 'pending':
7326
+ case 'unsubscribed':
7327
+ if (subscription.callbacks.size === 0) {
7328
+ /**
7329
+ * You can end up here when:
7330
+ *
7331
+ * - a subscription has recently unsubscribed
7332
+ * without having new callbacks added to it
7333
+ * while the unsubscribe was in flight, or
7334
+ * - when a pending subscription has its
7335
+ * listeners removed before a request was
7336
+ * sent to the server.
7337
+ *
7338
+ * Being that nobody is interested in this
7339
+ * subscription any longer, delete it.
7340
+ */
7341
+ delete this._subscriptionsByHash[hash];
7342
+
7343
+ if (subscription.state === 'unsubscribed') {
7344
+ delete this._subscriptionCallbacksByServerSubscriptionId[subscription.serverSubscriptionId];
7345
+ }
7187
7346
 
7188
- for (let id of slotUpdateKeys) {
7189
- const sub = this._slotUpdateSubscriptions[id];
7347
+ await this._updateSubscriptions();
7348
+ return;
7349
+ }
7190
7350
 
7191
- this._subscribe(sub, 'slotsUpdatesSubscribe', []);
7192
- }
7351
+ await (async () => {
7352
+ const {
7353
+ args,
7354
+ method
7355
+ } = subscription;
7193
7356
 
7194
- for (let id of signatureKeys) {
7195
- const sub = this._signatureSubscriptions[id];
7196
- const args = [sub.signature];
7197
- if (sub.options) args.push(sub.options);
7357
+ try {
7358
+ this._subscriptionsByHash[hash] = { ...subscription,
7359
+ state: 'subscribing'
7360
+ };
7361
+ const serverSubscriptionId = await this._rpcWebSocket.call(method, args);
7362
+ this._subscriptionsByHash[hash] = { ...subscription,
7363
+ serverSubscriptionId,
7364
+ state: 'subscribed'
7365
+ };
7366
+ this._subscriptionCallbacksByServerSubscriptionId[serverSubscriptionId] = subscription.callbacks;
7367
+ await this._updateSubscriptions();
7368
+ } catch (e) {
7369
+ if (e instanceof Error) {
7370
+ console.error(`${method} error for argument`, args, e.message);
7371
+ }
7372
+
7373
+ if (!isCurrentConnectionStillActive()) {
7374
+ return;
7375
+ } // TODO: Maybe add an 'errored' state or a retry limit?
7198
7376
 
7199
- this._subscribe(sub, 'signatureSubscribe', args);
7200
- }
7201
7377
 
7202
- for (let id of rootKeys) {
7203
- const sub = this._rootSubscriptions[id];
7378
+ this._subscriptionsByHash[hash] = { ...subscription,
7379
+ state: 'pending'
7380
+ };
7381
+ await this._updateSubscriptions();
7382
+ }
7383
+ })();
7384
+ break;
7204
7385
 
7205
- this._subscribe(sub, 'rootSubscribe', []);
7206
- }
7386
+ case 'subscribed':
7387
+ if (subscription.callbacks.size === 0) {
7388
+ // By the time we successfully set up a subscription
7389
+ // with the server, the client stopped caring about it.
7390
+ // Tear it down now.
7391
+ await (async () => {
7392
+ const {
7393
+ serverSubscriptionId,
7394
+ unsubscribeMethod
7395
+ } = subscription;
7396
+
7397
+ if (this._subscriptionsAutoDisposedByRpc.has(serverSubscriptionId)) {
7398
+ /**
7399
+ * Special case.
7400
+ * If we're dealing with a subscription that has been auto-
7401
+ * disposed by the RPC, then we can skip the RPC call to
7402
+ * tear down the subscription here.
7403
+ *
7404
+ * NOTE: There is a proposal to eliminate this special case, here:
7405
+ * https://github.com/solana-labs/solana/issues/18892
7406
+ */
7407
+ this._subscriptionsAutoDisposedByRpc.delete(serverSubscriptionId);
7408
+ } else {
7409
+ this._subscriptionsByHash[hash] = { ...subscription,
7410
+ state: 'unsubscribing'
7411
+ };
7412
+
7413
+ try {
7414
+ await this._rpcWebSocket.call(unsubscribeMethod, [serverSubscriptionId]);
7415
+ } catch (e) {
7416
+ if (e instanceof Error) {
7417
+ console.error(`${unsubscribeMethod} error:`, e.message);
7418
+ }
7419
+
7420
+ if (!isCurrentConnectionStillActive()) {
7421
+ return;
7422
+ } // TODO: Maybe add an 'errored' state or a retry limit?
7423
+
7424
+
7425
+ this._subscriptionsByHash[hash] = { ...subscription,
7426
+ state: 'subscribed'
7427
+ };
7428
+ await this._updateSubscriptions();
7429
+ return;
7430
+ }
7431
+ }
7207
7432
 
7208
- for (let id of logsKeys) {
7209
- const sub = this._logsSubscriptions[id];
7210
- let filter;
7433
+ this._subscriptionsByHash[hash] = { ...subscription,
7434
+ state: 'unsubscribed'
7435
+ };
7436
+ await this._updateSubscriptions();
7437
+ })();
7438
+ }
7211
7439
 
7212
- if (typeof sub.filter === 'object') {
7213
- filter = {
7214
- mentions: [sub.filter.toString()]
7215
- };
7216
- } else {
7217
- filter = sub.filter;
7440
+ break;
7218
7441
  }
7442
+ }));
7443
+ }
7444
+ /**
7445
+ * @internal
7446
+ */
7447
+
7448
+
7449
+ _handleServerNotification(serverSubscriptionId, callbackArgs) {
7450
+ const callbacks = this._subscriptionCallbacksByServerSubscriptionId[serverSubscriptionId];
7219
7451
 
7220
- this._subscribe(sub, 'logsSubscribe', this._buildArgs([filter], sub.commitment));
7452
+ if (callbacks === undefined) {
7453
+ return;
7221
7454
  }
7455
+
7456
+ callbacks.forEach(cb => {
7457
+ try {
7458
+ cb( // I failed to find a way to convince TypeScript that `cb` is of type
7459
+ // `TCallback` which is certainly compatible with `Parameters<TCallback>`.
7460
+ // See https://github.com/microsoft/TypeScript/issues/47615
7461
+ // @ts-ignore
7462
+ ...callbackArgs);
7463
+ } catch (e) {
7464
+ console.error(e);
7465
+ }
7466
+ });
7222
7467
  }
7223
7468
  /**
7224
7469
  * @internal
@@ -7226,14 +7471,71 @@ class Connection {
7226
7471
 
7227
7472
 
7228
7473
  _wsOnAccountNotification(notification) {
7229
- const res = create(notification, AccountNotificationResult);
7474
+ const {
7475
+ result,
7476
+ subscription
7477
+ } = create(notification, AccountNotificationResult);
7230
7478
 
7231
- for (const sub of Object.values(this._accountChangeSubscriptions)) {
7232
- if (sub.subscriptionId === res.subscription) {
7233
- sub.callback(res.result.value, res.result.context);
7234
- return;
7235
- }
7479
+ this._handleServerNotification(subscription, [result.value, result.context]);
7480
+ }
7481
+ /**
7482
+ * @internal
7483
+ */
7484
+
7485
+
7486
+ _makeSubscription(subscriptionConfig,
7487
+ /**
7488
+ * When preparing `args` for a call to `_makeSubscription`, be sure
7489
+ * to carefully apply a default `commitment` property, if necessary.
7490
+ *
7491
+ * - If the user supplied a `commitment` use that.
7492
+ * - Otherwise, if the `Connection::commitment` is set, use that.
7493
+ * - Otherwise, set it to the RPC server default: `finalized`.
7494
+ *
7495
+ * This is extremely important to ensure that these two fundamentally
7496
+ * identical subscriptions produce the same identifying hash:
7497
+ *
7498
+ * - A subscription made without specifying a commitment.
7499
+ * - A subscription made where the commitment specified is the same
7500
+ * as the default applied to the subscription above.
7501
+ *
7502
+ * Example; these two subscriptions must produce the same hash:
7503
+ *
7504
+ * - An `accountSubscribe` subscription for `'PUBKEY'`
7505
+ * - An `accountSubscribe` subscription for `'PUBKEY'` with commitment
7506
+ * `'finalized'`.
7507
+ *
7508
+ * See the 'making a subscription with defaulted params omitted' test
7509
+ * in `connection-subscriptions.ts` for more.
7510
+ */
7511
+ args) {
7512
+ const clientSubscriptionId = this._nextClientSubscriptionId++;
7513
+ const hash = fastStableStringify$1([subscriptionConfig.method, args], true
7514
+ /* isArrayProp */
7515
+ );
7516
+ const existingSubscription = this._subscriptionsByHash[hash];
7517
+
7518
+ if (existingSubscription === undefined) {
7519
+ this._subscriptionsByHash[hash] = { ...subscriptionConfig,
7520
+ args,
7521
+ callbacks: new Set([subscriptionConfig.callback]),
7522
+ state: 'pending'
7523
+ };
7524
+ } else {
7525
+ existingSubscription.callbacks.add(subscriptionConfig.callback);
7236
7526
  }
7527
+
7528
+ this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId] = async () => {
7529
+ delete this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId];
7530
+ const subscription = this._subscriptionsByHash[hash];
7531
+ assert(subscription !== undefined, `Could not find a \`Subscription\` when tearing down client subscription #${clientSubscriptionId}`);
7532
+ subscription.callbacks.delete(subscriptionConfig.callback);
7533
+ await this._updateSubscriptions();
7534
+ };
7535
+
7536
+ this._updateSubscriptions();
7537
+
7538
+ return clientSubscriptionId;
7237
7539
  }
7238
7540
  /**
7239
7541
  * Register a callback to be invoked whenever the specified account changes
@@ -7246,35 +7548,24 @@ class Connection {
7246
7548
 
7247
7549
 
7248
7550
  onAccountChange(publicKey, callback, commitment) {
7249
- const id = ++this._accountChangeSubscriptionCounter;
7250
- this._accountChangeSubscriptions[id] = {
7251
- publicKey: publicKey.toBase58(),
7252
- callback,
7253
- commitment,
7254
- subscriptionId: null
7255
- };
7256
-
7257
- this._updateSubscriptions();
7551
+ const args = this._buildArgs([publicKey.toBase58()], commitment || this._commitment || 'finalized', // Apply connection/server default.
7552
+ 'base64');
7258
7553
 
7259
- return id;
7554
+ return this._makeSubscription({
7555
+ callback,
7556
+ method: 'accountSubscribe',
7557
+ unsubscribeMethod: 'accountUnsubscribe'
7558
+ }, args);
7260
7559
  }
7261
7560
  /**
7262
7561
  * Deregister an account notification callback
7263
7562
  *
7264
- * @param id subscription id to deregister
7563
+ * @param id client subscription id to deregister
7265
7564
  */
7266
7565
 
7267
7566
 
7268
- async removeAccountChangeListener(id) {
7269
- if (this._accountChangeSubscriptions[id]) {
7270
- const subInfo = this._accountChangeSubscriptions[id];
7271
- delete this._accountChangeSubscriptions[id];
7272
- await this._unsubscribe(subInfo, 'accountUnsubscribe');
7273
-
7274
- this._updateSubscriptions();
7275
- } else {
7276
- console.warn(createSubscriptionWarningMessage(id, 'account change'));
7277
- }
7567
+ async removeAccountChangeListener(clientSubscriptionId) {
7568
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'account change');
7278
7569
  }
7279
7570
  /**
7280
7571
  * @internal
@@ -7282,21 +7573,15 @@ class Connection {
7282
7573
 
7283
7574
 
7284
7575
  _wsOnProgramAccountNotification(notification) {
7285
- const res = create(notification, ProgramAccountNotificationResult);
7576
+ const {
7577
+ result,
7578
+ subscription
7579
+ } = create(notification, ProgramAccountNotificationResult);
7286
7580
 
7287
- for (const sub of Object.values(this._programAccountChangeSubscriptions)) {
7288
- if (sub.subscriptionId === res.subscription) {
7289
- const {
7290
- value,
7291
- context
7292
- } = res.result;
7293
- sub.callback({
7294
- accountId: value.pubkey,
7295
- accountInfo: value.account
7296
- }, context);
7297
- return;
7298
- }
7299
- }
7581
+ this._handleServerNotification(subscription, [{
7582
+ accountId: result.value.pubkey,
7583
+ accountInfo: result.value.account
7584
+ }, result.context]);
7300
7585
  }
7301
7586
  /**
7302
7587
  * Register a callback to be invoked whenever accounts owned by the
@@ -7311,36 +7596,30 @@ class Connection {
7311
7596
 
7312
7597
 
7313
7598
  onProgramAccountChange(programId, callback, commitment, filters) {
7314
- const id = ++this._programAccountChangeSubscriptionCounter;
7315
- this._programAccountChangeSubscriptions[id] = {
7316
- programId: programId.toBase58(),
7599
+ const args = this._buildArgs([programId.toBase58()], commitment || this._commitment || 'finalized', // Apply connection/server default.
7600
+ 'base64'
7601
+ /* encoding */
7602
+ , filters ? {
7603
+ filters: filters
7604
+ } : undefined
7605
+ /* extra */
7606
+ );
7607
+
7608
+ return this._makeSubscription({
7317
7609
  callback,
7318
- commitment,
7319
- subscriptionId: null,
7320
- filters
7321
- };
7322
-
7323
- this._updateSubscriptions();
7324
-
7325
- return id;
7610
+ method: 'programSubscribe',
7611
+ unsubscribeMethod: 'programUnsubscribe'
7612
+ }, args);
7326
7613
  }
7327
7614
  /**
7328
7615
  * Deregister an account notification callback
7329
7616
  *
7330
- * @param id subscription id to deregister
7617
+ * @param id client subscription id to deregister
7331
7618
  */
7332
7619
 
7333
7620
 
7334
- async removeProgramAccountChangeListener(id) {
7335
- if (this._programAccountChangeSubscriptions[id]) {
7336
- const subInfo = this._programAccountChangeSubscriptions[id];
7337
- delete this._programAccountChangeSubscriptions[id];
7338
- await this._unsubscribe(subInfo, 'programUnsubscribe');
7339
-
7340
- this._updateSubscriptions();
7341
- } else {
7342
- console.warn(createSubscriptionWarningMessage(id, 'program account change'));
7343
- }
7621
+ async removeProgramAccountChangeListener(clientSubscriptionId) {
7622
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'program account change');
7344
7623
  }
7345
7624
  /**
7346
7625
  * Registers a callback to be invoked whenever logs are emitted.
@@ -7348,35 +7627,26 @@ class Connection {
7348
7627
 
7349
7628
 
7350
7629
  onLogs(filter, callback, commitment) {
7351
- const id = ++this._logsSubscriptionCounter;
7352
- this._logsSubscriptions[id] = {
7353
- filter,
7354
- callback,
7355
- commitment,
7356
- subscriptionId: null
7357
- };
7630
+ const args = this._buildArgs([typeof filter === 'object' ? {
7631
+ mentions: [filter.toString()]
7632
+ } : filter], commitment || this._commitment || 'finalized' // Apply connection/server default.
7633
+ );
7358
7634
 
7359
- this._updateSubscriptions();
7360
-
7361
- return id;
7635
+ return this._makeSubscription({
7636
+ callback,
7637
+ method: 'logsSubscribe',
7638
+ unsubscribeMethod: 'logsUnsubscribe'
7639
+ }, args);
7362
7640
  }
7363
7641
  /**
7364
7642
  * Deregister a logs callback.
7365
7643
  *
7366
- * @param id subscription id to deregister.
7644
+ * @param id client subscription id to deregister.
7367
7645
  */
7368
7646
 
7369
7647
 
7370
- async removeOnLogsListener(id) {
7371
- if (this._logsSubscriptions[id]) {
7372
- const subInfo = this._logsSubscriptions[id];
7373
- delete this._logsSubscriptions[id];
7374
- await this._unsubscribe(subInfo, 'logsUnsubscribe');
7375
-
7376
- this._updateSubscriptions();
7377
- } else {
7378
- console.warn(createSubscriptionWarningMessage(id, 'logs'));
7379
- }
7648
+ async removeOnLogsListener(clientSubscriptionId) {
7649
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'logs');
7380
7650
  }
7381
7651
  /**
7382
7652
  * @internal
@@ -7384,17 +7654,12 @@ class Connection {
7384
7654
 
7385
7655
 
7386
7656
  _wsOnLogsNotification(notification) {
7387
- const res = create(notification, LogsNotificationResult);
7388
- const keys = Object.keys(this._logsSubscriptions).map(Number);
7389
-
7390
- for (let id of keys) {
7391
- const sub = this._logsSubscriptions[id];
7657
+ const {
7658
+ result,
7659
+ subscription
7660
+ } = create(notification, LogsNotificationResult);
7392
7661
 
7393
- if (sub.subscriptionId === res.subscription) {
7394
- sub.callback(res.result.value, res.result.context);
7395
- return;
7396
- }
7397
- }
7662
+ this._handleServerNotification(subscription, [result.value, result.context]);
7398
7663
  }
7399
7664
  /**
7400
7665
  * @internal
@@ -7402,14 +7667,12 @@ class Connection {
7402
7667
 
7403
7668
 
7404
7669
  _wsOnSlotNotification(notification) {
7405
- const res = create(notification, SlotNotificationResult);
7670
+ const {
7671
+ result,
7672
+ subscription
7673
+ } = create(notification, SlotNotificationResult);
7406
7674
 
7407
- for (const sub of Object.values(this._slotSubscriptions)) {
7408
- if (sub.subscriptionId === res.subscription) {
7409
- sub.callback(res.result);
7410
- return;
7411
- }
7412
- }
7675
+ this._handleServerNotification(subscription, [result]);
7413
7676
  }
7414
7677
  /**
7415
7678
  * Register a callback to be invoked upon slot changes
@@ -7420,33 +7683,23 @@ class Connection {
7420
7683
 
7421
7684
 
7422
7685
  onSlotChange(callback) {
7423
- const id = ++this._slotSubscriptionCounter;
7424
- this._slotSubscriptions[id] = {
7686
+ return this._makeSubscription({
7425
7687
  callback,
7426
- subscriptionId: null
7427
- };
7428
-
7429
- this._updateSubscriptions();
7430
-
7431
- return id;
7688
+ method: 'slotSubscribe',
7689
+ unsubscribeMethod: 'slotUnsubscribe'
7690
+ }, []
7691
+ /* args */
7692
+ );
7432
7693
  }
7433
7694
  /**
7434
7695
  * Deregister a slot notification callback
7435
7696
  *
7436
- * @param id subscription id to deregister
7697
+ * @param id client subscription id to deregister
7437
7698
  */
7438
7699
 
7439
7700
 
7440
- async removeSlotChangeListener(id) {
7441
- if (this._slotSubscriptions[id]) {
7442
- const subInfo = this._slotSubscriptions[id];
7443
- delete this._slotSubscriptions[id];
7444
- await this._unsubscribe(subInfo, 'slotUnsubscribe');
7445
-
7446
- this._updateSubscriptions();
7447
- } else {
7448
- console.warn(createSubscriptionWarningMessage(id, 'slot change'));
7449
- }
7701
+ async removeSlotChangeListener(clientSubscriptionId) {
7702
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'slot change');
7450
7703
  }
7451
7704
  /**
7452
7705
  * @internal
@@ -7454,14 +7707,12 @@ class Connection {
7454
7707
 
7455
7708
 
7456
7709
  _wsOnSlotUpdatesNotification(notification) {
7457
- const res = create(notification, SlotUpdateNotificationResult);
7710
+ const {
7711
+ result,
7712
+ subscription
7713
+ } = create(notification, SlotUpdateNotificationResult);
7458
7714
 
7459
- for (const sub of Object.values(this._slotUpdateSubscriptions)) {
7460
- if (sub.subscriptionId === res.subscription) {
7461
- sub.callback(res.result);
7462
- return;
7463
- }
7464
- }
7715
+ this._handleServerNotification(subscription, [result]);
7465
7716
  }
7466
7717
  /**
7467
7718
  * Register a callback to be invoked upon slot updates. {@link SlotUpdate}'s
@@ -7473,32 +7724,36 @@ class Connection {
7473
7724
 
7474
7725
 
7475
7726
  onSlotUpdate(callback) {
7476
- const id = ++this._slotUpdateSubscriptionCounter;
7477
- this._slotUpdateSubscriptions[id] = {
7727
+ return this._makeSubscription({
7478
7728
  callback,
7479
- subscriptionId: null
7480
- };
7481
-
7482
- this._updateSubscriptions();
7483
-
7484
- return id;
7729
+ method: 'slotsUpdatesSubscribe',
7730
+ unsubscribeMethod: 'slotsUpdatesUnsubscribe'
7731
+ }, []
7732
+ /* args */
7733
+ );
7485
7734
  }
7486
7735
  /**
7487
7736
  * Deregister a slot update notification callback
7488
7737
  *
7489
- * @param id subscription id to deregister
7738
+ * @param id client subscription id to deregister
7490
7739
  */
7491
7740
 
7492
7741
 
7493
- async removeSlotUpdateListener(id) {
7494
- if (this._slotUpdateSubscriptions[id]) {
7495
- const subInfo = this._slotUpdateSubscriptions[id];
7496
- delete this._slotUpdateSubscriptions[id];
7497
- await this._unsubscribe(subInfo, 'slotsUpdatesUnsubscribe');
7742
+ async removeSlotUpdateListener(clientSubscriptionId) {
7743
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'slot update');
7744
+ }
7745
+ /**
7746
+ * @internal
7747
+ */
7498
7748
 
7499
- this._updateSubscriptions();
7749
+
7750
+ async _unsubscribeClientSubscription(clientSubscriptionId, subscriptionName) {
7751
+ const dispose = this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId];
7752
+
7753
+ if (dispose) {
7754
+ await dispose();
7500
7755
  } else {
7501
- console.warn(createSubscriptionWarningMessage(id, 'slot update'));
7756
+ console.warn('Ignored unsubscribe request because an active subscription with id ' + `\`${clientSubscriptionId}\` for '${subscriptionName}' events ` + 'could not be found.');
7502
7757
  }
7503
7758
  }
7504
7759
 
@@ -7545,30 +7800,34 @@ class Connection {
7545
7800
 
7546
7801
 
7547
7802
  _wsOnSignatureNotification(notification) {
7548
- const res = create(notification, SignatureNotificationResult);
7549
-
7550
- for (const [id, sub] of Object.entries(this._signatureSubscriptions)) {
7551
- if (sub.subscriptionId === res.subscription) {
7552
- if (res.result.value === 'receivedSignature') {
7553
- sub.callback({
7554
- type: 'received'
7555
- }, res.result.context);
7556
- } else {
7557
- // Signatures subscriptions are auto-removed by the RPC service so
7558
- // no need to explicitly send an unsubscribe message
7559
- delete this._signatureSubscriptions[Number(id)];
7560
-
7561
- this._updateSubscriptions();
7562
-
7563
- sub.callback({
7564
- type: 'status',
7565
- result: res.result.value
7566
- }, res.result.context);
7567
- }
7568
-
7569
- return;
7570
- }
7571
- }
7803
+ const {
7804
+ result,
7805
+ subscription
7806
+ } = create(notification, SignatureNotificationResult);
7807
+
7808
+ if (result.value !== 'receivedSignature') {
7809
+ /**
7810
+ * Special case.
7811
+ * After a signature is processed, RPCs automatically dispose of the
7812
+ * subscription on the server side. We need to track which of these
7813
+ * subscriptions have been disposed in such a way, so that we know
7814
+ * whether the client is dealing with a not-yet-processed signature
7815
+ * (in which case we must tear down the server subscription) or an
7816
+ * already-processed signature (in which case the client can simply
7817
+ * clear out the subscription locally without telling the server).
7818
+ *
7819
+ * NOTE: There is a proposal to eliminate this special case, here:
7820
+ * https://github.com/solana-labs/solana/issues/18892
7821
+ */
7822
+ this._subscriptionsAutoDisposedByRpc.add(subscription);
7823
+ }
7824
+
7825
+ this._handleServerNotification(subscription, result.value === 'receivedSignature' ? [{
7826
+ type: 'received'
7827
+ }, result.context] : [{
7828
+ type: 'status',
7829
+ result: result.value
7830
+ }, result.context]);
7572
7831
  }
7573
7832
  /**
7574
7833
  * Register a callback to be invoked upon signature updates
@@ -7581,23 +7840,26 @@ class Connection {
7581
7840
 
7582
7841
 
7583
7842
  onSignature(signature, callback, commitment) {
7584
- const id = ++this._signatureSubscriptionCounter;
7585
- this._signatureSubscriptions[id] = {
7586
- signature,
7843
+ const args = this._buildArgs([signature], commitment || this._commitment || 'finalized' // Apply connection/server default.
7844
+ );
7845
+
7846
+ const clientSubscriptionId = this._makeSubscription({
7587
7847
  callback: (notification, context) => {
7588
7848
  if (notification.type === 'status') {
7589
- callback(notification.result, context);
7849
+ callback(notification.result, context); // Signatures subscriptions are auto-removed by the RPC service
7850
+ // so no need to explicitly send an unsubscribe message.
7851
+
7852
+ try {
7853
+ this.removeSignatureListener(clientSubscriptionId); // eslint-disable-next-line no-empty
7854
+ } catch {// Already removed.
7855
+ }
7590
7856
  }
7591
7857
  },
7592
- options: {
7593
- commitment
7594
- },
7595
- subscriptionId: null
7596
- };
7858
+ method: 'signatureSubscribe',
7859
+ unsubscribeMethod: 'signatureUnsubscribe'
7860
+ }, args);
7597
7861
 
7598
- this._updateSubscriptions();
7599
-
7600
- return id;
7862
+ return clientSubscriptionId;
7601
7863
  }
7602
7864
  /**
7603
7865
  * Register a callback to be invoked when a transaction is
@@ -7612,35 +7874,43 @@ class Connection {
7612
7874
 
7613
7875
 
7614
7876
  onSignatureWithOptions(signature, callback, options) {
7615
- const id = ++this._signatureSubscriptionCounter;
7616
- this._signatureSubscriptions[id] = {
7617
- signature,
7618
- callback,
7619
- options,
7620
- subscriptionId: null
7877
+ const {
7878
+ commitment,
7879
+ ...extra
7880
+ } = { ...options,
7881
+ commitment: options && options.commitment || this._commitment || 'finalized' // Apply connection/server default.
7882
+
7621
7883
  };
7622
7884
 
7623
- this._updateSubscriptions();
7885
+ const args = this._buildArgs([signature], commitment, undefined
7886
+ /* encoding */
7887
+ , extra);
7624
7888
 
7625
- return id;
7889
+ const clientSubscriptionId = this._makeSubscription({
7890
+ callback: (notification, context) => {
7891
+ callback(notification, context); // Signatures subscriptions are auto-removed by the RPC service
7892
+ // so no need to explicitly send an unsubscribe message.
7893
+
7894
+ try {
7895
+ this.removeSignatureListener(clientSubscriptionId); // eslint-disable-next-line no-empty
7896
+ } catch {// Already removed.
7897
+ }
7898
+ },
7899
+ method: 'signatureSubscribe',
7900
+ unsubscribeMethod: 'signatureUnsubscribe'
7901
+ }, args);
7902
+
7903
+ return clientSubscriptionId;
7626
7904
  }
7627
7905
  /**
7628
7906
  * Deregister a signature notification callback
7629
7907
  *
7630
- * @param id subscription id to deregister
7908
+ * @param id client subscription id to deregister
7631
7909
  */
7632
7910
 
7633
7911
 
7634
- async removeSignatureListener(id) {
7635
- if (this._signatureSubscriptions[id]) {
7636
- const subInfo = this._signatureSubscriptions[id];
7637
- delete this._signatureSubscriptions[id];
7638
- await this._unsubscribe(subInfo, 'signatureUnsubscribe');
7639
-
7640
- this._updateSubscriptions();
7641
- } else {
7642
- console.warn(createSubscriptionWarningMessage(id, 'signature result'));
7643
- }
7912
+ async removeSignatureListener(clientSubscriptionId) {
7913
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'signature result');
7644
7914
  }
7645
7915
  /**
7646
7916
  * @internal
@@ -7648,14 +7918,12 @@ class Connection {
7648
7918
 
7649
7919
 
7650
7920
  _wsOnRootNotification(notification) {
7651
- const res = create(notification, RootNotificationResult);
7921
+ const {
7922
+ result,
7923
+ subscription
7924
+ } = create(notification, RootNotificationResult);
7652
7925
 
7653
- for (const sub of Object.values(this._rootSubscriptions)) {
7654
- if (sub.subscriptionId === res.subscription) {
7655
- sub.callback(res.result);
7656
- return;
7657
- }
7658
- }
7926
+ this._handleServerNotification(subscription, [result]);
7659
7927
  }
7660
7928
  /**
7661
7929
  * Register a callback to be invoked upon root changes
@@ -7666,33 +7934,23 @@ class Connection {
7666
7934
 
7667
7935
 
7668
7936
  onRootChange(callback) {
7669
- const id = ++this._rootSubscriptionCounter;
7670
- this._rootSubscriptions[id] = {
7937
+ return this._makeSubscription({
7671
7938
  callback,
7672
- subscriptionId: null
7673
- };
7674
-
7675
- this._updateSubscriptions();
7676
-
7677
- return id;
7939
+ method: 'rootSubscribe',
7940
+ unsubscribeMethod: 'rootUnsubscribe'
7941
+ }, []
7942
+ /* args */
7943
+ );
7678
7944
  }
7679
7945
  /**
7680
7946
  * Deregister a root notification callback
7681
7947
  *
7682
- * @param id subscription id to deregister
7948
+ * @param id client subscription id to deregister
7683
7949
  */
7684
7950
 
7685
7951
 
7686
- async removeRootChangeListener(id) {
7687
- if (this._rootSubscriptions[id]) {
7688
- const subInfo = this._rootSubscriptions[id];
7689
- delete this._rootSubscriptions[id];
7690
- await this._unsubscribe(subInfo, 'rootUnsubscribe');
7691
-
7692
- this._updateSubscriptions();
7693
- } else {
7694
- console.warn(createSubscriptionWarningMessage(id, 'root change'));
7695
- }
7952
+ async removeRootChangeListener(clientSubscriptionId) {
7953
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'root change');
7696
7954
  }
7697
7955
 
7698
7956
  }
@@ -9372,5 +9630,5 @@ function clusterApiUrl(cluster, tls) {
9372
9630
 
9373
9631
  const LAMPORTS_PER_SOL = 1000000000;
9374
9632
 
9375
- export { Account, Authorized, BLOCKHASH_CACHE_TIMEOUT_MS, BPF_LOADER_DEPRECATED_PROGRAM_ID, BPF_LOADER_PROGRAM_ID, BpfLoader, Connection, Ed25519Program, Enum, EpochSchedule, FeeCalculatorLayout, Keypair, LAMPORTS_PER_SOL, Loader, Lockup, MAX_SEED_LENGTH, Message, NONCE_ACCOUNT_LENGTH, NonceAccount, PACKET_DATA_SIZE, PublicKey, SOLANA_SCHEMA, STAKE_CONFIG_ID, STAKE_INSTRUCTION_LAYOUTS, SYSTEM_INSTRUCTION_LAYOUTS, SYSVAR_CLOCK_PUBKEY, SYSVAR_EPOCH_SCHEDULE_PUBKEY, SYSVAR_INSTRUCTIONS_PUBKEY, SYSVAR_RECENT_BLOCKHASHES_PUBKEY, SYSVAR_RENT_PUBKEY, SYSVAR_REWARDS_PUBKEY, SYSVAR_SLOT_HASHES_PUBKEY, SYSVAR_SLOT_HISTORY_PUBKEY, SYSVAR_STAKE_HISTORY_PUBKEY, Secp256k1Program, SendTransactionError, StakeAuthorizationLayout, StakeInstruction, StakeProgram, Struct, SystemInstruction, SystemProgram, Transaction, TransactionInstruction, VALIDATOR_INFO_KEY, VOTE_PROGRAM_ID, ValidatorInfo, VoteAccount, VoteAuthorizationLayout, VoteInit, VoteInstruction, VoteProgram, clusterApiUrl, sendAndConfirmRawTransaction, sendAndConfirmTransaction };
9633
+ export { Account, Authorized, BLOCKHASH_CACHE_TIMEOUT_MS, BPF_LOADER_DEPRECATED_PROGRAM_ID, BPF_LOADER_PROGRAM_ID, BpfLoader, COMPUTE_BUDGET_INSTRUCTION_LAYOUTS, ComputeBudgetInstruction, ComputeBudgetProgram, Connection, Ed25519Program, Enum, EpochSchedule, FeeCalculatorLayout, Keypair, LAMPORTS_PER_SOL, Loader, Lockup, MAX_SEED_LENGTH, Message, NONCE_ACCOUNT_LENGTH, NonceAccount, PublicKey, SOLANA_SCHEMA, STAKE_CONFIG_ID, STAKE_INSTRUCTION_LAYOUTS, SYSTEM_INSTRUCTION_LAYOUTS, SYSVAR_CLOCK_PUBKEY, SYSVAR_EPOCH_SCHEDULE_PUBKEY, SYSVAR_INSTRUCTIONS_PUBKEY, SYSVAR_RECENT_BLOCKHASHES_PUBKEY, SYSVAR_RENT_PUBKEY, SYSVAR_REWARDS_PUBKEY, SYSVAR_SLOT_HASHES_PUBKEY, SYSVAR_SLOT_HISTORY_PUBKEY, SYSVAR_STAKE_HISTORY_PUBKEY, Secp256k1Program, SendTransactionError, StakeAuthorizationLayout, StakeInstruction, StakeProgram, Struct, SystemInstruction, SystemProgram, Transaction, TransactionInstruction, VALIDATOR_INFO_KEY, VOTE_PROGRAM_ID, ValidatorInfo, VoteAccount, VoteAuthorizationLayout, VoteInit, VoteInstruction, VoteProgram, clusterApiUrl, sendAndConfirmRawTransaction, sendAndConfirmTransaction };
9376
9634
  //# sourceMappingURL=index.esm.js.map