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