@routstr/sdk 0.1.7 → 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.
@@ -127,6 +127,9 @@ var CashuSpender = class {
127
127
  return result;
128
128
  }
129
129
  async _getBalanceState() {
130
+ if (this.balanceManager) {
131
+ return this.balanceManager.getBalanceState();
132
+ }
130
133
  const mintBalances = await this.walletAdapter.getBalances();
131
134
  const units = this.walletAdapter.getMintUnits();
132
135
  let totalMintBalance = 0;
@@ -142,15 +145,16 @@ var CashuSpender = class {
142
145
  const providerBalances = {};
143
146
  let totalProviderBalance = 0;
144
147
  for (const pending of pendingDistribution) {
145
- providerBalances[pending.baseUrl] = pending.amount;
148
+ providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
146
149
  totalProviderBalance += pending.amount;
147
150
  }
148
151
  const apiKeys = this.storageAdapter.getAllApiKeys();
149
152
  for (const apiKey of apiKeys) {
150
153
  if (!providerBalances[apiKey.baseUrl]) {
151
- providerBalances[apiKey.baseUrl] = apiKey.balance;
152
- totalProviderBalance += apiKey.balance;
154
+ providerBalances[apiKey.baseUrl] = 0;
153
155
  }
156
+ providerBalances[apiKey.baseUrl] += apiKey.balance;
157
+ totalProviderBalance += apiKey.balance;
154
158
  }
155
159
  return {
156
160
  totalBalance: totalMintBalance + totalProviderBalance,
@@ -299,25 +303,12 @@ var CashuSpender = class {
299
303
  `[CashuSpender] _spendInternal: Could not reuse token, will create new token`
300
304
  );
301
305
  }
302
- const balances = await this.walletAdapter.getBalances();
303
- const units = this.walletAdapter.getMintUnits();
304
- let totalBalance = 0;
305
- for (const url in balances) {
306
- const balance = balances[url];
307
- const unit = units[url];
308
- const balanceInSats = getBalanceInSats(balance, unit);
309
- totalBalance += balanceInSats;
310
- }
311
- const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
312
- const totalPending = pendingDistribution.reduce(
313
- (sum, item) => sum + item.amount,
314
- 0
315
- );
306
+ const balanceState = await this._getBalanceState();
307
+ const totalAvailableBalance = balanceState.totalBalance;
316
308
  this._log(
317
309
  "DEBUG",
318
- `[CashuSpender] _spendInternal: totalBalance=${totalBalance}, totalPending=${totalPending}, adjustedAmount=${adjustedAmount}`
310
+ `[CashuSpender] _spendInternal: totalAvailableBalance=${totalAvailableBalance}, adjustedAmount=${adjustedAmount}`
319
311
  );
320
- const totalAvailableBalance = totalBalance + totalPending;
321
312
  if (totalAvailableBalance < adjustedAmount) {
322
313
  this._log(
323
314
  "ERROR",
@@ -325,8 +316,7 @@ var CashuSpender = class {
325
316
  );
326
317
  return this._createInsufficientBalanceError(
327
318
  adjustedAmount,
328
- balances,
329
- units,
319
+ balanceState.mintBalances,
330
320
  totalAvailableBalance
331
321
  );
332
322
  }
@@ -346,8 +336,7 @@ var CashuSpender = class {
346
336
  if ((tokenResult.error || "").includes("Insufficient balance")) {
347
337
  return this._createInsufficientBalanceError(
348
338
  adjustedAmount,
349
- balances,
350
- units,
339
+ balanceState.mintBalances,
351
340
  totalAvailableBalance
352
341
  );
353
342
  }
@@ -392,6 +381,7 @@ var CashuSpender = class {
392
381
  "DEBUG",
393
382
  `[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`
394
383
  );
384
+ const units = this.walletAdapter.getMintUnits();
395
385
  return {
396
386
  token,
397
387
  status: "success",
@@ -529,13 +519,11 @@ var CashuSpender = class {
529
519
  /**
530
520
  * Create an insufficient balance error result
531
521
  */
532
- _createInsufficientBalanceError(required, balances, units, availableBalance) {
522
+ _createInsufficientBalanceError(required, normalizedBalances, availableBalance) {
533
523
  let maxBalance = 0;
534
524
  let maxMintUrl = "";
535
- for (const mintUrl in balances) {
536
- const balance = balances[mintUrl];
537
- const unit = units[mintUrl];
538
- const balanceInSats = getBalanceInSats(balance, unit);
525
+ for (const mintUrl in normalizedBalances) {
526
+ const balanceInSats = normalizedBalances[mintUrl];
539
527
  if (balanceInSats > maxBalance) {
540
528
  maxBalance = balanceInSats;
541
529
  maxMintUrl = mintUrl;
@@ -596,6 +584,39 @@ var BalanceManager = class {
596
584
  }
597
585
  }
598
586
  cashuSpender;
587
+ async getBalanceState() {
588
+ const mintBalances = await this.walletAdapter.getBalances();
589
+ const units = this.walletAdapter.getMintUnits();
590
+ let totalMintBalance = 0;
591
+ const normalizedMintBalances = {};
592
+ for (const url in mintBalances) {
593
+ const balance = mintBalances[url];
594
+ const unit = units[url];
595
+ const balanceInSats = getBalanceInSats(balance, unit);
596
+ normalizedMintBalances[url] = balanceInSats;
597
+ totalMintBalance += balanceInSats;
598
+ }
599
+ const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
600
+ const providerBalances = {};
601
+ let totalProviderBalance = 0;
602
+ for (const pending of pendingDistribution) {
603
+ providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
604
+ totalProviderBalance += pending.amount;
605
+ }
606
+ const apiKeys = this.storageAdapter.getAllApiKeys();
607
+ for (const apiKey of apiKeys) {
608
+ if (!providerBalances[apiKey.baseUrl]) {
609
+ providerBalances[apiKey.baseUrl] = 0;
610
+ }
611
+ providerBalances[apiKey.baseUrl] += apiKey.balance;
612
+ totalProviderBalance += apiKey.balance;
613
+ }
614
+ return {
615
+ totalBalance: totalMintBalance + totalProviderBalance,
616
+ providerBalances,
617
+ mintBalances: normalizedMintBalances
618
+ };
619
+ }
599
620
  /**
600
621
  * Unified refund - handles both NIP-60 and legacy wallet refunds
601
622
  */
@@ -825,17 +846,17 @@ var BalanceManager = class {
825
846
  if (!adjustedAmount || isNaN(adjustedAmount)) {
826
847
  return { success: false, error: "Invalid top up amount" };
827
848
  }
849
+ const balanceState = await this.getBalanceState();
828
850
  const balances = await this.walletAdapter.getBalances();
829
851
  const units = this.walletAdapter.getMintUnits();
830
- let totalMintBalance = 0;
831
- for (const url in balances) {
832
- const unit = units[url];
833
- const balanceInSats = getBalanceInSats(balances[url], unit);
834
- totalMintBalance += balanceInSats;
835
- }
836
- const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
837
- const refundablePending = pendingDistribution.filter((entry) => entry.baseUrl !== baseUrl).reduce((sum, entry) => sum + entry.amount, 0);
838
- if (totalMintBalance < adjustedAmount && totalMintBalance + refundablePending >= adjustedAmount && retryCount < 1) {
852
+ const totalMintBalance = Object.values(balanceState.mintBalances).reduce(
853
+ (sum, value) => sum + value,
854
+ 0
855
+ );
856
+ const refundableProviderBalance = Object.entries(
857
+ balanceState.providerBalances
858
+ ).filter(([providerBaseUrl]) => providerBaseUrl !== baseUrl).reduce((sum, [, value]) => sum + value, 0);
859
+ if (totalMintBalance < adjustedAmount && totalMintBalance + refundableProviderBalance >= adjustedAmount && retryCount < 1) {
839
860
  await this._refundOtherProvidersForTopUp(baseUrl, mintUrl);
840
861
  return this.createProviderToken({
841
862
  ...options,
@@ -957,10 +978,14 @@ var BalanceManager = class {
957
978
  }
958
979
  async _refundOtherProvidersForTopUp(baseUrl, mintUrl) {
959
980
  const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
981
+ const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
960
982
  const toRefund = pendingDistribution.filter(
961
983
  (pending) => pending.baseUrl !== baseUrl
962
984
  );
963
- const refundResults = await Promise.allSettled(
985
+ const apiKeysToRefund = apiKeyDistribution.filter(
986
+ (apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0
987
+ );
988
+ const tokenRefundResults = await Promise.allSettled(
964
989
  toRefund.map(async (pending) => {
965
990
  const token = this.storageAdapter.getToken(pending.baseUrl);
966
991
  if (!token) {
@@ -978,11 +1003,32 @@ var BalanceManager = class {
978
1003
  return { baseUrl: pending.baseUrl, success: result.success };
979
1004
  })
980
1005
  );
981
- for (const result of refundResults) {
1006
+ for (const result of tokenRefundResults) {
982
1007
  if (result.status === "fulfilled" && result.value.success) {
983
1008
  this.storageAdapter.removeToken(result.value.baseUrl);
984
1009
  }
985
1010
  }
1011
+ const apiKeyRefundResults = await Promise.allSettled(
1012
+ apiKeysToRefund.map(async (apiKeyEntry) => {
1013
+ const fullApiKeyEntry = this.storageAdapter.getApiKey(
1014
+ apiKeyEntry.baseUrl
1015
+ );
1016
+ if (!fullApiKeyEntry) {
1017
+ return { baseUrl: apiKeyEntry.baseUrl, success: false };
1018
+ }
1019
+ const result = await this.refundApiKey({
1020
+ mintUrl,
1021
+ baseUrl: apiKeyEntry.baseUrl,
1022
+ apiKey: fullApiKeyEntry.key
1023
+ });
1024
+ return { baseUrl: apiKeyEntry.baseUrl, success: result.success };
1025
+ })
1026
+ );
1027
+ for (const result of apiKeyRefundResults) {
1028
+ if (result.status === "fulfilled" && result.value.success) {
1029
+ this.storageAdapter.updateApiKeyBalance(result.value.baseUrl, 0);
1030
+ }
1031
+ }
986
1032
  }
987
1033
  /**
988
1034
  * Fetch refund token from provider API
@@ -2177,10 +2223,34 @@ var RoutstrClient = class {
2177
2223
  }
2178
2224
  }
2179
2225
  if (status === 402 && !tryNextProvider && (this.mode === "apikeys" || this.mode === "lazyrefund")) {
2226
+ this.storageAdapter.getApiKey(baseUrl);
2227
+ let topupAmount = params.requiredSats;
2228
+ try {
2229
+ let currentBalance = 0;
2230
+ if (this.mode === "apikeys") {
2231
+ const currentBalanceInfo = await this.balanceManager.getTokenBalance(
2232
+ params.token,
2233
+ baseUrl
2234
+ );
2235
+ currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
2236
+ } else if (this.mode === "lazyrefund") {
2237
+ const distribution = this.storageAdapter.getCachedTokenDistribution();
2238
+ const tokenEntry = distribution.find((t) => t.baseUrl === baseUrl);
2239
+ currentBalance = tokenEntry?.amount ?? 0;
2240
+ }
2241
+ const shortfall = Math.max(0, params.requiredSats - currentBalance);
2242
+ topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
2243
+ } catch (e) {
2244
+ this._log(
2245
+ "WARN",
2246
+ "Could not get current token balance for topup calculation:",
2247
+ e
2248
+ );
2249
+ }
2180
2250
  const topupResult = await this.balanceManager.topUp({
2181
2251
  mintUrl,
2182
2252
  baseUrl,
2183
- amount: params.requiredSats * TOPUP_MARGIN,
2253
+ amount: topupAmount * TOPUP_MARGIN,
2184
2254
  token: params.token
2185
2255
  });
2186
2256
  this._log(
@@ -2261,7 +2331,10 @@ var RoutstrClient = class {
2261
2331
  retryToken = latestBalanceInfo.apiKey;
2262
2332
  }
2263
2333
  if (latestTokenBalance >= 0) {
2264
- this.storageAdapter.updateApiKeyBalance(baseUrl, latestTokenBalance);
2334
+ this.storageAdapter.updateApiKeyBalance(
2335
+ baseUrl,
2336
+ latestTokenBalance
2337
+ );
2265
2338
  }
2266
2339
  }
2267
2340
  } catch (error) {