@peridot-agent/agent-kit 0.2.0 → 0.2.1

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.
@@ -1,5 +1,5 @@
1
1
  import { StructuredTool } from '@langchain/core/tools';
2
- import { P as PeridotConfig } from '../../types-Cg95um6s.cjs';
2
+ import { P as PeridotConfig } from '../../types-BP04EsT6.cjs';
3
3
  import 'viem';
4
4
  import 'zod';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { StructuredTool } from '@langchain/core/tools';
2
- import { P as PeridotConfig } from '../../types-Cg95um6s.js';
2
+ import { P as PeridotConfig } from '../../types-BP04EsT6.js';
3
3
  import 'viem';
4
4
  import 'zod';
5
5
 
@@ -412,6 +412,121 @@ async function listMarkets(input, config) {
412
412
 
413
413
  // src/features/lending/read/get-portfolio.ts
414
414
  import { z as z3 } from "zod";
415
+ init_constants();
416
+
417
+ // src/shared/on-chain-position.ts
418
+ import Decimal from "decimal.js";
419
+ import { createPublicClient, http } from "viem";
420
+
421
+ // src/shared/abis.ts
422
+ import { parseAbi } from "viem";
423
+ var PTOKEN_ABI = parseAbi([
424
+ "function mint(uint256 mintAmount) returns (uint256)",
425
+ "function redeem(uint256 redeemTokens) returns (uint256)",
426
+ "function redeemUnderlying(uint256 redeemAmount) returns (uint256)",
427
+ "function borrow(uint256 borrowAmount) returns (uint256)",
428
+ "function repayBorrow(uint256 repayAmount) returns (uint256)",
429
+ "function repayBorrowBehalf(address borrower, uint256 repayAmount) returns (uint256)",
430
+ "function liquidateBorrow(address borrower, uint256 repayAmount, address pTokenCollateral) returns (uint256)",
431
+ "function balanceOf(address owner) view returns (uint256)",
432
+ "function borrowBalanceStored(address account) view returns (uint256)",
433
+ "function supplyRatePerBlock() view returns (uint256)",
434
+ "function borrowRatePerBlock() view returns (uint256)",
435
+ "function exchangeRateStored() view returns (uint256)",
436
+ "function underlying() view returns (address)"
437
+ ]);
438
+ var COMPTROLLER_ABI = parseAbi([
439
+ "function enterMarkets(address[] calldata pTokens) returns (uint256[] memory)",
440
+ "function exitMarket(address pTokenAddress) returns (uint256)",
441
+ "function getAccountLiquidity(address account) view returns (uint256 errorCode, uint256 liquidity, uint256 shortfall)",
442
+ "function getAllMarkets() view returns (address[] memory)",
443
+ "function markets(address pToken) view returns (bool isListed, uint256 collateralFactorMantissa, bool isComped)",
444
+ "function checkMembership(address account, address pToken) view returns (bool)"
445
+ ]);
446
+ var ERC20_ABI = parseAbi([
447
+ "function approve(address spender, uint256 amount) returns (bool)",
448
+ "function allowance(address owner, address spender) view returns (uint256)",
449
+ "function balanceOf(address owner) view returns (uint256)",
450
+ "function transfer(address to, uint256 amount) returns (bool)"
451
+ ]);
452
+
453
+ // src/shared/on-chain-position.ts
454
+ init_constants();
455
+ async function readOnChainPosition(address, chainId, config) {
456
+ const markets = PERIDOT_MARKETS[chainId];
457
+ if (!markets) {
458
+ throw new Error(`No markets configured for chain ${chainId}`);
459
+ }
460
+ const marketEntries = Object.entries(markets).filter(
461
+ (entry) => entry[1] !== void 0
462
+ );
463
+ if (marketEntries.length === 0) {
464
+ return { totalSuppliedUsd: 0, totalBorrowedUsd: 0, assets: [] };
465
+ }
466
+ const rpcUrl = config.rpcUrls?.[chainId] ?? DEFAULT_RPC_URLS[chainId];
467
+ if (!rpcUrl) {
468
+ throw new Error(
469
+ `No RPC URL available for chain ${chainId}. Provide one via config.rpcUrls[${chainId}].`
470
+ );
471
+ }
472
+ const viemClient = createPublicClient({ transport: http(rpcUrl) });
473
+ const apiClient = new PeridotApiClient(config);
474
+ const [metricsData, multicallResults] = await Promise.all([
475
+ apiClient.getMarketMetrics(),
476
+ viemClient.multicall({
477
+ contracts: marketEntries.flatMap(([, pTokenAddress]) => [
478
+ {
479
+ address: pTokenAddress,
480
+ abi: PTOKEN_ABI,
481
+ functionName: "balanceOf",
482
+ args: [address]
483
+ },
484
+ {
485
+ address: pTokenAddress,
486
+ abi: PTOKEN_ABI,
487
+ functionName: "exchangeRateStored"
488
+ },
489
+ {
490
+ address: pTokenAddress,
491
+ abi: PTOKEN_ABI,
492
+ functionName: "borrowBalanceStored",
493
+ args: [address]
494
+ }
495
+ ]),
496
+ allowFailure: true
497
+ })
498
+ ]);
499
+ const assets = [];
500
+ let totalSuppliedUsd = 0;
501
+ let totalBorrowedUsd = 0;
502
+ for (let i = 0; i < marketEntries.length; i++) {
503
+ const entry = marketEntries[i];
504
+ if (!entry) continue;
505
+ const [symbol] = entry;
506
+ const base = i * 3;
507
+ const balanceResult = multicallResults[base];
508
+ const exchangeRateResult = multicallResults[base + 1];
509
+ const borrowResult = multicallResults[base + 2];
510
+ if (!balanceResult || !exchangeRateResult || !borrowResult || balanceResult.status === "failure" || exchangeRateResult.status === "failure" || borrowResult.status === "failure") {
511
+ continue;
512
+ }
513
+ const cTokenBalance = balanceResult.result;
514
+ const exchangeRate = exchangeRateResult.result;
515
+ const borrowBalance = borrowResult.result;
516
+ const underlyingDecimals = ASSET_DECIMALS[symbol] ?? 18;
517
+ const priceUsd = metricsData[`${symbol}:${chainId}`]?.priceUsd ?? 0;
518
+ const suppliedTokens = new Decimal(cTokenBalance.toString()).mul(exchangeRate.toString()).div("1e18").div(new Decimal(10).pow(underlyingDecimals)).toNumber();
519
+ const borrowedTokens = new Decimal(borrowBalance.toString()).div(new Decimal(10).pow(underlyingDecimals)).toNumber();
520
+ const suppliedUsd = suppliedTokens * priceUsd;
521
+ const borrowedUsd = borrowedTokens * priceUsd;
522
+ if (suppliedUsd > 1e-3 || borrowedUsd > 1e-3) {
523
+ assets.push({ assetId: symbol, suppliedUsd, borrowedUsd, suppliedTokens, borrowedTokens, priceUsd });
524
+ totalSuppliedUsd += suppliedUsd;
525
+ totalBorrowedUsd += borrowedUsd;
526
+ }
527
+ }
528
+ return { totalSuppliedUsd, totalBorrowedUsd, assets };
529
+ }
415
530
 
416
531
  // src/shared/zod-utils.ts
417
532
  import { z as z2 } from "zod";
@@ -467,26 +582,47 @@ var Cache = class {
467
582
  // src/features/lending/read/get-portfolio.ts
468
583
  var portfolioCache = new Cache(3e4);
469
584
  var getPortfolioSchema = z3.object({
470
- address: evmAddress.describe("The wallet address (0x...) to look up")
585
+ address: evmAddress.describe("The wallet address (0x...) to look up"),
586
+ chainId: z3.number().int().default(BSC_MAINNET_CHAIN_ID).refine(isHubChain, { message: "chainId must be a hub chain (56=BSC, 143=Monad, 1868=Somnia)." }).describe("Hub chain ID to query. Defaults to BSC (56).")
471
587
  });
472
588
  async function getPortfolio(input, config) {
473
- const key = input.address.toLowerCase();
589
+ const key = `${input.address.toLowerCase()}:${input.chainId}`;
474
590
  return portfolioCache.getOrFetch(key, async () => {
475
- const client = new PeridotApiClient(config);
476
- const data = await client.getUserPortfolio(input.address);
591
+ const apiClient = new PeridotApiClient(config);
592
+ const [position, apyData] = await Promise.all([
593
+ readOnChainPosition(input.address, input.chainId, config),
594
+ apiClient.getMarketApy(input.chainId)
595
+ ]);
596
+ const { totalSuppliedUsd, totalBorrowedUsd, assets } = position;
597
+ let netApy = 0;
598
+ if (totalSuppliedUsd > 0) {
599
+ let weighted = 0;
600
+ for (const asset of assets) {
601
+ const apyEntry = apyData[asset.assetId.toLowerCase()]?.[input.chainId];
602
+ if (apyEntry) {
603
+ weighted += asset.suppliedUsd * (apyEntry.totalSupplyApy ?? 0);
604
+ weighted -= asset.borrowedUsd * (apyEntry.netBorrowApy ?? 0);
605
+ }
606
+ }
607
+ netApy = weighted / totalSuppliedUsd;
608
+ }
477
609
  return {
478
610
  address: input.address,
479
611
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
480
612
  portfolio: {
481
- currentValue: data.portfolio.currentValue,
482
- totalSupplied: data.portfolio.totalSupplied,
483
- totalBorrowed: data.portfolio.totalBorrowed,
484
- netApy: data.portfolio.netApy,
485
- healthFactor: data.portfolio.totalBorrowed > 0 ? data.portfolio.totalSupplied / data.portfolio.totalBorrowed : null
613
+ currentValue: totalSuppliedUsd - totalBorrowedUsd,
614
+ totalSupplied: totalSuppliedUsd,
615
+ totalBorrowed: totalBorrowedUsd,
616
+ netApy,
617
+ healthFactor: totalBorrowedUsd > 0 ? totalSuppliedUsd / totalBorrowedUsd : null
486
618
  },
487
- assets: data.assets,
488
- transactions: data.transactions,
489
- earnings: data.earnings
619
+ assets: assets.map((a) => ({
620
+ assetId: a.assetId,
621
+ supplied: a.suppliedUsd,
622
+ borrowed: a.borrowedUsd,
623
+ net: a.suppliedUsd - a.borrowedUsd,
624
+ percentage: totalSuppliedUsd > 0 ? (a.suppliedUsd - a.borrowedUsd) / totalSuppliedUsd * 100 : 0
625
+ }))
490
626
  };
491
627
  });
492
628
  }
@@ -568,39 +704,50 @@ async function getMarketRates(input, config) {
568
704
 
569
705
  // src/features/lending/read/get-user-position.ts
570
706
  import { z as z6 } from "zod";
707
+ init_constants();
571
708
  var getUserPositionSchema = z6.object({
572
- address: evmAddress.describe("The wallet address (0x...) to look up")
709
+ address: evmAddress.describe("The wallet address (0x...) to look up"),
710
+ chainId: z6.number().int().default(BSC_MAINNET_CHAIN_ID).refine(isHubChain, { message: "chainId must be a hub chain (56=BSC, 143=Monad, 1868=Somnia)." }).describe("Hub chain ID to query. Defaults to BSC (56).")
573
711
  });
574
712
  async function getUserPosition(input, config) {
575
- const client = new PeridotApiClient(config);
576
- const data = await client.getUserPortfolio(input.address);
577
- const { portfolio, assets, transactions } = data;
578
- const healthFactor = portfolio.totalBorrowed > 0 ? portfolio.totalSupplied / portfolio.totalBorrowed : null;
713
+ const apiClient = new PeridotApiClient(config);
714
+ const [position, apyData] = await Promise.all([
715
+ readOnChainPosition(input.address, input.chainId, config),
716
+ apiClient.getMarketApy(input.chainId)
717
+ ]);
718
+ const { totalSuppliedUsd, totalBorrowedUsd, assets } = position;
719
+ const healthFactor = totalBorrowedUsd > 0 ? totalSuppliedUsd / totalBorrowedUsd : null;
720
+ let netApyPct = 0;
721
+ if (totalSuppliedUsd > 0) {
722
+ let weightedApy = 0;
723
+ for (const asset of assets) {
724
+ const apyEntry = apyData[asset.assetId.toLowerCase()]?.[input.chainId];
725
+ if (apyEntry) {
726
+ weightedApy += asset.suppliedUsd * (apyEntry.totalSupplyApy ?? 0);
727
+ weightedApy -= asset.borrowedUsd * (apyEntry.netBorrowApy ?? 0);
728
+ }
729
+ }
730
+ netApyPct = weightedApy / totalSuppliedUsd;
731
+ }
579
732
  return {
580
733
  address: input.address,
581
- totalSuppliedUsd: portfolio.totalSupplied,
582
- totalBorrowedUsd: portfolio.totalBorrowed,
583
- netWorthUsd: portfolio.currentValue,
584
- netApyPct: portfolio.netApy,
734
+ totalSuppliedUsd,
735
+ totalBorrowedUsd,
736
+ netWorthUsd: totalSuppliedUsd - totalBorrowedUsd,
737
+ netApyPct,
585
738
  healthFactor,
586
739
  assets: assets.map((a) => ({
587
740
  assetId: a.assetId,
588
- suppliedUsd: a.supplied,
589
- borrowedUsd: a.borrowed,
590
- netUsd: a.net
741
+ suppliedUsd: a.suppliedUsd,
742
+ borrowedUsd: a.borrowedUsd,
743
+ netUsd: a.suppliedUsd - a.borrowedUsd
591
744
  })),
592
- transactions: {
593
- supplyCount: transactions.supplyCount,
594
- borrowCount: transactions.borrowCount,
595
- repayCount: transactions.repayCount,
596
- redeemCount: transactions.redeemCount
597
- },
598
745
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
599
746
  };
600
747
  }
601
748
 
602
749
  // src/features/lending/read/simulate-borrow.ts
603
- import Decimal from "decimal.js";
750
+ import Decimal2 from "decimal.js";
604
751
  import { z as z7 } from "zod";
605
752
  init_constants();
606
753
  var simulateBorrowSchema = z7.object({
@@ -623,10 +770,10 @@ function classifyRisk(hf) {
623
770
  return "liquidatable";
624
771
  }
625
772
  async function simulateBorrow(input, config) {
626
- const client = new PeridotApiClient(config);
627
- const [portfolioData, metricsData] = await Promise.all([
628
- client.getUserPortfolio(input.address),
629
- client.getMarketMetrics()
773
+ const apiClient = new PeridotApiClient(config);
774
+ const [position, metricsData] = await Promise.all([
775
+ readOnChainPosition(input.address, input.chainId, config),
776
+ apiClient.getMarketMetrics()
630
777
  ]);
631
778
  const assetUpper = input.asset.toUpperCase();
632
779
  const metricKey = `${assetUpper}:${input.chainId}`;
@@ -638,12 +785,11 @@ async function simulateBorrow(input, config) {
638
785
  if (isNaN(borrowAmountRaw) || borrowAmountRaw <= 0) {
639
786
  throw new Error(`Invalid borrow amount: "${input.amount}"`);
640
787
  }
641
- const borrowAmount = new Decimal(input.amount);
788
+ const borrowAmount = new Decimal2(input.amount);
642
789
  const borrowAmountUsd = borrowAmount.mul(metric.priceUsd).toNumber();
643
- const { totalSupplied, totalBorrowed } = portfolioData.portfolio;
644
- const currentHF = totalBorrowed > 0 ? new Decimal(totalSupplied).div(totalBorrowed).toNumber() : null;
645
- const projectedBorrowedUsd = new Decimal(totalBorrowed).add(borrowAmountUsd);
646
- if (totalSupplied === 0) {
790
+ const { totalSuppliedUsd, totalBorrowedUsd } = position;
791
+ const currentHF = totalBorrowedUsd > 0 ? new Decimal2(totalSuppliedUsd).div(totalBorrowedUsd).toNumber() : null;
792
+ if (totalSuppliedUsd === 0) {
647
793
  return {
648
794
  currentHealthFactor: null,
649
795
  projectedHealthFactor: null,
@@ -651,13 +797,14 @@ async function simulateBorrow(input, config) {
651
797
  isSafe: false,
652
798
  riskLevel: "liquidatable",
653
799
  maxSafeBorrowUsd: 0,
654
- warning: "Portfolio shows no supplied collateral (data is sourced from indexed DB snapshots and may not reflect activity from the last few minutes). If you recently supplied assets, wait for the next snapshot update. Otherwise, supply assets and enable them as collateral before borrowing."
800
+ warning: "No supplied collateral found on-chain for this address. Supply assets and enable them as collateral before borrowing."
655
801
  };
656
802
  }
657
- const projectedHF = new Decimal(totalSupplied).div(projectedBorrowedUsd).toNumber();
658
- const maxSafeBorrowUsd = Decimal.max(
803
+ const projectedBorrowedUsd = new Decimal2(totalBorrowedUsd).add(borrowAmountUsd);
804
+ const projectedHF = new Decimal2(totalSuppliedUsd).div(projectedBorrowedUsd).toNumber();
805
+ const maxSafeBorrowUsd = Decimal2.max(
659
806
  0,
660
- new Decimal(totalSupplied).div(RISK_THRESHOLDS.safe).sub(totalBorrowed)
807
+ new Decimal2(totalSuppliedUsd).div(RISK_THRESHOLDS.safe).sub(totalBorrowedUsd)
661
808
  ).toNumber();
662
809
  const riskLevel = classifyRisk(projectedHF);
663
810
  const isSafe = projectedHF >= RISK_THRESHOLDS.high;
@@ -681,43 +828,9 @@ async function simulateBorrow(input, config) {
681
828
  }
682
829
 
683
830
  // src/features/lending/read/get-account-liquidity.ts
684
- import Decimal2 from "decimal.js";
831
+ import Decimal3 from "decimal.js";
685
832
  import { z as z8 } from "zod";
686
- import { createPublicClient, http } from "viem";
687
-
688
- // src/shared/abis.ts
689
- import { parseAbi } from "viem";
690
- var PTOKEN_ABI = parseAbi([
691
- "function mint(uint256 mintAmount) returns (uint256)",
692
- "function redeem(uint256 redeemTokens) returns (uint256)",
693
- "function redeemUnderlying(uint256 redeemAmount) returns (uint256)",
694
- "function borrow(uint256 borrowAmount) returns (uint256)",
695
- "function repayBorrow(uint256 repayAmount) returns (uint256)",
696
- "function repayBorrowBehalf(address borrower, uint256 repayAmount) returns (uint256)",
697
- "function liquidateBorrow(address borrower, uint256 repayAmount, address pTokenCollateral) returns (uint256)",
698
- "function balanceOf(address owner) view returns (uint256)",
699
- "function borrowBalanceStored(address account) view returns (uint256)",
700
- "function supplyRatePerBlock() view returns (uint256)",
701
- "function borrowRatePerBlock() view returns (uint256)",
702
- "function exchangeRateStored() view returns (uint256)",
703
- "function underlying() view returns (address)"
704
- ]);
705
- var COMPTROLLER_ABI = parseAbi([
706
- "function enterMarkets(address[] calldata pTokens) returns (uint256[] memory)",
707
- "function exitMarket(address pTokenAddress) returns (uint256)",
708
- "function getAccountLiquidity(address account) view returns (uint256 errorCode, uint256 liquidity, uint256 shortfall)",
709
- "function getAllMarkets() view returns (address[] memory)",
710
- "function markets(address pToken) view returns (bool isListed, uint256 collateralFactorMantissa, bool isComped)",
711
- "function checkMembership(address account, address pToken) view returns (bool)"
712
- ]);
713
- var ERC20_ABI = parseAbi([
714
- "function approve(address spender, uint256 amount) returns (bool)",
715
- "function allowance(address owner, address spender) view returns (uint256)",
716
- "function balanceOf(address owner) view returns (uint256)",
717
- "function transfer(address to, uint256 amount) returns (bool)"
718
- ]);
719
-
720
- // src/features/lending/read/get-account-liquidity.ts
833
+ import { createPublicClient as createPublicClient2, http as http2 } from "viem";
721
834
  init_constants();
722
835
  var getAccountLiquiditySchema = z8.object({
723
836
  address: evmAddress.describe("The wallet address to check"),
@@ -731,7 +844,7 @@ async function getAccountLiquidity(input, config) {
731
844
  );
732
845
  }
733
846
  const controllerAddress = getControllerAddress(input.chainId);
734
- const client = createPublicClient({ transport: http(rpcUrl) });
847
+ const client = createPublicClient2({ transport: http2(rpcUrl) });
735
848
  const [error, liquidity, shortfall] = await client.readContract({
736
849
  address: controllerAddress,
737
850
  abi: COMPTROLLER_ABI,
@@ -741,8 +854,8 @@ async function getAccountLiquidity(input, config) {
741
854
  if (error !== 0n) {
742
855
  throw new Error(`Comptroller getAccountLiquidity returned error code ${error.toString()}`);
743
856
  }
744
- const liquidityUsd = new Decimal2(liquidity.toString()).div("1e18").toNumber();
745
- const shortfallUsd = new Decimal2(shortfall.toString()).div("1e18").toNumber();
857
+ const liquidityUsd = new Decimal3(liquidity.toString()).div("1e18").toNumber();
858
+ const shortfallUsd = new Decimal3(shortfall.toString()).div("1e18").toNumber();
746
859
  return {
747
860
  address: input.address,
748
861
  chainId: input.chainId,