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