@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/index.js
CHANGED
|
@@ -728,7 +728,7 @@ var auditLogger = AuditLogger.getInstance();
|
|
|
728
728
|
|
|
729
729
|
// wallet/tokenUtils.ts
|
|
730
730
|
function isNetworkErrorMessage(message) {
|
|
731
|
-
return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed");
|
|
731
|
+
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");
|
|
732
732
|
}
|
|
733
733
|
function getBalanceInSats(balance, unit) {
|
|
734
734
|
return unit === "msat" ? balance / 1e3 : balance;
|
|
@@ -1018,7 +1018,26 @@ var CashuSpender = class {
|
|
|
1018
1018
|
}
|
|
1019
1019
|
}
|
|
1020
1020
|
if (token && baseUrl) {
|
|
1021
|
-
|
|
1021
|
+
try {
|
|
1022
|
+
this.storageAdapter.setToken(baseUrl, token);
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
if (error instanceof Error && error.message.includes("Token already exists")) {
|
|
1025
|
+
this._log(
|
|
1026
|
+
"DEBUG",
|
|
1027
|
+
`[CashuSpender] _spendInternal: Token already exists for ${baseUrl}, receiving newly created token and using existing`
|
|
1028
|
+
);
|
|
1029
|
+
const receiveResult = await this.receiveToken(token);
|
|
1030
|
+
if (receiveResult.success) {
|
|
1031
|
+
this._log(
|
|
1032
|
+
"DEBUG",
|
|
1033
|
+
`[CashuSpender] _spendInternal: Token restored successfully, amount=${receiveResult.amount}`
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
token = this.storageAdapter.getToken(baseUrl);
|
|
1037
|
+
} else {
|
|
1038
|
+
throw error;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1022
1041
|
}
|
|
1023
1042
|
this._logTransaction("spend", {
|
|
1024
1043
|
amount: spentAmount,
|
|
@@ -1496,7 +1515,13 @@ var BalanceManager = class {
|
|
|
1496
1515
|
p2pkPubkey
|
|
1497
1516
|
} = options;
|
|
1498
1517
|
const adjustedAmount = Math.ceil(amount);
|
|
1518
|
+
console.log(
|
|
1519
|
+
`[BalanceManager.createProviderToken] Starting: baseUrl=${baseUrl}, mintUrl=${mintUrl}, amount=${amount}, adjustedAmount=${adjustedAmount}, retryCount=${retryCount}`
|
|
1520
|
+
);
|
|
1499
1521
|
if (!adjustedAmount || isNaN(adjustedAmount)) {
|
|
1522
|
+
console.error(
|
|
1523
|
+
`[BalanceManager.createProviderToken] FAILURE: Invalid amount - amount=${amount}, adjustedAmount=${adjustedAmount}`
|
|
1524
|
+
);
|
|
1500
1525
|
return { success: false, error: "Invalid top up amount" };
|
|
1501
1526
|
}
|
|
1502
1527
|
const balanceState = await this.getBalanceState();
|
|
@@ -1527,14 +1552,11 @@ var BalanceManager = class {
|
|
|
1527
1552
|
{ url: "", balance: 0 }
|
|
1528
1553
|
).url
|
|
1529
1554
|
);
|
|
1555
|
+
console.error(
|
|
1556
|
+
`[BalanceManager.createProviderToken] FAILURE: Insufficient balance - required=${adjustedAmount}, available=${totalMintBalance + targetProviderBalance}, totalMintBalance=${totalMintBalance}, targetProviderBalance=${targetProviderBalance}, refundableProviderBalance=${refundableProviderBalance}`
|
|
1557
|
+
);
|
|
1530
1558
|
return { success: false, error: error.message };
|
|
1531
1559
|
}
|
|
1532
|
-
if (targetProviderBalance >= adjustedAmount) {
|
|
1533
|
-
return {
|
|
1534
|
-
success: true,
|
|
1535
|
-
amountSpent: 0
|
|
1536
|
-
};
|
|
1537
|
-
}
|
|
1538
1560
|
const providerMints = baseUrl && this.providerRegistry ? this.providerRegistry.getProviderMints(baseUrl) : [];
|
|
1539
1561
|
let requiredAmount = adjustedAmount;
|
|
1540
1562
|
const supportedMintsOnly = providerMints.length > 0;
|
|
@@ -1568,6 +1590,9 @@ var BalanceManager = class {
|
|
|
1568
1590
|
maxMintUrl = mintUrl2;
|
|
1569
1591
|
}
|
|
1570
1592
|
}
|
|
1593
|
+
console.error(
|
|
1594
|
+
`[BalanceManager.createProviderToken] FAILURE: No candidate mints found - requiredAmount=${requiredAmount}, totalMintBalance=${totalMintBalance}, maxBalance=${maxBalance}, maxMintUrl=${maxMintUrl}, providerMints=${JSON.stringify(providerMints)}`
|
|
1595
|
+
);
|
|
1571
1596
|
const error = new InsufficientBalanceError(
|
|
1572
1597
|
adjustedAmount,
|
|
1573
1598
|
totalMintBalance,
|
|
@@ -1579,11 +1604,17 @@ var BalanceManager = class {
|
|
|
1579
1604
|
let lastError;
|
|
1580
1605
|
for (const candidateMint of candidates) {
|
|
1581
1606
|
try {
|
|
1607
|
+
console.log(
|
|
1608
|
+
`[BalanceManager.createProviderToken] Attempting mint: ${candidateMint}, amount: ${requiredAmount}`
|
|
1609
|
+
);
|
|
1582
1610
|
const token = await this.walletAdapter.sendToken(
|
|
1583
1611
|
candidateMint,
|
|
1584
1612
|
requiredAmount,
|
|
1585
1613
|
p2pkPubkey
|
|
1586
1614
|
);
|
|
1615
|
+
console.log(
|
|
1616
|
+
`[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`
|
|
1617
|
+
);
|
|
1587
1618
|
return {
|
|
1588
1619
|
success: true,
|
|
1589
1620
|
token,
|
|
@@ -1591,9 +1622,16 @@ var BalanceManager = class {
|
|
|
1591
1622
|
amountSpent: requiredAmount
|
|
1592
1623
|
};
|
|
1593
1624
|
} catch (error) {
|
|
1625
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1626
|
+
console.error(
|
|
1627
|
+
`[BalanceManager.createProviderToken] FAILURE: Mint ${candidateMint} failed with error: ${errorMsg}`
|
|
1628
|
+
);
|
|
1594
1629
|
if (error instanceof Error) {
|
|
1595
|
-
lastError =
|
|
1630
|
+
lastError = errorMsg;
|
|
1596
1631
|
if (isNetworkErrorMessage(error.message)) {
|
|
1632
|
+
console.warn(
|
|
1633
|
+
`[BalanceManager.createProviderToken] Network error from ${candidateMint}, trying next mint...`
|
|
1634
|
+
);
|
|
1597
1635
|
continue;
|
|
1598
1636
|
}
|
|
1599
1637
|
}
|
|
@@ -1603,6 +1641,9 @@ var BalanceManager = class {
|
|
|
1603
1641
|
};
|
|
1604
1642
|
}
|
|
1605
1643
|
}
|
|
1644
|
+
console.error(
|
|
1645
|
+
`[BalanceManager.createProviderToken] FAILURE: All candidate mints exhausted - lastError=${lastError}, candidates=${JSON.stringify(candidates)}`
|
|
1646
|
+
);
|
|
1606
1647
|
return {
|
|
1607
1648
|
success: false,
|
|
1608
1649
|
error: lastError || "All candidate mints failed while creating top up token"
|
|
@@ -1888,7 +1929,6 @@ var BalanceManager = class {
|
|
|
1888
1929
|
});
|
|
1889
1930
|
if (response.ok) {
|
|
1890
1931
|
const data = await response.json();
|
|
1891
|
-
console.log("TOKENA FASJDFAS", data);
|
|
1892
1932
|
return {
|
|
1893
1933
|
amount: data.balance,
|
|
1894
1934
|
reserved: data.reserved ?? 0,
|
|
@@ -2317,22 +2357,101 @@ function calculateImageTokens(width, height, detail = "auto") {
|
|
|
2317
2357
|
function isInsecureHttpUrl(url) {
|
|
2318
2358
|
return url.startsWith("http://");
|
|
2319
2359
|
}
|
|
2320
|
-
var ProviderManager = class {
|
|
2360
|
+
var ProviderManager = class _ProviderManager {
|
|
2321
2361
|
constructor(providerRegistry) {
|
|
2322
2362
|
this.providerRegistry = providerRegistry;
|
|
2323
2363
|
}
|
|
2324
2364
|
failedProviders = /* @__PURE__ */ new Set();
|
|
2365
|
+
/** Track when each provider last failed (provider URL -> timestamp) */
|
|
2366
|
+
lastFailed = /* @__PURE__ */ new Map();
|
|
2367
|
+
/** Providers on cooldown: [provider_url, cooldown_started_timestamp][] */
|
|
2368
|
+
providersOnCoolDown = [];
|
|
2369
|
+
/** Cooldown duration in milliseconds (5 minutes) */
|
|
2370
|
+
static COOLDOWN_DURATION_MS = 5 * 60 * 1e3;
|
|
2371
|
+
/**
|
|
2372
|
+
* Clean up expired cooldown entries
|
|
2373
|
+
*/
|
|
2374
|
+
cleanupExpiredCooldowns() {
|
|
2375
|
+
const now = Date.now();
|
|
2376
|
+
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
2377
|
+
([, timestamp]) => now - timestamp < _ProviderManager.COOLDOWN_DURATION_MS
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
/**
|
|
2381
|
+
* Get the cooldown duration in milliseconds
|
|
2382
|
+
*/
|
|
2383
|
+
getCooldownDurationMs() {
|
|
2384
|
+
return _ProviderManager.COOLDOWN_DURATION_MS;
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2387
|
+
* Check if a provider is currently on cooldown
|
|
2388
|
+
*/
|
|
2389
|
+
isOnCooldown(baseUrl) {
|
|
2390
|
+
this.cleanupExpiredCooldowns();
|
|
2391
|
+
return this.providersOnCoolDown.some(([url]) => url === baseUrl);
|
|
2392
|
+
}
|
|
2393
|
+
/**
|
|
2394
|
+
* Get all providers currently on cooldown
|
|
2395
|
+
*/
|
|
2396
|
+
getProvidersOnCooldown() {
|
|
2397
|
+
this.cleanupExpiredCooldowns();
|
|
2398
|
+
return [...this.providersOnCoolDown];
|
|
2399
|
+
}
|
|
2325
2400
|
/**
|
|
2326
2401
|
* Reset the failed providers list
|
|
2327
2402
|
*/
|
|
2328
2403
|
resetFailedProviders() {
|
|
2329
2404
|
this.failedProviders.clear();
|
|
2330
2405
|
}
|
|
2406
|
+
/**
|
|
2407
|
+
* Get the last failed timestamp for a provider
|
|
2408
|
+
*/
|
|
2409
|
+
getLastFailed(baseUrl) {
|
|
2410
|
+
return this.lastFailed.get(baseUrl);
|
|
2411
|
+
}
|
|
2412
|
+
/**
|
|
2413
|
+
* Get all providers with their last failed timestamps
|
|
2414
|
+
*/
|
|
2415
|
+
getAllLastFailed() {
|
|
2416
|
+
return new Map(this.lastFailed);
|
|
2417
|
+
}
|
|
2331
2418
|
/**
|
|
2332
2419
|
* Mark a provider as failed
|
|
2420
|
+
* If a provider fails twice within 5 minutes, it's added to cooldown
|
|
2333
2421
|
*/
|
|
2334
2422
|
markFailed(baseUrl) {
|
|
2423
|
+
const now = Date.now();
|
|
2424
|
+
const lastFailure = this.lastFailed.get(baseUrl);
|
|
2425
|
+
this.lastFailed.set(baseUrl, now);
|
|
2335
2426
|
this.failedProviders.add(baseUrl);
|
|
2427
|
+
if (lastFailure !== void 0 && now - lastFailure < _ProviderManager.COOLDOWN_DURATION_MS) {
|
|
2428
|
+
if (!this.isOnCooldown(baseUrl)) {
|
|
2429
|
+
this.providersOnCoolDown.push([baseUrl, now]);
|
|
2430
|
+
console.log(
|
|
2431
|
+
`Provider ${baseUrl} added to cooldown after second failure within 5 minutes`
|
|
2432
|
+
);
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Remove a provider from cooldown (e.g., after successful request)
|
|
2438
|
+
*/
|
|
2439
|
+
removeFromCooldown(baseUrl) {
|
|
2440
|
+
this.providersOnCoolDown = this.providersOnCoolDown.filter(
|
|
2441
|
+
([url]) => url !== baseUrl
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
/**
|
|
2445
|
+
* Clear all cooldown tracking
|
|
2446
|
+
*/
|
|
2447
|
+
clearCooldowns() {
|
|
2448
|
+
this.providersOnCoolDown = [];
|
|
2449
|
+
}
|
|
2450
|
+
/**
|
|
2451
|
+
* Clear all failure tracking (lastFailed timestamps)
|
|
2452
|
+
*/
|
|
2453
|
+
clearFailureHistory() {
|
|
2454
|
+
this.lastFailed.clear();
|
|
2336
2455
|
}
|
|
2337
2456
|
/**
|
|
2338
2457
|
* Check if a provider has failed
|
|
@@ -2361,7 +2480,7 @@ var ProviderManager = class {
|
|
|
2361
2480
|
const allProviders = this.providerRegistry.getAllProvidersModels();
|
|
2362
2481
|
const candidates = [];
|
|
2363
2482
|
for (const [baseUrl, models] of Object.entries(allProviders)) {
|
|
2364
|
-
if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl)) {
|
|
2483
|
+
if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl) || this.isOnCooldown(baseUrl)) {
|
|
2365
2484
|
continue;
|
|
2366
2485
|
}
|
|
2367
2486
|
if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl))) {
|
|
@@ -2408,6 +2527,7 @@ var ProviderManager = class {
|
|
|
2408
2527
|
const torMode = isTorContext();
|
|
2409
2528
|
for (const [baseUrl, models] of Object.entries(allProviders)) {
|
|
2410
2529
|
if (disabledProviders.has(baseUrl)) continue;
|
|
2530
|
+
if (this.isOnCooldown(baseUrl)) continue;
|
|
2411
2531
|
if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl)))
|
|
2412
2532
|
continue;
|
|
2413
2533
|
const model = models.find((m) => m.id === modelId);
|
|
@@ -2431,6 +2551,7 @@ var ProviderManager = class {
|
|
|
2431
2551
|
const results = [];
|
|
2432
2552
|
for (const [baseUrl, models] of Object.entries(allModels)) {
|
|
2433
2553
|
if (!includeDisabled && disabledProviders.has(baseUrl)) continue;
|
|
2554
|
+
if (this.isOnCooldown(baseUrl)) continue;
|
|
2434
2555
|
if (torMode && !baseUrl.includes(".onion")) continue;
|
|
2435
2556
|
if (!torMode && (baseUrl.includes(".onion") || isInsecureHttpUrl(baseUrl)))
|
|
2436
2557
|
continue;
|
|
@@ -2844,7 +2965,6 @@ var RoutstrClient = class {
|
|
|
2844
2965
|
body: body === void 0 || method === "GET" ? void 0 : JSON.stringify(body)
|
|
2845
2966
|
});
|
|
2846
2967
|
if (this.mode === "xcashu") this._log("DEBUG", "response,", response);
|
|
2847
|
-
this._log("DEBUG", "response,", response);
|
|
2848
2968
|
response.baseUrl = baseUrl;
|
|
2849
2969
|
response.token = token;
|
|
2850
2970
|
if (!response.ok) {
|
|
@@ -2867,7 +2987,7 @@ var RoutstrClient = class {
|
|
|
2867
2987
|
}
|
|
2868
2988
|
return response;
|
|
2869
2989
|
} catch (error) {
|
|
2870
|
-
if (
|
|
2990
|
+
if (isNetworkErrorMessage(error?.message || "")) {
|
|
2871
2991
|
return await this._handleErrorResponse(
|
|
2872
2992
|
params,
|
|
2873
2993
|
token,
|
|
@@ -3002,7 +3122,13 @@ var RoutstrClient = class {
|
|
|
3002
3122
|
"DEBUG",
|
|
3003
3123
|
`[RoutstrClient] _handleErrorResponse: Insufficient balance, need=${required}, have=${available}`
|
|
3004
3124
|
);
|
|
3005
|
-
throw new InsufficientBalanceError(
|
|
3125
|
+
throw new InsufficientBalanceError(
|
|
3126
|
+
required,
|
|
3127
|
+
available,
|
|
3128
|
+
0,
|
|
3129
|
+
"",
|
|
3130
|
+
message
|
|
3131
|
+
);
|
|
3006
3132
|
} else {
|
|
3007
3133
|
this._log(
|
|
3008
3134
|
"DEBUG",
|
|
@@ -3215,7 +3341,10 @@ var RoutstrClient = class {
|
|
|
3215
3341
|
retryCount: 0
|
|
3216
3342
|
});
|
|
3217
3343
|
}
|
|
3218
|
-
throw new FailoverError(
|
|
3344
|
+
throw new FailoverError(
|
|
3345
|
+
baseUrl,
|
|
3346
|
+
Array.from(this.providerManager.getFailedProviders())
|
|
3347
|
+
);
|
|
3219
3348
|
}
|
|
3220
3349
|
/**
|
|
3221
3350
|
* Handle post-response balance update for all modes
|
|
@@ -3365,12 +3494,6 @@ var RoutstrClient = class {
|
|
|
3365
3494
|
const distribution = this.storageAdapter.getCachedTokenDistribution();
|
|
3366
3495
|
return distribution.reduce((total, item) => total + item.amount, 0);
|
|
3367
3496
|
}
|
|
3368
|
-
/**
|
|
3369
|
-
* Check if error message indicates a network error
|
|
3370
|
-
*/
|
|
3371
|
-
_isNetworkError(message) {
|
|
3372
|
-
return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed");
|
|
3373
|
-
}
|
|
3374
3497
|
/**
|
|
3375
3498
|
* Handle errors and notify callbacks
|
|
3376
3499
|
*/
|