@agether/sdk 2.13.0 → 2.14.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.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agether CLI — Direct Morpho Blue Credit for AI Agents
4
+ *
5
+ * Architecture (v2 — Safe + Safe7579):
6
+ * - All commands sign transactions directly with the agent's private key
7
+ * - Uses MorphoClient for lending (ERC-4337 UserOps through Safe account)
8
+ * - Uses X402Client for paid API calls
9
+ *
10
+ * Supported chains: Ethereum (1, default), Base (8453), Base Sepolia (84532)
11
+ *
12
+ * Usage:
13
+ * agether init <private-key> [--agent-id <id>] [--chain <chainId>]
14
+ * agether register [--name <n>] Register ERC-8004 + Safe account
15
+ * agether balance Check balances
16
+ * agether status Show Morpho positions
17
+ * agether score Get credit score (x402-gated)
18
+ * agether markets List Morpho markets
19
+ * agether deposit --amount 0.05 --token WETH Deposit collateral
20
+ * agether borrow --amount 100 Borrow USDC
21
+ * agether deposit-and-borrow --amount 0.05 --token WETH --borrow 100
22
+ * agether repay --amount 50 Repay USDC
23
+ * agether withdraw --amount 0.05 --token WETH Withdraw collateral
24
+ * agether sponsor --amount 0.05 --token WETH --agent-id 123
25
+ * agether fund --amount 50 Fund Safe account with USDC
26
+ * agether x402 <url> [--method GET|POST] [--body <json>]
27
+ */
28
+ export {};
29
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG"}
package/dist/cli.js CHANGED
@@ -408,7 +408,7 @@ var init_MorphoClient = __esm({
408
408
  const chainId = this.config.chainId;
409
409
  const query = `{
410
410
  markets(
411
- first: 50
411
+ first: 500
412
412
  orderBy: SupplyAssetsUsd
413
413
  orderDirection: Desc
414
414
  where: { chainId_in: [${chainId}] }
@@ -514,7 +514,7 @@ var init_MorphoClient = __esm({
514
514
  const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
515
515
  loanAddr = resolved.address.toLowerCase();
516
516
  } catch {
517
- loanAddr = loanTokenSymbolOrAddress.toLowerCase();
517
+ loanAddr = void 0;
518
518
  }
519
519
  }
520
520
  }
@@ -522,6 +522,7 @@ var init_MorphoClient = __esm({
522
522
  for (const m of this._discoveredMarkets ?? []) {
523
523
  if (m.collateralAsset.address.toLowerCase() !== colAddr) continue;
524
524
  if (loanAddr && m.loanAsset.address.toLowerCase() !== loanAddr) continue;
525
+ if (!loanAddr && loanTokenSymbolOrAddress && m.loanAsset.symbol.toUpperCase() !== loanTokenSymbolOrAddress.toUpperCase()) continue;
525
526
  return {
526
527
  loanToken: m.loanAsset.address,
527
528
  collateralToken: m.collateralAsset.address,
@@ -530,6 +531,27 @@ var init_MorphoClient = __esm({
530
531
  lltv: m.lltv
531
532
  };
532
533
  }
534
+ if (!collateralSymbolOrAddress.startsWith("0x")) {
535
+ const searched = await this.searchMarkets(collateralSymbolOrAddress, { asCollateral: true });
536
+ const allResults = [...searched];
537
+ if (loanTokenSymbolOrAddress && !loanTokenSymbolOrAddress.startsWith("0x")) {
538
+ const loanSearched = await this.searchMarkets(loanTokenSymbolOrAddress, { asLoanToken: true });
539
+ const seen = new Set(allResults.map((r) => r.marketId));
540
+ for (const m of loanSearched) {
541
+ if (!seen.has(m.marketId)) allResults.push(m);
542
+ }
543
+ }
544
+ for (const m of allResults) {
545
+ if (colAddr.startsWith("0x") && m.collateralAddress.toLowerCase() !== colAddr) {
546
+ if (m.collateralToken.toUpperCase() !== collateralSymbolOrAddress.toUpperCase()) continue;
547
+ } else if (m.collateralToken.toUpperCase() !== collateralSymbolOrAddress.toUpperCase()) {
548
+ continue;
549
+ }
550
+ if (loanAddr && m.loanAddress.toLowerCase() !== loanAddr) continue;
551
+ if (!loanAddr && loanTokenSymbolOrAddress && m.loanToken.toUpperCase() !== loanTokenSymbolOrAddress.toUpperCase()) continue;
552
+ return this.getMarketParams(m.marketId);
553
+ }
554
+ }
533
555
  throw new AgetherError(
534
556
  `No Morpho market found for collateral ${collateralSymbolOrAddress}` + (loanTokenSymbolOrAddress ? ` with loan token ${loanTokenSymbolOrAddress}` : ""),
535
557
  "MARKET_NOT_FOUND"
@@ -560,26 +582,54 @@ var init_MorphoClient = __esm({
560
582
  };
561
583
  }
562
584
  /**
563
- * Full status: positions across all discovered markets.
585
+ * Full status: positions across all markets the user has interacted with.
586
+ *
587
+ * Uses Morpho GraphQL `marketPositions` to find ALL positions (not limited
588
+ * to the top-500 markets), then reads onchain data for accurate debt.
564
589
  */
565
590
  async getStatus() {
566
591
  const acctAddr = await this.getAccountAddress();
567
- const markets = await this.getMarkets();
592
+ const chainId = this.config.chainId;
568
593
  const positions = [];
569
594
  let totalDebtFloat = 0;
570
- for (const m of markets) {
571
- if (!m.collateralAsset || m.collateralAsset.address === import_ethers.ethers.ZeroAddress) continue;
572
- try {
573
- const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
574
- if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
595
+ try {
596
+ const posQuery = `{
597
+ marketPositions(
598
+ where: {
599
+ userAddress_in: ["${acctAddr}"]
600
+ chainId_in: [${chainId}]
601
+ }
602
+ first: 100
603
+ ) {
604
+ items {
605
+ supplyShares
606
+ borrowShares
607
+ collateral
608
+ market {
609
+ uniqueKey
610
+ loanAsset { symbol address decimals }
611
+ collateralAsset { symbol address decimals }
612
+ }
613
+ }
614
+ }
615
+ }`;
616
+ const resp = await import_axios.default.post(MORPHO_API_URL, { query: posQuery }, { timeout: 15e3 });
617
+ const items = resp.data?.data?.marketPositions?.items ?? [];
618
+ for (const item of items) {
619
+ const supplyShares = BigInt(item.supplyShares ?? "0");
620
+ const borrowShares = BigInt(item.borrowShares ?? "0");
621
+ const collateral = BigInt(item.collateral ?? "0");
622
+ if (collateral === 0n && borrowShares === 0n && supplyShares === 0n) continue;
623
+ const m = item.market;
624
+ if (!m?.collateralAsset || !m?.loanAsset) continue;
575
625
  const loanDecimals = m.loanAsset.decimals;
576
626
  let debt = 0n;
577
- if (pos.borrowShares > 0n) {
627
+ if (borrowShares > 0n) {
578
628
  try {
579
629
  const mkt = await this.morphoBlue.market(m.uniqueKey);
580
630
  const totalBorrowShares = BigInt(mkt.totalBorrowShares);
581
631
  const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
582
- debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
632
+ debt = totalBorrowShares > 0n ? (borrowShares * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
583
633
  totalDebtFloat += parseFloat(import_ethers.ethers.formatUnits(debt, loanDecimals));
584
634
  } catch (e) {
585
635
  console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
@@ -589,14 +639,46 @@ var init_MorphoClient = __esm({
589
639
  marketId: m.uniqueKey,
590
640
  collateralToken: m.collateralAsset.symbol,
591
641
  loanToken: m.loanAsset.symbol,
592
- collateral: import_ethers.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
593
- borrowShares: pos.borrowShares.toString(),
594
- supplyShares: pos.supplyShares.toString(),
642
+ collateral: import_ethers.ethers.formatUnits(collateral, m.collateralAsset.decimals),
643
+ borrowShares: borrowShares.toString(),
644
+ supplyShares: supplyShares.toString(),
595
645
  debt: import_ethers.ethers.formatUnits(debt, loanDecimals)
596
646
  });
597
- } catch (e) {
598
- console.warn(`[agether] position read failed for market:`, e instanceof Error ? e.message : e);
599
- continue;
647
+ }
648
+ } catch (e) {
649
+ console.warn("[agether] marketPositions API failed, falling back to market scan:", e instanceof Error ? e.message : e);
650
+ const markets = await this.getMarkets();
651
+ for (const m of markets) {
652
+ if (!m.collateralAsset || m.collateralAsset.address === import_ethers.ethers.ZeroAddress) continue;
653
+ try {
654
+ const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
655
+ if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
656
+ const loanDecimals = m.loanAsset.decimals;
657
+ let debt = 0n;
658
+ if (pos.borrowShares > 0n) {
659
+ try {
660
+ const mkt = await this.morphoBlue.market(m.uniqueKey);
661
+ const totalBorrowShares = BigInt(mkt.totalBorrowShares);
662
+ const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
663
+ debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
664
+ totalDebtFloat += parseFloat(import_ethers.ethers.formatUnits(debt, loanDecimals));
665
+ } catch (e2) {
666
+ console.warn(`[agether] debt calc failed:`, e2 instanceof Error ? e2.message : e2);
667
+ }
668
+ }
669
+ positions.push({
670
+ marketId: m.uniqueKey,
671
+ collateralToken: m.collateralAsset.symbol,
672
+ loanToken: m.loanAsset.symbol,
673
+ collateral: import_ethers.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
674
+ borrowShares: pos.borrowShares.toString(),
675
+ supplyShares: pos.supplyShares.toString(),
676
+ debt: import_ethers.ethers.formatUnits(debt, loanDecimals)
677
+ });
678
+ } catch (e2) {
679
+ console.warn(`[agether] position read failed:`, e2 instanceof Error ? e2.message : e2);
680
+ continue;
681
+ }
600
682
  }
601
683
  }
602
684
  return {
@@ -700,41 +782,39 @@ var init_MorphoClient = __esm({
700
782
  async getMarketRates(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
701
783
  const chainId = this.config.chainId;
702
784
  let collateralFilter = "";
785
+ let loanFilter = "";
786
+ let searchTerm = "";
703
787
  if (collateralSymbolOrAddress) {
704
- let colAddr;
705
788
  if (collateralSymbolOrAddress.startsWith("0x")) {
706
- colAddr = collateralSymbolOrAddress.toLowerCase();
789
+ collateralFilter = `, collateralAssetAddress_in: ["${collateralSymbolOrAddress.toLowerCase()}"]`;
707
790
  } else {
708
- try {
709
- const resolved = await this._resolveToken(collateralSymbolOrAddress);
710
- colAddr = resolved.address.toLowerCase();
711
- } catch {
712
- colAddr = collateralSymbolOrAddress.toLowerCase();
791
+ const cached = this._tokenCache.get(collateralSymbolOrAddress.toUpperCase());
792
+ if (cached) {
793
+ collateralFilter = `, collateralAssetAddress_in: ["${cached.address.toLowerCase()}"]`;
794
+ } else {
795
+ searchTerm = collateralSymbolOrAddress;
713
796
  }
714
797
  }
715
- collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
716
798
  }
717
- let loanFilter = "";
718
799
  if (loanTokenSymbolOrAddress) {
719
- let loanAddr;
720
800
  if (loanTokenSymbolOrAddress.startsWith("0x")) {
721
- loanAddr = loanTokenSymbolOrAddress.toLowerCase();
801
+ loanFilter = `, loanAssetAddress_in: ["${loanTokenSymbolOrAddress.toLowerCase()}"]`;
722
802
  } else {
723
- try {
724
- const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
725
- loanAddr = resolved.address.toLowerCase();
726
- } catch {
727
- loanAddr = loanTokenSymbolOrAddress.toLowerCase();
803
+ const cached = this._tokenCache.get(loanTokenSymbolOrAddress.toUpperCase());
804
+ if (cached) {
805
+ loanFilter = `, loanAssetAddress_in: ["${cached.address.toLowerCase()}"]`;
806
+ } else {
807
+ searchTerm = searchTerm || loanTokenSymbolOrAddress;
728
808
  }
729
809
  }
730
- loanFilter = `, loanAssetAddress_in: ["${loanAddr}"]`;
731
810
  }
811
+ const searchClause = searchTerm ? `, search: "${searchTerm}"` : "";
732
812
  const query = `{
733
813
  markets(
734
- first: 50
814
+ first: 100
735
815
  orderBy: SupplyAssetsUsd
736
816
  orderDirection: Desc
737
- where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter} }
817
+ where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter}${searchClause} }
738
818
  ) {
739
819
  items {
740
820
  uniqueKey
@@ -753,7 +833,15 @@ var init_MorphoClient = __esm({
753
833
  }`;
754
834
  try {
755
835
  const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
756
- const items = resp.data?.data?.markets?.items ?? [];
836
+ let items = resp.data?.data?.markets?.items ?? [];
837
+ if (searchTerm && collateralSymbolOrAddress && !collateralSymbolOrAddress.startsWith("0x")) {
838
+ const sym = collateralSymbolOrAddress.toUpperCase();
839
+ items = items.filter((m) => m.collateralAsset?.symbol?.toUpperCase() === sym);
840
+ }
841
+ if (searchTerm && loanTokenSymbolOrAddress && !loanTokenSymbolOrAddress.startsWith("0x")) {
842
+ const sym = loanTokenSymbolOrAddress.toUpperCase();
843
+ items = items.filter((m) => m.loanAsset?.symbol?.toUpperCase() === sym);
844
+ }
757
845
  return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers.ethers.ZeroAddress).map((m) => {
758
846
  const loanDecimals = m.loanAsset?.decimals ?? 18;
759
847
  return {
@@ -774,6 +862,205 @@ var init_MorphoClient = __esm({
774
862
  return [];
775
863
  }
776
864
  }
865
+ // ════════════════════════════════════════════════════════
866
+ // Market Search & Wallet Discovery
867
+ // ════════════════════════════════════════════════════════
868
+ /**
869
+ * Search Morpho markets by token name using the Morpho GraphQL API `search` field.
870
+ * No hardcoded token lists — uses Morpho's built-in fuzzy search.
871
+ *
872
+ * @param search - token name or symbol (e.g. 'WETH', 'staked ETH', 'ezETH')
873
+ * @param options.asCollateral - only return markets where the searched token is collateral
874
+ * @param options.asLoanToken - only return markets where the searched token is the loan asset
875
+ */
876
+ async searchMarkets(search, options) {
877
+ const chainId = this.config.chainId;
878
+ const query = `{
879
+ markets(
880
+ first: 100
881
+ orderBy: SupplyAssetsUsd
882
+ orderDirection: Desc
883
+ where: { chainId_in: [${chainId}], search: "${search}" }
884
+ ) {
885
+ items {
886
+ uniqueKey
887
+ lltv
888
+ loanAsset { address symbol decimals }
889
+ collateralAsset { address symbol decimals }
890
+ state {
891
+ borrowAssets
892
+ supplyAssets
893
+ utilization
894
+ supplyApy
895
+ borrowApy
896
+ }
897
+ }
898
+ }
899
+ }`;
900
+ try {
901
+ const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
902
+ let items = resp.data?.data?.markets?.items ?? [];
903
+ items = items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers.ethers.ZeroAddress);
904
+ const searchUpper = search.toUpperCase();
905
+ if (options?.asCollateral) {
906
+ items = items.filter((m) => m.collateralAsset?.symbol?.toUpperCase() === searchUpper);
907
+ }
908
+ if (options?.asLoanToken) {
909
+ items = items.filter((m) => m.loanAsset?.symbol?.toUpperCase() === searchUpper);
910
+ }
911
+ return items.map((m) => {
912
+ const loanDecimals = m.loanAsset?.decimals ?? 18;
913
+ const collateralDecimals = m.collateralAsset?.decimals ?? 18;
914
+ return {
915
+ collateralToken: m.collateralAsset.symbol,
916
+ loanToken: m.loanAsset.symbol,
917
+ loanDecimals,
918
+ collateralDecimals,
919
+ supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
920
+ borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
921
+ utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
922
+ totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
923
+ totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
924
+ lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
925
+ marketId: m.uniqueKey,
926
+ collateralAddress: m.collateralAsset.address,
927
+ loanAddress: m.loanAsset.address
928
+ };
929
+ });
930
+ } catch (e) {
931
+ console.warn("[agether] searchMarkets failed:", e instanceof Error ? e.message : e);
932
+ return [];
933
+ }
934
+ }
935
+ /**
936
+ * Scan the AgentAccount wallet for all ERC-20 tokens that appear in Morpho
937
+ * markets on the current chain. Returns tokens where balance > 0.
938
+ *
939
+ * Uses the full market list (500 markets) to discover all relevant tokens,
940
+ * then checks on-chain balance for each unique token address.
941
+ *
942
+ * @returns Array of tokens with non-zero balance, sorted by balance descending.
943
+ */
944
+ async getWalletTokenBalances() {
945
+ const acctAddr = await this.getAccountAddress();
946
+ await this.getMarkets();
947
+ const uniqueTokens = /* @__PURE__ */ new Map();
948
+ for (const [key, info] of this._tokenCache.entries()) {
949
+ if (key.startsWith("0x") && !uniqueTokens.has(key)) {
950
+ uniqueTokens.set(key, info);
951
+ }
952
+ }
953
+ const results = [];
954
+ try {
955
+ const ethBalance = await this.provider.getBalance(acctAddr);
956
+ if (ethBalance > 0n) {
957
+ results.push({
958
+ symbol: "ETH",
959
+ address: import_ethers.ethers.ZeroAddress,
960
+ decimals: 18,
961
+ balance: ethBalance,
962
+ balanceFormatted: import_ethers.ethers.formatEther(ethBalance)
963
+ });
964
+ }
965
+ } catch {
966
+ }
967
+ const tokenEntries = Array.from(uniqueTokens.values());
968
+ const batchSize = 20;
969
+ for (let i = 0; i < tokenEntries.length; i += batchSize) {
970
+ const batch = tokenEntries.slice(i, i + batchSize);
971
+ const checks = batch.map(async (info) => {
972
+ try {
973
+ const token = new import_ethers.Contract(info.address, ERC20_ABI, this.provider);
974
+ const balance = await token.balanceOf(acctAddr);
975
+ if (balance > 0n) {
976
+ return {
977
+ symbol: info.symbol,
978
+ address: info.address,
979
+ decimals: info.decimals,
980
+ balance,
981
+ balanceFormatted: import_ethers.ethers.formatUnits(balance, info.decimals)
982
+ };
983
+ }
984
+ } catch {
985
+ }
986
+ return null;
987
+ });
988
+ const batchResults = await Promise.all(checks);
989
+ for (const r of batchResults) {
990
+ if (r) results.push(r);
991
+ }
992
+ }
993
+ results.sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
994
+ return results;
995
+ }
996
+ /**
997
+ * Find borrowing opportunities for the agent.
998
+ *
999
+ * - If `collateralSymbol` is provided: find all markets where that token is collateral.
1000
+ * - If omitted: scan wallet balances and find markets for each token the agent holds.
1001
+ *
1002
+ * Returns markets grouped by collateral token with APY, liquidity, and balance info.
1003
+ *
1004
+ * @param collateralSymbol - optional, e.g. 'WETH'. If omitted, scans wallet.
1005
+ */
1006
+ async findBorrowingOptions(collateralSymbol) {
1007
+ let tokensToCheck;
1008
+ if (collateralSymbol) {
1009
+ tokensToCheck = [{ symbol: collateralSymbol, balanceFormatted: "N/A" }];
1010
+ try {
1011
+ const balance = await this.getTokenBalance(collateralSymbol);
1012
+ const info = await this._resolveToken(collateralSymbol);
1013
+ tokensToCheck = [{ symbol: collateralSymbol, balanceFormatted: import_ethers.ethers.formatUnits(balance, info.decimals) }];
1014
+ } catch {
1015
+ }
1016
+ } else {
1017
+ const walletTokens = await this.getWalletTokenBalances();
1018
+ tokensToCheck = walletTokens.filter((t) => t.symbol !== "ETH").map((t) => ({ symbol: t.symbol, balanceFormatted: t.balanceFormatted }));
1019
+ if (tokensToCheck.length === 0) {
1020
+ return [];
1021
+ }
1022
+ }
1023
+ const results = [];
1024
+ for (const token of tokensToCheck) {
1025
+ const markets = await this.searchMarkets(token.symbol, { asCollateral: true });
1026
+ if (markets.length === 0) continue;
1027
+ results.push({
1028
+ collateralToken: token.symbol,
1029
+ collateralBalance: token.balanceFormatted,
1030
+ markets: markets.map((m) => ({
1031
+ loanToken: m.loanToken,
1032
+ borrowApy: `${(m.borrowApy * 100).toFixed(2)}%`,
1033
+ supplyApy: `${(m.supplyApy * 100).toFixed(2)}%`,
1034
+ lltv: m.lltv,
1035
+ utilization: `${(m.utilization * 100).toFixed(1)}%`,
1036
+ availableLiquidity: `$${(m.totalSupplyUsd - m.totalBorrowUsd).toFixed(0)}`,
1037
+ marketId: m.marketId
1038
+ }))
1039
+ });
1040
+ }
1041
+ return results;
1042
+ }
1043
+ /**
1044
+ * Find supply/lending opportunities for a specific loan token.
1045
+ *
1046
+ * "What can I supply to earn WETH?" → shows all markets where WETH is the loan token
1047
+ * (user supplies WETH to earn yield from borrowers).
1048
+ *
1049
+ * @param loanTokenSymbol - e.g. 'USDC', 'WETH'
1050
+ */
1051
+ async findSupplyOptions(loanTokenSymbol) {
1052
+ const markets = await this.searchMarkets(loanTokenSymbol, { asLoanToken: true });
1053
+ return markets.map((m) => ({
1054
+ collateralToken: m.collateralToken,
1055
+ loanToken: m.loanToken,
1056
+ supplyApy: `${(m.supplyApy * 100).toFixed(2)}%`,
1057
+ borrowApy: `${(m.borrowApy * 100).toFixed(2)}%`,
1058
+ lltv: m.lltv,
1059
+ utilization: `${(m.utilization * 100).toFixed(1)}%`,
1060
+ totalSupply: `$${m.totalSupplyUsd.toFixed(0)}`,
1061
+ marketId: m.marketId
1062
+ }));
1063
+ }
777
1064
  /**
778
1065
  * Estimate theoretical yield for a given collateral amount over a period.
779
1066
  *
@@ -1678,6 +1965,46 @@ var init_MorphoClient = __esm({
1678
1965
  /** Find the first market where the agent has collateral deposited. */
1679
1966
  async _findActiveMarket() {
1680
1967
  const acctAddr = await this.getAccountAddress();
1968
+ const chainId = this.config.chainId;
1969
+ try {
1970
+ const posQuery = `{
1971
+ marketPositions(
1972
+ where: { userAddress_in: ["${acctAddr}"], chainId_in: [${chainId}] }
1973
+ first: 50
1974
+ ) {
1975
+ items {
1976
+ collateral
1977
+ market {
1978
+ uniqueKey
1979
+ oracleAddress
1980
+ irmAddress
1981
+ lltv
1982
+ loanAsset { address symbol decimals }
1983
+ collateralAsset { address symbol decimals }
1984
+ }
1985
+ }
1986
+ }
1987
+ }`;
1988
+ const resp = await import_axios.default.post(MORPHO_API_URL, { query: posQuery }, { timeout: 1e4 });
1989
+ const items = resp.data?.data?.marketPositions?.items ?? [];
1990
+ for (const item of items) {
1991
+ if (BigInt(item.collateral ?? "0") > 0n && item.market?.collateralAsset) {
1992
+ const m = item.market;
1993
+ return {
1994
+ params: {
1995
+ loanToken: m.loanAsset.address,
1996
+ collateralToken: m.collateralAsset.address,
1997
+ oracle: m.oracleAddress,
1998
+ irm: m.irmAddress,
1999
+ lltv: BigInt(m.lltv)
2000
+ },
2001
+ symbol: m.collateralAsset.symbol
2002
+ };
2003
+ }
2004
+ }
2005
+ } catch (e) {
2006
+ console.warn("[agether] _findActiveMarket GraphQL failed, falling back:", e instanceof Error ? e.message : e);
2007
+ }
1681
2008
  const markets = await this.getMarkets();
1682
2009
  for (const m of markets) {
1683
2010
  if (!m.collateralAsset || m.collateralAsset.address === import_ethers.ethers.ZeroAddress) continue;
@@ -1706,6 +2033,46 @@ var init_MorphoClient = __esm({
1706
2033
  /** Find the first market where the agent has a supply (lending) position. */
1707
2034
  async _findActiveSupplyMarket() {
1708
2035
  const acctAddr = await this.getAccountAddress();
2036
+ const chainId = this.config.chainId;
2037
+ try {
2038
+ const posQuery = `{
2039
+ marketPositions(
2040
+ where: { userAddress_in: ["${acctAddr}"], chainId_in: [${chainId}] }
2041
+ first: 50
2042
+ ) {
2043
+ items {
2044
+ supplyShares
2045
+ market {
2046
+ uniqueKey
2047
+ oracleAddress
2048
+ irmAddress
2049
+ lltv
2050
+ loanAsset { address symbol decimals }
2051
+ collateralAsset { address symbol decimals }
2052
+ }
2053
+ }
2054
+ }
2055
+ }`;
2056
+ const resp = await import_axios.default.post(MORPHO_API_URL, { query: posQuery }, { timeout: 1e4 });
2057
+ const items = resp.data?.data?.marketPositions?.items ?? [];
2058
+ for (const item of items) {
2059
+ if (BigInt(item.supplyShares ?? "0") > 0n && item.market?.collateralAsset) {
2060
+ const m = item.market;
2061
+ return {
2062
+ params: {
2063
+ loanToken: m.loanAsset.address,
2064
+ collateralToken: m.collateralAsset.address,
2065
+ oracle: m.oracleAddress,
2066
+ irm: m.irmAddress,
2067
+ lltv: BigInt(m.lltv)
2068
+ },
2069
+ symbol: m.collateralAsset.symbol
2070
+ };
2071
+ }
2072
+ }
2073
+ } catch (e) {
2074
+ console.warn("[agether] _findActiveSupplyMarket GraphQL failed, falling back:", e instanceof Error ? e.message : e);
2075
+ }
1709
2076
  const markets = await this.getMarkets();
1710
2077
  for (const m of markets) {
1711
2078
  if (!m.collateralAsset || m.collateralAsset.address === import_ethers.ethers.ZeroAddress) continue;
@@ -1776,6 +2143,24 @@ var init_MorphoClient = __esm({
1776
2143
  await this.getMarkets();
1777
2144
  const fromApi = this._tokenCache.get(key);
1778
2145
  if (fromApi) return fromApi;
2146
+ if (!symbolOrAddress.startsWith("0x")) {
2147
+ const searchResults = await this.searchMarkets(symbolOrAddress);
2148
+ const sym = symbolOrAddress.toUpperCase();
2149
+ for (const m of searchResults) {
2150
+ if (m.collateralToken.toUpperCase() === sym) {
2151
+ const info = { address: m.collateralAddress, symbol: m.collateralToken, decimals: m.collateralDecimals };
2152
+ this._tokenCache.set(sym, info);
2153
+ this._tokenCache.set(m.collateralAddress.toLowerCase(), info);
2154
+ return info;
2155
+ }
2156
+ if (m.loanToken.toUpperCase() === sym) {
2157
+ const info = { address: m.loanAddress, symbol: m.loanToken, decimals: m.loanDecimals };
2158
+ this._tokenCache.set(sym, info);
2159
+ this._tokenCache.set(m.loanAddress.toLowerCase(), info);
2160
+ return info;
2161
+ }
2162
+ }
2163
+ }
1779
2164
  throw new AgetherError(
1780
2165
  `Unknown token: ${symbolOrAddress}. No Morpho market found with this token.`,
1781
2166
  "UNKNOWN_TOKEN"