@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.
@@ -2108,6 +2108,16 @@ class Account {
2108
2108
 
2109
2109
  const BPF_LOADER_DEPRECATED_PROGRAM_ID = new PublicKey('BPFLoader1111111111111111111111111111111111');
2110
2110
 
2111
+ /**
2112
+ * Maximum over-the-wire size of a Transaction
2113
+ *
2114
+ * 1280 is IPv6 minimum MTU
2115
+ * 40 bytes is the size of the IPv6 header
2116
+ * 8 bytes is the size of the fragment header
2117
+ */
2118
+ const PACKET_DATA_SIZE = 1280 - 40 - 8;
2119
+ const SIGNATURE_LENGTH_IN_BYTES = 64;
2120
+
2111
2121
  /**
2112
2122
  * Layout for a public key
2113
2123
  */
@@ -2367,20 +2377,8 @@ function assert (condition, message) {
2367
2377
 
2368
2378
  /**
2369
2379
  * Default (empty) signature
2370
- *
2371
- * Signatures are 64 bytes in length
2372
2380
  */
2373
- const DEFAULT_SIGNATURE = buffer.Buffer.alloc(64).fill(0);
2374
- /**
2375
- * Maximum over-the-wire size of a Transaction
2376
- *
2377
- * 1280 is IPv6 minimum MTU
2378
- * 40 bytes is the size of the IPv6 header
2379
- * 8 bytes is the size of the fragment header
2380
- */
2381
-
2382
- const PACKET_DATA_SIZE = 1280 - 40 - 8;
2383
- const SIGNATURE_LENGTH = 64;
2381
+ const DEFAULT_SIGNATURE = buffer.Buffer.alloc(SIGNATURE_LENGTH_IN_BYTES).fill(0);
2384
2382
  /**
2385
2383
  * Account metadata used to define instructions
2386
2384
  */
@@ -3010,8 +3008,8 @@ class Transaction {
3010
3008
  let signatures = [];
3011
3009
 
3012
3010
  for (let i = 0; i < signatureCount; i++) {
3013
- const signature = byteArray.slice(0, SIGNATURE_LENGTH);
3014
- byteArray = byteArray.slice(SIGNATURE_LENGTH);
3011
+ const signature = byteArray.slice(0, SIGNATURE_LENGTH_IN_BYTES);
3012
+ byteArray = byteArray.slice(SIGNATURE_LENGTH_IN_BYTES);
3015
3013
  signatures.push(bs58__default["default"].encode(buffer.Buffer.from(signature)));
3016
3014
  }
3017
3015
 
@@ -3900,11 +3898,11 @@ class SystemProgram {
3900
3898
  }
3901
3899
  SystemProgram.programId = new PublicKey('11111111111111111111111111111111');
3902
3900
 
3903
- // Keep program chunks under PACKET_DATA_SIZE, leaving enough room for the
3904
3901
  // rest of the Transaction fields
3905
3902
  //
3906
3903
  // TODO: replace 300 with a proper constant for the size of the other
3907
3904
  // Transaction fields
3905
+
3908
3906
  const CHUNK_SIZE = PACKET_DATA_SIZE - 300;
3909
3907
  /**
3910
3908
  * Program loader interface
@@ -4104,6 +4102,136 @@ class BpfLoader {
4104
4102
 
4105
4103
  }
4106
4104
 
4105
+ /**
4106
+ * Compute Budget Instruction class
4107
+ */
4108
+
4109
+ class ComputeBudgetInstruction {
4110
+ /**
4111
+ * @internal
4112
+ */
4113
+ constructor() {}
4114
+ /**
4115
+ * Decode a compute budget instruction and retrieve the instruction type.
4116
+ */
4117
+
4118
+
4119
+ static decodeInstructionType(instruction) {
4120
+ this.checkProgramId(instruction.programId);
4121
+ const instructionTypeLayout = BufferLayout__namespace.u8('instruction');
4122
+ const typeIndex = instructionTypeLayout.decode(instruction.data);
4123
+ let type;
4124
+
4125
+ for (const [ixType, layout] of Object.entries(COMPUTE_BUDGET_INSTRUCTION_LAYOUTS)) {
4126
+ if (layout.index == typeIndex) {
4127
+ type = ixType;
4128
+ break;
4129
+ }
4130
+ }
4131
+
4132
+ if (!type) {
4133
+ throw new Error('Instruction type incorrect; not a ComputeBudgetInstruction');
4134
+ }
4135
+
4136
+ return type;
4137
+ }
4138
+ /**
4139
+ * Decode request units compute budget instruction and retrieve the instruction params.
4140
+ */
4141
+
4142
+
4143
+ static decodeRequestUnits(instruction) {
4144
+ this.checkProgramId(instruction.programId);
4145
+ const {
4146
+ units,
4147
+ additionalFee
4148
+ } = decodeData(COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestUnits, instruction.data);
4149
+ return {
4150
+ units,
4151
+ additionalFee
4152
+ };
4153
+ }
4154
+ /**
4155
+ * Decode request heap frame compute budget instruction and retrieve the instruction params.
4156
+ */
4157
+
4158
+
4159
+ static decodeRequestHeapFrame(instruction) {
4160
+ this.checkProgramId(instruction.programId);
4161
+ const {
4162
+ bytes
4163
+ } = decodeData(COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestHeapFrame, instruction.data);
4164
+ return {
4165
+ bytes
4166
+ };
4167
+ }
4168
+ /**
4169
+ * @internal
4170
+ */
4171
+
4172
+
4173
+ static checkProgramId(programId) {
4174
+ if (!programId.equals(ComputeBudgetProgram.programId)) {
4175
+ throw new Error('invalid instruction; programId is not ComputeBudgetProgram');
4176
+ }
4177
+ }
4178
+
4179
+ }
4180
+ /**
4181
+ * An enumeration of valid ComputeBudgetInstructionType's
4182
+ */
4183
+
4184
+ /**
4185
+ * An enumeration of valid ComputeBudget InstructionType's
4186
+ * @internal
4187
+ */
4188
+ const COMPUTE_BUDGET_INSTRUCTION_LAYOUTS = Object.freeze({
4189
+ RequestUnits: {
4190
+ index: 0,
4191
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u8('instruction'), BufferLayout__namespace.u32('units'), BufferLayout__namespace.u32('additionalFee')])
4192
+ },
4193
+ RequestHeapFrame: {
4194
+ index: 1,
4195
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u8('instruction'), BufferLayout__namespace.u32('bytes')])
4196
+ }
4197
+ });
4198
+ /**
4199
+ * Factory class for transaction instructions to interact with the Compute Budget program
4200
+ */
4201
+
4202
+ class ComputeBudgetProgram {
4203
+ /**
4204
+ * @internal
4205
+ */
4206
+ constructor() {}
4207
+ /**
4208
+ * Public key that identifies the Compute Budget program
4209
+ */
4210
+
4211
+
4212
+ static requestUnits(params) {
4213
+ const type = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestUnits;
4214
+ const data = encodeData(type, params);
4215
+ return new TransactionInstruction({
4216
+ keys: [],
4217
+ programId: this.programId,
4218
+ data
4219
+ });
4220
+ }
4221
+
4222
+ static requestHeapFrame(params) {
4223
+ const type = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS.RequestHeapFrame;
4224
+ const data = encodeData(type, params);
4225
+ return new TransactionInstruction({
4226
+ keys: [],
4227
+ programId: this.programId,
4228
+ data
4229
+ });
4230
+ }
4231
+
4232
+ }
4233
+ ComputeBudgetProgram.programId = new PublicKey('ComputeBudget111111111111111111111111111111');
4234
+
4107
4235
  var browserPonyfill = {exports: {}};
4108
4236
 
4109
4237
  (function (module, exports) {
@@ -4665,6 +4793,82 @@ module.exports = exports;
4665
4793
 
4666
4794
  var crossFetch = /*@__PURE__*/getDefaultExportFromCjs(browserPonyfill.exports);
4667
4795
 
4796
+ var objToString = Object.prototype.toString;
4797
+ var objKeys = Object.keys || function(obj) {
4798
+ var keys = [];
4799
+ for (var name in obj) {
4800
+ keys.push(name);
4801
+ }
4802
+ return keys;
4803
+ };
4804
+
4805
+ function stringify(val, isArrayProp) {
4806
+ var i, max, str, keys, key, propVal, toStr;
4807
+ if (val === true) {
4808
+ return "true";
4809
+ }
4810
+ if (val === false) {
4811
+ return "false";
4812
+ }
4813
+ switch (typeof val) {
4814
+ case "object":
4815
+ if (val === null) {
4816
+ return null;
4817
+ } else if (val.toJSON && typeof val.toJSON === "function") {
4818
+ return stringify(val.toJSON(), isArrayProp);
4819
+ } else {
4820
+ toStr = objToString.call(val);
4821
+ if (toStr === "[object Array]") {
4822
+ str = '[';
4823
+ max = val.length - 1;
4824
+ for(i = 0; i < max; i++) {
4825
+ str += stringify(val[i], true) + ',';
4826
+ }
4827
+ if (max > -1) {
4828
+ str += stringify(val[i], true);
4829
+ }
4830
+ return str + ']';
4831
+ } else if (toStr === "[object Object]") {
4832
+ // only object is left
4833
+ keys = objKeys(val).sort();
4834
+ max = keys.length;
4835
+ str = "";
4836
+ i = 0;
4837
+ while (i < max) {
4838
+ key = keys[i];
4839
+ propVal = stringify(val[key], false);
4840
+ if (propVal !== undefined) {
4841
+ if (str) {
4842
+ str += ',';
4843
+ }
4844
+ str += JSON.stringify(key) + ':' + propVal;
4845
+ }
4846
+ i++;
4847
+ }
4848
+ return '{' + str + '}';
4849
+ } else {
4850
+ return JSON.stringify(val);
4851
+ }
4852
+ }
4853
+ case "function":
4854
+ case "undefined":
4855
+ return isArrayProp ? null : undefined;
4856
+ case "string":
4857
+ return JSON.stringify(val);
4858
+ default:
4859
+ return isFinite(val) ? val : null;
4860
+ }
4861
+ }
4862
+
4863
+ var fastStableStringify = function(val) {
4864
+ var returnVal = stringify(val, false);
4865
+ if (returnVal !== undefined) {
4866
+ return ''+ returnVal;
4867
+ }
4868
+ };
4869
+
4870
+ var fastStableStringify$1 = fastStableStringify;
4871
+
4668
4872
  const MINIMUM_SLOT_PER_EPOCH = 32; // Returns the number of trailing zeros in the binary representation of self.
4669
4873
 
4670
4874
  function trailingZeros(n) {
@@ -4831,6 +5035,12 @@ const BufferFromRawAccountData = superstruct.coerce(superstruct.instance(buffer.
4831
5035
  */
4832
5036
 
4833
5037
  const BLOCKHASH_CACHE_TIMEOUT_MS = 30 * 1000;
5038
+ /**
5039
+ * HACK.
5040
+ * Copied from rpc-websockets/dist/lib/client.
5041
+ * Otherwise, `yarn build` fails with:
5042
+ * https://gist.github.com/steveluscher/c057eca81d479ef705cdb53162f9971d
5043
+ */
4834
5044
 
4835
5045
  /**
4836
5046
  * @internal
@@ -5699,14 +5909,9 @@ const LogsNotificationResult = superstruct.type({
5699
5909
  * Filter for log subscriptions.
5700
5910
  */
5701
5911
 
5702
- function createSubscriptionWarningMessage(id, label) {
5703
- return 'Ignored unsubscribe request because an active subscription ' + `with id \`${id}\` for '${label}' events could not be found.`;
5704
- }
5705
5912
  /**
5706
5913
  * A connection to a fullnode JSON RPC endpoint
5707
5914
  */
5708
-
5709
-
5710
5915
  class Connection {
5711
5916
  /** @internal */
5712
5917
 
@@ -5730,21 +5935,13 @@ class Connection {
5730
5935
 
5731
5936
  /** @internal */
5732
5937
 
5733
- /** @internal */
5734
-
5735
- /** @internal */
5736
-
5737
- /** @internal */
5738
-
5739
- /** @internal */
5740
-
5741
- /** @internal */
5742
-
5743
- /** @internal */
5744
-
5745
- /** @internal */
5746
-
5747
- /** @internal */
5938
+ /** @internal
5939
+ * A number that we increment every time an active connection closes.
5940
+ * Used to determine whether the same socket connection that was open
5941
+ * when an async operation started is the same one that's active when
5942
+ * its continuation fires.
5943
+ *
5944
+ */
5748
5945
 
5749
5946
  /** @internal */
5750
5947
 
@@ -5760,7 +5957,19 @@ class Connection {
5760
5957
 
5761
5958
  /** @internal */
5762
5959
 
5763
- /** @internal */
5960
+ /**
5961
+ * Special case.
5962
+ * After a signature is processed, RPCs automatically dispose of the
5963
+ * subscription on the server side. We need to track which of these
5964
+ * subscriptions have been disposed in such a way, so that we know
5965
+ * whether the client is dealing with a not-yet-processed signature
5966
+ * (in which case we must tear down the server subscription) or an
5967
+ * already-processed signature (in which case the client can simply
5968
+ * clear out the subscription locally without telling the server).
5969
+ *
5970
+ * NOTE: There is a proposal to eliminate this special case, here:
5971
+ * https://github.com/solana-labs/solana/issues/18892
5972
+ */
5764
5973
 
5765
5974
  /** @internal */
5766
5975
 
@@ -5782,6 +5991,7 @@ class Connection {
5782
5991
  this._rpcWebSocketConnected = false;
5783
5992
  this._rpcWebSocketHeartbeat = null;
5784
5993
  this._rpcWebSocketIdleTimeout = null;
5994
+ this._rpcWebSocketGeneration = 0;
5785
5995
  this._disableBlockhashCaching = false;
5786
5996
  this._pollingBlockhash = false;
5787
5997
  this._blockhashInfo = {
@@ -5790,20 +6000,11 @@ class Connection {
5790
6000
  transactionSignatures: [],
5791
6001
  simulatedSignatures: []
5792
6002
  };
5793
- this._accountChangeSubscriptionCounter = 0;
5794
- this._accountChangeSubscriptions = {};
5795
- this._programAccountChangeSubscriptionCounter = 0;
5796
- this._programAccountChangeSubscriptions = {};
5797
- this._rootSubscriptionCounter = 0;
5798
- this._rootSubscriptions = {};
5799
- this._signatureSubscriptionCounter = 0;
5800
- this._signatureSubscriptions = {};
5801
- this._slotSubscriptionCounter = 0;
5802
- this._slotSubscriptions = {};
5803
- this._logsSubscriptionCounter = 0;
5804
- this._logsSubscriptions = {};
5805
- this._slotUpdateSubscriptionCounter = 0;
5806
- this._slotUpdateSubscriptions = {};
6003
+ this._nextClientSubscriptionId = 0;
6004
+ this._subscriptionDisposeFunctionsByClientSubscriptionId = {};
6005
+ this._subscriptionCallbacksByServerSubscriptionId = {};
6006
+ this._subscriptionsByHash = {};
6007
+ this._subscriptionsAutoDisposedByRpc = new Set();
5807
6008
  let url = new URL(endpoint);
5808
6009
  const useHttps = url.protocol === 'https:';
5809
6010
  let wsEndpoint;
@@ -7571,6 +7772,8 @@ class Connection {
7571
7772
 
7572
7773
 
7573
7774
  _wsOnClose(code) {
7775
+ this._rpcWebSocketGeneration++;
7776
+
7574
7777
  if (this._rpcWebSocketHeartbeat) {
7575
7778
  clearInterval(this._rpcWebSocketHeartbeat);
7576
7779
  this._rpcWebSocketHeartbeat = null;
@@ -7584,85 +7787,20 @@ class Connection {
7584
7787
  } // implicit close, prepare subscriptions for auto-reconnect
7585
7788
 
7586
7789
 
7587
- this._resetSubscriptions();
7588
- }
7589
- /**
7590
- * @internal
7591
- */
7592
-
7593
-
7594
- async _subscribe(sub, rpcMethod, rpcArgs) {
7595
- if (sub.subscriptionId == null) {
7596
- sub.subscriptionId = 'subscribing';
7597
-
7598
- try {
7599
- const id = await this._rpcWebSocket.call(rpcMethod, rpcArgs);
7600
-
7601
- if (typeof id === 'number' && sub.subscriptionId === 'subscribing') {
7602
- // eslint-disable-next-line require-atomic-updates
7603
- sub.subscriptionId = id;
7604
- }
7605
- } catch (err) {
7606
- if (sub.subscriptionId === 'subscribing') {
7607
- // eslint-disable-next-line require-atomic-updates
7608
- sub.subscriptionId = null;
7609
- }
7610
-
7611
- if (err instanceof Error) {
7612
- console.error(`${rpcMethod} error for argument`, rpcArgs, err.message);
7613
- }
7614
- }
7615
- }
7616
- }
7617
- /**
7618
- * @internal
7619
- */
7620
-
7621
-
7622
- async _unsubscribe(sub, rpcMethod) {
7623
- const subscriptionId = sub.subscriptionId;
7624
-
7625
- if (subscriptionId != null && typeof subscriptionId != 'string') {
7626
- const unsubscribeId = subscriptionId;
7627
-
7628
- try {
7629
- await this._rpcWebSocket.call(rpcMethod, [unsubscribeId]);
7630
- } catch (err) {
7631
- if (err instanceof Error) {
7632
- console.error(`${rpcMethod} error:`, err.message);
7633
- }
7634
- }
7635
- }
7636
- }
7637
- /**
7638
- * @internal
7639
- */
7640
-
7641
-
7642
- _resetSubscriptions() {
7643
- Object.values(this._accountChangeSubscriptions).forEach(s => s.subscriptionId = null);
7644
- Object.values(this._logsSubscriptions).forEach(s => s.subscriptionId = null);
7645
- Object.values(this._programAccountChangeSubscriptions).forEach(s => s.subscriptionId = null);
7646
- Object.values(this._rootSubscriptions).forEach(s => s.subscriptionId = null);
7647
- Object.values(this._signatureSubscriptions).forEach(s => s.subscriptionId = null);
7648
- Object.values(this._slotSubscriptions).forEach(s => s.subscriptionId = null);
7649
- Object.values(this._slotUpdateSubscriptions).forEach(s => s.subscriptionId = null);
7790
+ this._subscriptionCallbacksByServerSubscriptionId = {};
7791
+ Object.entries(this._subscriptionsByHash).forEach(([hash, subscription]) => {
7792
+ this._subscriptionsByHash[hash] = { ...subscription,
7793
+ state: 'pending'
7794
+ };
7795
+ });
7650
7796
  }
7651
7797
  /**
7652
7798
  * @internal
7653
7799
  */
7654
7800
 
7655
7801
 
7656
- _updateSubscriptions() {
7657
- const accountKeys = Object.keys(this._accountChangeSubscriptions).map(Number);
7658
- const programKeys = Object.keys(this._programAccountChangeSubscriptions).map(Number);
7659
- const slotKeys = Object.keys(this._slotSubscriptions).map(Number);
7660
- const slotUpdateKeys = Object.keys(this._slotUpdateSubscriptions).map(Number);
7661
- const signatureKeys = Object.keys(this._signatureSubscriptions).map(Number);
7662
- const rootKeys = Object.keys(this._rootSubscriptions).map(Number);
7663
- const logsKeys = Object.keys(this._logsSubscriptions).map(Number);
7664
-
7665
- if (accountKeys.length === 0 && programKeys.length === 0 && slotKeys.length === 0 && slotUpdateKeys.length === 0 && signatureKeys.length === 0 && rootKeys.length === 0 && logsKeys.length === 0) {
7802
+ async _updateSubscriptions() {
7803
+ if (Object.keys(this._subscriptionsByHash).length === 0) {
7666
7804
  if (this._rpcWebSocketConnected) {
7667
7805
  this._rpcWebSocketConnected = false;
7668
7806
  this._rpcWebSocketIdleTimeout = setTimeout(() => {
@@ -7694,60 +7832,167 @@ class Connection {
7694
7832
  return;
7695
7833
  }
7696
7834
 
7697
- for (let id of accountKeys) {
7698
- const sub = this._accountChangeSubscriptions[id];
7835
+ const activeWebSocketGeneration = this._rpcWebSocketGeneration;
7699
7836
 
7700
- this._subscribe(sub, 'accountSubscribe', this._buildArgs([sub.publicKey], sub.commitment, 'base64'));
7701
- }
7702
-
7703
- for (let id of programKeys) {
7704
- const sub = this._programAccountChangeSubscriptions[id];
7837
+ const isCurrentConnectionStillActive = () => {
7838
+ return activeWebSocketGeneration === this._rpcWebSocketGeneration;
7839
+ };
7705
7840
 
7706
- this._subscribe(sub, 'programSubscribe', this._buildArgs([sub.programId], sub.commitment, 'base64', {
7707
- filters: sub.filters
7708
- }));
7709
- }
7841
+ await Promise.all( // Don't be tempted to change this to `Object.entries`. We call
7842
+ // `_updateSubscriptions` recursively when processing the state,
7843
+ // so it's important that we look up the *current* version of
7844
+ // each subscription, every time we process a hash.
7845
+ Object.keys(this._subscriptionsByHash).map(async hash => {
7846
+ const subscription = this._subscriptionsByHash[hash];
7710
7847
 
7711
- for (let id of slotKeys) {
7712
- const sub = this._slotSubscriptions[id];
7848
+ if (subscription === undefined) {
7849
+ // This entry has since been deleted. Skip.
7850
+ return;
7851
+ }
7713
7852
 
7714
- this._subscribe(sub, 'slotSubscribe', []);
7715
- }
7853
+ switch (subscription.state) {
7854
+ case 'pending':
7855
+ case 'unsubscribed':
7856
+ if (subscription.callbacks.size === 0) {
7857
+ /**
7858
+ * You can end up here when:
7859
+ *
7860
+ * - a subscription has recently unsubscribed
7861
+ * without having new callbacks added to it
7862
+ * while the unsubscribe was in flight, or
7863
+ * - when a pending subscription has its
7864
+ * listeners removed before a request was
7865
+ * sent to the server.
7866
+ *
7867
+ * Being that nobody is interested in this
7868
+ * subscription any longer, delete it.
7869
+ */
7870
+ delete this._subscriptionsByHash[hash];
7871
+
7872
+ if (subscription.state === 'unsubscribed') {
7873
+ delete this._subscriptionCallbacksByServerSubscriptionId[subscription.serverSubscriptionId];
7874
+ }
7716
7875
 
7717
- for (let id of slotUpdateKeys) {
7718
- const sub = this._slotUpdateSubscriptions[id];
7876
+ await this._updateSubscriptions();
7877
+ return;
7878
+ }
7719
7879
 
7720
- this._subscribe(sub, 'slotsUpdatesSubscribe', []);
7721
- }
7880
+ await (async () => {
7881
+ const {
7882
+ args,
7883
+ method
7884
+ } = subscription;
7722
7885
 
7723
- for (let id of signatureKeys) {
7724
- const sub = this._signatureSubscriptions[id];
7725
- const args = [sub.signature];
7726
- if (sub.options) args.push(sub.options);
7886
+ try {
7887
+ this._subscriptionsByHash[hash] = { ...subscription,
7888
+ state: 'subscribing'
7889
+ };
7890
+ const serverSubscriptionId = await this._rpcWebSocket.call(method, args);
7891
+ this._subscriptionsByHash[hash] = { ...subscription,
7892
+ serverSubscriptionId,
7893
+ state: 'subscribed'
7894
+ };
7895
+ this._subscriptionCallbacksByServerSubscriptionId[serverSubscriptionId] = subscription.callbacks;
7896
+ await this._updateSubscriptions();
7897
+ } catch (e) {
7898
+ if (e instanceof Error) {
7899
+ console.error(`${method} error for argument`, args, e.message);
7900
+ }
7901
+
7902
+ if (!isCurrentConnectionStillActive()) {
7903
+ return;
7904
+ } // TODO: Maybe add an 'errored' state or a retry limit?
7727
7905
 
7728
- this._subscribe(sub, 'signatureSubscribe', args);
7729
- }
7730
7906
 
7731
- for (let id of rootKeys) {
7732
- const sub = this._rootSubscriptions[id];
7907
+ this._subscriptionsByHash[hash] = { ...subscription,
7908
+ state: 'pending'
7909
+ };
7910
+ await this._updateSubscriptions();
7911
+ }
7912
+ })();
7913
+ break;
7733
7914
 
7734
- this._subscribe(sub, 'rootSubscribe', []);
7735
- }
7915
+ case 'subscribed':
7916
+ if (subscription.callbacks.size === 0) {
7917
+ // By the time we successfully set up a subscription
7918
+ // with the server, the client stopped caring about it.
7919
+ // Tear it down now.
7920
+ await (async () => {
7921
+ const {
7922
+ serverSubscriptionId,
7923
+ unsubscribeMethod
7924
+ } = subscription;
7925
+
7926
+ if (this._subscriptionsAutoDisposedByRpc.has(serverSubscriptionId)) {
7927
+ /**
7928
+ * Special case.
7929
+ * If we're dealing with a subscription that has been auto-
7930
+ * disposed by the RPC, then we can skip the RPC call to
7931
+ * tear down the subscription here.
7932
+ *
7933
+ * NOTE: There is a proposal to eliminate this special case, here:
7934
+ * https://github.com/solana-labs/solana/issues/18892
7935
+ */
7936
+ this._subscriptionsAutoDisposedByRpc.delete(serverSubscriptionId);
7937
+ } else {
7938
+ this._subscriptionsByHash[hash] = { ...subscription,
7939
+ state: 'unsubscribing'
7940
+ };
7941
+
7942
+ try {
7943
+ await this._rpcWebSocket.call(unsubscribeMethod, [serverSubscriptionId]);
7944
+ } catch (e) {
7945
+ if (e instanceof Error) {
7946
+ console.error(`${unsubscribeMethod} error:`, e.message);
7947
+ }
7948
+
7949
+ if (!isCurrentConnectionStillActive()) {
7950
+ return;
7951
+ } // TODO: Maybe add an 'errored' state or a retry limit?
7952
+
7953
+
7954
+ this._subscriptionsByHash[hash] = { ...subscription,
7955
+ state: 'subscribed'
7956
+ };
7957
+ await this._updateSubscriptions();
7958
+ return;
7959
+ }
7960
+ }
7736
7961
 
7737
- for (let id of logsKeys) {
7738
- const sub = this._logsSubscriptions[id];
7739
- let filter;
7962
+ this._subscriptionsByHash[hash] = { ...subscription,
7963
+ state: 'unsubscribed'
7964
+ };
7965
+ await this._updateSubscriptions();
7966
+ })();
7967
+ }
7740
7968
 
7741
- if (typeof sub.filter === 'object') {
7742
- filter = {
7743
- mentions: [sub.filter.toString()]
7744
- };
7745
- } else {
7746
- filter = sub.filter;
7969
+ break;
7747
7970
  }
7971
+ }));
7972
+ }
7973
+ /**
7974
+ * @internal
7975
+ */
7976
+
7977
+
7978
+ _handleServerNotification(serverSubscriptionId, callbackArgs) {
7979
+ const callbacks = this._subscriptionCallbacksByServerSubscriptionId[serverSubscriptionId];
7748
7980
 
7749
- this._subscribe(sub, 'logsSubscribe', this._buildArgs([filter], sub.commitment));
7981
+ if (callbacks === undefined) {
7982
+ return;
7750
7983
  }
7984
+
7985
+ callbacks.forEach(cb => {
7986
+ try {
7987
+ cb( // I failed to find a way to convince TypeScript that `cb` is of type
7988
+ // `TCallback` which is certainly compatible with `Parameters<TCallback>`.
7989
+ // See https://github.com/microsoft/TypeScript/issues/47615
7990
+ // @ts-ignore
7991
+ ...callbackArgs);
7992
+ } catch (e) {
7993
+ console.error(e);
7994
+ }
7995
+ });
7751
7996
  }
7752
7997
  /**
7753
7998
  * @internal
@@ -7755,14 +8000,71 @@ class Connection {
7755
8000
 
7756
8001
 
7757
8002
  _wsOnAccountNotification(notification) {
7758
- const res = superstruct.create(notification, AccountNotificationResult);
8003
+ const {
8004
+ result,
8005
+ subscription
8006
+ } = superstruct.create(notification, AccountNotificationResult);
7759
8007
 
7760
- for (const sub of Object.values(this._accountChangeSubscriptions)) {
7761
- if (sub.subscriptionId === res.subscription) {
7762
- sub.callback(res.result.value, res.result.context);
7763
- return;
7764
- }
8008
+ this._handleServerNotification(subscription, [result.value, result.context]);
8009
+ }
8010
+ /**
8011
+ * @internal
8012
+ */
8013
+
8014
+
8015
+ _makeSubscription(subscriptionConfig,
8016
+ /**
8017
+ * When preparing `args` for a call to `_makeSubscription`, be sure
8018
+ * to carefully apply a default `commitment` property, if necessary.
8019
+ *
8020
+ * - If the user supplied a `commitment` use that.
8021
+ * - Otherwise, if the `Connection::commitment` is set, use that.
8022
+ * - Otherwise, set it to the RPC server default: `finalized`.
8023
+ *
8024
+ * This is extremely important to ensure that these two fundamentally
8025
+ * identical subscriptions produce the same identifying hash:
8026
+ *
8027
+ * - A subscription made without specifying a commitment.
8028
+ * - A subscription made where the commitment specified is the same
8029
+ * as the default applied to the subscription above.
8030
+ *
8031
+ * Example; these two subscriptions must produce the same hash:
8032
+ *
8033
+ * - An `accountSubscribe` subscription for `'PUBKEY'`
8034
+ * - An `accountSubscribe` subscription for `'PUBKEY'` with commitment
8035
+ * `'finalized'`.
8036
+ *
8037
+ * See the 'making a subscription with defaulted params omitted' test
8038
+ * in `connection-subscriptions.ts` for more.
8039
+ */
8040
+ args) {
8041
+ const clientSubscriptionId = this._nextClientSubscriptionId++;
8042
+ const hash = fastStableStringify$1([subscriptionConfig.method, args], true
8043
+ /* isArrayProp */
8044
+ );
8045
+ const existingSubscription = this._subscriptionsByHash[hash];
8046
+
8047
+ if (existingSubscription === undefined) {
8048
+ this._subscriptionsByHash[hash] = { ...subscriptionConfig,
8049
+ args,
8050
+ callbacks: new Set([subscriptionConfig.callback]),
8051
+ state: 'pending'
8052
+ };
8053
+ } else {
8054
+ existingSubscription.callbacks.add(subscriptionConfig.callback);
7765
8055
  }
8056
+
8057
+ this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId] = async () => {
8058
+ delete this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId];
8059
+ const subscription = this._subscriptionsByHash[hash];
8060
+ assert(subscription !== undefined, `Could not find a \`Subscription\` when tearing down client subscription #${clientSubscriptionId}`);
8061
+ subscription.callbacks.delete(subscriptionConfig.callback);
8062
+ await this._updateSubscriptions();
8063
+ };
8064
+
8065
+ this._updateSubscriptions();
8066
+
8067
+ return clientSubscriptionId;
7766
8068
  }
7767
8069
  /**
7768
8070
  * Register a callback to be invoked whenever the specified account changes
@@ -7775,35 +8077,24 @@ class Connection {
7775
8077
 
7776
8078
 
7777
8079
  onAccountChange(publicKey, callback, commitment) {
7778
- const id = ++this._accountChangeSubscriptionCounter;
7779
- this._accountChangeSubscriptions[id] = {
7780
- publicKey: publicKey.toBase58(),
7781
- callback,
7782
- commitment,
7783
- subscriptionId: null
7784
- };
7785
-
7786
- this._updateSubscriptions();
8080
+ const args = this._buildArgs([publicKey.toBase58()], commitment || this._commitment || 'finalized', // Apply connection/server default.
8081
+ 'base64');
7787
8082
 
7788
- return id;
8083
+ return this._makeSubscription({
8084
+ callback,
8085
+ method: 'accountSubscribe',
8086
+ unsubscribeMethod: 'accountUnsubscribe'
8087
+ }, args);
7789
8088
  }
7790
8089
  /**
7791
8090
  * Deregister an account notification callback
7792
8091
  *
7793
- * @param id subscription id to deregister
8092
+ * @param id client subscription id to deregister
7794
8093
  */
7795
8094
 
7796
8095
 
7797
- async removeAccountChangeListener(id) {
7798
- if (this._accountChangeSubscriptions[id]) {
7799
- const subInfo = this._accountChangeSubscriptions[id];
7800
- delete this._accountChangeSubscriptions[id];
7801
- await this._unsubscribe(subInfo, 'accountUnsubscribe');
7802
-
7803
- this._updateSubscriptions();
7804
- } else {
7805
- console.warn(createSubscriptionWarningMessage(id, 'account change'));
7806
- }
8096
+ async removeAccountChangeListener(clientSubscriptionId) {
8097
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'account change');
7807
8098
  }
7808
8099
  /**
7809
8100
  * @internal
@@ -7811,21 +8102,15 @@ class Connection {
7811
8102
 
7812
8103
 
7813
8104
  _wsOnProgramAccountNotification(notification) {
7814
- const res = superstruct.create(notification, ProgramAccountNotificationResult);
8105
+ const {
8106
+ result,
8107
+ subscription
8108
+ } = superstruct.create(notification, ProgramAccountNotificationResult);
7815
8109
 
7816
- for (const sub of Object.values(this._programAccountChangeSubscriptions)) {
7817
- if (sub.subscriptionId === res.subscription) {
7818
- const {
7819
- value,
7820
- context
7821
- } = res.result;
7822
- sub.callback({
7823
- accountId: value.pubkey,
7824
- accountInfo: value.account
7825
- }, context);
7826
- return;
7827
- }
7828
- }
8110
+ this._handleServerNotification(subscription, [{
8111
+ accountId: result.value.pubkey,
8112
+ accountInfo: result.value.account
8113
+ }, result.context]);
7829
8114
  }
7830
8115
  /**
7831
8116
  * Register a callback to be invoked whenever accounts owned by the
@@ -7840,36 +8125,30 @@ class Connection {
7840
8125
 
7841
8126
 
7842
8127
  onProgramAccountChange(programId, callback, commitment, filters) {
7843
- const id = ++this._programAccountChangeSubscriptionCounter;
7844
- this._programAccountChangeSubscriptions[id] = {
7845
- programId: programId.toBase58(),
8128
+ const args = this._buildArgs([programId.toBase58()], commitment || this._commitment || 'finalized', // Apply connection/server default.
8129
+ 'base64'
8130
+ /* encoding */
8131
+ , filters ? {
8132
+ filters: filters
8133
+ } : undefined
8134
+ /* extra */
8135
+ );
8136
+
8137
+ return this._makeSubscription({
7846
8138
  callback,
7847
- commitment,
7848
- subscriptionId: null,
7849
- filters
7850
- };
7851
-
7852
- this._updateSubscriptions();
7853
-
7854
- return id;
8139
+ method: 'programSubscribe',
8140
+ unsubscribeMethod: 'programUnsubscribe'
8141
+ }, args);
7855
8142
  }
7856
8143
  /**
7857
8144
  * Deregister an account notification callback
7858
8145
  *
7859
- * @param id subscription id to deregister
8146
+ * @param id client subscription id to deregister
7860
8147
  */
7861
8148
 
7862
8149
 
7863
- async removeProgramAccountChangeListener(id) {
7864
- if (this._programAccountChangeSubscriptions[id]) {
7865
- const subInfo = this._programAccountChangeSubscriptions[id];
7866
- delete this._programAccountChangeSubscriptions[id];
7867
- await this._unsubscribe(subInfo, 'programUnsubscribe');
7868
-
7869
- this._updateSubscriptions();
7870
- } else {
7871
- console.warn(createSubscriptionWarningMessage(id, 'program account change'));
7872
- }
8150
+ async removeProgramAccountChangeListener(clientSubscriptionId) {
8151
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'program account change');
7873
8152
  }
7874
8153
  /**
7875
8154
  * Registers a callback to be invoked whenever logs are emitted.
@@ -7877,35 +8156,26 @@ class Connection {
7877
8156
 
7878
8157
 
7879
8158
  onLogs(filter, callback, commitment) {
7880
- const id = ++this._logsSubscriptionCounter;
7881
- this._logsSubscriptions[id] = {
7882
- filter,
7883
- callback,
7884
- commitment,
7885
- subscriptionId: null
7886
- };
8159
+ const args = this._buildArgs([typeof filter === 'object' ? {
8160
+ mentions: [filter.toString()]
8161
+ } : filter], commitment || this._commitment || 'finalized' // Apply connection/server default.
8162
+ );
7887
8163
 
7888
- this._updateSubscriptions();
7889
-
7890
- return id;
8164
+ return this._makeSubscription({
8165
+ callback,
8166
+ method: 'logsSubscribe',
8167
+ unsubscribeMethod: 'logsUnsubscribe'
8168
+ }, args);
7891
8169
  }
7892
8170
  /**
7893
8171
  * Deregister a logs callback.
7894
8172
  *
7895
- * @param id subscription id to deregister.
8173
+ * @param id client subscription id to deregister.
7896
8174
  */
7897
8175
 
7898
8176
 
7899
- async removeOnLogsListener(id) {
7900
- if (this._logsSubscriptions[id]) {
7901
- const subInfo = this._logsSubscriptions[id];
7902
- delete this._logsSubscriptions[id];
7903
- await this._unsubscribe(subInfo, 'logsUnsubscribe');
7904
-
7905
- this._updateSubscriptions();
7906
- } else {
7907
- console.warn(createSubscriptionWarningMessage(id, 'logs'));
7908
- }
8177
+ async removeOnLogsListener(clientSubscriptionId) {
8178
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'logs');
7909
8179
  }
7910
8180
  /**
7911
8181
  * @internal
@@ -7913,17 +8183,12 @@ class Connection {
7913
8183
 
7914
8184
 
7915
8185
  _wsOnLogsNotification(notification) {
7916
- const res = superstruct.create(notification, LogsNotificationResult);
7917
- const keys = Object.keys(this._logsSubscriptions).map(Number);
7918
-
7919
- for (let id of keys) {
7920
- const sub = this._logsSubscriptions[id];
8186
+ const {
8187
+ result,
8188
+ subscription
8189
+ } = superstruct.create(notification, LogsNotificationResult);
7921
8190
 
7922
- if (sub.subscriptionId === res.subscription) {
7923
- sub.callback(res.result.value, res.result.context);
7924
- return;
7925
- }
7926
- }
8191
+ this._handleServerNotification(subscription, [result.value, result.context]);
7927
8192
  }
7928
8193
  /**
7929
8194
  * @internal
@@ -7931,14 +8196,12 @@ class Connection {
7931
8196
 
7932
8197
 
7933
8198
  _wsOnSlotNotification(notification) {
7934
- const res = superstruct.create(notification, SlotNotificationResult);
8199
+ const {
8200
+ result,
8201
+ subscription
8202
+ } = superstruct.create(notification, SlotNotificationResult);
7935
8203
 
7936
- for (const sub of Object.values(this._slotSubscriptions)) {
7937
- if (sub.subscriptionId === res.subscription) {
7938
- sub.callback(res.result);
7939
- return;
7940
- }
7941
- }
8204
+ this._handleServerNotification(subscription, [result]);
7942
8205
  }
7943
8206
  /**
7944
8207
  * Register a callback to be invoked upon slot changes
@@ -7949,33 +8212,23 @@ class Connection {
7949
8212
 
7950
8213
 
7951
8214
  onSlotChange(callback) {
7952
- const id = ++this._slotSubscriptionCounter;
7953
- this._slotSubscriptions[id] = {
8215
+ return this._makeSubscription({
7954
8216
  callback,
7955
- subscriptionId: null
7956
- };
7957
-
7958
- this._updateSubscriptions();
7959
-
7960
- return id;
8217
+ method: 'slotSubscribe',
8218
+ unsubscribeMethod: 'slotUnsubscribe'
8219
+ }, []
8220
+ /* args */
8221
+ );
7961
8222
  }
7962
8223
  /**
7963
8224
  * Deregister a slot notification callback
7964
8225
  *
7965
- * @param id subscription id to deregister
8226
+ * @param id client subscription id to deregister
7966
8227
  */
7967
8228
 
7968
8229
 
7969
- async removeSlotChangeListener(id) {
7970
- if (this._slotSubscriptions[id]) {
7971
- const subInfo = this._slotSubscriptions[id];
7972
- delete this._slotSubscriptions[id];
7973
- await this._unsubscribe(subInfo, 'slotUnsubscribe');
7974
-
7975
- this._updateSubscriptions();
7976
- } else {
7977
- console.warn(createSubscriptionWarningMessage(id, 'slot change'));
7978
- }
8230
+ async removeSlotChangeListener(clientSubscriptionId) {
8231
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'slot change');
7979
8232
  }
7980
8233
  /**
7981
8234
  * @internal
@@ -7983,14 +8236,12 @@ class Connection {
7983
8236
 
7984
8237
 
7985
8238
  _wsOnSlotUpdatesNotification(notification) {
7986
- const res = superstruct.create(notification, SlotUpdateNotificationResult);
8239
+ const {
8240
+ result,
8241
+ subscription
8242
+ } = superstruct.create(notification, SlotUpdateNotificationResult);
7987
8243
 
7988
- for (const sub of Object.values(this._slotUpdateSubscriptions)) {
7989
- if (sub.subscriptionId === res.subscription) {
7990
- sub.callback(res.result);
7991
- return;
7992
- }
7993
- }
8244
+ this._handleServerNotification(subscription, [result]);
7994
8245
  }
7995
8246
  /**
7996
8247
  * Register a callback to be invoked upon slot updates. {@link SlotUpdate}'s
@@ -8002,32 +8253,36 @@ class Connection {
8002
8253
 
8003
8254
 
8004
8255
  onSlotUpdate(callback) {
8005
- const id = ++this._slotUpdateSubscriptionCounter;
8006
- this._slotUpdateSubscriptions[id] = {
8256
+ return this._makeSubscription({
8007
8257
  callback,
8008
- subscriptionId: null
8009
- };
8010
-
8011
- this._updateSubscriptions();
8012
-
8013
- return id;
8258
+ method: 'slotsUpdatesSubscribe',
8259
+ unsubscribeMethod: 'slotsUpdatesUnsubscribe'
8260
+ }, []
8261
+ /* args */
8262
+ );
8014
8263
  }
8015
8264
  /**
8016
8265
  * Deregister a slot update notification callback
8017
8266
  *
8018
- * @param id subscription id to deregister
8267
+ * @param id client subscription id to deregister
8019
8268
  */
8020
8269
 
8021
8270
 
8022
- async removeSlotUpdateListener(id) {
8023
- if (this._slotUpdateSubscriptions[id]) {
8024
- const subInfo = this._slotUpdateSubscriptions[id];
8025
- delete this._slotUpdateSubscriptions[id];
8026
- await this._unsubscribe(subInfo, 'slotsUpdatesUnsubscribe');
8271
+ async removeSlotUpdateListener(clientSubscriptionId) {
8272
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'slot update');
8273
+ }
8274
+ /**
8275
+ * @internal
8276
+ */
8027
8277
 
8028
- this._updateSubscriptions();
8278
+
8279
+ async _unsubscribeClientSubscription(clientSubscriptionId, subscriptionName) {
8280
+ const dispose = this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId];
8281
+
8282
+ if (dispose) {
8283
+ await dispose();
8029
8284
  } else {
8030
- console.warn(createSubscriptionWarningMessage(id, 'slot update'));
8285
+ console.warn('Ignored unsubscribe request because an active subscription with id ' + `\`${clientSubscriptionId}\` for '${subscriptionName}' events ` + 'could not be found.');
8031
8286
  }
8032
8287
  }
8033
8288
 
@@ -8074,30 +8329,34 @@ class Connection {
8074
8329
 
8075
8330
 
8076
8331
  _wsOnSignatureNotification(notification) {
8077
- const res = superstruct.create(notification, SignatureNotificationResult);
8078
-
8079
- for (const [id, sub] of Object.entries(this._signatureSubscriptions)) {
8080
- if (sub.subscriptionId === res.subscription) {
8081
- if (res.result.value === 'receivedSignature') {
8082
- sub.callback({
8083
- type: 'received'
8084
- }, res.result.context);
8085
- } else {
8086
- // Signatures subscriptions are auto-removed by the RPC service so
8087
- // no need to explicitly send an unsubscribe message
8088
- delete this._signatureSubscriptions[Number(id)];
8089
-
8090
- this._updateSubscriptions();
8091
-
8092
- sub.callback({
8093
- type: 'status',
8094
- result: res.result.value
8095
- }, res.result.context);
8096
- }
8097
-
8098
- return;
8099
- }
8100
- }
8332
+ const {
8333
+ result,
8334
+ subscription
8335
+ } = superstruct.create(notification, SignatureNotificationResult);
8336
+
8337
+ if (result.value !== 'receivedSignature') {
8338
+ /**
8339
+ * Special case.
8340
+ * After a signature is processed, RPCs automatically dispose of the
8341
+ * subscription on the server side. We need to track which of these
8342
+ * subscriptions have been disposed in such a way, so that we know
8343
+ * whether the client is dealing with a not-yet-processed signature
8344
+ * (in which case we must tear down the server subscription) or an
8345
+ * already-processed signature (in which case the client can simply
8346
+ * clear out the subscription locally without telling the server).
8347
+ *
8348
+ * NOTE: There is a proposal to eliminate this special case, here:
8349
+ * https://github.com/solana-labs/solana/issues/18892
8350
+ */
8351
+ this._subscriptionsAutoDisposedByRpc.add(subscription);
8352
+ }
8353
+
8354
+ this._handleServerNotification(subscription, result.value === 'receivedSignature' ? [{
8355
+ type: 'received'
8356
+ }, result.context] : [{
8357
+ type: 'status',
8358
+ result: result.value
8359
+ }, result.context]);
8101
8360
  }
8102
8361
  /**
8103
8362
  * Register a callback to be invoked upon signature updates
@@ -8110,23 +8369,26 @@ class Connection {
8110
8369
 
8111
8370
 
8112
8371
  onSignature(signature, callback, commitment) {
8113
- const id = ++this._signatureSubscriptionCounter;
8114
- this._signatureSubscriptions[id] = {
8115
- signature,
8372
+ const args = this._buildArgs([signature], commitment || this._commitment || 'finalized' // Apply connection/server default.
8373
+ );
8374
+
8375
+ const clientSubscriptionId = this._makeSubscription({
8116
8376
  callback: (notification, context) => {
8117
8377
  if (notification.type === 'status') {
8118
- callback(notification.result, context);
8378
+ callback(notification.result, context); // Signatures subscriptions are auto-removed by the RPC service
8379
+ // so no need to explicitly send an unsubscribe message.
8380
+
8381
+ try {
8382
+ this.removeSignatureListener(clientSubscriptionId); // eslint-disable-next-line no-empty
8383
+ } catch {// Already removed.
8384
+ }
8119
8385
  }
8120
8386
  },
8121
- options: {
8122
- commitment
8123
- },
8124
- subscriptionId: null
8125
- };
8387
+ method: 'signatureSubscribe',
8388
+ unsubscribeMethod: 'signatureUnsubscribe'
8389
+ }, args);
8126
8390
 
8127
- this._updateSubscriptions();
8128
-
8129
- return id;
8391
+ return clientSubscriptionId;
8130
8392
  }
8131
8393
  /**
8132
8394
  * Register a callback to be invoked when a transaction is
@@ -8141,35 +8403,43 @@ class Connection {
8141
8403
 
8142
8404
 
8143
8405
  onSignatureWithOptions(signature, callback, options) {
8144
- const id = ++this._signatureSubscriptionCounter;
8145
- this._signatureSubscriptions[id] = {
8146
- signature,
8147
- callback,
8148
- options,
8149
- subscriptionId: null
8406
+ const {
8407
+ commitment,
8408
+ ...extra
8409
+ } = { ...options,
8410
+ commitment: options && options.commitment || this._commitment || 'finalized' // Apply connection/server default.
8411
+
8150
8412
  };
8151
8413
 
8152
- this._updateSubscriptions();
8414
+ const args = this._buildArgs([signature], commitment, undefined
8415
+ /* encoding */
8416
+ , extra);
8153
8417
 
8154
- return id;
8418
+ const clientSubscriptionId = this._makeSubscription({
8419
+ callback: (notification, context) => {
8420
+ callback(notification, context); // Signatures subscriptions are auto-removed by the RPC service
8421
+ // so no need to explicitly send an unsubscribe message.
8422
+
8423
+ try {
8424
+ this.removeSignatureListener(clientSubscriptionId); // eslint-disable-next-line no-empty
8425
+ } catch {// Already removed.
8426
+ }
8427
+ },
8428
+ method: 'signatureSubscribe',
8429
+ unsubscribeMethod: 'signatureUnsubscribe'
8430
+ }, args);
8431
+
8432
+ return clientSubscriptionId;
8155
8433
  }
8156
8434
  /**
8157
8435
  * Deregister a signature notification callback
8158
8436
  *
8159
- * @param id subscription id to deregister
8437
+ * @param id client subscription id to deregister
8160
8438
  */
8161
8439
 
8162
8440
 
8163
- async removeSignatureListener(id) {
8164
- if (this._signatureSubscriptions[id]) {
8165
- const subInfo = this._signatureSubscriptions[id];
8166
- delete this._signatureSubscriptions[id];
8167
- await this._unsubscribe(subInfo, 'signatureUnsubscribe');
8168
-
8169
- this._updateSubscriptions();
8170
- } else {
8171
- console.warn(createSubscriptionWarningMessage(id, 'signature result'));
8172
- }
8441
+ async removeSignatureListener(clientSubscriptionId) {
8442
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'signature result');
8173
8443
  }
8174
8444
  /**
8175
8445
  * @internal
@@ -8177,14 +8447,12 @@ class Connection {
8177
8447
 
8178
8448
 
8179
8449
  _wsOnRootNotification(notification) {
8180
- const res = superstruct.create(notification, RootNotificationResult);
8450
+ const {
8451
+ result,
8452
+ subscription
8453
+ } = superstruct.create(notification, RootNotificationResult);
8181
8454
 
8182
- for (const sub of Object.values(this._rootSubscriptions)) {
8183
- if (sub.subscriptionId === res.subscription) {
8184
- sub.callback(res.result);
8185
- return;
8186
- }
8187
- }
8455
+ this._handleServerNotification(subscription, [result]);
8188
8456
  }
8189
8457
  /**
8190
8458
  * Register a callback to be invoked upon root changes
@@ -8195,33 +8463,23 @@ class Connection {
8195
8463
 
8196
8464
 
8197
8465
  onRootChange(callback) {
8198
- const id = ++this._rootSubscriptionCounter;
8199
- this._rootSubscriptions[id] = {
8466
+ return this._makeSubscription({
8200
8467
  callback,
8201
- subscriptionId: null
8202
- };
8203
-
8204
- this._updateSubscriptions();
8205
-
8206
- return id;
8468
+ method: 'rootSubscribe',
8469
+ unsubscribeMethod: 'rootUnsubscribe'
8470
+ }, []
8471
+ /* args */
8472
+ );
8207
8473
  }
8208
8474
  /**
8209
8475
  * Deregister a root notification callback
8210
8476
  *
8211
- * @param id subscription id to deregister
8477
+ * @param id client subscription id to deregister
8212
8478
  */
8213
8479
 
8214
8480
 
8215
- async removeRootChangeListener(id) {
8216
- if (this._rootSubscriptions[id]) {
8217
- const subInfo = this._rootSubscriptions[id];
8218
- delete this._rootSubscriptions[id];
8219
- await this._unsubscribe(subInfo, 'rootUnsubscribe');
8220
-
8221
- this._updateSubscriptions();
8222
- } else {
8223
- console.warn(createSubscriptionWarningMessage(id, 'root change'));
8224
- }
8481
+ async removeRootChangeListener(clientSubscriptionId) {
8482
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'root change');
8225
8483
  }
8226
8484
 
8227
8485
  }
@@ -9907,6 +10165,9 @@ exports.BLOCKHASH_CACHE_TIMEOUT_MS = BLOCKHASH_CACHE_TIMEOUT_MS;
9907
10165
  exports.BPF_LOADER_DEPRECATED_PROGRAM_ID = BPF_LOADER_DEPRECATED_PROGRAM_ID;
9908
10166
  exports.BPF_LOADER_PROGRAM_ID = BPF_LOADER_PROGRAM_ID;
9909
10167
  exports.BpfLoader = BpfLoader;
10168
+ exports.COMPUTE_BUDGET_INSTRUCTION_LAYOUTS = COMPUTE_BUDGET_INSTRUCTION_LAYOUTS;
10169
+ exports.ComputeBudgetInstruction = ComputeBudgetInstruction;
10170
+ exports.ComputeBudgetProgram = ComputeBudgetProgram;
9910
10171
  exports.Connection = Connection;
9911
10172
  exports.Ed25519Program = Ed25519Program;
9912
10173
  exports.Enum = Enum;
@@ -9920,7 +10181,6 @@ exports.MAX_SEED_LENGTH = MAX_SEED_LENGTH;
9920
10181
  exports.Message = Message;
9921
10182
  exports.NONCE_ACCOUNT_LENGTH = NONCE_ACCOUNT_LENGTH;
9922
10183
  exports.NonceAccount = NonceAccount;
9923
- exports.PACKET_DATA_SIZE = PACKET_DATA_SIZE;
9924
10184
  exports.PublicKey = PublicKey;
9925
10185
  exports.SOLANA_SCHEMA = SOLANA_SCHEMA;
9926
10186
  exports.STAKE_CONFIG_ID = STAKE_CONFIG_ID;