@solana/web3.js 1.41.1 → 1.41.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3099,6 +3099,92 @@ function sleep(ms) {
3099
3099
  return new Promise(resolve => setTimeout(resolve, ms));
3100
3100
  }
3101
3101
 
3102
+ const encodeDecode = (layout) => {
3103
+ const decode = layout.decode.bind(layout);
3104
+ const encode = layout.encode.bind(layout);
3105
+ return { decode, encode };
3106
+ };
3107
+
3108
+ var browser = {};
3109
+
3110
+ Object.defineProperty(browser, "__esModule", { value: true });
3111
+ /**
3112
+ * Convert a little-endian buffer into a BigInt.
3113
+ * @param buf The little-endian buffer to convert
3114
+ * @returns A BigInt with the little-endian representation of buf.
3115
+ */
3116
+ function toBigIntLE(buf) {
3117
+ {
3118
+ const reversed = Buffer.from(buf);
3119
+ reversed.reverse();
3120
+ const hex = reversed.toString('hex');
3121
+ if (hex.length === 0) {
3122
+ return BigInt(0);
3123
+ }
3124
+ return BigInt(`0x${hex}`);
3125
+ }
3126
+ }
3127
+ var toBigIntLE_1 = browser.toBigIntLE = toBigIntLE;
3128
+ /**
3129
+ * Convert a big-endian buffer into a BigInt
3130
+ * @param buf The big-endian buffer to convert.
3131
+ * @returns A BigInt with the big-endian representation of buf.
3132
+ */
3133
+ function toBigIntBE(buf) {
3134
+ {
3135
+ const hex = buf.toString('hex');
3136
+ if (hex.length === 0) {
3137
+ return BigInt(0);
3138
+ }
3139
+ return BigInt(`0x${hex}`);
3140
+ }
3141
+ }
3142
+ browser.toBigIntBE = toBigIntBE;
3143
+ /**
3144
+ * Convert a BigInt to a little-endian buffer.
3145
+ * @param num The BigInt to convert.
3146
+ * @param width The number of bytes that the resulting buffer should be.
3147
+ * @returns A little-endian buffer representation of num.
3148
+ */
3149
+ function toBufferLE(num, width) {
3150
+ {
3151
+ const hex = num.toString(16);
3152
+ const buffer = Buffer.from(hex.padStart(width * 2, '0').slice(0, width * 2), 'hex');
3153
+ buffer.reverse();
3154
+ return buffer;
3155
+ }
3156
+ }
3157
+ var toBufferLE_1 = browser.toBufferLE = toBufferLE;
3158
+ /**
3159
+ * Convert a BigInt to a big-endian buffer.
3160
+ * @param num The BigInt to convert.
3161
+ * @param width The number of bytes that the resulting buffer should be.
3162
+ * @returns A big-endian buffer representation of num.
3163
+ */
3164
+ function toBufferBE(num, width) {
3165
+ {
3166
+ const hex = num.toString(16);
3167
+ return Buffer.from(hex.padStart(width * 2, '0').slice(0, width * 2), 'hex');
3168
+ }
3169
+ }
3170
+ browser.toBufferBE = toBufferBE;
3171
+
3172
+ const bigInt = (length) => (property) => {
3173
+ const layout = BufferLayout.blob(length, property);
3174
+ const { encode, decode } = encodeDecode(layout);
3175
+ const bigIntLayout = layout;
3176
+ bigIntLayout.decode = (buffer, offset) => {
3177
+ const src = decode(buffer, offset);
3178
+ return toBigIntLE_1(Buffer.from(src));
3179
+ };
3180
+ bigIntLayout.encode = (bigInt, buffer, offset) => {
3181
+ const src = toBufferLE_1(bigInt, length);
3182
+ return encode(src, buffer, offset);
3183
+ };
3184
+ return bigIntLayout;
3185
+ };
3186
+ const u64 = bigInt(8);
3187
+
3102
3188
  /**
3103
3189
  * Populate a buffer of instruction data using an InstructionType
3104
3190
  * @internal
@@ -3488,7 +3574,7 @@ const SYSTEM_INSTRUCTION_LAYOUTS = Object.freeze({
3488
3574
  },
3489
3575
  Transfer: {
3490
3576
  index: 2,
3491
- layout: BufferLayout__namespace.struct([BufferLayout__namespace.u32('instruction'), BufferLayout__namespace.ns64('lamports')])
3577
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u32('instruction'), u64('lamports')])
3492
3578
  },
3493
3579
  CreateWithSeed: {
3494
3580
  index: 3,
@@ -3524,7 +3610,7 @@ const SYSTEM_INSTRUCTION_LAYOUTS = Object.freeze({
3524
3610
  },
3525
3611
  TransferWithSeed: {
3526
3612
  index: 11,
3527
- layout: BufferLayout__namespace.struct([BufferLayout__namespace.u32('instruction'), BufferLayout__namespace.ns64('lamports'), rustString('seed'), publicKey('programId')])
3613
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u32('instruction'), u64('lamports'), rustString('seed'), publicKey('programId')])
3528
3614
  }
3529
3615
  });
3530
3616
  /**
@@ -3577,7 +3663,7 @@ class SystemProgram {
3577
3663
  if ('basePubkey' in params) {
3578
3664
  const type = SYSTEM_INSTRUCTION_LAYOUTS.TransferWithSeed;
3579
3665
  data = encodeData(type, {
3580
- lamports: params.lamports,
3666
+ lamports: BigInt(params.lamports),
3581
3667
  seed: params.seed,
3582
3668
  programId: toBuffer(params.programId.toBuffer())
3583
3669
  });
@@ -3597,7 +3683,7 @@ class SystemProgram {
3597
3683
  } else {
3598
3684
  const type = SYSTEM_INSTRUCTION_LAYOUTS.Transfer;
3599
3685
  data = encodeData(type, {
3600
- lamports: params.lamports
3686
+ lamports: BigInt(params.lamports)
3601
3687
  });
3602
3688
  keys = [{
3603
3689
  pubkey: params.fromPubkey,
@@ -4793,6 +4879,82 @@ module.exports = exports;
4793
4879
 
4794
4880
  var crossFetch = /*@__PURE__*/getDefaultExportFromCjs(browserPonyfill.exports);
4795
4881
 
4882
+ var objToString = Object.prototype.toString;
4883
+ var objKeys = Object.keys || function(obj) {
4884
+ var keys = [];
4885
+ for (var name in obj) {
4886
+ keys.push(name);
4887
+ }
4888
+ return keys;
4889
+ };
4890
+
4891
+ function stringify(val, isArrayProp) {
4892
+ var i, max, str, keys, key, propVal, toStr;
4893
+ if (val === true) {
4894
+ return "true";
4895
+ }
4896
+ if (val === false) {
4897
+ return "false";
4898
+ }
4899
+ switch (typeof val) {
4900
+ case "object":
4901
+ if (val === null) {
4902
+ return null;
4903
+ } else if (val.toJSON && typeof val.toJSON === "function") {
4904
+ return stringify(val.toJSON(), isArrayProp);
4905
+ } else {
4906
+ toStr = objToString.call(val);
4907
+ if (toStr === "[object Array]") {
4908
+ str = '[';
4909
+ max = val.length - 1;
4910
+ for(i = 0; i < max; i++) {
4911
+ str += stringify(val[i], true) + ',';
4912
+ }
4913
+ if (max > -1) {
4914
+ str += stringify(val[i], true);
4915
+ }
4916
+ return str + ']';
4917
+ } else if (toStr === "[object Object]") {
4918
+ // only object is left
4919
+ keys = objKeys(val).sort();
4920
+ max = keys.length;
4921
+ str = "";
4922
+ i = 0;
4923
+ while (i < max) {
4924
+ key = keys[i];
4925
+ propVal = stringify(val[key], false);
4926
+ if (propVal !== undefined) {
4927
+ if (str) {
4928
+ str += ',';
4929
+ }
4930
+ str += JSON.stringify(key) + ':' + propVal;
4931
+ }
4932
+ i++;
4933
+ }
4934
+ return '{' + str + '}';
4935
+ } else {
4936
+ return JSON.stringify(val);
4937
+ }
4938
+ }
4939
+ case "function":
4940
+ case "undefined":
4941
+ return isArrayProp ? null : undefined;
4942
+ case "string":
4943
+ return JSON.stringify(val);
4944
+ default:
4945
+ return isFinite(val) ? val : null;
4946
+ }
4947
+ }
4948
+
4949
+ var fastStableStringify = function(val) {
4950
+ var returnVal = stringify(val, false);
4951
+ if (returnVal !== undefined) {
4952
+ return ''+ returnVal;
4953
+ }
4954
+ };
4955
+
4956
+ var fastStableStringify$1 = fastStableStringify;
4957
+
4796
4958
  const MINIMUM_SLOT_PER_EPOCH = 32; // Returns the number of trailing zeros in the binary representation of self.
4797
4959
 
4798
4960
  function trailingZeros(n) {
@@ -4959,6 +5121,12 @@ const BufferFromRawAccountData = superstruct.coerce(superstruct.instance(buffer.
4959
5121
  */
4960
5122
 
4961
5123
  const BLOCKHASH_CACHE_TIMEOUT_MS = 30 * 1000;
5124
+ /**
5125
+ * HACK.
5126
+ * Copied from rpc-websockets/dist/lib/client.
5127
+ * Otherwise, `yarn build` fails with:
5128
+ * https://gist.github.com/steveluscher/c057eca81d479ef705cdb53162f9971d
5129
+ */
4962
5130
 
4963
5131
  /**
4964
5132
  * @internal
@@ -5827,14 +5995,9 @@ const LogsNotificationResult = superstruct.type({
5827
5995
  * Filter for log subscriptions.
5828
5996
  */
5829
5997
 
5830
- function createSubscriptionWarningMessage(id, label) {
5831
- return 'Ignored unsubscribe request because an active subscription ' + `with id \`${id}\` for '${label}' events could not be found.`;
5832
- }
5833
5998
  /**
5834
5999
  * A connection to a fullnode JSON RPC endpoint
5835
6000
  */
5836
-
5837
-
5838
6001
  class Connection {
5839
6002
  /** @internal */
5840
6003
 
@@ -5858,21 +6021,13 @@ class Connection {
5858
6021
 
5859
6022
  /** @internal */
5860
6023
 
5861
- /** @internal */
5862
-
5863
- /** @internal */
5864
-
5865
- /** @internal */
5866
-
5867
- /** @internal */
5868
-
5869
- /** @internal */
5870
-
5871
- /** @internal */
5872
-
5873
- /** @internal */
5874
-
5875
- /** @internal */
6024
+ /** @internal
6025
+ * A number that we increment every time an active connection closes.
6026
+ * Used to determine whether the same socket connection that was open
6027
+ * when an async operation started is the same one that's active when
6028
+ * its continuation fires.
6029
+ *
6030
+ */
5876
6031
 
5877
6032
  /** @internal */
5878
6033
 
@@ -5888,7 +6043,19 @@ class Connection {
5888
6043
 
5889
6044
  /** @internal */
5890
6045
 
5891
- /** @internal */
6046
+ /**
6047
+ * Special case.
6048
+ * After a signature is processed, RPCs automatically dispose of the
6049
+ * subscription on the server side. We need to track which of these
6050
+ * subscriptions have been disposed in such a way, so that we know
6051
+ * whether the client is dealing with a not-yet-processed signature
6052
+ * (in which case we must tear down the server subscription) or an
6053
+ * already-processed signature (in which case the client can simply
6054
+ * clear out the subscription locally without telling the server).
6055
+ *
6056
+ * NOTE: There is a proposal to eliminate this special case, here:
6057
+ * https://github.com/solana-labs/solana/issues/18892
6058
+ */
5892
6059
 
5893
6060
  /** @internal */
5894
6061
 
@@ -5910,6 +6077,7 @@ class Connection {
5910
6077
  this._rpcWebSocketConnected = false;
5911
6078
  this._rpcWebSocketHeartbeat = null;
5912
6079
  this._rpcWebSocketIdleTimeout = null;
6080
+ this._rpcWebSocketGeneration = 0;
5913
6081
  this._disableBlockhashCaching = false;
5914
6082
  this._pollingBlockhash = false;
5915
6083
  this._blockhashInfo = {
@@ -5918,20 +6086,11 @@ class Connection {
5918
6086
  transactionSignatures: [],
5919
6087
  simulatedSignatures: []
5920
6088
  };
5921
- this._accountChangeSubscriptionCounter = 0;
5922
- this._accountChangeSubscriptions = {};
5923
- this._programAccountChangeSubscriptionCounter = 0;
5924
- this._programAccountChangeSubscriptions = {};
5925
- this._rootSubscriptionCounter = 0;
5926
- this._rootSubscriptions = {};
5927
- this._signatureSubscriptionCounter = 0;
5928
- this._signatureSubscriptions = {};
5929
- this._slotSubscriptionCounter = 0;
5930
- this._slotSubscriptions = {};
5931
- this._logsSubscriptionCounter = 0;
5932
- this._logsSubscriptions = {};
5933
- this._slotUpdateSubscriptionCounter = 0;
5934
- this._slotUpdateSubscriptions = {};
6089
+ this._nextClientSubscriptionId = 0;
6090
+ this._subscriptionDisposeFunctionsByClientSubscriptionId = {};
6091
+ this._subscriptionCallbacksByServerSubscriptionId = {};
6092
+ this._subscriptionsByHash = {};
6093
+ this._subscriptionsAutoDisposedByRpc = new Set();
5935
6094
  let url = new URL(endpoint);
5936
6095
  const useHttps = url.protocol === 'https:';
5937
6096
  let wsEndpoint;
@@ -7699,6 +7858,8 @@ class Connection {
7699
7858
 
7700
7859
 
7701
7860
  _wsOnClose(code) {
7861
+ this._rpcWebSocketGeneration++;
7862
+
7702
7863
  if (this._rpcWebSocketHeartbeat) {
7703
7864
  clearInterval(this._rpcWebSocketHeartbeat);
7704
7865
  this._rpcWebSocketHeartbeat = null;
@@ -7712,85 +7873,20 @@ class Connection {
7712
7873
  } // implicit close, prepare subscriptions for auto-reconnect
7713
7874
 
7714
7875
 
7715
- this._resetSubscriptions();
7716
- }
7717
- /**
7718
- * @internal
7719
- */
7720
-
7721
-
7722
- async _subscribe(sub, rpcMethod, rpcArgs) {
7723
- if (sub.subscriptionId == null) {
7724
- sub.subscriptionId = 'subscribing';
7725
-
7726
- try {
7727
- const id = await this._rpcWebSocket.call(rpcMethod, rpcArgs);
7728
-
7729
- if (typeof id === 'number' && sub.subscriptionId === 'subscribing') {
7730
- // eslint-disable-next-line require-atomic-updates
7731
- sub.subscriptionId = id;
7732
- }
7733
- } catch (err) {
7734
- if (sub.subscriptionId === 'subscribing') {
7735
- // eslint-disable-next-line require-atomic-updates
7736
- sub.subscriptionId = null;
7737
- }
7738
-
7739
- if (err instanceof Error) {
7740
- console.error(`${rpcMethod} error for argument`, rpcArgs, err.message);
7741
- }
7742
- }
7743
- }
7744
- }
7745
- /**
7746
- * @internal
7747
- */
7748
-
7749
-
7750
- async _unsubscribe(sub, rpcMethod) {
7751
- const subscriptionId = sub.subscriptionId;
7752
-
7753
- if (subscriptionId != null && typeof subscriptionId != 'string') {
7754
- const unsubscribeId = subscriptionId;
7755
-
7756
- try {
7757
- await this._rpcWebSocket.call(rpcMethod, [unsubscribeId]);
7758
- } catch (err) {
7759
- if (err instanceof Error) {
7760
- console.error(`${rpcMethod} error:`, err.message);
7761
- }
7762
- }
7763
- }
7764
- }
7765
- /**
7766
- * @internal
7767
- */
7768
-
7769
-
7770
- _resetSubscriptions() {
7771
- Object.values(this._accountChangeSubscriptions).forEach(s => s.subscriptionId = null);
7772
- Object.values(this._logsSubscriptions).forEach(s => s.subscriptionId = null);
7773
- Object.values(this._programAccountChangeSubscriptions).forEach(s => s.subscriptionId = null);
7774
- Object.values(this._rootSubscriptions).forEach(s => s.subscriptionId = null);
7775
- Object.values(this._signatureSubscriptions).forEach(s => s.subscriptionId = null);
7776
- Object.values(this._slotSubscriptions).forEach(s => s.subscriptionId = null);
7777
- Object.values(this._slotUpdateSubscriptions).forEach(s => s.subscriptionId = null);
7876
+ this._subscriptionCallbacksByServerSubscriptionId = {};
7877
+ Object.entries(this._subscriptionsByHash).forEach(([hash, subscription]) => {
7878
+ this._subscriptionsByHash[hash] = { ...subscription,
7879
+ state: 'pending'
7880
+ };
7881
+ });
7778
7882
  }
7779
7883
  /**
7780
7884
  * @internal
7781
7885
  */
7782
7886
 
7783
7887
 
7784
- _updateSubscriptions() {
7785
- const accountKeys = Object.keys(this._accountChangeSubscriptions).map(Number);
7786
- const programKeys = Object.keys(this._programAccountChangeSubscriptions).map(Number);
7787
- const slotKeys = Object.keys(this._slotSubscriptions).map(Number);
7788
- const slotUpdateKeys = Object.keys(this._slotUpdateSubscriptions).map(Number);
7789
- const signatureKeys = Object.keys(this._signatureSubscriptions).map(Number);
7790
- const rootKeys = Object.keys(this._rootSubscriptions).map(Number);
7791
- const logsKeys = Object.keys(this._logsSubscriptions).map(Number);
7792
-
7793
- if (accountKeys.length === 0 && programKeys.length === 0 && slotKeys.length === 0 && slotUpdateKeys.length === 0 && signatureKeys.length === 0 && rootKeys.length === 0 && logsKeys.length === 0) {
7888
+ async _updateSubscriptions() {
7889
+ if (Object.keys(this._subscriptionsByHash).length === 0) {
7794
7890
  if (this._rpcWebSocketConnected) {
7795
7891
  this._rpcWebSocketConnected = false;
7796
7892
  this._rpcWebSocketIdleTimeout = setTimeout(() => {
@@ -7822,60 +7918,167 @@ class Connection {
7822
7918
  return;
7823
7919
  }
7824
7920
 
7825
- for (let id of accountKeys) {
7826
- const sub = this._accountChangeSubscriptions[id];
7827
-
7828
- this._subscribe(sub, 'accountSubscribe', this._buildArgs([sub.publicKey], sub.commitment, 'base64'));
7829
- }
7921
+ const activeWebSocketGeneration = this._rpcWebSocketGeneration;
7830
7922
 
7831
- for (let id of programKeys) {
7832
- const sub = this._programAccountChangeSubscriptions[id];
7923
+ const isCurrentConnectionStillActive = () => {
7924
+ return activeWebSocketGeneration === this._rpcWebSocketGeneration;
7925
+ };
7833
7926
 
7834
- this._subscribe(sub, 'programSubscribe', this._buildArgs([sub.programId], sub.commitment, 'base64', {
7835
- filters: sub.filters
7836
- }));
7837
- }
7927
+ await Promise.all( // Don't be tempted to change this to `Object.entries`. We call
7928
+ // `_updateSubscriptions` recursively when processing the state,
7929
+ // so it's important that we look up the *current* version of
7930
+ // each subscription, every time we process a hash.
7931
+ Object.keys(this._subscriptionsByHash).map(async hash => {
7932
+ const subscription = this._subscriptionsByHash[hash];
7838
7933
 
7839
- for (let id of slotKeys) {
7840
- const sub = this._slotSubscriptions[id];
7934
+ if (subscription === undefined) {
7935
+ // This entry has since been deleted. Skip.
7936
+ return;
7937
+ }
7841
7938
 
7842
- this._subscribe(sub, 'slotSubscribe', []);
7843
- }
7939
+ switch (subscription.state) {
7940
+ case 'pending':
7941
+ case 'unsubscribed':
7942
+ if (subscription.callbacks.size === 0) {
7943
+ /**
7944
+ * You can end up here when:
7945
+ *
7946
+ * - a subscription has recently unsubscribed
7947
+ * without having new callbacks added to it
7948
+ * while the unsubscribe was in flight, or
7949
+ * - when a pending subscription has its
7950
+ * listeners removed before a request was
7951
+ * sent to the server.
7952
+ *
7953
+ * Being that nobody is interested in this
7954
+ * subscription any longer, delete it.
7955
+ */
7956
+ delete this._subscriptionsByHash[hash];
7957
+
7958
+ if (subscription.state === 'unsubscribed') {
7959
+ delete this._subscriptionCallbacksByServerSubscriptionId[subscription.serverSubscriptionId];
7960
+ }
7844
7961
 
7845
- for (let id of slotUpdateKeys) {
7846
- const sub = this._slotUpdateSubscriptions[id];
7962
+ await this._updateSubscriptions();
7963
+ return;
7964
+ }
7847
7965
 
7848
- this._subscribe(sub, 'slotsUpdatesSubscribe', []);
7849
- }
7966
+ await (async () => {
7967
+ const {
7968
+ args,
7969
+ method
7970
+ } = subscription;
7850
7971
 
7851
- for (let id of signatureKeys) {
7852
- const sub = this._signatureSubscriptions[id];
7853
- const args = [sub.signature];
7854
- if (sub.options) args.push(sub.options);
7972
+ try {
7973
+ this._subscriptionsByHash[hash] = { ...subscription,
7974
+ state: 'subscribing'
7975
+ };
7976
+ const serverSubscriptionId = await this._rpcWebSocket.call(method, args);
7977
+ this._subscriptionsByHash[hash] = { ...subscription,
7978
+ serverSubscriptionId,
7979
+ state: 'subscribed'
7980
+ };
7981
+ this._subscriptionCallbacksByServerSubscriptionId[serverSubscriptionId] = subscription.callbacks;
7982
+ await this._updateSubscriptions();
7983
+ } catch (e) {
7984
+ if (e instanceof Error) {
7985
+ console.error(`${method} error for argument`, args, e.message);
7986
+ }
7987
+
7988
+ if (!isCurrentConnectionStillActive()) {
7989
+ return;
7990
+ } // TODO: Maybe add an 'errored' state or a retry limit?
7855
7991
 
7856
- this._subscribe(sub, 'signatureSubscribe', args);
7857
- }
7858
7992
 
7859
- for (let id of rootKeys) {
7860
- const sub = this._rootSubscriptions[id];
7993
+ this._subscriptionsByHash[hash] = { ...subscription,
7994
+ state: 'pending'
7995
+ };
7996
+ await this._updateSubscriptions();
7997
+ }
7998
+ })();
7999
+ break;
7861
8000
 
7862
- this._subscribe(sub, 'rootSubscribe', []);
7863
- }
8001
+ case 'subscribed':
8002
+ if (subscription.callbacks.size === 0) {
8003
+ // By the time we successfully set up a subscription
8004
+ // with the server, the client stopped caring about it.
8005
+ // Tear it down now.
8006
+ await (async () => {
8007
+ const {
8008
+ serverSubscriptionId,
8009
+ unsubscribeMethod
8010
+ } = subscription;
8011
+
8012
+ if (this._subscriptionsAutoDisposedByRpc.has(serverSubscriptionId)) {
8013
+ /**
8014
+ * Special case.
8015
+ * If we're dealing with a subscription that has been auto-
8016
+ * disposed by the RPC, then we can skip the RPC call to
8017
+ * tear down the subscription here.
8018
+ *
8019
+ * NOTE: There is a proposal to eliminate this special case, here:
8020
+ * https://github.com/solana-labs/solana/issues/18892
8021
+ */
8022
+ this._subscriptionsAutoDisposedByRpc.delete(serverSubscriptionId);
8023
+ } else {
8024
+ this._subscriptionsByHash[hash] = { ...subscription,
8025
+ state: 'unsubscribing'
8026
+ };
8027
+
8028
+ try {
8029
+ await this._rpcWebSocket.call(unsubscribeMethod, [serverSubscriptionId]);
8030
+ } catch (e) {
8031
+ if (e instanceof Error) {
8032
+ console.error(`${unsubscribeMethod} error:`, e.message);
8033
+ }
8034
+
8035
+ if (!isCurrentConnectionStillActive()) {
8036
+ return;
8037
+ } // TODO: Maybe add an 'errored' state or a retry limit?
8038
+
8039
+
8040
+ this._subscriptionsByHash[hash] = { ...subscription,
8041
+ state: 'subscribed'
8042
+ };
8043
+ await this._updateSubscriptions();
8044
+ return;
8045
+ }
8046
+ }
7864
8047
 
7865
- for (let id of logsKeys) {
7866
- const sub = this._logsSubscriptions[id];
7867
- let filter;
8048
+ this._subscriptionsByHash[hash] = { ...subscription,
8049
+ state: 'unsubscribed'
8050
+ };
8051
+ await this._updateSubscriptions();
8052
+ })();
8053
+ }
7868
8054
 
7869
- if (typeof sub.filter === 'object') {
7870
- filter = {
7871
- mentions: [sub.filter.toString()]
7872
- };
7873
- } else {
7874
- filter = sub.filter;
8055
+ break;
7875
8056
  }
8057
+ }));
8058
+ }
8059
+ /**
8060
+ * @internal
8061
+ */
8062
+
7876
8063
 
7877
- this._subscribe(sub, 'logsSubscribe', this._buildArgs([filter], sub.commitment));
8064
+ _handleServerNotification(serverSubscriptionId, callbackArgs) {
8065
+ const callbacks = this._subscriptionCallbacksByServerSubscriptionId[serverSubscriptionId];
8066
+
8067
+ if (callbacks === undefined) {
8068
+ return;
7878
8069
  }
8070
+
8071
+ callbacks.forEach(cb => {
8072
+ try {
8073
+ cb( // I failed to find a way to convince TypeScript that `cb` is of type
8074
+ // `TCallback` which is certainly compatible with `Parameters<TCallback>`.
8075
+ // See https://github.com/microsoft/TypeScript/issues/47615
8076
+ // @ts-ignore
8077
+ ...callbackArgs);
8078
+ } catch (e) {
8079
+ console.error(e);
8080
+ }
8081
+ });
7879
8082
  }
7880
8083
  /**
7881
8084
  * @internal
@@ -7883,14 +8086,71 @@ class Connection {
7883
8086
 
7884
8087
 
7885
8088
  _wsOnAccountNotification(notification) {
7886
- const res = superstruct.create(notification, AccountNotificationResult);
8089
+ const {
8090
+ result,
8091
+ subscription
8092
+ } = superstruct.create(notification, AccountNotificationResult);
7887
8093
 
7888
- for (const sub of Object.values(this._accountChangeSubscriptions)) {
7889
- if (sub.subscriptionId === res.subscription) {
7890
- sub.callback(res.result.value, res.result.context);
7891
- return;
7892
- }
8094
+ this._handleServerNotification(subscription, [result.value, result.context]);
8095
+ }
8096
+ /**
8097
+ * @internal
8098
+ */
8099
+
8100
+
8101
+ _makeSubscription(subscriptionConfig,
8102
+ /**
8103
+ * When preparing `args` for a call to `_makeSubscription`, be sure
8104
+ * to carefully apply a default `commitment` property, if necessary.
8105
+ *
8106
+ * - If the user supplied a `commitment` use that.
8107
+ * - Otherwise, if the `Connection::commitment` is set, use that.
8108
+ * - Otherwise, set it to the RPC server default: `finalized`.
8109
+ *
8110
+ * This is extremely important to ensure that these two fundamentally
8111
+ * identical subscriptions produce the same identifying hash:
8112
+ *
8113
+ * - A subscription made without specifying a commitment.
8114
+ * - A subscription made where the commitment specified is the same
8115
+ * as the default applied to the subscription above.
8116
+ *
8117
+ * Example; these two subscriptions must produce the same hash:
8118
+ *
8119
+ * - An `accountSubscribe` subscription for `'PUBKEY'`
8120
+ * - An `accountSubscribe` subscription for `'PUBKEY'` with commitment
8121
+ * `'finalized'`.
8122
+ *
8123
+ * See the 'making a subscription with defaulted params omitted' test
8124
+ * in `connection-subscriptions.ts` for more.
8125
+ */
8126
+ args) {
8127
+ const clientSubscriptionId = this._nextClientSubscriptionId++;
8128
+ const hash = fastStableStringify$1([subscriptionConfig.method, args], true
8129
+ /* isArrayProp */
8130
+ );
8131
+ const existingSubscription = this._subscriptionsByHash[hash];
8132
+
8133
+ if (existingSubscription === undefined) {
8134
+ this._subscriptionsByHash[hash] = { ...subscriptionConfig,
8135
+ args,
8136
+ callbacks: new Set([subscriptionConfig.callback]),
8137
+ state: 'pending'
8138
+ };
8139
+ } else {
8140
+ existingSubscription.callbacks.add(subscriptionConfig.callback);
7893
8141
  }
8142
+
8143
+ this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId] = async () => {
8144
+ delete this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId];
8145
+ const subscription = this._subscriptionsByHash[hash];
8146
+ assert(subscription !== undefined, `Could not find a \`Subscription\` when tearing down client subscription #${clientSubscriptionId}`);
8147
+ subscription.callbacks.delete(subscriptionConfig.callback);
8148
+ await this._updateSubscriptions();
8149
+ };
8150
+
8151
+ this._updateSubscriptions();
8152
+
8153
+ return clientSubscriptionId;
7894
8154
  }
7895
8155
  /**
7896
8156
  * Register a callback to be invoked whenever the specified account changes
@@ -7903,35 +8163,24 @@ class Connection {
7903
8163
 
7904
8164
 
7905
8165
  onAccountChange(publicKey, callback, commitment) {
7906
- const id = ++this._accountChangeSubscriptionCounter;
7907
- this._accountChangeSubscriptions[id] = {
7908
- publicKey: publicKey.toBase58(),
7909
- callback,
7910
- commitment,
7911
- subscriptionId: null
7912
- };
8166
+ const args = this._buildArgs([publicKey.toBase58()], commitment || this._commitment || 'finalized', // Apply connection/server default.
8167
+ 'base64');
7913
8168
 
7914
- this._updateSubscriptions();
7915
-
7916
- return id;
8169
+ return this._makeSubscription({
8170
+ callback,
8171
+ method: 'accountSubscribe',
8172
+ unsubscribeMethod: 'accountUnsubscribe'
8173
+ }, args);
7917
8174
  }
7918
8175
  /**
7919
8176
  * Deregister an account notification callback
7920
8177
  *
7921
- * @param id subscription id to deregister
8178
+ * @param id client subscription id to deregister
7922
8179
  */
7923
8180
 
7924
8181
 
7925
- async removeAccountChangeListener(id) {
7926
- if (this._accountChangeSubscriptions[id]) {
7927
- const subInfo = this._accountChangeSubscriptions[id];
7928
- delete this._accountChangeSubscriptions[id];
7929
- await this._unsubscribe(subInfo, 'accountUnsubscribe');
7930
-
7931
- this._updateSubscriptions();
7932
- } else {
7933
- console.warn(createSubscriptionWarningMessage(id, 'account change'));
7934
- }
8182
+ async removeAccountChangeListener(clientSubscriptionId) {
8183
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'account change');
7935
8184
  }
7936
8185
  /**
7937
8186
  * @internal
@@ -7939,21 +8188,15 @@ class Connection {
7939
8188
 
7940
8189
 
7941
8190
  _wsOnProgramAccountNotification(notification) {
7942
- const res = superstruct.create(notification, ProgramAccountNotificationResult);
8191
+ const {
8192
+ result,
8193
+ subscription
8194
+ } = superstruct.create(notification, ProgramAccountNotificationResult);
7943
8195
 
7944
- for (const sub of Object.values(this._programAccountChangeSubscriptions)) {
7945
- if (sub.subscriptionId === res.subscription) {
7946
- const {
7947
- value,
7948
- context
7949
- } = res.result;
7950
- sub.callback({
7951
- accountId: value.pubkey,
7952
- accountInfo: value.account
7953
- }, context);
7954
- return;
7955
- }
7956
- }
8196
+ this._handleServerNotification(subscription, [{
8197
+ accountId: result.value.pubkey,
8198
+ accountInfo: result.value.account
8199
+ }, result.context]);
7957
8200
  }
7958
8201
  /**
7959
8202
  * Register a callback to be invoked whenever accounts owned by the
@@ -7968,36 +8211,30 @@ class Connection {
7968
8211
 
7969
8212
 
7970
8213
  onProgramAccountChange(programId, callback, commitment, filters) {
7971
- const id = ++this._programAccountChangeSubscriptionCounter;
7972
- this._programAccountChangeSubscriptions[id] = {
7973
- programId: programId.toBase58(),
8214
+ const args = this._buildArgs([programId.toBase58()], commitment || this._commitment || 'finalized', // Apply connection/server default.
8215
+ 'base64'
8216
+ /* encoding */
8217
+ , filters ? {
8218
+ filters: filters
8219
+ } : undefined
8220
+ /* extra */
8221
+ );
8222
+
8223
+ return this._makeSubscription({
7974
8224
  callback,
7975
- commitment,
7976
- subscriptionId: null,
7977
- filters
7978
- };
7979
-
7980
- this._updateSubscriptions();
7981
-
7982
- return id;
8225
+ method: 'programSubscribe',
8226
+ unsubscribeMethod: 'programUnsubscribe'
8227
+ }, args);
7983
8228
  }
7984
8229
  /**
7985
8230
  * Deregister an account notification callback
7986
8231
  *
7987
- * @param id subscription id to deregister
8232
+ * @param id client subscription id to deregister
7988
8233
  */
7989
8234
 
7990
8235
 
7991
- async removeProgramAccountChangeListener(id) {
7992
- if (this._programAccountChangeSubscriptions[id]) {
7993
- const subInfo = this._programAccountChangeSubscriptions[id];
7994
- delete this._programAccountChangeSubscriptions[id];
7995
- await this._unsubscribe(subInfo, 'programUnsubscribe');
7996
-
7997
- this._updateSubscriptions();
7998
- } else {
7999
- console.warn(createSubscriptionWarningMessage(id, 'program account change'));
8000
- }
8236
+ async removeProgramAccountChangeListener(clientSubscriptionId) {
8237
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'program account change');
8001
8238
  }
8002
8239
  /**
8003
8240
  * Registers a callback to be invoked whenever logs are emitted.
@@ -8005,35 +8242,26 @@ class Connection {
8005
8242
 
8006
8243
 
8007
8244
  onLogs(filter, callback, commitment) {
8008
- const id = ++this._logsSubscriptionCounter;
8009
- this._logsSubscriptions[id] = {
8010
- filter,
8011
- callback,
8012
- commitment,
8013
- subscriptionId: null
8014
- };
8015
-
8016
- this._updateSubscriptions();
8245
+ const args = this._buildArgs([typeof filter === 'object' ? {
8246
+ mentions: [filter.toString()]
8247
+ } : filter], commitment || this._commitment || 'finalized' // Apply connection/server default.
8248
+ );
8017
8249
 
8018
- return id;
8250
+ return this._makeSubscription({
8251
+ callback,
8252
+ method: 'logsSubscribe',
8253
+ unsubscribeMethod: 'logsUnsubscribe'
8254
+ }, args);
8019
8255
  }
8020
8256
  /**
8021
8257
  * Deregister a logs callback.
8022
8258
  *
8023
- * @param id subscription id to deregister.
8259
+ * @param id client subscription id to deregister.
8024
8260
  */
8025
8261
 
8026
8262
 
8027
- async removeOnLogsListener(id) {
8028
- if (this._logsSubscriptions[id]) {
8029
- const subInfo = this._logsSubscriptions[id];
8030
- delete this._logsSubscriptions[id];
8031
- await this._unsubscribe(subInfo, 'logsUnsubscribe');
8032
-
8033
- this._updateSubscriptions();
8034
- } else {
8035
- console.warn(createSubscriptionWarningMessage(id, 'logs'));
8036
- }
8263
+ async removeOnLogsListener(clientSubscriptionId) {
8264
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'logs');
8037
8265
  }
8038
8266
  /**
8039
8267
  * @internal
@@ -8041,17 +8269,12 @@ class Connection {
8041
8269
 
8042
8270
 
8043
8271
  _wsOnLogsNotification(notification) {
8044
- const res = superstruct.create(notification, LogsNotificationResult);
8045
- const keys = Object.keys(this._logsSubscriptions).map(Number);
8046
-
8047
- for (let id of keys) {
8048
- const sub = this._logsSubscriptions[id];
8272
+ const {
8273
+ result,
8274
+ subscription
8275
+ } = superstruct.create(notification, LogsNotificationResult);
8049
8276
 
8050
- if (sub.subscriptionId === res.subscription) {
8051
- sub.callback(res.result.value, res.result.context);
8052
- return;
8053
- }
8054
- }
8277
+ this._handleServerNotification(subscription, [result.value, result.context]);
8055
8278
  }
8056
8279
  /**
8057
8280
  * @internal
@@ -8059,14 +8282,12 @@ class Connection {
8059
8282
 
8060
8283
 
8061
8284
  _wsOnSlotNotification(notification) {
8062
- const res = superstruct.create(notification, SlotNotificationResult);
8285
+ const {
8286
+ result,
8287
+ subscription
8288
+ } = superstruct.create(notification, SlotNotificationResult);
8063
8289
 
8064
- for (const sub of Object.values(this._slotSubscriptions)) {
8065
- if (sub.subscriptionId === res.subscription) {
8066
- sub.callback(res.result);
8067
- return;
8068
- }
8069
- }
8290
+ this._handleServerNotification(subscription, [result]);
8070
8291
  }
8071
8292
  /**
8072
8293
  * Register a callback to be invoked upon slot changes
@@ -8077,33 +8298,23 @@ class Connection {
8077
8298
 
8078
8299
 
8079
8300
  onSlotChange(callback) {
8080
- const id = ++this._slotSubscriptionCounter;
8081
- this._slotSubscriptions[id] = {
8301
+ return this._makeSubscription({
8082
8302
  callback,
8083
- subscriptionId: null
8084
- };
8085
-
8086
- this._updateSubscriptions();
8087
-
8088
- return id;
8303
+ method: 'slotSubscribe',
8304
+ unsubscribeMethod: 'slotUnsubscribe'
8305
+ }, []
8306
+ /* args */
8307
+ );
8089
8308
  }
8090
8309
  /**
8091
8310
  * Deregister a slot notification callback
8092
8311
  *
8093
- * @param id subscription id to deregister
8312
+ * @param id client subscription id to deregister
8094
8313
  */
8095
8314
 
8096
8315
 
8097
- async removeSlotChangeListener(id) {
8098
- if (this._slotSubscriptions[id]) {
8099
- const subInfo = this._slotSubscriptions[id];
8100
- delete this._slotSubscriptions[id];
8101
- await this._unsubscribe(subInfo, 'slotUnsubscribe');
8102
-
8103
- this._updateSubscriptions();
8104
- } else {
8105
- console.warn(createSubscriptionWarningMessage(id, 'slot change'));
8106
- }
8316
+ async removeSlotChangeListener(clientSubscriptionId) {
8317
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'slot change');
8107
8318
  }
8108
8319
  /**
8109
8320
  * @internal
@@ -8111,14 +8322,12 @@ class Connection {
8111
8322
 
8112
8323
 
8113
8324
  _wsOnSlotUpdatesNotification(notification) {
8114
- const res = superstruct.create(notification, SlotUpdateNotificationResult);
8325
+ const {
8326
+ result,
8327
+ subscription
8328
+ } = superstruct.create(notification, SlotUpdateNotificationResult);
8115
8329
 
8116
- for (const sub of Object.values(this._slotUpdateSubscriptions)) {
8117
- if (sub.subscriptionId === res.subscription) {
8118
- sub.callback(res.result);
8119
- return;
8120
- }
8121
- }
8330
+ this._handleServerNotification(subscription, [result]);
8122
8331
  }
8123
8332
  /**
8124
8333
  * Register a callback to be invoked upon slot updates. {@link SlotUpdate}'s
@@ -8130,32 +8339,36 @@ class Connection {
8130
8339
 
8131
8340
 
8132
8341
  onSlotUpdate(callback) {
8133
- const id = ++this._slotUpdateSubscriptionCounter;
8134
- this._slotUpdateSubscriptions[id] = {
8342
+ return this._makeSubscription({
8135
8343
  callback,
8136
- subscriptionId: null
8137
- };
8138
-
8139
- this._updateSubscriptions();
8140
-
8141
- return id;
8344
+ method: 'slotsUpdatesSubscribe',
8345
+ unsubscribeMethod: 'slotsUpdatesUnsubscribe'
8346
+ }, []
8347
+ /* args */
8348
+ );
8142
8349
  }
8143
8350
  /**
8144
8351
  * Deregister a slot update notification callback
8145
8352
  *
8146
- * @param id subscription id to deregister
8353
+ * @param id client subscription id to deregister
8354
+ */
8355
+
8356
+
8357
+ async removeSlotUpdateListener(clientSubscriptionId) {
8358
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'slot update');
8359
+ }
8360
+ /**
8361
+ * @internal
8147
8362
  */
8148
8363
 
8149
8364
 
8150
- async removeSlotUpdateListener(id) {
8151
- if (this._slotUpdateSubscriptions[id]) {
8152
- const subInfo = this._slotUpdateSubscriptions[id];
8153
- delete this._slotUpdateSubscriptions[id];
8154
- await this._unsubscribe(subInfo, 'slotsUpdatesUnsubscribe');
8365
+ async _unsubscribeClientSubscription(clientSubscriptionId, subscriptionName) {
8366
+ const dispose = this._subscriptionDisposeFunctionsByClientSubscriptionId[clientSubscriptionId];
8155
8367
 
8156
- this._updateSubscriptions();
8368
+ if (dispose) {
8369
+ await dispose();
8157
8370
  } else {
8158
- console.warn(createSubscriptionWarningMessage(id, 'slot update'));
8371
+ console.warn('Ignored unsubscribe request because an active subscription with id ' + `\`${clientSubscriptionId}\` for '${subscriptionName}' events ` + 'could not be found.');
8159
8372
  }
8160
8373
  }
8161
8374
 
@@ -8202,30 +8415,34 @@ class Connection {
8202
8415
 
8203
8416
 
8204
8417
  _wsOnSignatureNotification(notification) {
8205
- const res = superstruct.create(notification, SignatureNotificationResult);
8206
-
8207
- for (const [id, sub] of Object.entries(this._signatureSubscriptions)) {
8208
- if (sub.subscriptionId === res.subscription) {
8209
- if (res.result.value === 'receivedSignature') {
8210
- sub.callback({
8211
- type: 'received'
8212
- }, res.result.context);
8213
- } else {
8214
- // Signatures subscriptions are auto-removed by the RPC service so
8215
- // no need to explicitly send an unsubscribe message
8216
- delete this._signatureSubscriptions[Number(id)];
8217
-
8218
- this._updateSubscriptions();
8219
-
8220
- sub.callback({
8221
- type: 'status',
8222
- result: res.result.value
8223
- }, res.result.context);
8224
- }
8225
-
8226
- return;
8227
- }
8228
- }
8418
+ const {
8419
+ result,
8420
+ subscription
8421
+ } = superstruct.create(notification, SignatureNotificationResult);
8422
+
8423
+ if (result.value !== 'receivedSignature') {
8424
+ /**
8425
+ * Special case.
8426
+ * After a signature is processed, RPCs automatically dispose of the
8427
+ * subscription on the server side. We need to track which of these
8428
+ * subscriptions have been disposed in such a way, so that we know
8429
+ * whether the client is dealing with a not-yet-processed signature
8430
+ * (in which case we must tear down the server subscription) or an
8431
+ * already-processed signature (in which case the client can simply
8432
+ * clear out the subscription locally without telling the server).
8433
+ *
8434
+ * NOTE: There is a proposal to eliminate this special case, here:
8435
+ * https://github.com/solana-labs/solana/issues/18892
8436
+ */
8437
+ this._subscriptionsAutoDisposedByRpc.add(subscription);
8438
+ }
8439
+
8440
+ this._handleServerNotification(subscription, result.value === 'receivedSignature' ? [{
8441
+ type: 'received'
8442
+ }, result.context] : [{
8443
+ type: 'status',
8444
+ result: result.value
8445
+ }, result.context]);
8229
8446
  }
8230
8447
  /**
8231
8448
  * Register a callback to be invoked upon signature updates
@@ -8238,23 +8455,26 @@ class Connection {
8238
8455
 
8239
8456
 
8240
8457
  onSignature(signature, callback, commitment) {
8241
- const id = ++this._signatureSubscriptionCounter;
8242
- this._signatureSubscriptions[id] = {
8243
- signature,
8458
+ const args = this._buildArgs([signature], commitment || this._commitment || 'finalized' // Apply connection/server default.
8459
+ );
8460
+
8461
+ const clientSubscriptionId = this._makeSubscription({
8244
8462
  callback: (notification, context) => {
8245
8463
  if (notification.type === 'status') {
8246
- callback(notification.result, context);
8464
+ callback(notification.result, context); // Signatures subscriptions are auto-removed by the RPC service
8465
+ // so no need to explicitly send an unsubscribe message.
8466
+
8467
+ try {
8468
+ this.removeSignatureListener(clientSubscriptionId); // eslint-disable-next-line no-empty
8469
+ } catch {// Already removed.
8470
+ }
8247
8471
  }
8248
8472
  },
8249
- options: {
8250
- commitment
8251
- },
8252
- subscriptionId: null
8253
- };
8254
-
8255
- this._updateSubscriptions();
8473
+ method: 'signatureSubscribe',
8474
+ unsubscribeMethod: 'signatureUnsubscribe'
8475
+ }, args);
8256
8476
 
8257
- return id;
8477
+ return clientSubscriptionId;
8258
8478
  }
8259
8479
  /**
8260
8480
  * Register a callback to be invoked when a transaction is
@@ -8269,35 +8489,43 @@ class Connection {
8269
8489
 
8270
8490
 
8271
8491
  onSignatureWithOptions(signature, callback, options) {
8272
- const id = ++this._signatureSubscriptionCounter;
8273
- this._signatureSubscriptions[id] = {
8274
- signature,
8275
- callback,
8276
- options,
8277
- subscriptionId: null
8492
+ const {
8493
+ commitment,
8494
+ ...extra
8495
+ } = { ...options,
8496
+ commitment: options && options.commitment || this._commitment || 'finalized' // Apply connection/server default.
8497
+
8278
8498
  };
8279
8499
 
8280
- this._updateSubscriptions();
8500
+ const args = this._buildArgs([signature], commitment, undefined
8501
+ /* encoding */
8502
+ , extra);
8503
+
8504
+ const clientSubscriptionId = this._makeSubscription({
8505
+ callback: (notification, context) => {
8506
+ callback(notification, context); // Signatures subscriptions are auto-removed by the RPC service
8507
+ // so no need to explicitly send an unsubscribe message.
8508
+
8509
+ try {
8510
+ this.removeSignatureListener(clientSubscriptionId); // eslint-disable-next-line no-empty
8511
+ } catch {// Already removed.
8512
+ }
8513
+ },
8514
+ method: 'signatureSubscribe',
8515
+ unsubscribeMethod: 'signatureUnsubscribe'
8516
+ }, args);
8281
8517
 
8282
- return id;
8518
+ return clientSubscriptionId;
8283
8519
  }
8284
8520
  /**
8285
8521
  * Deregister a signature notification callback
8286
8522
  *
8287
- * @param id subscription id to deregister
8523
+ * @param id client subscription id to deregister
8288
8524
  */
8289
8525
 
8290
8526
 
8291
- async removeSignatureListener(id) {
8292
- if (this._signatureSubscriptions[id]) {
8293
- const subInfo = this._signatureSubscriptions[id];
8294
- delete this._signatureSubscriptions[id];
8295
- await this._unsubscribe(subInfo, 'signatureUnsubscribe');
8296
-
8297
- this._updateSubscriptions();
8298
- } else {
8299
- console.warn(createSubscriptionWarningMessage(id, 'signature result'));
8300
- }
8527
+ async removeSignatureListener(clientSubscriptionId) {
8528
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'signature result');
8301
8529
  }
8302
8530
  /**
8303
8531
  * @internal
@@ -8305,14 +8533,12 @@ class Connection {
8305
8533
 
8306
8534
 
8307
8535
  _wsOnRootNotification(notification) {
8308
- const res = superstruct.create(notification, RootNotificationResult);
8536
+ const {
8537
+ result,
8538
+ subscription
8539
+ } = superstruct.create(notification, RootNotificationResult);
8309
8540
 
8310
- for (const sub of Object.values(this._rootSubscriptions)) {
8311
- if (sub.subscriptionId === res.subscription) {
8312
- sub.callback(res.result);
8313
- return;
8314
- }
8315
- }
8541
+ this._handleServerNotification(subscription, [result]);
8316
8542
  }
8317
8543
  /**
8318
8544
  * Register a callback to be invoked upon root changes
@@ -8323,33 +8549,23 @@ class Connection {
8323
8549
 
8324
8550
 
8325
8551
  onRootChange(callback) {
8326
- const id = ++this._rootSubscriptionCounter;
8327
- this._rootSubscriptions[id] = {
8552
+ return this._makeSubscription({
8328
8553
  callback,
8329
- subscriptionId: null
8330
- };
8331
-
8332
- this._updateSubscriptions();
8333
-
8334
- return id;
8554
+ method: 'rootSubscribe',
8555
+ unsubscribeMethod: 'rootUnsubscribe'
8556
+ }, []
8557
+ /* args */
8558
+ );
8335
8559
  }
8336
8560
  /**
8337
8561
  * Deregister a root notification callback
8338
8562
  *
8339
- * @param id subscription id to deregister
8563
+ * @param id client subscription id to deregister
8340
8564
  */
8341
8565
 
8342
8566
 
8343
- async removeRootChangeListener(id) {
8344
- if (this._rootSubscriptions[id]) {
8345
- const subInfo = this._rootSubscriptions[id];
8346
- delete this._rootSubscriptions[id];
8347
- await this._unsubscribe(subInfo, 'rootUnsubscribe');
8348
-
8349
- this._updateSubscriptions();
8350
- } else {
8351
- console.warn(createSubscriptionWarningMessage(id, 'root change'));
8352
- }
8567
+ async removeRootChangeListener(clientSubscriptionId) {
8568
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'root change');
8353
8569
  }
8354
8570
 
8355
8571
  }
@@ -10051,7 +10267,9 @@ exports.MAX_SEED_LENGTH = MAX_SEED_LENGTH;
10051
10267
  exports.Message = Message;
10052
10268
  exports.NONCE_ACCOUNT_LENGTH = NONCE_ACCOUNT_LENGTH;
10053
10269
  exports.NonceAccount = NonceAccount;
10270
+ exports.PACKET_DATA_SIZE = PACKET_DATA_SIZE;
10054
10271
  exports.PublicKey = PublicKey;
10272
+ exports.SIGNATURE_LENGTH_IN_BYTES = SIGNATURE_LENGTH_IN_BYTES;
10055
10273
  exports.SOLANA_SCHEMA = SOLANA_SCHEMA;
10056
10274
  exports.STAKE_CONFIG_ID = STAKE_CONFIG_ID;
10057
10275
  exports.STAKE_INSTRUCTION_LAYOUTS = STAKE_INSTRUCTION_LAYOUTS;