@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.
- package/dist/client/index.js +114 -41
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +114 -41
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +126 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +126 -44
- package/dist/index.mjs.map +1 -1
- package/dist/storage/index.d.mts +31 -0
- package/dist/storage/index.d.ts +31 -0
- package/dist/storage/index.js +12 -3
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +12 -3
- package/dist/storage/index.mjs.map +1 -1
- package/dist/wallet/index.d.mts +7 -1
- package/dist/wallet/index.d.ts +7 -1
- package/dist/wallet/index.js +85 -39
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +85 -39
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -766,6 +766,9 @@ var CashuSpender = class {
|
|
|
766
766
|
return result;
|
|
767
767
|
}
|
|
768
768
|
async _getBalanceState() {
|
|
769
|
+
if (this.balanceManager) {
|
|
770
|
+
return this.balanceManager.getBalanceState();
|
|
771
|
+
}
|
|
769
772
|
const mintBalances = await this.walletAdapter.getBalances();
|
|
770
773
|
const units = this.walletAdapter.getMintUnits();
|
|
771
774
|
let totalMintBalance = 0;
|
|
@@ -781,15 +784,16 @@ var CashuSpender = class {
|
|
|
781
784
|
const providerBalances = {};
|
|
782
785
|
let totalProviderBalance = 0;
|
|
783
786
|
for (const pending of pendingDistribution) {
|
|
784
|
-
providerBalances[pending.baseUrl] = pending.amount;
|
|
787
|
+
providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
|
|
785
788
|
totalProviderBalance += pending.amount;
|
|
786
789
|
}
|
|
787
790
|
const apiKeys = this.storageAdapter.getAllApiKeys();
|
|
788
791
|
for (const apiKey of apiKeys) {
|
|
789
792
|
if (!providerBalances[apiKey.baseUrl]) {
|
|
790
|
-
providerBalances[apiKey.baseUrl] =
|
|
791
|
-
totalProviderBalance += apiKey.balance;
|
|
793
|
+
providerBalances[apiKey.baseUrl] = 0;
|
|
792
794
|
}
|
|
795
|
+
providerBalances[apiKey.baseUrl] += apiKey.balance;
|
|
796
|
+
totalProviderBalance += apiKey.balance;
|
|
793
797
|
}
|
|
794
798
|
return {
|
|
795
799
|
totalBalance: totalMintBalance + totalProviderBalance,
|
|
@@ -938,25 +942,12 @@ var CashuSpender = class {
|
|
|
938
942
|
`[CashuSpender] _spendInternal: Could not reuse token, will create new token`
|
|
939
943
|
);
|
|
940
944
|
}
|
|
941
|
-
const
|
|
942
|
-
const
|
|
943
|
-
let totalBalance = 0;
|
|
944
|
-
for (const url in balances) {
|
|
945
|
-
const balance = balances[url];
|
|
946
|
-
const unit = units[url];
|
|
947
|
-
const balanceInSats = getBalanceInSats(balance, unit);
|
|
948
|
-
totalBalance += balanceInSats;
|
|
949
|
-
}
|
|
950
|
-
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
951
|
-
const totalPending = pendingDistribution.reduce(
|
|
952
|
-
(sum, item) => sum + item.amount,
|
|
953
|
-
0
|
|
954
|
-
);
|
|
945
|
+
const balanceState = await this._getBalanceState();
|
|
946
|
+
const totalAvailableBalance = balanceState.totalBalance;
|
|
955
947
|
this._log(
|
|
956
948
|
"DEBUG",
|
|
957
|
-
`[CashuSpender] _spendInternal:
|
|
949
|
+
`[CashuSpender] _spendInternal: totalAvailableBalance=${totalAvailableBalance}, adjustedAmount=${adjustedAmount}`
|
|
958
950
|
);
|
|
959
|
-
const totalAvailableBalance = totalBalance + totalPending;
|
|
960
951
|
if (totalAvailableBalance < adjustedAmount) {
|
|
961
952
|
this._log(
|
|
962
953
|
"ERROR",
|
|
@@ -964,8 +955,7 @@ var CashuSpender = class {
|
|
|
964
955
|
);
|
|
965
956
|
return this._createInsufficientBalanceError(
|
|
966
957
|
adjustedAmount,
|
|
967
|
-
|
|
968
|
-
units,
|
|
958
|
+
balanceState.mintBalances,
|
|
969
959
|
totalAvailableBalance
|
|
970
960
|
);
|
|
971
961
|
}
|
|
@@ -985,8 +975,7 @@ var CashuSpender = class {
|
|
|
985
975
|
if ((tokenResult.error || "").includes("Insufficient balance")) {
|
|
986
976
|
return this._createInsufficientBalanceError(
|
|
987
977
|
adjustedAmount,
|
|
988
|
-
|
|
989
|
-
units,
|
|
978
|
+
balanceState.mintBalances,
|
|
990
979
|
totalAvailableBalance
|
|
991
980
|
);
|
|
992
981
|
}
|
|
@@ -1031,6 +1020,7 @@ var CashuSpender = class {
|
|
|
1031
1020
|
"DEBUG",
|
|
1032
1021
|
`[CashuSpender] _spendInternal: Successfully spent ${spentAmount}, returning token with balance=${spentAmount}`
|
|
1033
1022
|
);
|
|
1023
|
+
const units = this.walletAdapter.getMintUnits();
|
|
1034
1024
|
return {
|
|
1035
1025
|
token,
|
|
1036
1026
|
status: "success",
|
|
@@ -1168,13 +1158,11 @@ var CashuSpender = class {
|
|
|
1168
1158
|
/**
|
|
1169
1159
|
* Create an insufficient balance error result
|
|
1170
1160
|
*/
|
|
1171
|
-
_createInsufficientBalanceError(required,
|
|
1161
|
+
_createInsufficientBalanceError(required, normalizedBalances, availableBalance) {
|
|
1172
1162
|
let maxBalance = 0;
|
|
1173
1163
|
let maxMintUrl = "";
|
|
1174
|
-
for (const mintUrl in
|
|
1175
|
-
const
|
|
1176
|
-
const unit = units[mintUrl];
|
|
1177
|
-
const balanceInSats = getBalanceInSats(balance, unit);
|
|
1164
|
+
for (const mintUrl in normalizedBalances) {
|
|
1165
|
+
const balanceInSats = normalizedBalances[mintUrl];
|
|
1178
1166
|
if (balanceInSats > maxBalance) {
|
|
1179
1167
|
maxBalance = balanceInSats;
|
|
1180
1168
|
maxMintUrl = mintUrl;
|
|
@@ -1235,6 +1223,39 @@ var BalanceManager = class {
|
|
|
1235
1223
|
}
|
|
1236
1224
|
}
|
|
1237
1225
|
cashuSpender;
|
|
1226
|
+
async getBalanceState() {
|
|
1227
|
+
const mintBalances = await this.walletAdapter.getBalances();
|
|
1228
|
+
const units = this.walletAdapter.getMintUnits();
|
|
1229
|
+
let totalMintBalance = 0;
|
|
1230
|
+
const normalizedMintBalances = {};
|
|
1231
|
+
for (const url in mintBalances) {
|
|
1232
|
+
const balance = mintBalances[url];
|
|
1233
|
+
const unit = units[url];
|
|
1234
|
+
const balanceInSats = getBalanceInSats(balance, unit);
|
|
1235
|
+
normalizedMintBalances[url] = balanceInSats;
|
|
1236
|
+
totalMintBalance += balanceInSats;
|
|
1237
|
+
}
|
|
1238
|
+
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
1239
|
+
const providerBalances = {};
|
|
1240
|
+
let totalProviderBalance = 0;
|
|
1241
|
+
for (const pending of pendingDistribution) {
|
|
1242
|
+
providerBalances[pending.baseUrl] = (providerBalances[pending.baseUrl] || 0) + pending.amount;
|
|
1243
|
+
totalProviderBalance += pending.amount;
|
|
1244
|
+
}
|
|
1245
|
+
const apiKeys = this.storageAdapter.getAllApiKeys();
|
|
1246
|
+
for (const apiKey of apiKeys) {
|
|
1247
|
+
if (!providerBalances[apiKey.baseUrl]) {
|
|
1248
|
+
providerBalances[apiKey.baseUrl] = 0;
|
|
1249
|
+
}
|
|
1250
|
+
providerBalances[apiKey.baseUrl] += apiKey.balance;
|
|
1251
|
+
totalProviderBalance += apiKey.balance;
|
|
1252
|
+
}
|
|
1253
|
+
return {
|
|
1254
|
+
totalBalance: totalMintBalance + totalProviderBalance,
|
|
1255
|
+
providerBalances,
|
|
1256
|
+
mintBalances: normalizedMintBalances
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1238
1259
|
/**
|
|
1239
1260
|
* Unified refund - handles both NIP-60 and legacy wallet refunds
|
|
1240
1261
|
*/
|
|
@@ -1464,17 +1485,17 @@ var BalanceManager = class {
|
|
|
1464
1485
|
if (!adjustedAmount || isNaN(adjustedAmount)) {
|
|
1465
1486
|
return { success: false, error: "Invalid top up amount" };
|
|
1466
1487
|
}
|
|
1488
|
+
const balanceState = await this.getBalanceState();
|
|
1467
1489
|
const balances = await this.walletAdapter.getBalances();
|
|
1468
1490
|
const units = this.walletAdapter.getMintUnits();
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
if (totalMintBalance < adjustedAmount && totalMintBalance + refundablePending >= adjustedAmount && retryCount < 1) {
|
|
1491
|
+
const totalMintBalance = Object.values(balanceState.mintBalances).reduce(
|
|
1492
|
+
(sum, value) => sum + value,
|
|
1493
|
+
0
|
|
1494
|
+
);
|
|
1495
|
+
const refundableProviderBalance = Object.entries(
|
|
1496
|
+
balanceState.providerBalances
|
|
1497
|
+
).filter(([providerBaseUrl]) => providerBaseUrl !== baseUrl).reduce((sum, [, value]) => sum + value, 0);
|
|
1498
|
+
if (totalMintBalance < adjustedAmount && totalMintBalance + refundableProviderBalance >= adjustedAmount && retryCount < 1) {
|
|
1478
1499
|
await this._refundOtherProvidersForTopUp(baseUrl, mintUrl);
|
|
1479
1500
|
return this.createProviderToken({
|
|
1480
1501
|
...options,
|
|
@@ -1596,10 +1617,14 @@ var BalanceManager = class {
|
|
|
1596
1617
|
}
|
|
1597
1618
|
async _refundOtherProvidersForTopUp(baseUrl, mintUrl) {
|
|
1598
1619
|
const pendingDistribution = this.storageAdapter.getCachedTokenDistribution();
|
|
1620
|
+
const apiKeyDistribution = this.storageAdapter.getApiKeyDistribution();
|
|
1599
1621
|
const toRefund = pendingDistribution.filter(
|
|
1600
1622
|
(pending) => pending.baseUrl !== baseUrl
|
|
1601
1623
|
);
|
|
1602
|
-
const
|
|
1624
|
+
const apiKeysToRefund = apiKeyDistribution.filter(
|
|
1625
|
+
(apiKey) => apiKey.baseUrl !== baseUrl && apiKey.amount > 0
|
|
1626
|
+
);
|
|
1627
|
+
const tokenRefundResults = await Promise.allSettled(
|
|
1603
1628
|
toRefund.map(async (pending) => {
|
|
1604
1629
|
const token = this.storageAdapter.getToken(pending.baseUrl);
|
|
1605
1630
|
if (!token) {
|
|
@@ -1617,11 +1642,32 @@ var BalanceManager = class {
|
|
|
1617
1642
|
return { baseUrl: pending.baseUrl, success: result.success };
|
|
1618
1643
|
})
|
|
1619
1644
|
);
|
|
1620
|
-
for (const result of
|
|
1645
|
+
for (const result of tokenRefundResults) {
|
|
1621
1646
|
if (result.status === "fulfilled" && result.value.success) {
|
|
1622
1647
|
this.storageAdapter.removeToken(result.value.baseUrl);
|
|
1623
1648
|
}
|
|
1624
1649
|
}
|
|
1650
|
+
const apiKeyRefundResults = await Promise.allSettled(
|
|
1651
|
+
apiKeysToRefund.map(async (apiKeyEntry) => {
|
|
1652
|
+
const fullApiKeyEntry = this.storageAdapter.getApiKey(
|
|
1653
|
+
apiKeyEntry.baseUrl
|
|
1654
|
+
);
|
|
1655
|
+
if (!fullApiKeyEntry) {
|
|
1656
|
+
return { baseUrl: apiKeyEntry.baseUrl, success: false };
|
|
1657
|
+
}
|
|
1658
|
+
const result = await this.refundApiKey({
|
|
1659
|
+
mintUrl,
|
|
1660
|
+
baseUrl: apiKeyEntry.baseUrl,
|
|
1661
|
+
apiKey: fullApiKeyEntry.key
|
|
1662
|
+
});
|
|
1663
|
+
return { baseUrl: apiKeyEntry.baseUrl, success: result.success };
|
|
1664
|
+
})
|
|
1665
|
+
);
|
|
1666
|
+
for (const result of apiKeyRefundResults) {
|
|
1667
|
+
if (result.status === "fulfilled" && result.value.success) {
|
|
1668
|
+
this.storageAdapter.updateApiKeyBalance(result.value.baseUrl, 0);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1625
1671
|
}
|
|
1626
1672
|
/**
|
|
1627
1673
|
* Fetch refund token from provider API
|
|
@@ -2872,10 +2918,34 @@ var RoutstrClient = class {
|
|
|
2872
2918
|
}
|
|
2873
2919
|
}
|
|
2874
2920
|
if (status === 402 && !tryNextProvider && (this.mode === "apikeys" || this.mode === "lazyrefund")) {
|
|
2921
|
+
this.storageAdapter.getApiKey(baseUrl);
|
|
2922
|
+
let topupAmount = params.requiredSats;
|
|
2923
|
+
try {
|
|
2924
|
+
let currentBalance = 0;
|
|
2925
|
+
if (this.mode === "apikeys") {
|
|
2926
|
+
const currentBalanceInfo = await this.balanceManager.getTokenBalance(
|
|
2927
|
+
params.token,
|
|
2928
|
+
baseUrl
|
|
2929
|
+
);
|
|
2930
|
+
currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
|
|
2931
|
+
} else if (this.mode === "lazyrefund") {
|
|
2932
|
+
const distribution = this.storageAdapter.getCachedTokenDistribution();
|
|
2933
|
+
const tokenEntry = distribution.find((t) => t.baseUrl === baseUrl);
|
|
2934
|
+
currentBalance = tokenEntry?.amount ?? 0;
|
|
2935
|
+
}
|
|
2936
|
+
const shortfall = Math.max(0, params.requiredSats - currentBalance);
|
|
2937
|
+
topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
|
|
2938
|
+
} catch (e) {
|
|
2939
|
+
this._log(
|
|
2940
|
+
"WARN",
|
|
2941
|
+
"Could not get current token balance for topup calculation:",
|
|
2942
|
+
e
|
|
2943
|
+
);
|
|
2944
|
+
}
|
|
2875
2945
|
const topupResult = await this.balanceManager.topUp({
|
|
2876
2946
|
mintUrl,
|
|
2877
2947
|
baseUrl,
|
|
2878
|
-
amount:
|
|
2948
|
+
amount: topupAmount * TOPUP_MARGIN,
|
|
2879
2949
|
token: params.token
|
|
2880
2950
|
});
|
|
2881
2951
|
this._log(
|
|
@@ -2956,7 +3026,10 @@ var RoutstrClient = class {
|
|
|
2956
3026
|
retryToken = latestBalanceInfo.apiKey;
|
|
2957
3027
|
}
|
|
2958
3028
|
if (latestTokenBalance >= 0) {
|
|
2959
|
-
this.storageAdapter.updateApiKeyBalance(
|
|
3029
|
+
this.storageAdapter.updateApiKeyBalance(
|
|
3030
|
+
baseUrl,
|
|
3031
|
+
latestTokenBalance
|
|
3032
|
+
);
|
|
2960
3033
|
}
|
|
2961
3034
|
}
|
|
2962
3035
|
} catch (error) {
|
|
@@ -3730,7 +3803,8 @@ var SDK_STORAGE_KEYS = {
|
|
|
3730
3803
|
CHILD_KEYS: "child_keys",
|
|
3731
3804
|
ROUTSTR21_MODELS: "routstr21Models",
|
|
3732
3805
|
LAST_ROUTSTR21_MODELS_UPDATE: "lastRoutstr21ModelsUpdate",
|
|
3733
|
-
CACHED_RECEIVE_TOKENS: "cached_receive_tokens"
|
|
3806
|
+
CACHED_RECEIVE_TOKENS: "cached_receive_tokens",
|
|
3807
|
+
USAGE_TRACKING: "usage_tracking"
|
|
3734
3808
|
};
|
|
3735
3809
|
|
|
3736
3810
|
// storage/store.ts
|
|
@@ -3765,7 +3839,8 @@ var createSdkStore = async ({
|
|
|
3765
3839
|
rawChildKeys,
|
|
3766
3840
|
rawRoutstr21Models,
|
|
3767
3841
|
rawLastRoutstr21ModelsUpdate,
|
|
3768
|
-
rawCachedReceiveTokens
|
|
3842
|
+
rawCachedReceiveTokens,
|
|
3843
|
+
rawUsageTracking
|
|
3769
3844
|
] = await Promise.all([
|
|
3770
3845
|
driver.getItem(
|
|
3771
3846
|
SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
|
|
@@ -3795,7 +3870,8 @@ var createSdkStore = async ({
|
|
|
3795
3870
|
SDK_STORAGE_KEYS.LAST_ROUTSTR21_MODELS_UPDATE,
|
|
3796
3871
|
null
|
|
3797
3872
|
),
|
|
3798
|
-
driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, [])
|
|
3873
|
+
driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, []),
|
|
3874
|
+
driver.getItem(SDK_STORAGE_KEYS.USAGE_TRACKING, [])
|
|
3799
3875
|
]);
|
|
3800
3876
|
const modelsFromAllProviders = Object.fromEntries(
|
|
3801
3877
|
Object.entries(rawModels).map(([baseUrl, models]) => [
|
|
@@ -3853,6 +3929,7 @@ var createSdkStore = async ({
|
|
|
3853
3929
|
unit: entry.unit || "sat",
|
|
3854
3930
|
createdAt: entry.createdAt ?? Date.now()
|
|
3855
3931
|
}));
|
|
3932
|
+
const usageTracking = rawUsageTracking;
|
|
3856
3933
|
return createStore((set, get) => ({
|
|
3857
3934
|
modelsFromAllProviders,
|
|
3858
3935
|
lastUsedModel,
|
|
@@ -3868,6 +3945,7 @@ var createSdkStore = async ({
|
|
|
3868
3945
|
routstr21Models,
|
|
3869
3946
|
lastRoutstr21ModelsUpdate,
|
|
3870
3947
|
cachedReceiveTokens,
|
|
3948
|
+
usageTracking,
|
|
3871
3949
|
setModelsFromAllProviders: (value) => {
|
|
3872
3950
|
const normalized = {};
|
|
3873
3951
|
for (const [baseUrl, models] of Object.entries(value)) {
|
|
@@ -3984,6 +4062,10 @@ var createSdkStore = async ({
|
|
|
3984
4062
|
}));
|
|
3985
4063
|
void driver.setItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, normalized);
|
|
3986
4064
|
set({ cachedReceiveTokens: normalized });
|
|
4065
|
+
},
|
|
4066
|
+
setUsageTracking: (value) => {
|
|
4067
|
+
void driver.setItem(SDK_STORAGE_KEYS.USAGE_TRACKING, value);
|
|
4068
|
+
set({ usageTracking: value });
|
|
3987
4069
|
}
|
|
3988
4070
|
}));
|
|
3989
4071
|
};
|