@routstr/sdk 0.2.2 → 0.2.4
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/client/index.d.mts +43 -4
- package/dist/client/index.d.ts +43 -4
- package/dist/client/index.js +145 -22
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +145 -22
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.js +145 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +145 -22
- package/dist/index.mjs.map +1 -1
- package/dist/wallet/index.js +50 -10
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +50 -10
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/client/index.mjs
CHANGED
|
@@ -79,7 +79,7 @@ var auditLogger = AuditLogger.getInstance();
|
|
|
79
79
|
|
|
80
80
|
// wallet/tokenUtils.ts
|
|
81
81
|
function isNetworkErrorMessage(message) {
|
|
82
|
-
return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed");
|
|
82
|
+
return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed") || message.includes("ERR_TLS_CERT_ALTNAME_INVALID") || message.includes("ERR_TLS_CERT_NOT_YET_VALID") || message.includes("ERR_TLS_CERT_EXPIRED") || message.includes("UNABLE_TO_VERIFY_LEAF_SIGNATURE") || message.includes("SELF_SIGNED_CERT_IN_CHAIN");
|
|
83
83
|
}
|
|
84
84
|
function getBalanceInSats(balance, unit) {
|
|
85
85
|
return unit === "msat" ? balance / 1e3 : balance;
|
|
@@ -369,7 +369,26 @@ var CashuSpender = class {
|
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
371
|
if (token && baseUrl) {
|
|
372
|
-
|
|
372
|
+
try {
|
|
373
|
+
this.storageAdapter.setToken(baseUrl, token);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
if (error instanceof Error && error.message.includes("Token already exists")) {
|
|
376
|
+
this._log(
|
|
377
|
+
"DEBUG",
|
|
378
|
+
`[CashuSpender] _spendInternal: Token already exists for ${baseUrl}, receiving newly created token and using existing`
|
|
379
|
+
);
|
|
380
|
+
const receiveResult = await this.receiveToken(token);
|
|
381
|
+
if (receiveResult.success) {
|
|
382
|
+
this._log(
|
|
383
|
+
"DEBUG",
|
|
384
|
+
`[CashuSpender] _spendInternal: Token restored successfully, amount=${receiveResult.amount}`
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
token = this.storageAdapter.getToken(baseUrl);
|
|
388
|
+
} else {
|
|
389
|
+
throw error;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
373
392
|
}
|
|
374
393
|
this._logTransaction("spend", {
|
|
375
394
|
amount: spentAmount,
|
|
@@ -847,7 +866,13 @@ var BalanceManager = class {
|
|
|
847
866
|
p2pkPubkey
|
|
848
867
|
} = options;
|
|
849
868
|
const adjustedAmount = Math.ceil(amount);
|
|
869
|
+
console.log(
|
|
870
|
+
`[BalanceManager.createProviderToken] Starting: baseUrl=${baseUrl}, mintUrl=${mintUrl}, amount=${amount}, adjustedAmount=${adjustedAmount}, retryCount=${retryCount}`
|
|
871
|
+
);
|
|
850
872
|
if (!adjustedAmount || isNaN(adjustedAmount)) {
|
|
873
|
+
console.error(
|
|
874
|
+
`[BalanceManager.createProviderToken] FAILURE: Invalid amount - amount=${amount}, adjustedAmount=${adjustedAmount}`
|
|
875
|
+
);
|
|
851
876
|
return { success: false, error: "Invalid top up amount" };
|
|
852
877
|
}
|
|
853
878
|
const balanceState = await this.getBalanceState();
|
|
@@ -878,14 +903,11 @@ var BalanceManager = class {
|
|
|
878
903
|
{ url: "", balance: 0 }
|
|
879
904
|
).url
|
|
880
905
|
);
|
|
906
|
+
console.error(
|
|
907
|
+
`[BalanceManager.createProviderToken] FAILURE: Insufficient balance - required=${adjustedAmount}, available=${totalMintBalance + targetProviderBalance}, totalMintBalance=${totalMintBalance}, targetProviderBalance=${targetProviderBalance}, refundableProviderBalance=${refundableProviderBalance}`
|
|
908
|
+
);
|
|
881
909
|
return { success: false, error: error.message };
|
|
882
910
|
}
|
|
883
|
-
if (targetProviderBalance >= adjustedAmount) {
|
|
884
|
-
return {
|
|
885
|
-
success: true,
|
|
886
|
-
amountSpent: 0
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
911
|
const providerMints = baseUrl && this.providerRegistry ? this.providerRegistry.getProviderMints(baseUrl) : [];
|
|
890
912
|
let requiredAmount = adjustedAmount;
|
|
891
913
|
const supportedMintsOnly = providerMints.length > 0;
|
|
@@ -919,6 +941,9 @@ var BalanceManager = class {
|
|
|
919
941
|
maxMintUrl = mintUrl2;
|
|
920
942
|
}
|
|
921
943
|
}
|
|
944
|
+
console.error(
|
|
945
|
+
`[BalanceManager.createProviderToken] FAILURE: No candidate mints found - requiredAmount=${requiredAmount}, totalMintBalance=${totalMintBalance}, maxBalance=${maxBalance}, maxMintUrl=${maxMintUrl}, providerMints=${JSON.stringify(providerMints)}`
|
|
946
|
+
);
|
|
922
947
|
const error = new InsufficientBalanceError(
|
|
923
948
|
adjustedAmount,
|
|
924
949
|
totalMintBalance,
|
|
@@ -930,11 +955,17 @@ var BalanceManager = class {
|
|
|
930
955
|
let lastError;
|
|
931
956
|
for (const candidateMint of candidates) {
|
|
932
957
|
try {
|
|
958
|
+
console.log(
|
|
959
|
+
`[BalanceManager.createProviderToken] Attempting mint: ${candidateMint}, amount: ${requiredAmount}`
|
|
960
|
+
);
|
|
933
961
|
const token = await this.walletAdapter.sendToken(
|
|
934
962
|
candidateMint,
|
|
935
963
|
requiredAmount,
|
|
936
964
|
p2pkPubkey
|
|
937
965
|
);
|
|
966
|
+
console.log(
|
|
967
|
+
`[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`
|
|
968
|
+
);
|
|
938
969
|
return {
|
|
939
970
|
success: true,
|
|
940
971
|
token,
|
|
@@ -942,9 +973,16 @@ var BalanceManager = class {
|
|
|
942
973
|
amountSpent: requiredAmount
|
|
943
974
|
};
|
|
944
975
|
} catch (error) {
|
|
976
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
977
|
+
console.error(
|
|
978
|
+
`[BalanceManager.createProviderToken] FAILURE: Mint ${candidateMint} failed with error: ${errorMsg}`
|
|
979
|
+
);
|
|
945
980
|
if (error instanceof Error) {
|
|
946
|
-
lastError =
|
|
981
|
+
lastError = errorMsg;
|
|
947
982
|
if (isNetworkErrorMessage(error.message)) {
|
|
983
|
+
console.warn(
|
|
984
|
+
`[BalanceManager.createProviderToken] Network error from ${candidateMint}, trying next mint...`
|
|
985
|
+
);
|
|
948
986
|
continue;
|
|
949
987
|
}
|
|
950
988
|
}
|
|
@@ -954,6 +992,9 @@ var BalanceManager = class {
|
|
|
954
992
|
};
|
|
955
993
|
}
|
|
956
994
|
}
|
|
995
|
+
console.error(
|
|
996
|
+
`[BalanceManager.createProviderToken] FAILURE: All candidate mints exhausted - lastError=${lastError}, candidates=${JSON.stringify(candidates)}`
|
|
997
|
+
);
|
|
957
998
|
return {
|
|
958
999
|
success: false,
|
|
959
1000
|
error: lastError || "All candidate mints failed while creating top up token"
|
|
@@ -1239,7 +1280,6 @@ var BalanceManager = class {
|
|
|
1239
1280
|
});
|
|
1240
1281
|
if (response.ok) {
|
|
1241
1282
|
const data = await response.json();
|
|
1242
|
-
console.log("TOKENA FASJDFAS", data);
|
|
1243
1283
|
return {
|
|
1244
1284
|
amount: data.balance,
|
|
1245
1285
|
reserved: data.reserved ?? 0,
|
|
@@ -1612,22 +1652,101 @@ function calculateImageTokens(width, height, detail = "auto") {
|
|
|
1612
1652
|
function isInsecureHttpUrl(url) {
|
|
1613
1653
|
return url.startsWith("http://");
|
|
1614
1654
|
}
|
|
1615
|
-
var ProviderManager = class {
|
|
1655
|
+
var ProviderManager = class _ProviderManager {
|
|
1616
1656
|
constructor(providerRegistry) {
|
|
1617
1657
|
this.providerRegistry = providerRegistry;
|
|
1618
1658
|
}
|
|
1619
1659
|
failedProviders = /* @__PURE__ */ new Set();
|
|
1660
|
+
/** Track when each provider last failed (provider URL -> timestamp) */
|
|
1661
|
+
lastFailed = /* @__PURE__ */ new Map();
|
|
1662
|
+
/** Providers on cooldown: [provider_url, cooldown_started_timestamp][] */
|
|
1663
|
+
providersOnCoolDown = [];
|
|
1664
|
+
/** Cooldown duration in milliseconds (5 minutes) */
|
|
1665
|
+
static COOLDOWN_DURATION_MS = 5 * 60 * 1e3;
|
|
1666
|
+
/**
|
|
1667
|
+
* Clean up expired cooldown entries
|
|
1668
|
+
*/
|
|
1669
|
+
cleanupExpiredCooldowns() {
|
|
1670
|
+
const now = Date.now();
|
|
1671
|
+
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
1672
|
+
([, timestamp]) => now - timestamp < _ProviderManager.COOLDOWN_DURATION_MS
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
/**
|
|
1676
|
+
* Get the cooldown duration in milliseconds
|
|
1677
|
+
*/
|
|
1678
|
+
getCooldownDurationMs() {
|
|
1679
|
+
return _ProviderManager.COOLDOWN_DURATION_MS;
|
|
1680
|
+
}
|
|
1681
|
+
/**
|
|
1682
|
+
* Check if a provider is currently on cooldown
|
|
1683
|
+
*/
|
|
1684
|
+
isOnCooldown(baseUrl) {
|
|
1685
|
+
this.cleanupExpiredCooldowns();
|
|
1686
|
+
return this.providersOnCoolDown.some(([url]) => url === baseUrl);
|
|
1687
|
+
}
|
|
1688
|
+
/**
|
|
1689
|
+
* Get all providers currently on cooldown
|
|
1690
|
+
*/
|
|
1691
|
+
getProvidersOnCooldown() {
|
|
1692
|
+
this.cleanupExpiredCooldowns();
|
|
1693
|
+
return [...this.providersOnCoolDown];
|
|
1694
|
+
}
|
|
1620
1695
|
/**
|
|
1621
1696
|
* Reset the failed providers list
|
|
1622
1697
|
*/
|
|
1623
1698
|
resetFailedProviders() {
|
|
1624
1699
|
this.failedProviders.clear();
|
|
1625
1700
|
}
|
|
1701
|
+
/**
|
|
1702
|
+
* Get the last failed timestamp for a provider
|
|
1703
|
+
*/
|
|
1704
|
+
getLastFailed(baseUrl) {
|
|
1705
|
+
return this.lastFailed.get(baseUrl);
|
|
1706
|
+
}
|
|
1707
|
+
/**
|
|
1708
|
+
* Get all providers with their last failed timestamps
|
|
1709
|
+
*/
|
|
1710
|
+
getAllLastFailed() {
|
|
1711
|
+
return new Map(this.lastFailed);
|
|
1712
|
+
}
|
|
1626
1713
|
/**
|
|
1627
1714
|
* Mark a provider as failed
|
|
1715
|
+
* If a provider fails twice within 5 minutes, it's added to cooldown
|
|
1628
1716
|
*/
|
|
1629
1717
|
markFailed(baseUrl) {
|
|
1718
|
+
const now = Date.now();
|
|
1719
|
+
const lastFailure = this.lastFailed.get(baseUrl);
|
|
1720
|
+
this.lastFailed.set(baseUrl, now);
|
|
1630
1721
|
this.failedProviders.add(baseUrl);
|
|
1722
|
+
if (lastFailure !== void 0 && now - lastFailure < _ProviderManager.COOLDOWN_DURATION_MS) {
|
|
1723
|
+
if (!this.isOnCooldown(baseUrl)) {
|
|
1724
|
+
this.providersOnCoolDown.push([baseUrl, now]);
|
|
1725
|
+
console.log(
|
|
1726
|
+
`Provider ${baseUrl} added to cooldown after second failure within 5 minutes`
|
|
1727
|
+
);
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
/**
|
|
1732
|
+
* Remove a provider from cooldown (e.g., after successful request)
|
|
1733
|
+
*/
|
|
1734
|
+
removeFromCooldown(baseUrl) {
|
|
1735
|
+
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
1736
|
+
([url]) => url !== baseUrl
|
|
1737
|
+
);
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* Clear all cooldown tracking
|
|
1741
|
+
*/
|
|
1742
|
+
clearCooldowns() {
|
|
1743
|
+
this.providersOnCoolDown = [];
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Clear all failure tracking (lastFailed timestamps)
|
|
1747
|
+
*/
|
|
1748
|
+
clearFailureHistory() {
|
|
1749
|
+
this.lastFailed.clear();
|
|
1631
1750
|
}
|
|
1632
1751
|
/**
|
|
1633
1752
|
* Check if a provider has failed
|
|
@@ -1656,7 +1775,7 @@ var ProviderManager = class {
|
|
|
1656
1775
|
const allProviders = this.providerRegistry.getAllProvidersModels();
|
|
1657
1776
|
const candidates = [];
|
|
1658
1777
|
for (const [baseUrl, models] of Object.entries(allProviders)) {
|
|
1659
|
-
if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl)) {
|
|
1778
|
+
if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl) || this.isOnCooldown(baseUrl)) {
|
|
1660
1779
|
continue;
|
|
1661
1780
|
}
|
|
1662
1781
|
if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl))) {
|
|
@@ -1703,6 +1822,7 @@ var ProviderManager = class {
|
|
|
1703
1822
|
const torMode = isTorContext();
|
|
1704
1823
|
for (const [baseUrl, models] of Object.entries(allProviders)) {
|
|
1705
1824
|
if (disabledProviders.has(baseUrl)) continue;
|
|
1825
|
+
if (this.isOnCooldown(baseUrl)) continue;
|
|
1706
1826
|
if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl)))
|
|
1707
1827
|
continue;
|
|
1708
1828
|
const model = models.find((m) => m.id === modelId);
|
|
@@ -1726,6 +1846,7 @@ var ProviderManager = class {
|
|
|
1726
1846
|
const results = [];
|
|
1727
1847
|
for (const [baseUrl, models] of Object.entries(allModels)) {
|
|
1728
1848
|
if (!includeDisabled && disabledProviders.has(baseUrl)) continue;
|
|
1849
|
+
if (this.isOnCooldown(baseUrl)) continue;
|
|
1729
1850
|
if (torMode && !baseUrl.includes(".onion")) continue;
|
|
1730
1851
|
if (!torMode && (baseUrl.includes(".onion") || isInsecureHttpUrl(baseUrl)))
|
|
1731
1852
|
continue;
|
|
@@ -2139,7 +2260,6 @@ var RoutstrClient = class {
|
|
|
2139
2260
|
body: body === void 0 || method === "GET" ? void 0 : JSON.stringify(body)
|
|
2140
2261
|
});
|
|
2141
2262
|
if (this.mode === "xcashu") this._log("DEBUG", "response,", response);
|
|
2142
|
-
this._log("DEBUG", "response,", response);
|
|
2143
2263
|
response.baseUrl = baseUrl;
|
|
2144
2264
|
response.token = token;
|
|
2145
2265
|
if (!response.ok) {
|
|
@@ -2162,7 +2282,7 @@ var RoutstrClient = class {
|
|
|
2162
2282
|
}
|
|
2163
2283
|
return response;
|
|
2164
2284
|
} catch (error) {
|
|
2165
|
-
if (
|
|
2285
|
+
if (isNetworkErrorMessage(error?.message || "")) {
|
|
2166
2286
|
return await this._handleErrorResponse(
|
|
2167
2287
|
params,
|
|
2168
2288
|
token,
|
|
@@ -2297,7 +2417,13 @@ var RoutstrClient = class {
|
|
|
2297
2417
|
"DEBUG",
|
|
2298
2418
|
`[RoutstrClient] _handleErrorResponse: Insufficient balance, need=${required}, have=${available}`
|
|
2299
2419
|
);
|
|
2300
|
-
throw new InsufficientBalanceError(
|
|
2420
|
+
throw new InsufficientBalanceError(
|
|
2421
|
+
required,
|
|
2422
|
+
available,
|
|
2423
|
+
0,
|
|
2424
|
+
"",
|
|
2425
|
+
message
|
|
2426
|
+
);
|
|
2301
2427
|
} else {
|
|
2302
2428
|
this._log(
|
|
2303
2429
|
"DEBUG",
|
|
@@ -2510,7 +2636,10 @@ var RoutstrClient = class {
|
|
|
2510
2636
|
retryCount: 0
|
|
2511
2637
|
});
|
|
2512
2638
|
}
|
|
2513
|
-
throw new FailoverError(
|
|
2639
|
+
throw new FailoverError(
|
|
2640
|
+
baseUrl,
|
|
2641
|
+
Array.from(this.providerManager.getFailedProviders())
|
|
2642
|
+
);
|
|
2514
2643
|
}
|
|
2515
2644
|
/**
|
|
2516
2645
|
* Handle post-response balance update for all modes
|
|
@@ -2660,12 +2789,6 @@ var RoutstrClient = class {
|
|
|
2660
2789
|
const distribution = this.storageAdapter.getCachedTokenDistribution();
|
|
2661
2790
|
return distribution.reduce((total, item) => total + item.amount, 0);
|
|
2662
2791
|
}
|
|
2663
|
-
/**
|
|
2664
|
-
* Check if error message indicates a network error
|
|
2665
|
-
*/
|
|
2666
|
-
_isNetworkError(message) {
|
|
2667
|
-
return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed");
|
|
2668
|
-
}
|
|
2669
2792
|
/**
|
|
2670
2793
|
* Handle errors and notify callbacks
|
|
2671
2794
|
*/
|