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