@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/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 [suiBalance, suiPriceUsd, ...stableResults] = await Promise.all([
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 compoundBalance(rawBalance, currentIndex) {
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
- return Number(result) / 10 ** NAVI_BALANCE_DECIMALS;
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 = trade.usdValue - costOfSold;
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 = trade.usdValue - costOfSold;
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
- const price = assetPrices[pos.asset] ?? 0;
3472
- if (pos.earning) {
3473
- investmentValue += pos.totalAmount * price;
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
- const price = assetPrices[sp.asset] ?? 0;
3499
- investmentValue += sp.totalAmount * price;
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
- if (strategySuiTotal > 0) {
3505
- const suiPrice2 = assetPrices["SUI"] ?? 0;
3506
- const gasSui = Math.max(0, bal.gasReserve.sui - strategySuiTotal);
3507
- bal.gasReserve = { sui: gasSui, usdEquiv: gasSui * suiPrice2 };
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;