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