@t2000/sdk 0.5.6 → 0.6.2

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
@@ -426,8 +426,6 @@ async function reportFee(agentAddress, operation, feeAmount, feeRate, txDigest)
426
426
  } catch {
427
427
  }
428
428
  }
429
-
430
- // src/protocols/navi.ts
431
429
  var USDC_TYPE = SUPPORTED_ASSETS.USDC.type;
432
430
  var RATE_DECIMALS = 27;
433
431
  var LTV_DECIMALS = 27;
@@ -438,6 +436,8 @@ var SUI_SYSTEM_STATE = "0x05";
438
436
  var NAVI_BALANCE_DECIMALS = 9;
439
437
  var CONFIG_API = "https://open-api.naviprotocol.io/api/navi/config?env=prod";
440
438
  var POOLS_API = "https://open-api.naviprotocol.io/api/navi/pools?env=prod";
439
+ var PACKAGE_API = "https://open-api.naviprotocol.io/api/package";
440
+ var packageCache = null;
441
441
  function toBigInt(v) {
442
442
  if (typeof v === "bigint") return v;
443
443
  return BigInt(String(v));
@@ -462,9 +462,22 @@ async function fetchJson(url) {
462
462
  const json = await res.json();
463
463
  return json.data ?? json;
464
464
  }
465
+ async function getLatestPackageId() {
466
+ if (packageCache && Date.now() - packageCache.ts < CACHE_TTL) return packageCache.id;
467
+ const res = await fetch(PACKAGE_API);
468
+ if (!res.ok) throw new T2000Error("PROTOCOL_UNAVAILABLE", `NAVI package API error: ${res.status}`);
469
+ const json = await res.json();
470
+ if (!json.packageId) throw new T2000Error("PROTOCOL_UNAVAILABLE", "NAVI package API returned no packageId");
471
+ packageCache = { id: json.packageId, ts: Date.now() };
472
+ return json.packageId;
473
+ }
465
474
  async function getConfig(fresh = false) {
466
475
  if (configCache && !fresh && Date.now() - configCache.ts < CACHE_TTL) return configCache.data;
467
- const data = await fetchJson(CONFIG_API);
476
+ const [data, latestPkg] = await Promise.all([
477
+ fetchJson(CONFIG_API),
478
+ getLatestPackageId()
479
+ ]);
480
+ data.package = latestPkg;
468
481
  configCache = { data, ts: Date.now() };
469
482
  return data;
470
483
  }
@@ -482,6 +495,24 @@ async function getUsdcPool() {
482
495
  if (!usdc) throw new T2000Error("PROTOCOL_UNAVAILABLE", "USDC pool not found on NAVI");
483
496
  return usdc;
484
497
  }
498
+ function addOracleUpdate(tx, config, pool) {
499
+ const feed = config.oracle.feeds?.find((f2) => f2.assetId === pool.id);
500
+ if (!feed) {
501
+ throw new T2000Error("PROTOCOL_UNAVAILABLE", `Oracle feed not found for asset ${pool.token?.symbol ?? pool.id}`);
502
+ }
503
+ tx.moveCall({
504
+ target: `${config.oracle.packageId}::oracle_pro::update_single_price_v2`,
505
+ arguments: [
506
+ tx.object(CLOCK),
507
+ tx.object(config.oracle.oracleConfig),
508
+ tx.object(config.oracle.priceOracle),
509
+ tx.object(config.oracle.supraOracleHolder),
510
+ tx.object(feed.pythPriceInfoObject),
511
+ tx.object(config.oracle.switchboardAggregator),
512
+ tx.pure.address(feed.feedId)
513
+ ]
514
+ });
515
+ }
485
516
  function rateToApy(rawRate) {
486
517
  if (!rawRate || rawRate === "0") return 0;
487
518
  return Number(BigInt(rawRate)) / 10 ** RATE_DECIMALS * 100;
@@ -504,7 +535,7 @@ function compoundBalance(rawBalance, currentIndex) {
504
535
  if (!rawBalance || !currentIndex || currentIndex === "0") return 0;
505
536
  const scale = BigInt("1" + "0".repeat(RATE_DECIMALS));
506
537
  const half = scale / 2n;
507
- const result = (rawBalance * scale + half) / BigInt(currentIndex);
538
+ const result = (rawBalance * BigInt(currentIndex) + half) / scale;
508
539
  return Number(result) / 10 ** NAVI_BALANCE_DECIMALS;
509
540
  }
510
541
  async function getUserState(client, address) {
@@ -538,23 +569,25 @@ async function fetchCoins(client, owner, coinType) {
538
569
  }
539
570
  return all;
540
571
  }
541
- function mergeCoinsPtb(tx, coins, amount) {
572
+ function mergeCoins(tx, coins) {
542
573
  if (coins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", "No coins to merge");
543
574
  const primary = tx.object(coins[0].coinObjectId);
544
575
  if (coins.length > 1) {
545
576
  tx.mergeCoins(primary, coins.slice(1).map((c) => tx.object(c.coinObjectId)));
546
577
  }
547
- const [split] = tx.splitCoins(primary, [amount]);
548
- return split;
578
+ return primary;
549
579
  }
550
580
  async function buildSaveTx(client, address, amount, options = {}) {
581
+ if (!amount || amount <= 0 || !Number.isFinite(amount)) {
582
+ throw new T2000Error("INVALID_AMOUNT", "Save amount must be a positive number");
583
+ }
551
584
  const rawAmount = Number(usdcToRaw(amount));
552
585
  const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
553
586
  const coins = await fetchCoins(client, address, USDC_TYPE);
554
587
  if (coins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", "No USDC coins found");
555
588
  const tx = new transactions.Transaction();
556
589
  tx.setSender(address);
557
- const coinObj = mergeCoinsPtb(tx, coins, rawAmount);
590
+ const coinObj = mergeCoins(tx, coins);
558
591
  if (options.collectFee) {
559
592
  addCollectFeeToTx(tx, coinObj, "save");
560
593
  }
@@ -588,8 +621,9 @@ async function buildWithdrawTx(client, address, amount) {
588
621
  const rawAmount = Number(usdcToRaw(effectiveAmount));
589
622
  const tx = new transactions.Transaction();
590
623
  tx.setSender(address);
591
- tx.moveCall({
592
- target: `${config.package}::incentive_v3::entry_withdraw_v2`,
624
+ addOracleUpdate(tx, config, pool);
625
+ const [balance] = tx.moveCall({
626
+ target: `${config.package}::incentive_v3::withdraw_v2`,
593
627
  arguments: [
594
628
  tx.object(CLOCK),
595
629
  tx.object(config.oracle.priceOracle),
@@ -600,17 +634,28 @@ async function buildWithdrawTx(client, address, amount) {
600
634
  tx.object(config.incentiveV2),
601
635
  tx.object(config.incentiveV3),
602
636
  tx.object(SUI_SYSTEM_STATE)
603
- ]
637
+ ],
638
+ typeArguments: [pool.suiCoinType]
639
+ });
640
+ const [coin] = tx.moveCall({
641
+ target: "0x2::coin::from_balance",
642
+ arguments: [balance],
643
+ typeArguments: [pool.suiCoinType]
604
644
  });
645
+ tx.transferObjects([coin], address);
605
646
  return { tx, effectiveAmount };
606
647
  }
607
648
  async function buildBorrowTx(client, address, amount, options = {}) {
649
+ if (!amount || amount <= 0 || !Number.isFinite(amount)) {
650
+ throw new T2000Error("INVALID_AMOUNT", "Borrow amount must be a positive number");
651
+ }
608
652
  const rawAmount = Number(usdcToRaw(amount));
609
653
  const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
610
654
  const tx = new transactions.Transaction();
611
655
  tx.setSender(address);
612
- tx.moveCall({
613
- target: `${config.package}::incentive_v3::entry_borrow_v2`,
656
+ addOracleUpdate(tx, config, pool);
657
+ const [balance] = tx.moveCall({
658
+ target: `${config.package}::incentive_v3::borrow_v2`,
614
659
  arguments: [
615
660
  tx.object(CLOCK),
616
661
  tx.object(config.oracle.priceOracle),
@@ -621,18 +666,32 @@ async function buildBorrowTx(client, address, amount, options = {}) {
621
666
  tx.object(config.incentiveV2),
622
667
  tx.object(config.incentiveV3),
623
668
  tx.object(SUI_SYSTEM_STATE)
624
- ]
669
+ ],
670
+ typeArguments: [pool.suiCoinType]
671
+ });
672
+ const [borrowedCoin] = tx.moveCall({
673
+ target: "0x2::coin::from_balance",
674
+ arguments: [balance],
675
+ typeArguments: [pool.suiCoinType]
625
676
  });
677
+ if (options.collectFee) {
678
+ addCollectFeeToTx(tx, borrowedCoin, "borrow");
679
+ }
680
+ tx.transferObjects([borrowedCoin], address);
626
681
  return tx;
627
682
  }
628
683
  async function buildRepayTx(client, address, amount) {
684
+ if (!amount || amount <= 0 || !Number.isFinite(amount)) {
685
+ throw new T2000Error("INVALID_AMOUNT", "Repay amount must be a positive number");
686
+ }
629
687
  const rawAmount = Number(usdcToRaw(amount));
630
688
  const [config, pool] = await Promise.all([getConfig(), getUsdcPool()]);
631
689
  const coins = await fetchCoins(client, address, USDC_TYPE);
632
690
  if (coins.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", "No USDC coins to repay with");
633
691
  const tx = new transactions.Transaction();
634
692
  tx.setSender(address);
635
- const coinObj = mergeCoinsPtb(tx, coins, rawAmount);
693
+ addOracleUpdate(tx, config, pool);
694
+ const coinObj = mergeCoins(tx, coins);
636
695
  tx.moveCall({
637
696
  target: `${config.package}::incentive_v3::entry_repay`,
638
697
  arguments: [
@@ -1002,7 +1061,7 @@ var ProtocolRegistry = class {
1002
1061
  }
1003
1062
  }
1004
1063
  if (candidates.length === 0) {
1005
- throw new Error(`No lending adapter supports saving ${asset}`);
1064
+ throw new T2000Error("ASSET_NOT_SUPPORTED", `No lending adapter supports saving ${asset}`);
1006
1065
  }
1007
1066
  candidates.sort((a, b) => b.rate.saveApy - a.rate.saveApy);
1008
1067
  return candidates[0];
@@ -1020,7 +1079,7 @@ var ProtocolRegistry = class {
1020
1079
  }
1021
1080
  }
1022
1081
  if (candidates.length === 0) {
1023
- throw new Error(`No lending adapter supports borrowing ${asset}`);
1082
+ throw new T2000Error("ASSET_NOT_SUPPORTED", `No lending adapter supports borrowing ${asset}`);
1024
1083
  }
1025
1084
  candidates.sort((a, b) => a.rate.borrowApy - b.rate.borrowApy);
1026
1085
  return candidates[0];
@@ -1037,7 +1096,7 @@ var ProtocolRegistry = class {
1037
1096
  }
1038
1097
  }
1039
1098
  if (candidates.length === 0) {
1040
- throw new Error(`No swap adapter supports ${from} \u2192 ${to}`);
1099
+ throw new T2000Error("ASSET_NOT_SUPPORTED", `No swap adapter supports ${from} \u2192 ${to}`);
1041
1100
  }
1042
1101
  candidates.sort((a, b) => b.quote.expectedOutput - a.quote.expectedOutput);
1043
1102
  return candidates[0];
@@ -1100,7 +1159,7 @@ var NaviAdapter = class {
1100
1159
  const rates = await getRates(this.client);
1101
1160
  const key = asset.toUpperCase();
1102
1161
  const r = rates[key];
1103
- if (!r) throw new Error(`NAVI does not support ${asset}`);
1162
+ if (!r) throw new T2000Error("ASSET_NOT_SUPPORTED", `NAVI does not support ${asset}`);
1104
1163
  return { asset, saveApy: r.saveApy, borrowApy: r.borrowApy };
1105
1164
  }
1106
1165
  async getPositions(address) {
@@ -2303,7 +2362,10 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2303
2362
  return { positions };
2304
2363
  }
2305
2364
  async rates() {
2306
- return getRates(this.client);
2365
+ const allRatesResult = await this.registry.allRates("USDC");
2366
+ if (allRatesResult.length === 0) return { USDC: { saveApy: 0, borrowApy: 0 } };
2367
+ const best = allRatesResult.reduce((a, b) => b.rates.saveApy > a.rates.saveApy ? b : a);
2368
+ return { USDC: { saveApy: best.rates.saveApy, borrowApy: best.rates.borrowApy } };
2307
2369
  }
2308
2370
  async allRates(asset = "USDC") {
2309
2371
  return this.registry.allRates(asset);