@t2000/sdk 0.16.23 → 0.16.25
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/LICENSE +21 -0
- package/dist/adapters/index.cjs +14 -9
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.js +14 -9
- package/dist/adapters/index.js.map +1 -1
- package/dist/index.cjs +68 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +68 -50
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -402,6 +402,7 @@ async function buildSendTx({
|
|
|
402
402
|
}
|
|
403
403
|
|
|
404
404
|
// src/wallet/balance.ts
|
|
405
|
+
var SUI_PRICE_FALLBACK = 1;
|
|
405
406
|
var _cachedSuiPrice = 0;
|
|
406
407
|
var _priceLastFetched = 0;
|
|
407
408
|
var PRICE_CACHE_TTL_MS = 6e4;
|
|
@@ -431,17 +432,24 @@ async function fetchSuiPrice(client) {
|
|
|
431
432
|
}
|
|
432
433
|
} catch {
|
|
433
434
|
}
|
|
434
|
-
return _cachedSuiPrice;
|
|
435
|
+
return _cachedSuiPrice || SUI_PRICE_FALLBACK;
|
|
435
436
|
}
|
|
436
437
|
async function queryBalance(client, address) {
|
|
437
438
|
const stableBalancePromises = STABLE_ASSETS.map(
|
|
438
|
-
(asset) => client.getBalance({ owner: address, coinType: SUPPORTED_ASSETS[asset].type }).then((b) => ({ asset, amount: Number(b.totalBalance) / 10 ** SUPPORTED_ASSETS[asset].decimals }))
|
|
439
|
+
(asset) => client.getBalance({ owner: address, coinType: SUPPORTED_ASSETS[asset].type }).then((b) => ({ asset, amount: Number(b.totalBalance) / 10 ** SUPPORTED_ASSETS[asset].decimals })).catch(() => ({ asset, amount: 0 }))
|
|
439
440
|
);
|
|
440
|
-
const
|
|
441
|
+
const nonSuiInvestmentAssets = Object.keys(INVESTMENT_ASSETS).filter((a) => a !== "SUI");
|
|
442
|
+
const investBalancePromises = nonSuiInvestmentAssets.map(
|
|
443
|
+
(asset) => client.getBalance({ owner: address, coinType: INVESTMENT_ASSETS[asset].type }).then((b) => ({ asset, amount: Number(b.totalBalance) / 10 ** INVESTMENT_ASSETS[asset].decimals })).catch(() => ({ asset, amount: 0 }))
|
|
444
|
+
);
|
|
445
|
+
const [suiBalance, suiPriceUsd, ...rest] = await Promise.all([
|
|
441
446
|
client.getBalance({ owner: address, coinType: SUPPORTED_ASSETS.SUI.type }),
|
|
442
447
|
fetchSuiPrice(client),
|
|
443
|
-
...stableBalancePromises
|
|
448
|
+
...stableBalancePromises,
|
|
449
|
+
...investBalancePromises
|
|
444
450
|
]);
|
|
451
|
+
const stableResults = rest.slice(0, STABLE_ASSETS.length);
|
|
452
|
+
const investResults = rest.slice(STABLE_ASSETS.length);
|
|
445
453
|
const stables = {};
|
|
446
454
|
let totalStables = 0;
|
|
447
455
|
for (const { asset, amount } of stableResults) {
|
|
@@ -452,6 +460,13 @@ async function queryBalance(client, address) {
|
|
|
452
460
|
const savings = 0;
|
|
453
461
|
const usdEquiv = suiAmount * suiPriceUsd;
|
|
454
462
|
const total = totalStables + savings + usdEquiv;
|
|
463
|
+
const assets = {
|
|
464
|
+
USDC: stables.USDC ?? 0,
|
|
465
|
+
SUI: suiAmount
|
|
466
|
+
};
|
|
467
|
+
for (const { asset, amount } of investResults) {
|
|
468
|
+
assets[asset] = amount;
|
|
469
|
+
}
|
|
455
470
|
return {
|
|
456
471
|
available: totalStables,
|
|
457
472
|
savings,
|
|
@@ -464,10 +479,7 @@ async function queryBalance(client, address) {
|
|
|
464
479
|
},
|
|
465
480
|
total,
|
|
466
481
|
stables,
|
|
467
|
-
assets
|
|
468
|
-
USDC: stables.USDC ?? 0,
|
|
469
|
-
SUI: suiAmount
|
|
470
|
-
}
|
|
482
|
+
assets
|
|
471
483
|
};
|
|
472
484
|
}
|
|
473
485
|
|
|
@@ -694,12 +706,17 @@ function normalizeHealthFactor(raw) {
|
|
|
694
706
|
const v = raw / 10 ** RATE_DECIMALS;
|
|
695
707
|
return v > 1e5 ? Infinity : v;
|
|
696
708
|
}
|
|
697
|
-
function
|
|
709
|
+
function naviStorageDecimals(poolId, tokenDecimals) {
|
|
710
|
+
if (poolId <= 10) return NAVI_BALANCE_DECIMALS;
|
|
711
|
+
return tokenDecimals;
|
|
712
|
+
}
|
|
713
|
+
function compoundBalance(rawBalance, currentIndex, pool) {
|
|
698
714
|
if (!rawBalance || !currentIndex || currentIndex === "0") return 0;
|
|
699
715
|
const scale = BigInt("1" + "0".repeat(RATE_DECIMALS));
|
|
700
716
|
const half = scale / 2n;
|
|
701
717
|
const result = (rawBalance * BigInt(currentIndex) + half) / scale;
|
|
702
|
-
|
|
718
|
+
const decimals = pool ? naviStorageDecimals(pool.id, pool.token.decimals) : NAVI_BALANCE_DECIMALS;
|
|
719
|
+
return Number(result) / 10 ** decimals;
|
|
703
720
|
}
|
|
704
721
|
async function getUserState(client, address) {
|
|
705
722
|
const config = await getConfig();
|
|
@@ -783,7 +800,7 @@ async function buildWithdrawTx(client, address, amount, options = {}) {
|
|
|
783
800
|
getUserState(client, address)
|
|
784
801
|
]);
|
|
785
802
|
const assetState = states.find((s) => s.assetId === pool.id);
|
|
786
|
-
const deposited = assetState ? compoundBalance(assetState.supplyBalance, pool.currentSupplyIndex) : 0;
|
|
803
|
+
const deposited = assetState ? compoundBalance(assetState.supplyBalance, pool.currentSupplyIndex, pool) : 0;
|
|
787
804
|
const effectiveAmount = Math.min(amount, Math.max(0, deposited - WITHDRAW_DUST_BUFFER));
|
|
788
805
|
if (effectiveAmount <= 0) throw new T2000Error("NO_COLLATERAL", `Nothing to withdraw for ${assetInfo.displayName} on NAVI`);
|
|
789
806
|
const rawAmount = Number(stableToRaw(effectiveAmount, assetInfo.decimals));
|
|
@@ -826,7 +843,7 @@ async function addWithdrawToTx(tx, client, address, amount, options = {}) {
|
|
|
826
843
|
getUserState(client, address)
|
|
827
844
|
]);
|
|
828
845
|
const assetState = states.find((s) => s.assetId === pool.id);
|
|
829
|
-
const deposited = assetState ? compoundBalance(assetState.supplyBalance, pool.currentSupplyIndex) : 0;
|
|
846
|
+
const deposited = assetState ? compoundBalance(assetState.supplyBalance, pool.currentSupplyIndex, pool) : 0;
|
|
830
847
|
const effectiveAmount = Math.min(amount, Math.max(0, deposited - WITHDRAW_DUST_BUFFER));
|
|
831
848
|
if (effectiveAmount <= 0) throw new T2000Error("NO_COLLATERAL", `Nothing to withdraw for ${assetInfo.displayName} on NAVI`);
|
|
832
849
|
const rawAmount = Number(stableToRaw(effectiveAmount, assetInfo.decimals));
|
|
@@ -997,8 +1014,8 @@ async function getHealthFactor(client, addressOrKeypair) {
|
|
|
997
1014
|
for (const state of states) {
|
|
998
1015
|
const pool = pools.find((p) => p.id === state.assetId);
|
|
999
1016
|
if (!pool) continue;
|
|
1000
|
-
const supplyBal = compoundBalance(state.supplyBalance, pool.currentSupplyIndex);
|
|
1001
|
-
const borrowBal = compoundBalance(state.borrowBalance, pool.currentBorrowIndex);
|
|
1017
|
+
const supplyBal = compoundBalance(state.supplyBalance, pool.currentSupplyIndex, pool);
|
|
1018
|
+
const borrowBal = compoundBalance(state.borrowBalance, pool.currentBorrowIndex, pool);
|
|
1002
1019
|
const price = pool.token?.price ?? 1;
|
|
1003
1020
|
supplied += supplyBal * price;
|
|
1004
1021
|
borrowed += borrowBal * price;
|
|
@@ -1085,8 +1102,8 @@ async function getPositions(client, addressOrKeypair) {
|
|
|
1085
1102
|
const pool = pools.find((p) => p.id === state.assetId);
|
|
1086
1103
|
if (!pool) continue;
|
|
1087
1104
|
const symbol = resolvePoolSymbol(pool);
|
|
1088
|
-
const supplyBal = compoundBalance(state.supplyBalance, pool.currentSupplyIndex);
|
|
1089
|
-
const borrowBal = compoundBalance(state.borrowBalance, pool.currentBorrowIndex);
|
|
1105
|
+
const supplyBal = compoundBalance(state.supplyBalance, pool.currentSupplyIndex, pool);
|
|
1106
|
+
const borrowBal = compoundBalance(state.borrowBalance, pool.currentBorrowIndex, pool);
|
|
1090
1107
|
if (supplyBal > 1e-4) {
|
|
1091
1108
|
positions.push({
|
|
1092
1109
|
protocol: "navi",
|
|
@@ -1118,7 +1135,7 @@ async function maxWithdrawAmount(client, addressOrKeypair) {
|
|
|
1118
1135
|
maxAmount = Math.max(0, hf.supplied - hf.borrowed * MIN_HEALTH_FACTOR / ltv);
|
|
1119
1136
|
}
|
|
1120
1137
|
const remainingSupply = hf.supplied - maxAmount;
|
|
1121
|
-
const hfAfter = hf.borrowed > 0 ? remainingSupply / hf.borrowed : Infinity;
|
|
1138
|
+
const hfAfter = hf.borrowed > 0 ? remainingSupply * ltv / hf.borrowed : Infinity;
|
|
1122
1139
|
return { maxAmount, healthFactorAfter: hfAfter, currentHF: hf.healthFactor };
|
|
1123
1140
|
}
|
|
1124
1141
|
async function maxBorrowAmount(client, addressOrKeypair) {
|
|
@@ -2917,8 +2934,9 @@ var PortfolioManager = class {
|
|
|
2917
2934
|
throw new T2000Error("INSUFFICIENT_INVESTMENT", `No ${trade.asset} position to sell`);
|
|
2918
2935
|
}
|
|
2919
2936
|
const sellAmount = Math.min(trade.amount, pos.totalAmount);
|
|
2937
|
+
const effectiveUsdValue = trade.amount > 0 && sellAmount < trade.amount ? trade.usdValue * (sellAmount / trade.amount) : trade.usdValue;
|
|
2920
2938
|
const costOfSold = pos.avgPrice * sellAmount;
|
|
2921
|
-
const realizedPnL =
|
|
2939
|
+
const realizedPnL = effectiveUsdValue - costOfSold;
|
|
2922
2940
|
pos.totalAmount -= sellAmount;
|
|
2923
2941
|
pos.costBasis -= costOfSold;
|
|
2924
2942
|
if (pos.totalAmount < 1e-6) {
|
|
@@ -3016,8 +3034,9 @@ var PortfolioManager = class {
|
|
|
3016
3034
|
throw new T2000Error("INSUFFICIENT_INVESTMENT", `No ${trade.asset} position in strategy '${strategyKey}'`);
|
|
3017
3035
|
}
|
|
3018
3036
|
const sellAmount = Math.min(trade.amount, pos.totalAmount);
|
|
3037
|
+
const effectiveUsdValue = trade.amount > 0 && sellAmount < trade.amount ? trade.usdValue * (sellAmount / trade.amount) : trade.usdValue;
|
|
3019
3038
|
const costOfSold = pos.avgPrice * sellAmount;
|
|
3020
|
-
const realizedPnL =
|
|
3039
|
+
const realizedPnL = effectiveUsdValue - costOfSold;
|
|
3021
3040
|
pos.totalAmount -= sellAmount;
|
|
3022
3041
|
pos.costBasis -= costOfSold;
|
|
3023
3042
|
if (pos.totalAmount < 1e-6) {
|
|
@@ -3466,45 +3485,44 @@ To access invested funds: t2000 invest sell ${params.amount} ${asset}`,
|
|
|
3466
3485
|
}
|
|
3467
3486
|
let investmentValue = 0;
|
|
3468
3487
|
let investmentCostBasis = 0;
|
|
3488
|
+
const trackedAmounts = {};
|
|
3489
|
+
const trackedCostBasis = {};
|
|
3490
|
+
const earningAssetSet = /* @__PURE__ */ new Set();
|
|
3469
3491
|
for (const pos of portfolioPositions) {
|
|
3470
3492
|
if (!(pos.asset in INVESTMENT_ASSETS)) continue;
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
investmentCostBasis += pos.costBasis;
|
|
3475
|
-
if (pos.asset === "SUI") {
|
|
3476
|
-
const gasSui = Math.max(0, bal.gasReserve.sui);
|
|
3477
|
-
bal.gasReserve = { sui: gasSui, usdEquiv: gasSui * price };
|
|
3478
|
-
}
|
|
3479
|
-
} else if (pos.asset === "SUI") {
|
|
3480
|
-
const actualHeld = Math.min(pos.totalAmount, bal.gasReserve.sui);
|
|
3481
|
-
investmentValue += actualHeld * price;
|
|
3482
|
-
if (actualHeld < pos.totalAmount && pos.totalAmount > 0) {
|
|
3483
|
-
investmentCostBasis += pos.costBasis * (actualHeld / pos.totalAmount);
|
|
3484
|
-
} else {
|
|
3485
|
-
investmentCostBasis += pos.costBasis;
|
|
3486
|
-
}
|
|
3487
|
-
const gasSui = Math.max(0, bal.gasReserve.sui - pos.totalAmount);
|
|
3488
|
-
bal.gasReserve = { sui: gasSui, usdEquiv: gasSui * price };
|
|
3489
|
-
} else {
|
|
3490
|
-
investmentValue += pos.totalAmount * price;
|
|
3491
|
-
investmentCostBasis += pos.costBasis;
|
|
3492
|
-
}
|
|
3493
|
+
trackedAmounts[pos.asset] = (trackedAmounts[pos.asset] ?? 0) + pos.totalAmount;
|
|
3494
|
+
trackedCostBasis[pos.asset] = (trackedCostBasis[pos.asset] ?? 0) + pos.costBasis;
|
|
3495
|
+
if (pos.earning) earningAssetSet.add(pos.asset);
|
|
3493
3496
|
}
|
|
3494
|
-
let strategySuiTotal = 0;
|
|
3495
3497
|
for (const key of this.portfolio.getAllStrategyKeys()) {
|
|
3496
3498
|
for (const sp of this.portfolio.getStrategyPositions(key)) {
|
|
3497
3499
|
if (!(sp.asset in INVESTMENT_ASSETS)) continue;
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
investmentCostBasis += sp.costBasis;
|
|
3501
|
-
if (sp.asset === "SUI") strategySuiTotal += sp.totalAmount;
|
|
3500
|
+
trackedAmounts[sp.asset] = (trackedAmounts[sp.asset] ?? 0) + sp.totalAmount;
|
|
3501
|
+
trackedCostBasis[sp.asset] = (trackedCostBasis[sp.asset] ?? 0) + sp.costBasis;
|
|
3502
3502
|
}
|
|
3503
3503
|
}
|
|
3504
|
-
|
|
3505
|
-
const
|
|
3506
|
-
const
|
|
3507
|
-
|
|
3504
|
+
for (const asset of Object.keys(INVESTMENT_ASSETS)) {
|
|
3505
|
+
const price = assetPrices[asset] ?? 0;
|
|
3506
|
+
const tracked = trackedAmounts[asset] ?? 0;
|
|
3507
|
+
const costBasis = trackedCostBasis[asset] ?? 0;
|
|
3508
|
+
if (asset === "SUI") {
|
|
3509
|
+
const actualSui = earningAssetSet.has("SUI") ? tracked : Math.min(tracked, bal.gasReserve.sui);
|
|
3510
|
+
investmentValue += actualSui * price;
|
|
3511
|
+
if (actualSui < tracked && tracked > 0) {
|
|
3512
|
+
investmentCostBasis += costBasis * (actualSui / tracked);
|
|
3513
|
+
} else {
|
|
3514
|
+
investmentCostBasis += costBasis;
|
|
3515
|
+
}
|
|
3516
|
+
if (!earningAssetSet.has("SUI")) {
|
|
3517
|
+
const gasSui = Math.max(0, bal.gasReserve.sui - tracked);
|
|
3518
|
+
bal.gasReserve = { sui: gasSui, usdEquiv: gasSui * price };
|
|
3519
|
+
}
|
|
3520
|
+
} else {
|
|
3521
|
+
const onChainAmount = bal.assets[asset] ?? 0;
|
|
3522
|
+
const effectiveAmount = Math.max(tracked, onChainAmount);
|
|
3523
|
+
investmentValue += effectiveAmount * price;
|
|
3524
|
+
investmentCostBasis += costBasis;
|
|
3525
|
+
}
|
|
3508
3526
|
}
|
|
3509
3527
|
bal.investment = investmentValue;
|
|
3510
3528
|
bal.investmentPnL = investmentValue - investmentCostBasis;
|