starknet 10.6.7 → 10.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7476,6 +7476,8 @@ var Subscription = class {
7476
7476
  maxBufferSize;
7477
7477
  handler = null;
7478
7478
  _isClosed = false;
7479
+ // The unsubscribe request currently on the wire, shared by concurrent callers.
7480
+ pendingUnsubscribe = null;
7479
7481
  /**
7480
7482
  * @internal
7481
7483
  * @param options - Subscription configuration options
@@ -7494,6 +7496,20 @@ var Subscription = class {
7494
7496
  get isClosed() {
7495
7497
  return this._isClosed;
7496
7498
  }
7499
+ /**
7500
+ * Closes the subscription locally, without contacting the node.
7501
+ *
7502
+ * Used when the channel knows the subscription is gone and cannot be recovered — a
7503
+ * re-subscribe refused after a reconnection, for instance. Without it the object would keep
7504
+ * reporting itself as open while no event could ever reach its handler again.
7505
+ * @internal
7506
+ */
7507
+ _markClosed() {
7508
+ if (this._isClosed) return;
7509
+ this._isClosed = true;
7510
+ this.events.emit("unsubscribe", void 0);
7511
+ this.events.clear();
7512
+ }
7497
7513
  /**
7498
7514
  * Internal method to handle incoming events from the WebSocket channel.
7499
7515
  * If a handler is attached, it's invoked immediately. Otherwise, the event is buffered.
@@ -7540,14 +7556,24 @@ var Subscription = class {
7540
7556
  if (this._isClosed) {
7541
7557
  return true;
7542
7558
  }
7543
- const success = await this.channel.unsubscribe(this.id);
7544
- if (success) {
7545
- this._isClosed = true;
7546
- this.channel.removeSubscription(this.id);
7547
- this.events.emit("unsubscribe", void 0);
7548
- this.events.clear();
7559
+ if (this.pendingUnsubscribe) {
7560
+ return this.pendingUnsubscribe;
7549
7561
  }
7550
- return success;
7562
+ this.pendingUnsubscribe = (async () => {
7563
+ try {
7564
+ const success = await this.channel.unsubscribe(this.id);
7565
+ if (success) {
7566
+ this._isClosed = true;
7567
+ this.channel.removeSubscription(this.id);
7568
+ this.events.emit("unsubscribe", void 0);
7569
+ this.events.clear();
7570
+ }
7571
+ return success;
7572
+ } finally {
7573
+ this.pendingUnsubscribe = null;
7574
+ }
7575
+ })();
7576
+ return this.pendingUnsubscribe;
7551
7577
  }
7552
7578
  };
7553
7579
 
@@ -7580,6 +7606,15 @@ var WebSocketChannel = class {
7580
7606
  requestQueue = [];
7581
7607
  /** Abort handles for the requests currently on the wire, one per pending `sendReceive`. */
7582
7608
  inFlight = /* @__PURE__ */ new Set();
7609
+ /**
7610
+ * Callers blocked in `waitForUnsubscription`, keyed by subscription id.
7611
+ *
7612
+ * Held here rather than as `unsubscribe` event listeners because that event only ever
7613
+ * announces success: a waiter attached to it cannot learn that the node refused the
7614
+ * unsubscribe or that the connection went away, and would wait forever with no timeout of
7615
+ * its own to fall back on.
7616
+ */
7617
+ unsubscribeWaiters = /* @__PURE__ */ new Map();
7583
7618
  events = new EventEmitter();
7584
7619
  openListener = (ev) => {
7585
7620
  this.scheduleReconnectAttemptsReset();
@@ -7776,6 +7811,7 @@ var WebSocketChannel = class {
7776
7811
  this.isReconnecting = false;
7777
7812
  this._rejectRequestQueue("the connection was closed by the user");
7778
7813
  this._rejectInFlight("the connection was closed by the user");
7814
+ this._rejectUnsubscribeWaiters("the connection was closed by the user");
7779
7815
  this.websocket.close(code, reason);
7780
7816
  }
7781
7817
  /**
@@ -7800,14 +7836,48 @@ var WebSocketChannel = class {
7800
7836
  * @returns {Promise<boolean>} A Promise that resolves with `true` if the unsubscription was successful.
7801
7837
  */
7802
7838
  async unsubscribe(subscriptionId) {
7803
- const status = await this.sendReceive("starknet_unsubscribe", {
7804
- subscription_id: subscriptionId
7805
- });
7839
+ let status;
7840
+ try {
7841
+ status = await this.sendReceive("starknet_unsubscribe", {
7842
+ subscription_id: subscriptionId
7843
+ });
7844
+ } catch (error) {
7845
+ this._settleUnsubscribeWaiters(subscriptionId, error);
7846
+ throw error;
7847
+ }
7806
7848
  if (status) {
7807
7849
  this.events.emit("unsubscribe", subscriptionId);
7850
+ this._settleUnsubscribeWaiters(subscriptionId);
7851
+ } else {
7852
+ this._settleUnsubscribeWaiters(
7853
+ subscriptionId,
7854
+ new Error(`Node refused to unsubscribe subscription ${subscriptionId}`)
7855
+ );
7808
7856
  }
7809
7857
  return status;
7810
7858
  }
7859
+ /** Settles every caller waiting on one subscription id: resolved, or rejected with `error`. */
7860
+ _settleUnsubscribeWaiters(subscriptionId, error) {
7861
+ const waiters = this.unsubscribeWaiters.get(subscriptionId);
7862
+ if (!waiters) return;
7863
+ this.unsubscribeWaiters.delete(subscriptionId);
7864
+ waiters.forEach((waiter) => error ? waiter.reject(error) : waiter.resolve());
7865
+ }
7866
+ /**
7867
+ * Rejects every caller still waiting on any subscription.
7868
+ *
7869
+ * Once the connection is gone, no unsubscribe can be observed on it: a reconnection restores
7870
+ * each subscription under a fresh id, so the id being waited on will never be announced.
7871
+ */
7872
+ _rejectUnsubscribeWaiters(reason) {
7873
+ if (this.unsubscribeWaiters.size === 0) return;
7874
+ Array.from(this.unsubscribeWaiters.keys()).forEach(
7875
+ (id) => this._settleUnsubscribeWaiters(
7876
+ id,
7877
+ new WebSocketNotConnectedError(`Subscription ${id} was never unsubscribed: ${reason}`)
7878
+ )
7879
+ );
7880
+ }
7811
7881
  /**
7812
7882
  * Returns a Promise that resolves when a specific subscription is successfully unsubscribed.
7813
7883
  * @param {SUBSCRIPTION_ID} targetId - The ID of the subscription to wait for.
@@ -7819,14 +7889,10 @@ var WebSocketChannel = class {
7819
7889
  * ```
7820
7890
  */
7821
7891
  waitForUnsubscription(targetId) {
7822
- return new Promise((resolve) => {
7823
- const listener = (unsubId) => {
7824
- if (unsubId === targetId) {
7825
- this.events.off("unsubscribe", listener);
7826
- resolve();
7827
- }
7828
- };
7829
- this.events.on("unsubscribe", listener);
7892
+ return new Promise((resolve, reject) => {
7893
+ const waiters = this.unsubscribeWaiters.get(targetId) ?? /* @__PURE__ */ new Set();
7894
+ waiters.add({ resolve, reject });
7895
+ this.unsubscribeWaiters.set(targetId, waiters);
7830
7896
  });
7831
7897
  }
7832
7898
  /**
@@ -7893,6 +7959,7 @@ var WebSocketChannel = class {
7893
7959
  logger.info(`Subscription ${sub.method} restored with new ID: ${newSubId}`);
7894
7960
  } catch (error) {
7895
7961
  logger.error(`Failed to restore subscription ${sub.method}:`, error);
7962
+ sub._markClosed();
7896
7963
  }
7897
7964
  });
7898
7965
  await Promise.all(restorePromises);
@@ -7962,6 +8029,7 @@ var WebSocketChannel = class {
7962
8029
  this.websocket.removeEventListener("message", this.messageListener);
7963
8030
  this.websocket.removeEventListener("error", this.errorListener);
7964
8031
  this._rejectInFlight("the connection was closed");
8032
+ this._rejectUnsubscribeWaiters("the connection was closed");
7965
8033
  this.events.emit("close", ev);
7966
8034
  if (!this.userInitiatedClose) {
7967
8035
  this._startReconnect();
@@ -7983,9 +8051,16 @@ var WebSocketChannel = class {
7983
8051
  if (subscription) {
7984
8052
  subscription._handleEvent(result);
7985
8053
  } else {
7986
- logger.warn(
7987
- `WebSocketChannel: Received event for untracked subscription ID: ${subscription_id}.`
7988
- );
8054
+ queueMicrotask(() => {
8055
+ const registered = this.activeSubscriptions.get(subscription_id);
8056
+ if (registered) {
8057
+ registered._handleEvent(result);
8058
+ } else {
8059
+ logger.warn(
8060
+ `WebSocketChannel: Received event for untracked subscription ID: ${subscription_id}.`
8061
+ );
8062
+ }
8063
+ });
7989
8064
  }
7990
8065
  }
7991
8066
  logger.debug("onMessageProxy:", event.data);
@@ -13036,7 +13111,7 @@ function fromWalletApiCall(call) {
13036
13111
  }
13037
13112
  function toWalletApiActions(actions) {
13038
13113
  return actions.map(
13039
- (action) => action.type === "subaccount_invoke" ? { ...action, calls: action.calls.map(toWalletApiCall) } : action
13114
+ (action) => action.type === "shadow_account_invoke" ? { ...action, calls: action.calls.map(toWalletApiCall) } : action
13040
13115
  );
13041
13116
  }
13042
13117
 
@@ -13055,7 +13130,7 @@ __export(connectV6_exports, {
13055
13130
  strk20Balances: () => strk20Balances,
13056
13131
  strk20InvokeTransaction: () => strk20InvokeTransaction,
13057
13132
  strk20PrepareInvoke: () => strk20PrepareInvoke,
13058
- strk20SubaccountCommitment: () => strk20SubaccountCommitment,
13133
+ strk20ShadowAccountCommitment: () => strk20ShadowAccountCommitment,
13059
13134
  subscribeWalletEvent: () => subscribeWalletEvent2,
13060
13135
  supportedSpecs: () => supportedSpecs3,
13061
13136
  supportedWalletApi: () => supportedWalletApi3,
@@ -13143,9 +13218,9 @@ function strk20InvokeTransaction(walletWSF, actions) {
13143
13218
  params: { actions }
13144
13219
  });
13145
13220
  }
13146
- function strk20SubaccountCommitment(walletWSF, dapp_name, nonce) {
13221
+ function strk20ShadowAccountCommitment(walletWSF, dapp_name, nonce) {
13147
13222
  return walletWSF.features["starknet:walletApi"].request({
13148
- type: "wallet_strk20SubaccountCommitment",
13223
+ type: "wallet_strk20ShadowAccountCommitment",
13149
13224
  params: { dapp_name, nonce }
13150
13225
  });
13151
13226
  }
@@ -13237,25 +13312,25 @@ var WalletAccountV6 = class _WalletAccountV6 extends WalletAccountV5 {
13237
13312
  return strk20InvokeTransaction(this.v6Provider, toWalletApiActions(actions));
13238
13313
  }
13239
13314
  /**
13240
- * Compute the commitment of a DAPP STRK20 sub-account. The commitment is computed
13315
+ * Compute the commitment of a DAPP STRK20 shadow account. The commitment is computed
13241
13316
  * locally by the wallet from the user private state ; no transaction is sent.
13242
13317
  *
13243
- * When `nonce` is given, the full commitment of this single sub-account is returned.
13318
+ * When `nonce` is given, the full commitment of this single shadow account is returned.
13244
13319
  * When `nonce` is omitted, the partial (nonce independent) commitment is returned
13245
- * instead : it is shared by every sub-account the user derives for this DAPP, so it can
13246
- * be published once to let a DAPP recognize all the sub-accounts of a user without
13247
- * learning any individual nonce.
13248
- * @param {STRK20_DAPP_NAME} dappName - The DAPP that scopes the sub-account(s).
13249
- * @param {FELT} [nonce] - The sub-account nonce ; each nonce selects a distinct sub-account for this user + DAPP. Omit it to get the partial commitment.
13250
- * @returns {Promise<FELT>} The sub-account commitment.
13320
+ * instead : it is shared by every shadow account the user derives for this DAPP, so it
13321
+ * can be published once to let a DAPP recognize all the shadow accounts of a user
13322
+ * without learning any individual nonce.
13323
+ * @param {STRK20_DAPP_NAME} dappName - The DAPP that scopes the shadow account(s).
13324
+ * @param {FELT} [nonce] - The shadow account nonce ; each nonce selects a distinct shadow account for this user + DAPP. Omit it to get the partial commitment.
13325
+ * @returns {Promise<FELT>} The shadow account commitment.
13251
13326
  * @example
13252
13327
  * ```typescript
13253
- * const commitment = await myWalletAccount.strk20SubaccountCommitment('myDapp', '0x0');
13328
+ * const commitment = await myWalletAccount.strk20ShadowAccountCommitment('myDapp', '0x0');
13254
13329
  * // commitment = '0x5f2e...'
13255
13330
  * ```
13256
13331
  */
13257
- strk20SubaccountCommitment(dappName, nonce) {
13258
- return strk20SubaccountCommitment(this.v6Provider, dappName, nonce);
13332
+ strk20ShadowAccountCommitment(dappName, nonce) {
13333
+ return strk20ShadowAccountCommitment(this.v6Provider, dappName, nonce);
13259
13334
  }
13260
13335
  static async connect(provider, walletProvider, cairoVersion, paymaster, silentMode = false) {
13261
13336
  const { accounts } = await standardConnect2(walletProvider, silentMode);