@routstr/sdk 0.1.6 → 0.1.8

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.d.mts CHANGED
@@ -5,7 +5,7 @@ import { ModelManager } from './discovery/index.mjs';
5
5
  export { MintDiscovery, ModelManagerConfig } from './discovery/index.mjs';
6
6
  import { W as WalletAdapter, S as StorageAdapter, P as ProviderRegistry } from './interfaces-B85Wx7ni.mjs';
7
7
  export { A as ApiKeyEntry, C as ChildKeyEntry, R as RoutstrClientOptions, a as StreamingCallbacks } from './interfaces-B85Wx7ni.mjs';
8
- export { BalanceManager, CashuSpender, CreateProviderTokenOptions, ProviderTokenResult, RefundApiKeyOptions, RefundOptions, SpendOptions, TopUpOptions } from './wallet/index.mjs';
8
+ export { BalanceManager, BalanceState, CashuSpender, CreateProviderTokenOptions, ProviderTokenResult, RefundApiKeyOptions, RefundOptions, SpendOptions, TopUpOptions } from './wallet/index.mjs';
9
9
  import { DebugLevel } from './client/index.mjs';
10
10
  export { AlertLevel, FetchOptions, ModelProviderPrice, ProviderManager, RouteRequestParams, RoutstrClient, RoutstrClientMode, StreamCallbacks, StreamProcessor } from './client/index.mjs';
11
11
  export { SDK_STORAGE_KEYS, SdkStore, StorageDriver, createDiscoveryAdapterFromStore, createIndexedDBDriver, createMemoryDriver, createProviderRegistryFromStore, createSdkStore, createSqliteDriver, createStorageAdapterFromStore, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, localStorageDriver } from './storage/index.mjs';
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ import { ModelManager } from './discovery/index.js';
5
5
  export { MintDiscovery, ModelManagerConfig } from './discovery/index.js';
6
6
  import { W as WalletAdapter, S as StorageAdapter, P as ProviderRegistry } from './interfaces-BVNyAmKu.js';
7
7
  export { A as ApiKeyEntry, C as ChildKeyEntry, R as RoutstrClientOptions, a as StreamingCallbacks } from './interfaces-BVNyAmKu.js';
8
- export { BalanceManager, CashuSpender, CreateProviderTokenOptions, ProviderTokenResult, RefundApiKeyOptions, RefundOptions, SpendOptions, TopUpOptions } from './wallet/index.js';
8
+ export { BalanceManager, BalanceState, CashuSpender, CreateProviderTokenOptions, ProviderTokenResult, RefundApiKeyOptions, RefundOptions, SpendOptions, TopUpOptions } from './wallet/index.js';
9
9
  import { DebugLevel } from './client/index.js';
10
10
  export { AlertLevel, FetchOptions, ModelProviderPrice, ProviderManager, RouteRequestParams, RoutstrClient, RoutstrClientMode, StreamCallbacks, StreamProcessor } from './client/index.js';
11
11
  export { SDK_STORAGE_KEYS, SdkStore, StorageDriver, createDiscoveryAdapterFromStore, createIndexedDBDriver, createMemoryDriver, createProviderRegistryFromStore, createSdkStore, createSqliteDriver, createStorageAdapterFromStore, getDefaultDiscoveryAdapter, getDefaultProviderRegistry, getDefaultSdkDriver, getDefaultSdkStore, getDefaultStorageAdapter, localStorageDriver } from './storage/index.js';
package/dist/index.js CHANGED
@@ -768,6 +768,9 @@ var CashuSpender = class {
768
768
  return result;
769
769
  }
770
770
  async _getBalanceState() {
771
+ if (this.balanceManager) {
772
+ return this.balanceManager.getBalanceState();
773
+ }
771
774
  const mintBalances = await this.walletAdapter.getBalances();
772
775
  const units = this.walletAdapter.getMintUnits();
773
776
  let totalMintBalance = 0;
@@ -783,15 +786,16 @@ var CashuSpender = class {
783
786
  const providerBalances = {};
784
787
  let totalProviderBalance = 0;
785
788
  for (const pending of pendingDistribution) {
786
- providerBalances[pending.baseUrl] = pending.amount;
789
+ providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
787
790
  totalProviderBalance += pending.amount;
788
791
  }
789
792
  const apiKeys = this.storageAdapter.getAllApiKeys();
790
793
  for (const apiKey of apiKeys) {
791
794
  if (!providerBalances[apiKey.baseUrl]) {
792
- providerBalances[apiKey.baseUrl] = apiKey.balance;
793
- totalProviderBalance += apiKey.balance;
795
+ providerBalances[apiKey.baseUrl] = 0;
794
796
  }
797
+ providerBalances[apiKey.baseUrl] += apiKey.balance;
798
+ totalProviderBalance += apiKey.balance;
795
799
  }
796
800
  return {
797
801
  totalBalance: totalMintBalance + totalProviderBalance,
@@ -940,25 +944,12 @@ var CashuSpender = class {
940
944
  `[CashuSpender] _spendInternal: Could not reuse token, will create new token`
941
945
  );
942
946
  }
943
- const balances = await this.walletAdapter.getBalances();
944
- const units = this.walletAdapter.getMintUnits();
945
- let totalBalance = 0;
946
- for (const url in balances) {
947
- const balance = balances[url];
948
- const unit = units[url];
949
- const balanceInSats = getBalanceInSats(balance, unit);
950
- totalBalance += balanceInSats;
951
- }
952
- const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
953
- const totalPending = pendingDistribution.reduce(
954
- (sum, item) => sum + item.amount,
955
- 0
956
- );
947
+ const balanceState = await this._getBalanceState();
948
+ const totalAvailableBalance = balanceState.totalBalance;
957
949
  this._log(
958
950
  "DEBUG",
959
- `[CashuSpender] _spendInternal: totalBalance=${totalBalance}, totalPending=${totalPending}, adjustedAmount=${adjustedAmount}`
951
+ `[CashuSpender] _spendInternal: totalAvailableBalance=${totalAvailableBalance}, adjustedAmount=${adjustedAmount}`
960
952
  );
961
- const totalAvailableBalance = totalBalance + totalPending;
962
953
  if (totalAvailableBalance < adjustedAmount) {
963
954
  this._log(
964
955
  "ERROR",
@@ -966,8 +957,7 @@ var CashuSpender = class {
966
957
  );
967
958
  return this._createInsufficientBalanceError(
968
959
  adjustedAmount,
969
- balances,
970
- units,
960
+ balanceState.mintBalances,
971
961
  totalAvailableBalance
972
962
  );
973
963
  }
@@ -987,8 +977,7 @@ var CashuSpender = class {
987
977
  if ((tokenResult.error || "").includes("Insufficient balance")) {
988
978
  return this._createInsufficientBalanceError(
989
979
  adjustedAmount,
990
- balances,
991
- units,
980
+ balanceState.mintBalances,
992
981
  totalAvailableBalance
993
982
  );
994
983
  }
@@ -1033,6 +1022,7 @@ var CashuSpender = class {
1033
1022
  "DEBUG",
1034
1023
  `[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`
1035
1024
  );
1025
+ const units = this.walletAdapter.getMintUnits();
1036
1026
  return {
1037
1027
  token,
1038
1028
  status: "success",
@@ -1170,13 +1160,11 @@ var CashuSpender = class {
1170
1160
  /**
1171
1161
  * Create an insufficient balance error result
1172
1162
  */
1173
- _createInsufficientBalanceError(required, balances, units, availableBalance) {
1163
+ _createInsufficientBalanceError(required, normalizedBalances, availableBalance) {
1174
1164
  let maxBalance = 0;
1175
1165
  let maxMintUrl = "";
1176
- for (const mintUrl in balances) {
1177
- const balance = balances[mintUrl];
1178
- const unit = units[mintUrl];
1179
- const balanceInSats = getBalanceInSats(balance, unit);
1166
+ for (const mintUrl in normalizedBalances) {
1167
+ const balanceInSats = normalizedBalances[mintUrl];
1180
1168
  if (balanceInSats > maxBalance) {
1181
1169
  maxBalance = balanceInSats;
1182
1170
  maxMintUrl = mintUrl;
@@ -1237,6 +1225,39 @@ var BalanceManager = class {
1237
1225
  }
1238
1226
  }
1239
1227
  cashuSpender;
1228
+ async getBalanceState() {
1229
+ const mintBalances = await this.walletAdapter.getBalances();
1230
+ const units = this.walletAdapter.getMintUnits();
1231
+ let totalMintBalance = 0;
1232
+ const normalizedMintBalances = {};
1233
+ for (const url in mintBalances) {
1234
+ const balance = mintBalances[url];
1235
+ const unit = units[url];
1236
+ const balanceInSats = getBalanceInSats(balance, unit);
1237
+ normalizedMintBalances[url] = balanceInSats;
1238
+ totalMintBalance += balanceInSats;
1239
+ }
1240
+ const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
1241
+ const providerBalances = {};
1242
+ let totalProviderBalance = 0;
1243
+ for (const pending of pendingDistribution) {
1244
+ providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
1245
+ totalProviderBalance += pending.amount;
1246
+ }
1247
+ const apiKeys = this.storageAdapter.getAllApiKeys();
1248
+ for (const apiKey of apiKeys) {
1249
+ if (!providerBalances[apiKey.baseUrl]) {
1250
+ providerBalances[apiKey.baseUrl] = 0;
1251
+ }
1252
+ providerBalances[apiKey.baseUrl] += apiKey.balance;
1253
+ totalProviderBalance += apiKey.balance;
1254
+ }
1255
+ return {
1256
+ totalBalance: totalMintBalance + totalProviderBalance,
1257
+ providerBalances,
1258
+ mintBalances: normalizedMintBalances
1259
+ };
1260
+ }
1240
1261
  /**
1241
1262
  * Unified refund - handles both NIP-60 and legacy wallet refunds
1242
1263
  */
@@ -1466,17 +1487,17 @@ var BalanceManager = class {
1466
1487
  if (!adjustedAmount || isNaN(adjustedAmount)) {
1467
1488
  return { success: false, error: "Invalid top up amount" };
1468
1489
  }
1490
+ const balanceState = await this.getBalanceState();
1469
1491
  const balances = await this.walletAdapter.getBalances();
1470
1492
  const units = this.walletAdapter.getMintUnits();
1471
- let totalMintBalance = 0;
1472
- for (const url in balances) {
1473
- const unit = units[url];
1474
- const balanceInSats = getBalanceInSats(balances[url], unit);
1475
- totalMintBalance += balanceInSats;
1476
- }
1477
- const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
1478
- const refundablePending = pendingDistribution.filter((entry) => entry.baseUrl !== baseUrl).reduce((sum, entry) => sum + entry.amount, 0);
1479
- if (totalMintBalance < adjustedAmount && totalMintBalance + refundablePending >= adjustedAmount && retryCount < 1) {
1493
+ const totalMintBalance = Object.values(balanceState.mintBalances).reduce(
1494
+ (sum, value) => sum + value,
1495
+ 0
1496
+ );
1497
+ const refundableProviderBalance = Object.entries(
1498
+ balanceState.providerBalances
1499
+ ).filter(([providerBaseUrl]) => providerBaseUrl !== baseUrl).reduce((sum, [, value]) => sum + value, 0);
1500
+ if (totalMintBalance < adjustedAmount && totalMintBalance + refundableProviderBalance >= adjustedAmount && retryCount < 1) {
1480
1501
  await this._refundOtherProvidersForTopUp(baseUrl, mintUrl);
1481
1502
  return this.createProviderToken({
1482
1503
  ...options,
@@ -1598,10 +1619,14 @@ var BalanceManager = class {
1598
1619
  }
1599
1620
  async _refundOtherProvidersForTopUp(baseUrl, mintUrl) {
1600
1621
  const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
1622
+ const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
1601
1623
  const toRefund = pendingDistribution.filter(
1602
1624
  (pending) => pending.baseUrl !== baseUrl
1603
1625
  );
1604
- const refundResults = await Promise.allSettled(
1626
+ const apiKeysToRefund = apiKeyDistribution.filter(
1627
+ (apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0
1628
+ );
1629
+ const tokenRefundResults = await Promise.allSettled(
1605
1630
  toRefund.map(async (pending) => {
1606
1631
  const token = this.storageAdapter.getToken(pending.baseUrl);
1607
1632
  if (!token) {
@@ -1619,11 +1644,32 @@ var BalanceManager = class {
1619
1644
  return { baseUrl: pending.baseUrl, success: result.success };
1620
1645
  })
1621
1646
  );
1622
- for (const result of refundResults) {
1647
+ for (const result of tokenRefundResults) {
1623
1648
  if (result.status === "fulfilled" && result.value.success) {
1624
1649
  this.storageAdapter.removeToken(result.value.baseUrl);
1625
1650
  }
1626
1651
  }
1652
+ const apiKeyRefundResults = await Promise.allSettled(
1653
+ apiKeysToRefund.map(async (apiKeyEntry) => {
1654
+ const fullApiKeyEntry = this.storageAdapter.getApiKey(
1655
+ apiKeyEntry.baseUrl
1656
+ );
1657
+ if (!fullApiKeyEntry) {
1658
+ return { baseUrl: apiKeyEntry.baseUrl, success: false };
1659
+ }
1660
+ const result = await this.refundApiKey({
1661
+ mintUrl,
1662
+ baseUrl: apiKeyEntry.baseUrl,
1663
+ apiKey: fullApiKeyEntry.key
1664
+ });
1665
+ return { baseUrl: apiKeyEntry.baseUrl, success: result.success };
1666
+ })
1667
+ );
1668
+ for (const result of apiKeyRefundResults) {
1669
+ if (result.status === "fulfilled" && result.value.success) {
1670
+ this.storageAdapter.updateApiKeyBalance(result.value.baseUrl, 0);
1671
+ }
1672
+ }
1627
1673
  }
1628
1674
  /**
1629
1675
  * Fetch refund token from provider API
@@ -1822,6 +1868,14 @@ var BalanceManager = class {
1822
1868
  console.log(response.status);
1823
1869
  const data = await response.json();
1824
1870
  console.log("FAILED ", data);
1871
+ const isInvalidApiKey = response.status === 401 && data?.code === "invalid_api_key" && data?.message?.includes("proofs already spent");
1872
+ return {
1873
+ amount: -1,
1874
+ reserved: data.reserved ?? 0,
1875
+ unit: "msat",
1876
+ apiKey: data.api_key,
1877
+ isInvalidApiKey
1878
+ };
1825
1879
  }
1826
1880
  } catch (error) {
1827
1881
  console.error("ERRORR IN RESTPONSE", error);
@@ -2770,7 +2824,8 @@ var RoutstrClient = class {
2770
2824
  response.status,
2771
2825
  requestId,
2772
2826
  this.mode === "xcashu" ? response.headers.get("x-cashu") ?? void 0 : void 0,
2773
- bodyText
2827
+ bodyText,
2828
+ params.retryCount ?? 0
2774
2829
  );
2775
2830
  }
2776
2831
  return response;
@@ -2779,8 +2834,12 @@ var RoutstrClient = class {
2779
2834
  return await this._handleErrorResponse(
2780
2835
  params,
2781
2836
  token,
2782
- -1
2837
+ -1,
2783
2838
  // just for Network Error to skip all statuses
2839
+ void 0,
2840
+ void 0,
2841
+ void 0,
2842
+ params.retryCount ?? 0
2784
2843
  );
2785
2844
  }
2786
2845
  throw error;
@@ -2789,7 +2848,8 @@ var RoutstrClient = class {
2789
2848
  /**
2790
2849
  * Handle error responses with failover
2791
2850
  */
2792
- async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody) {
2851
+ async _handleErrorResponse(params, token, status, requestId, xCashuRefundToken, responseBody, retryCount = 0) {
2852
+ const MAX_RETRIES_PER_PROVIDER = 2;
2793
2853
  const { path, method, body, selectedModel, baseUrl, mintUrl } = params;
2794
2854
  let tryNextProvider = false;
2795
2855
  this._log(
@@ -2860,10 +2920,34 @@ var RoutstrClient = class {
2860
2920
  }
2861
2921
  }
2862
2922
  if (status === 402 && !tryNextProvider && (this.mode === "apikeys" || this.mode === "lazyrefund")) {
2923
+ this.storageAdapter.getApiKey(baseUrl);
2924
+ let topupAmount = params.requiredSats;
2925
+ try {
2926
+ let currentBalance = 0;
2927
+ if (this.mode === "apikeys") {
2928
+ const currentBalanceInfo = await this.balanceManager.getTokenBalance(
2929
+ params.token,
2930
+ baseUrl
2931
+ );
2932
+ currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
2933
+ } else if (this.mode === "lazyrefund") {
2934
+ const distribution = this.storageAdapter.getCachedTokenDistribution();
2935
+ const tokenEntry = distribution.find((t) => t.baseUrl === baseUrl);
2936
+ currentBalance = tokenEntry?.amount ?? 0;
2937
+ }
2938
+ const shortfall = Math.max(0, params.requiredSats - currentBalance);
2939
+ topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
2940
+ } catch (e) {
2941
+ this._log(
2942
+ "WARN",
2943
+ "Could not get current token balance for topup calculation:",
2944
+ e
2945
+ );
2946
+ }
2863
2947
  const topupResult = await this.balanceManager.topUp({
2864
2948
  mintUrl,
2865
2949
  baseUrl,
2866
- amount: params.requiredSats * TOPUP_MARGIN,
2950
+ amount: topupAmount * TOPUP_MARGIN,
2867
2951
  token: params.token
2868
2952
  });
2869
2953
  this._log(
@@ -2895,12 +2979,26 @@ var RoutstrClient = class {
2895
2979
  `[RoutstrClient] _handleErrorResponse: Topup successful, will retry with new token`
2896
2980
  );
2897
2981
  }
2898
- if (!tryNextProvider)
2899
- return this._makeRequest({
2900
- ...params,
2901
- token: params.token,
2902
- headers: this._withAuthHeader(params.baseHeaders, params.token)
2903
- });
2982
+ if (!tryNextProvider) {
2983
+ if (retryCount < MAX_RETRIES_PER_PROVIDER) {
2984
+ this._log(
2985
+ "DEBUG",
2986
+ `[RoutstrClient] _handleErrorResponse: Retrying 402 (attempt ${retryCount + 1}/${MAX_RETRIES_PER_PROVIDER})`
2987
+ );
2988
+ return this._makeRequest({
2989
+ ...params,
2990
+ token: params.token,
2991
+ headers: this._withAuthHeader(params.baseHeaders, params.token),
2992
+ retryCount: retryCount + 1
2993
+ });
2994
+ } else {
2995
+ this._log(
2996
+ "DEBUG",
2997
+ `[RoutstrClient] _handleErrorResponse: 402 retry limit reached (${retryCount}/${MAX_RETRIES_PER_PROVIDER}), failing over to next provider`
2998
+ );
2999
+ tryNextProvider = true;
3000
+ }
3001
+ }
2904
3002
  }
2905
3003
  const isInsufficientBalance413 = status === 413 && responseBody?.includes("Insufficient balance");
2906
3004
  if (isInsufficientBalance413 && !tryNextProvider && this.mode === "apikeys") {
@@ -2910,19 +3008,31 @@ var RoutstrClient = class {
2910
3008
  params.token,
2911
3009
  baseUrl
2912
3010
  );
2913
- const latestTokenBalance = latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
2914
- if (latestBalanceInfo.apiKey) {
2915
- const storedApiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
2916
- if (storedApiKeyEntry?.key !== latestBalanceInfo.apiKey) {
2917
- if (storedApiKeyEntry) {
2918
- this.storageAdapter.removeApiKey(baseUrl);
3011
+ if (latestBalanceInfo.isInvalidApiKey) {
3012
+ this._log(
3013
+ "DEBUG",
3014
+ `[RoutstrClient] _handleErrorResponse: Invalid API key (proofs already spent), removing for ${baseUrl}`
3015
+ );
3016
+ this.storageAdapter.removeApiKey(baseUrl);
3017
+ tryNextProvider = true;
3018
+ } else {
3019
+ const latestTokenBalance = latestBalanceInfo.unit === "msat" ? latestBalanceInfo.amount / 1e3 : latestBalanceInfo.amount;
3020
+ if (latestBalanceInfo.apiKey) {
3021
+ const storedApiKeyEntry = this.storageAdapter.getApiKey(baseUrl);
3022
+ if (storedApiKeyEntry?.key !== latestBalanceInfo.apiKey) {
3023
+ if (storedApiKeyEntry) {
3024
+ this.storageAdapter.removeApiKey(baseUrl);
3025
+ }
3026
+ this.storageAdapter.setApiKey(baseUrl, latestBalanceInfo.apiKey);
2919
3027
  }
2920
- this.storageAdapter.setApiKey(baseUrl, latestBalanceInfo.apiKey);
3028
+ retryToken = latestBalanceInfo.apiKey;
3029
+ }
3030
+ if (latestTokenBalance >= 0) {
3031
+ this.storageAdapter.updateApiKeyBalance(
3032
+ baseUrl,
3033
+ latestTokenBalance
3034
+ );
2921
3035
  }
2922
- retryToken = latestBalanceInfo.apiKey;
2923
- }
2924
- if (latestTokenBalance >= 0) {
2925
- this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
2926
3036
  }
2927
3037
  } catch (error) {
2928
3038
  this._log(
@@ -2931,11 +3041,24 @@ var RoutstrClient = class {
2931
3041
  error
2932
3042
  );
2933
3043
  }
2934
- return this._makeRequest({
2935
- ...params,
2936
- token: retryToken,
2937
- headers: this._withAuthHeader(params.baseHeaders, retryToken)
2938
- });
3044
+ if (retryCount < MAX_RETRIES_PER_PROVIDER) {
3045
+ this._log(
3046
+ "DEBUG",
3047
+ `[RoutstrClient] _handleErrorResponse: Retrying 413 (attempt ${retryCount + 1}/${MAX_RETRIES_PER_PROVIDER})`
3048
+ );
3049
+ return this._makeRequest({
3050
+ ...params,
3051
+ token: retryToken,
3052
+ headers: this._withAuthHeader(params.baseHeaders, retryToken),
3053
+ retryCount: retryCount + 1
3054
+ });
3055
+ } else {
3056
+ this._log(
3057
+ "DEBUG",
3058
+ `[RoutstrClient] _handleErrorResponse: 413 retry limit reached (${retryCount}/${MAX_RETRIES_PER_PROVIDER}), failing over to next provider`
3059
+ );
3060
+ tryNextProvider = true;
3061
+ }
2939
3062
  }
2940
3063
  if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
2941
3064
  this._log(
@@ -3051,7 +3174,8 @@ var RoutstrClient = class {
3051
3174
  selectedModel: newModel,
3052
3175
  token: spendResult.token,
3053
3176
  requiredSats: newRequiredSats,
3054
- headers: this._withAuthHeader(params.baseHeaders, spendResult.token)
3177
+ headers: this._withAuthHeader(params.baseHeaders, spendResult.token),
3178
+ retryCount: 0
3055
3179
  });
3056
3180
  }
3057
3181
  throw new FailoverError(baseUrl, Array.from(this.providerManager));
@@ -3681,12 +3805,13 @@ var SDK_STORAGE_KEYS = {
3681
3805
  CHILD_KEYS: "child_keys",
3682
3806
  ROUTSTR21_MODELS: "routstr21Models",
3683
3807
  LAST_ROUTSTR21_MODELS_UPDATE: "lastRoutstr21ModelsUpdate",
3684
- CACHED_RECEIVE_TOKENS: "cached_receive_tokens"
3808
+ CACHED_RECEIVE_TOKENS: "cached_receive_tokens",
3809
+ USAGE_TRACKING: "usage_tracking"
3685
3810
  };
3686
3811
 
3687
3812
  // storage/store.ts
3688
3813
  var normalizeBaseUrl = (baseUrl) => baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
3689
- var getTokenBalance = (token) => {
3814
+ var getCashuTokenBalance = (token) => {
3690
3815
  try {
3691
3816
  const decoded = cashuTs.getDecodedToken(token);
3692
3817
  const unitDivisor = decoded.unit === "msat" ? 1e3 : 1;
@@ -3716,7 +3841,8 @@ var createSdkStore = async ({
3716
3841
  rawChildKeys,
3717
3842
  rawRoutstr21Models,
3718
3843
  rawLastRoutstr21ModelsUpdate,
3719
- rawCachedReceiveTokens
3844
+ rawCachedReceiveTokens,
3845
+ rawUsageTracking
3720
3846
  ] = await Promise.all([
3721
3847
  driver.getItem(
3722
3848
  SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
@@ -3746,7 +3872,8 @@ var createSdkStore = async ({
3746
3872
  SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
3747
3873
  null
3748
3874
  ),
3749
- driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, [])
3875
+ driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, []),
3876
+ driver.getItem(SDK_STORAGE_KEYS.USAGE_TRACKING, [])
3750
3877
  ]);
3751
3878
  const modelsFromAllProviders = Object.fromEntries(
3752
3879
  Object.entries(rawModels).map(([baseUrl, models]) => [
@@ -3779,7 +3906,7 @@ var createSdkStore = async ({
3779
3906
  const cachedTokens = rawCachedTokens.map((entry) => ({
3780
3907
  ...entry,
3781
3908
  baseUrl: normalizeBaseUrl(entry.baseUrl),
3782
- balance: typeof entry.balance === "number" ? entry.balance : getTokenBalance(entry.token),
3909
+ balance: typeof entry.balance === "number" ? entry.balance : getCashuTokenBalance(entry.token),
3783
3910
  lastUsed: entry.lastUsed ?? null
3784
3911
  }));
3785
3912
  const apiKeys = rawApiKeys.map((entry) => ({
@@ -3804,6 +3931,7 @@ var createSdkStore = async ({
3804
3931
  unit: entry.unit || "sat",
3805
3932
  createdAt: entry.createdAt ?? Date.now()
3806
3933
  }));
3934
+ const usageTracking = rawUsageTracking;
3807
3935
  return vanilla.createStore((set, get) => ({
3808
3936
  modelsFromAllProviders,
3809
3937
  lastUsedModel,
@@ -3819,6 +3947,7 @@ var createSdkStore = async ({
3819
3947
  routstr21Models,
3820
3948
  lastRoutstr21ModelsUpdate,
3821
3949
  cachedReceiveTokens,
3950
+ usageTracking,
3822
3951
  setModelsFromAllProviders: (value) => {
3823
3952
  const normalized = {};
3824
3953
  for (const [baseUrl, models] of Object.entries(value)) {
@@ -3883,7 +4012,7 @@ var createSdkStore = async ({
3883
4012
  const normalized = updates.map((entry) => ({
3884
4013
  ...entry,
3885
4014
  baseUrl: normalizeBaseUrl(entry.baseUrl),
3886
- balance: typeof entry.balance === "number" ? entry.balance : getTokenBalance(entry.token),
4015
+ balance: typeof entry.balance === "number" ? entry.balance : getCashuTokenBalance(entry.token),
3887
4016
  lastUsed: entry.lastUsed ?? null
3888
4017
  }));
3889
4018
  void driver.setItem(SDK_STORAGE_KEYS.LOCAL_CASHU_TOKENS, normalized);
@@ -3935,6 +4064,10 @@ var createSdkStore = async ({
3935
4064
  }));
3936
4065
  void driver.setItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, normalized);
3937
4066
  set({ cachedReceiveTokens: normalized });
4067
+ },
4068
+ setUsageTracking: (value) => {
4069
+ void driver.setItem(SDK_STORAGE_KEYS.USAGE_TRACKING, value);
4070
+ set({ usageTracking: value });
3938
4071
  }
3939
4072
  }));
3940
4073
  };
@@ -3982,7 +4115,7 @@ var createStorageAdapterFromStore = (store) => ({
3982
4115
  setToken: (baseUrl, token) => {
3983
4116
  const normalized = normalizeBaseUrl(baseUrl);
3984
4117
  const tokens = store.getState().cachedTokens;
3985
- const balance = getTokenBalance(token);
4118
+ const balance = getCashuTokenBalance(token);
3986
4119
  const existingIndex = tokens.findIndex(
3987
4120
  (entry) => entry.baseUrl === normalized
3988
4121
  );