@routstr/sdk 0.2.6 → 0.2.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.
@@ -1,3 +1,5 @@
1
+ import { getDecodedToken } from '@cashu/cashu-ts';
2
+
1
3
  // core/errors.ts
2
4
  var InsufficientBalanceError = class extends Error {
3
5
  constructor(required, available, maxMintBalance = 0, maxMintUrl = "", customMessage) {
@@ -75,8 +77,6 @@ function selectMintWithBalance(balances, units, amount, excludeMints = []) {
75
77
  }
76
78
  return { selectedMintUrl: null, selectedMintBalance: 0 };
77
79
  }
78
-
79
- // wallet/CashuSpender.ts
80
80
  var CashuSpender = class {
81
81
  constructor(walletAdapter, storageAdapter, _providerRegistry, balanceManager) {
82
82
  this.walletAdapter = walletAdapter;
@@ -87,23 +87,43 @@ var CashuSpender = class {
87
87
  _isBusy = false;
88
88
  debugLevel = "WARN";
89
89
  async receiveToken(token) {
90
- const result = await this.walletAdapter.receiveToken(token);
91
- if (!result.success && result.message?.includes("Failed to fetch mint")) {
92
- const cachedTokens = this.storageAdapter.getCachedReceiveTokens();
93
- const existingIndex = cachedTokens.findIndex((t) => t.token === token);
94
- if (existingIndex === -1) {
95
- this.storageAdapter.setCachedReceiveTokens([
96
- ...cachedTokens,
97
- {
98
- token,
99
- amount: result.amount,
100
- unit: result.unit,
101
- createdAt: Date.now()
102
- }
103
- ]);
90
+ try {
91
+ const result = await this.walletAdapter.receiveToken(token);
92
+ return result;
93
+ } catch (error) {
94
+ const errorMessage = error instanceof Error ? error.message : String(error);
95
+ if (errorMessage.includes("Failed to fetch mint")) {
96
+ const cachedTokens = this.storageAdapter.getCachedReceiveTokens();
97
+ const existingIndex = cachedTokens.findIndex((t) => t.token === token);
98
+ if (existingIndex === -1) {
99
+ const { amount: amount2, unit: unit2 } = this._decodeTokenAmount(token);
100
+ this.storageAdapter.setCachedReceiveTokens([
101
+ ...cachedTokens,
102
+ {
103
+ token,
104
+ amount: amount2,
105
+ unit: unit2,
106
+ createdAt: Date.now()
107
+ }
108
+ ]);
109
+ }
104
110
  }
111
+ const { amount, unit } = this._decodeTokenAmount(token);
112
+ return { success: false, amount, unit, message: errorMessage };
113
+ }
114
+ }
115
+ _decodeTokenAmount(token) {
116
+ try {
117
+ const decoded = getDecodedToken(token);
118
+ const amount = decoded.proofs.reduce(
119
+ (acc, proof) => acc + proof.amount,
120
+ 0
121
+ );
122
+ const unit = decoded.unit || "sat";
123
+ return { amount, unit };
124
+ } catch {
125
+ return { amount: 0, unit: "sat" };
105
126
  }
106
- return result;
107
127
  }
108
128
  async _getBalanceState() {
109
129
  if (this.balanceManager) {
@@ -423,8 +443,9 @@ var CashuSpender = class {
423
443
  return null;
424
444
  }
425
445
  /**
426
- * Refund all xcashu tokens from storage and increment tryCounts on failure.
427
- * Reuses receiveToken from BalanceManager/CashuSpender for receiving refunds.
446
+ * Refund all xcashu tokens from storage by calling the provider's refund endpoint.
447
+ * The xcashu token acts as an API key to claim the refund, and the response contains
448
+ * the actual refunded Cashu token which is then received into the wallet.
428
449
  * @param mintUrl - The mint URL for receiving tokens
429
450
  * @param excludeBaseUrls - Base URLs to exclude from refund (optional)
430
451
  * @returns Results for each xcashu token refund attempt
@@ -437,7 +458,20 @@ var CashuSpender = class {
437
458
  if (excludedUrls.has(baseUrl)) continue;
438
459
  for (const xcashuToken of tokens) {
439
460
  try {
440
- const receiveResult = await this.receiveToken(xcashuToken.token);
461
+ if (!this.balanceManager) {
462
+ throw new Error("BalanceManager not available for xcashu refund");
463
+ }
464
+ const fetchResult = await this.balanceManager.fetchRefundToken(
465
+ baseUrl,
466
+ xcashuToken.token,
467
+ true
468
+ );
469
+ if (!fetchResult.success || !fetchResult.token) {
470
+ throw new Error(
471
+ fetchResult.error || "Failed to fetch refund token from provider"
472
+ );
473
+ }
474
+ const receiveResult = await this.receiveToken(fetchResult.token);
441
475
  if (receiveResult.success) {
442
476
  this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
443
477
  results.push({
@@ -452,7 +486,10 @@ var CashuSpender = class {
452
486
  } else {
453
487
  const currentTryCount = xcashuToken.tryCount ?? 0;
454
488
  const newTryCount = currentTryCount + 1;
455
- this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
489
+ this.storageAdapter.updateXcashuTokenTryCount(
490
+ xcashuToken.token,
491
+ newTryCount
492
+ );
456
493
  results.push({
457
494
  baseUrl,
458
495
  token: xcashuToken.token,
@@ -461,13 +498,16 @@ var CashuSpender = class {
461
498
  });
462
499
  this._log(
463
500
  "DEBUG",
464
- `[CashuSpender] refundXcashuTokens: Failed to refund xcashu token for ${baseUrl}, incremented tryCount to ${newTryCount}`
501
+ `[CashuSpender] refundXcashuTokens: Failed to receive refund token for ${baseUrl}, incremented tryCount to ${newTryCount}: ${receiveResult.message}`
465
502
  );
466
503
  }
467
504
  } catch (error) {
468
505
  const currentTryCount = xcashuToken.tryCount ?? 0;
469
506
  const newTryCount = currentTryCount + 1;
470
- this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
507
+ this.storageAdapter.updateXcashuTokenTryCount(
508
+ xcashuToken.token,
509
+ newTryCount
510
+ );
471
511
  const errorMessage = error instanceof Error ? error.message : String(error);
472
512
  results.push({
473
513
  baseUrl,
@@ -504,7 +544,10 @@ var CashuSpender = class {
504
544
  if (refundResult.success) {
505
545
  this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
506
546
  } else {
507
- this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, apiKeyEntry.amount);
547
+ this.storageAdapter.updateApiKeyBalance(
548
+ apiKeyEntry.baseUrl,
549
+ apiKeyEntry.amount
550
+ );
508
551
  }
509
552
  results.push({
510
553
  baseUrl: apiKeyEntry.baseUrl,
@@ -642,7 +685,7 @@ var BalanceManager = class {
642
685
  }
643
686
  let fetchResult;
644
687
  try {
645
- fetchResult = await this._fetchRefundTokenWithApiKey(baseUrl, apiKey);
688
+ fetchResult = await this.fetchRefundToken(baseUrl, apiKey);
646
689
  if (!fetchResult.success) {
647
690
  return {
648
691
  success: false,
@@ -670,6 +713,7 @@ var BalanceManager = class {
670
713
  return {
671
714
  success: receiveResult.success,
672
715
  refundedAmount: totalAmountMsat,
716
+ message: receiveResult.message,
673
717
  requestId: fetchResult.requestId
674
718
  };
675
719
  } catch (error) {
@@ -678,9 +722,9 @@ var BalanceManager = class {
678
722
  }
679
723
  }
680
724
  /**
681
- * Fetch refund token from provider API using API key authentication
725
+ * Fetch refund token from provider API using API key (or xcashu token) authentication
682
726
  */
683
- async _fetchRefundTokenWithApiKey(baseUrl, apiKey) {
727
+ async fetchRefundToken(baseUrl, apiKeyOrToken, xCashu = false) {
684
728
  if (!baseUrl) {
685
729
  return {
686
730
  success: false,
@@ -694,12 +738,17 @@ var BalanceManager = class {
694
738
  controller.abort();
695
739
  }, 6e4);
696
740
  try {
741
+ const headers = {
742
+ "Content-Type": "application/json"
743
+ };
744
+ if (xCashu) {
745
+ headers["X-Cashu"] = apiKeyOrToken;
746
+ } else {
747
+ headers["Authorization"] = `Bearer ${apiKeyOrToken}`;
748
+ }
697
749
  const response = await fetch(url, {
698
750
  method: "POST",
699
- headers: {
700
- Authorization: `Bearer ${apiKey}`,
701
- "Content-Type": "application/json"
702
- },
751
+ headers,
703
752
  signal: controller.signal
704
753
  });
705
754
  clearTimeout(timeoutId);
@@ -720,10 +769,7 @@ var BalanceManager = class {
720
769
  };
721
770
  } catch (error) {
722
771
  clearTimeout(timeoutId);
723
- console.error(
724
- "[BalanceManager._fetchRefundTokenWithApiKey] Fetch error",
725
- error
726
- );
772
+ console.error("[BalanceManager.fetchRefundToken] Fetch error", error);
727
773
  if (error instanceof Error) {
728
774
  if (error.name === "AbortError") {
729
775
  return {
@@ -770,11 +816,7 @@ var BalanceManager = class {
770
816
  };
771
817
  }
772
818
  cashuToken = tokenResult.token;
773
- const topUpResult = await this._postTopUp(
774
- baseUrl,
775
- apiKey,
776
- cashuToken
777
- );
819
+ const topUpResult = await this._postTopUp(baseUrl, apiKey, cashuToken);
778
820
  requestId = topUpResult.requestId;
779
821
  console.log(topUpResult);
780
822
  if (!topUpResult.success) {
@@ -1140,7 +1182,7 @@ var BalanceManager = class {
1140
1182
  console.log(response.status);
1141
1183
  const data = await response.json();
1142
1184
  console.log("FAILED ", data);
1143
- const isInvalidApiKey = response.status === 401 && data?.code === "invalid_api_key" && data?.message?.includes("proofs already spent");
1185
+ const isInvalidApiKey = response.status === 401 && data?.detail?.error?.code === "invalid_api_key" && data?.detail?.error?.message?.includes("proofs already spent");
1144
1186
  return {
1145
1187
  amount: -1,
1146
1188
  reserved: data.reserved ?? 0,