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