@agether/sdk 2.3.4 → 2.5.0

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,27 @@
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
+ * Usage:
11
+ * agether init <private-key> [--agent-id <id>] Initialize
12
+ * agether register [--name <n>] Register ERC-8004 + Safe account
13
+ * agether balance Check balances
14
+ * agether status Show Morpho positions
15
+ * agether score Get credit score (x402-gated)
16
+ * agether markets List Morpho markets
17
+ * agether deposit --amount 0.05 --token WETH Deposit collateral
18
+ * agether borrow --amount 100 Borrow USDC
19
+ * agether deposit-and-borrow --amount 0.05 --token WETH --borrow 100
20
+ * agether repay --amount 50 Repay USDC
21
+ * agether withdraw --amount 0.05 --token WETH Withdraw collateral
22
+ * agether sponsor --amount 0.05 --token WETH --agent-id 123
23
+ * agether fund --amount 50 Fund Safe account with USDC
24
+ * agether x402 <url> [--method GET|POST] [--body <json>]
25
+ */
26
+ export {};
27
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
package/dist/cli.js CHANGED
@@ -274,7 +274,7 @@ var MorphoClient_exports = {};
274
274
  __export(MorphoClient_exports, {
275
275
  MorphoClient: () => MorphoClient
276
276
  });
277
- var import_ethers, import_axios, BASE_COLLATERALS, MORPHO_API_URL, MODE_SINGLE, MODE_BATCH, morphoIface, erc20Iface, MorphoClient;
277
+ var import_ethers, import_axios, MORPHO_API_URL, MODE_SINGLE, MODE_BATCH, morphoIface, erc20Iface, MorphoClient;
278
278
  var init_MorphoClient = __esm({
279
279
  "src/clients/MorphoClient.ts"() {
280
280
  "use strict";
@@ -283,11 +283,6 @@ var init_MorphoClient = __esm({
283
283
  init_types();
284
284
  init_abis();
285
285
  init_config();
286
- BASE_COLLATERALS = {
287
- WETH: { address: "0x4200000000000000000000000000000000000006", symbol: "WETH", decimals: 18 },
288
- wstETH: { address: "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", symbol: "wstETH", decimals: 18 },
289
- cbETH: { address: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", symbol: "cbETH", decimals: 18 }
290
- };
291
286
  MORPHO_API_URL = "https://api.morpho.org/graphql";
292
287
  MODE_SINGLE = "0x0000000000000000000000000000000000000000000000000000000000000000";
293
288
  MODE_BATCH = "0x0100000000000000000000000000000000000000000000000000000000000000";
@@ -296,6 +291,8 @@ var init_MorphoClient = __esm({
296
291
  MorphoClient = class {
297
292
  constructor(config) {
298
293
  this._marketCache = /* @__PURE__ */ new Map();
294
+ /** Dynamic token registry: symbol (uppercase) → { address, symbol, decimals } */
295
+ this._tokenCache = /* @__PURE__ */ new Map();
299
296
  this._discoveredAt = 0;
300
297
  const chainId = config.chainId ?? 8453 /* Base */;
301
298
  const defaultCfg = getDefaultConfig(chainId);
@@ -482,15 +479,16 @@ var init_MorphoClient = __esm({
482
479
  const usdc = new import_ethers.Contract(this.config.contracts.usdc, ERC20_ABI, this.provider);
483
480
  const ethBal = await this.provider.getBalance(eoaAddr);
484
481
  const usdcBal = await usdc.balanceOf(eoaAddr);
482
+ const discoveredTokens = await this._getDiscoveredTokens();
485
483
  const eoaCollateral = {};
486
- for (const [symbol, info] of Object.entries(BASE_COLLATERALS)) {
484
+ for (const info of discoveredTokens) {
487
485
  try {
488
486
  const token = new import_ethers.Contract(info.address, ERC20_ABI, this.provider);
489
487
  const bal = await token.balanceOf(eoaAddr);
490
- eoaCollateral[symbol] = import_ethers.ethers.formatUnits(bal, info.decimals);
488
+ eoaCollateral[info.symbol] = import_ethers.ethers.formatUnits(bal, info.decimals);
491
489
  } catch (e) {
492
- console.warn(`[agether] EOA collateral fetch failed for ${symbol}:`, e instanceof Error ? e.message : e);
493
- eoaCollateral[symbol] = "0";
490
+ console.warn(`[agether] EOA collateral fetch failed for ${info.symbol}:`, e instanceof Error ? e.message : e);
491
+ eoaCollateral[info.symbol] = "0";
494
492
  }
495
493
  }
496
494
  const result = {
@@ -505,14 +503,14 @@ var init_MorphoClient = __esm({
505
503
  const acctEth = await this.provider.getBalance(acctAddr);
506
504
  const acctUsdc = await usdc.balanceOf(acctAddr);
507
505
  const acctCollateral = {};
508
- for (const [symbol, info] of Object.entries(BASE_COLLATERALS)) {
506
+ for (const info of discoveredTokens) {
509
507
  try {
510
508
  const token = new import_ethers.Contract(info.address, ERC20_ABI, this.provider);
511
509
  const bal = await token.balanceOf(acctAddr);
512
- acctCollateral[symbol] = import_ethers.ethers.formatUnits(bal, info.decimals);
510
+ acctCollateral[info.symbol] = import_ethers.ethers.formatUnits(bal, info.decimals);
513
511
  } catch (e) {
514
- console.warn(`[agether] AgentAccount collateral fetch failed for ${symbol}:`, e instanceof Error ? e.message : e);
515
- acctCollateral[symbol] = "0";
512
+ console.warn(`[agether] AgentAccount collateral fetch failed for ${info.symbol}:`, e instanceof Error ? e.message : e);
513
+ acctCollateral[info.symbol] = "0";
516
514
  }
517
515
  }
518
516
  result.agentAccount = {
@@ -598,6 +596,28 @@ var init_MorphoClient = __esm({
598
596
  irm: mi.irm,
599
597
  lltv: mi.lltv
600
598
  });
599
+ this._tokenCache.set(mi.collateralAsset.symbol.toUpperCase(), {
600
+ address: mi.collateralAsset.address,
601
+ symbol: mi.collateralAsset.symbol,
602
+ decimals: mi.collateralAsset.decimals
603
+ });
604
+ this._tokenCache.set(mi.collateralAsset.address.toLowerCase(), {
605
+ address: mi.collateralAsset.address,
606
+ symbol: mi.collateralAsset.symbol,
607
+ decimals: mi.collateralAsset.decimals
608
+ });
609
+ }
610
+ if (mi.loanAsset.address !== import_ethers.ethers.ZeroAddress) {
611
+ this._tokenCache.set(mi.loanAsset.symbol.toUpperCase(), {
612
+ address: mi.loanAsset.address,
613
+ symbol: mi.loanAsset.symbol,
614
+ decimals: mi.loanAsset.decimals
615
+ });
616
+ this._tokenCache.set(mi.loanAsset.address.toLowerCase(), {
617
+ address: mi.loanAsset.address,
618
+ symbol: mi.loanAsset.symbol,
619
+ decimals: mi.loanAsset.decimals
620
+ });
601
621
  }
602
622
  }
603
623
  return this._discoveredMarkets;
@@ -611,8 +631,17 @@ var init_MorphoClient = __esm({
611
631
  * Tries cache → API → onchain idToMarketParams.
612
632
  */
613
633
  async findMarketForCollateral(collateralSymbolOrAddress) {
614
- const colInfo = BASE_COLLATERALS[collateralSymbolOrAddress];
615
- const colAddr = (colInfo?.address ?? collateralSymbolOrAddress).toLowerCase();
634
+ let colAddr;
635
+ if (collateralSymbolOrAddress.startsWith("0x")) {
636
+ colAddr = collateralSymbolOrAddress.toLowerCase();
637
+ } else {
638
+ try {
639
+ const resolved = await this._resolveToken(collateralSymbolOrAddress);
640
+ colAddr = resolved.address.toLowerCase();
641
+ } catch {
642
+ colAddr = collateralSymbolOrAddress.toLowerCase();
643
+ }
644
+ }
616
645
  const cached = this._marketCache.get(colAddr);
617
646
  if (cached) return cached;
618
647
  await this.getMarkets();
@@ -771,8 +800,17 @@ var init_MorphoClient = __esm({
771
800
  const usdcAddr = this.config.contracts.usdc.toLowerCase();
772
801
  let collateralFilter = "";
773
802
  if (collateralSymbolOrAddress) {
774
- const colInfo = BASE_COLLATERALS[collateralSymbolOrAddress];
775
- const colAddr = (colInfo?.address ?? collateralSymbolOrAddress).toLowerCase();
803
+ let colAddr;
804
+ if (collateralSymbolOrAddress.startsWith("0x")) {
805
+ colAddr = collateralSymbolOrAddress.toLowerCase();
806
+ } else {
807
+ try {
808
+ const resolved = await this._resolveToken(collateralSymbolOrAddress);
809
+ colAddr = resolved.address.toLowerCase();
810
+ } catch {
811
+ colAddr = collateralSymbolOrAddress.toLowerCase();
812
+ }
813
+ }
776
814
  collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
777
815
  }
778
816
  const query = `{
@@ -831,8 +869,7 @@ var init_MorphoClient = __esm({
831
869
  * @returns Estimated yield in USD for the period
832
870
  */
833
871
  async getYieldEstimate(collateralSymbol, amount, periodDays = 1, ethPriceUsd) {
834
- const colInfo = BASE_COLLATERALS[collateralSymbol];
835
- if (!colInfo) throw new AgetherError(`Unknown collateral: ${collateralSymbol}`, "UNKNOWN_COLLATERAL");
872
+ const colInfo = await this._resolveToken(collateralSymbol);
836
873
  const rates = await this.getMarketRates(collateralSymbol);
837
874
  if (rates.length === 0) {
838
875
  throw new AgetherError(`No market found for ${collateralSymbol}`, "MARKET_NOT_FOUND");
@@ -870,7 +907,259 @@ var init_MorphoClient = __esm({
870
907
  };
871
908
  }
872
909
  // ════════════════════════════════════════════════════════
873
- // Lending Operations (all via AgentAccount.executeBatch)
910
+ // Supply-Side (Lending) earn yield by supplying USDC
911
+ // ════════════════════════════════════════════════════════
912
+ /**
913
+ * Supply USDC to a Morpho Blue market as a lender (earn yield).
914
+ *
915
+ * Unlike `supplyCollateral` (borrower-side), this is the **lender-side**:
916
+ * you deposit the loanToken (USDC) into the market's supply pool and earn
917
+ * interest paid by borrowers.
918
+ *
919
+ * @param usdcAmount - Amount of USDC to supply (e.g. '500')
920
+ * @param collateralSymbol - Market collateral token to identify which market (e.g. 'WETH')
921
+ * Optional — defaults to highest-APY market
922
+ */
923
+ async supplyAsset(usdcAmount, collateralSymbol) {
924
+ const acctAddr = await this.getAccountAddress();
925
+ const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
926
+ const morphoAddr = this.config.contracts.morphoBlue;
927
+ const usdcAddr = this.config.contracts.usdc;
928
+ let params;
929
+ let usedCollateral;
930
+ if (collateralSymbol) {
931
+ params = await this.findMarketForCollateral(collateralSymbol);
932
+ usedCollateral = collateralSymbol;
933
+ } else {
934
+ const rates = await this.getMarketRates();
935
+ if (rates.length === 0) throw new AgetherError("No markets available", "NO_MARKETS");
936
+ const best = rates.reduce((a, b) => a.supplyApy > b.supplyApy ? a : b);
937
+ params = await this.findMarketForCollateral(best.collateralToken);
938
+ usedCollateral = best.collateralToken;
939
+ }
940
+ const marketId = import_ethers.ethers.keccak256(
941
+ import_ethers.ethers.AbiCoder.defaultAbiCoder().encode(
942
+ ["address", "address", "address", "address", "uint256"],
943
+ [params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
944
+ )
945
+ );
946
+ const usdcContract = new import_ethers.Contract(usdcAddr, ERC20_ABI, this._signer);
947
+ const acctBalance = await usdcContract.balanceOf(acctAddr);
948
+ if (acctBalance < amount) {
949
+ const shortfall = amount - acctBalance;
950
+ const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
951
+ if (eoaBalance < shortfall) {
952
+ throw new AgetherError(
953
+ `Insufficient USDC. Need ${usdcAmount}, AgentAccount has ${import_ethers.ethers.formatUnits(acctBalance, 6)}, EOA has ${import_ethers.ethers.formatUnits(eoaBalance, 6)}.`,
954
+ "INSUFFICIENT_BALANCE"
955
+ );
956
+ }
957
+ const transferTx = await usdcContract.transfer(acctAddr, shortfall);
958
+ await transferTx.wait();
959
+ this._refreshSigner();
960
+ }
961
+ const targets = [usdcAddr, morphoAddr];
962
+ const values = [0n, 0n];
963
+ const datas = [
964
+ erc20Iface.encodeFunctionData("approve", [morphoAddr, amount]),
965
+ morphoIface.encodeFunctionData("supply", [
966
+ this._toTuple(params),
967
+ amount,
968
+ 0n,
969
+ acctAddr,
970
+ "0x"
971
+ ])
972
+ ];
973
+ const receipt = await this.batch(targets, values, datas);
974
+ return {
975
+ tx: receipt.hash,
976
+ amount: usdcAmount,
977
+ marketId,
978
+ collateralToken: usedCollateral,
979
+ agentAccount: acctAddr
980
+ };
981
+ }
982
+ /**
983
+ * Withdraw supplied USDC (+ earned interest) from a Morpho Blue market.
984
+ *
985
+ * @param usdcAmount - Amount to withdraw (e.g. '100' or 'all' for full position)
986
+ * @param collateralSymbol - Market collateral to identify which market
987
+ * @param receiver - Destination address (defaults to EOA)
988
+ */
989
+ async withdrawSupply(usdcAmount, collateralSymbol, receiver) {
990
+ const acctAddr = await this.getAccountAddress();
991
+ const morphoAddr = this.config.contracts.morphoBlue;
992
+ const dest = receiver || await this.getSignerAddress();
993
+ let params;
994
+ if (collateralSymbol) {
995
+ params = await this.findMarketForCollateral(collateralSymbol);
996
+ } else {
997
+ const { params: p } = await this._findActiveSupplyMarket();
998
+ params = p;
999
+ }
1000
+ const marketId = import_ethers.ethers.keccak256(
1001
+ import_ethers.ethers.AbiCoder.defaultAbiCoder().encode(
1002
+ ["address", "address", "address", "address", "uint256"],
1003
+ [params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
1004
+ )
1005
+ );
1006
+ let withdrawAssets;
1007
+ let withdrawShares;
1008
+ if (usdcAmount === "all") {
1009
+ const pos = await this.morphoBlue.position(marketId, acctAddr);
1010
+ withdrawShares = BigInt(pos.supplyShares);
1011
+ withdrawAssets = 0n;
1012
+ if (withdrawShares === 0n) throw new AgetherError("No supply position to withdraw", "NO_SUPPLY");
1013
+ } else {
1014
+ withdrawAssets = import_ethers.ethers.parseUnits(usdcAmount, 6);
1015
+ withdrawShares = 0n;
1016
+ }
1017
+ const data = morphoIface.encodeFunctionData("withdraw", [
1018
+ this._toTuple(params),
1019
+ withdrawAssets,
1020
+ withdrawShares,
1021
+ acctAddr,
1022
+ dest
1023
+ ]);
1024
+ const receipt = await this.exec(morphoAddr, data);
1025
+ let remainingSupply = "0";
1026
+ try {
1027
+ const pos = await this.morphoBlue.position(marketId, acctAddr);
1028
+ const mkt = await this.morphoBlue.market(marketId);
1029
+ const totalSupplyAssets = BigInt(mkt.totalSupplyAssets);
1030
+ const totalSupplyShares = BigInt(mkt.totalSupplyShares);
1031
+ const currentAssets = totalSupplyShares > 0n ? BigInt(pos.supplyShares) * totalSupplyAssets / totalSupplyShares : 0n;
1032
+ remainingSupply = import_ethers.ethers.formatUnits(currentAssets, 6);
1033
+ } catch (e) {
1034
+ console.warn("[agether] failed to read remaining supply:", e instanceof Error ? e.message : e);
1035
+ }
1036
+ return {
1037
+ tx: receipt.hash,
1038
+ amount: usdcAmount,
1039
+ remainingSupply,
1040
+ destination: dest
1041
+ };
1042
+ }
1043
+ /**
1044
+ * Get supply (lending) positions with yield tracking.
1045
+ *
1046
+ * Uses Morpho GraphQL API (no eth_getLogs / no DB / no indexer):
1047
+ * 1. `userByAddress` → all market positions with current supplyAssets, supplyApy
1048
+ * 2. `transactions` → all MarketSupply/MarketWithdraw history for net deposited
1049
+ * 3. earnedYield = currentSupplyAssets − netDeposited
1050
+ *
1051
+ * @param collateralSymbol - Market collateral token (optional, returns all if omitted)
1052
+ */
1053
+ async getSupplyPositions(collateralSymbol) {
1054
+ const acctAddr = (await this.getAccountAddress()).toLowerCase();
1055
+ const chainId = this.config.chainId;
1056
+ const positionsQuery = `{
1057
+ userByAddress(address: "${acctAddr}", chainId: ${chainId}) {
1058
+ marketPositions {
1059
+ market {
1060
+ uniqueKey
1061
+ loanAsset { symbol address decimals }
1062
+ collateralAsset { symbol address }
1063
+ state { supplyApy }
1064
+ }
1065
+ state {
1066
+ supplyShares
1067
+ supplyAssets
1068
+ supplyAssetsUsd
1069
+ }
1070
+ }
1071
+ }
1072
+ }`;
1073
+ const posResp = await import_axios.default.post(MORPHO_API_URL, { query: positionsQuery }, { timeout: 15e3 });
1074
+ const user = posResp.data?.data?.userByAddress;
1075
+ if (!user?.marketPositions) return [];
1076
+ const activePositions = user.marketPositions.filter(
1077
+ (p) => p.state && BigInt(p.state.supplyShares ?? "0") > 0n
1078
+ );
1079
+ if (activePositions.length === 0) return [];
1080
+ const filtered = collateralSymbol ? activePositions.filter((p) => {
1081
+ const sym = p.market.collateralAsset?.symbol;
1082
+ return sym && sym.toUpperCase() === collateralSymbol.toUpperCase();
1083
+ }) : activePositions;
1084
+ if (filtered.length === 0) return [];
1085
+ const netDepositedMap = await this._computeNetDepositedAll(acctAddr, chainId);
1086
+ const results = [];
1087
+ for (const p of filtered) {
1088
+ const currentAssets = BigInt(p.state.supplyAssets ?? "0");
1089
+ const marketKey = p.market.uniqueKey.toLowerCase();
1090
+ const netDeposited = netDepositedMap.get(marketKey) ?? 0n;
1091
+ const earnedYield = currentAssets > netDeposited ? currentAssets - netDeposited : 0n;
1092
+ results.push({
1093
+ marketId: p.market.uniqueKey,
1094
+ loanToken: p.market.loanAsset.symbol,
1095
+ collateralToken: p.market.collateralAsset?.symbol ?? "none",
1096
+ supplyShares: p.state.supplyShares,
1097
+ suppliedAssets: import_ethers.ethers.formatUnits(currentAssets, p.market.loanAsset.decimals),
1098
+ netDeposited: import_ethers.ethers.formatUnits(netDeposited, p.market.loanAsset.decimals),
1099
+ earnedYield: import_ethers.ethers.formatUnits(earnedYield, p.market.loanAsset.decimals),
1100
+ supplyApy: p.market.state?.supplyApy ?? 0
1101
+ });
1102
+ }
1103
+ return results;
1104
+ }
1105
+ /**
1106
+ * Pay a recipient using ONLY earned yield from a supply position.
1107
+ *
1108
+ * Computes available yield, verifies the requested amount doesn't exceed it,
1109
+ * then withdraws from the supply position and sends directly to the recipient.
1110
+ *
1111
+ * @param recipient - Address to receive the USDC
1112
+ * @param usdcAmount - Amount to pay from yield (e.g. '5.50')
1113
+ * @param collateralSymbol - Market collateral to identify which supply position
1114
+ */
1115
+ async payFromYield(recipient, usdcAmount, collateralSymbol) {
1116
+ const acctAddr = await this.getAccountAddress();
1117
+ const morphoAddr = this.config.contracts.morphoBlue;
1118
+ const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
1119
+ const positions = await this.getSupplyPositions(collateralSymbol);
1120
+ if (positions.length === 0) {
1121
+ throw new AgetherError("No supply position found", "NO_SUPPLY");
1122
+ }
1123
+ const pos = positions.reduce(
1124
+ (a, b) => parseFloat(a.earnedYield) > parseFloat(b.earnedYield) ? a : b
1125
+ );
1126
+ const availableYield = import_ethers.ethers.parseUnits(pos.earnedYield, 6);
1127
+ if (amount > availableYield) {
1128
+ throw new AgetherError(
1129
+ `Requested ${usdcAmount} USDC exceeds available yield of ${pos.earnedYield} USDC. Use withdrawSupply to withdraw principal.`,
1130
+ "EXCEEDS_YIELD"
1131
+ );
1132
+ }
1133
+ const params = await this.findMarketForCollateral(pos.collateralToken);
1134
+ const data = morphoIface.encodeFunctionData("withdraw", [
1135
+ this._toTuple(params),
1136
+ amount,
1137
+ 0n,
1138
+ acctAddr,
1139
+ recipient
1140
+ ]);
1141
+ const receipt = await this.exec(morphoAddr, data);
1142
+ let remainingYield = "0";
1143
+ let remainingSupply = "0";
1144
+ try {
1145
+ const updatedPositions = await this.getSupplyPositions(pos.collateralToken);
1146
+ if (updatedPositions.length > 0) {
1147
+ remainingYield = updatedPositions[0].earnedYield;
1148
+ remainingSupply = updatedPositions[0].suppliedAssets;
1149
+ }
1150
+ } catch (e) {
1151
+ console.warn("[agether] failed to read remaining yield:", e instanceof Error ? e.message : e);
1152
+ }
1153
+ return {
1154
+ tx: receipt.hash,
1155
+ yieldWithdrawn: usdcAmount,
1156
+ recipient,
1157
+ remainingYield,
1158
+ remainingSupply
1159
+ };
1160
+ }
1161
+ // ════════════════════════════════════════════════════════
1162
+ // Collateral & Borrowing Operations (all via AgentAccount)
874
1163
  // ════════════════════════════════════════════════════════
875
1164
  /**
876
1165
  * Deposit collateral into Morpho Blue.
@@ -882,8 +1171,7 @@ var init_MorphoClient = __esm({
882
1171
  */
883
1172
  async supplyCollateral(tokenSymbol, amount, marketParams) {
884
1173
  const acctAddr = await this.getAccountAddress();
885
- const colInfo = BASE_COLLATERALS[tokenSymbol];
886
- if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
1174
+ const colInfo = await this._resolveToken(tokenSymbol);
887
1175
  const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
888
1176
  const weiAmount = import_ethers.ethers.parseUnits(amount, colInfo.decimals);
889
1177
  const morphoAddr = this.config.contracts.morphoBlue;
@@ -1004,8 +1292,7 @@ var init_MorphoClient = __esm({
1004
1292
  */
1005
1293
  async depositAndBorrow(tokenSymbol, collateralAmount, borrowUsdcAmount, marketParams) {
1006
1294
  const acctAddr = await this.getAccountAddress();
1007
- const colInfo = BASE_COLLATERALS[tokenSymbol];
1008
- if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
1295
+ const colInfo = await this._resolveToken(tokenSymbol);
1009
1296
  const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
1010
1297
  const colWei = import_ethers.ethers.parseUnits(collateralAmount, colInfo.decimals);
1011
1298
  const borrowWei = import_ethers.ethers.parseUnits(borrowUsdcAmount, 6);
@@ -1173,8 +1460,7 @@ var init_MorphoClient = __esm({
1173
1460
  */
1174
1461
  async withdrawCollateral(tokenSymbol, amount, marketParams, receiver) {
1175
1462
  const acctAddr = await this.getAccountAddress();
1176
- const colInfo = BASE_COLLATERALS[tokenSymbol];
1177
- if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
1463
+ const colInfo = await this._resolveToken(tokenSymbol);
1178
1464
  const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
1179
1465
  const morphoAddr = this.config.contracts.morphoBlue;
1180
1466
  const usdcAddr = this.config.contracts.usdc;
@@ -1271,8 +1557,7 @@ var init_MorphoClient = __esm({
1271
1557
  * (The agent must then supplyCollateral themselves via their own account.)
1272
1558
  */
1273
1559
  async sponsor(target, tokenSymbol, amount) {
1274
- const colInfo = BASE_COLLATERALS[tokenSymbol];
1275
- if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
1560
+ const colInfo = await this._resolveToken(tokenSymbol);
1276
1561
  let targetAddr;
1277
1562
  if (target.address) {
1278
1563
  targetAddr = target.address;
@@ -1501,6 +1786,131 @@ var init_MorphoClient = __esm({
1501
1786
  const params = await this.findMarketForCollateral("WETH");
1502
1787
  return { params, symbol: "WETH" };
1503
1788
  }
1789
+ /** Find the first market where the agent has a supply (lending) position. */
1790
+ async _findActiveSupplyMarket() {
1791
+ const acctAddr = await this.getAccountAddress();
1792
+ const markets = await this.getMarkets();
1793
+ for (const m of markets) {
1794
+ if (!m.collateralAsset || m.collateralAsset.address === import_ethers.ethers.ZeroAddress) continue;
1795
+ try {
1796
+ const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
1797
+ if (BigInt(pos.supplyShares) > 0n) {
1798
+ return {
1799
+ params: {
1800
+ loanToken: m.loanAsset.address,
1801
+ collateralToken: m.collateralAsset.address,
1802
+ oracle: m.oracle,
1803
+ irm: m.irm,
1804
+ lltv: m.lltv
1805
+ },
1806
+ symbol: m.collateralAsset.symbol
1807
+ };
1808
+ }
1809
+ } catch (e) {
1810
+ console.warn("[agether] _findActiveSupplyMarket position check failed:", e instanceof Error ? e.message : e);
1811
+ continue;
1812
+ }
1813
+ }
1814
+ throw new AgetherError("No active supply position found", "NO_SUPPLY");
1815
+ }
1816
+ /**
1817
+ * Resolve a token symbol or address to { address, symbol, decimals }.
1818
+ *
1819
+ * Uses the dynamic `_tokenCache` populated by `getMarkets()` from the
1820
+ * Morpho GraphQL API — no hardcoded token list needed.
1821
+ *
1822
+ * @param symbolOrAddress - e.g. 'WETH', 'wstETH', or '0x4200...'
1823
+ */
1824
+ async _resolveToken(symbolOrAddress) {
1825
+ const key = symbolOrAddress.startsWith("0x") ? symbolOrAddress.toLowerCase() : symbolOrAddress.toUpperCase();
1826
+ const cached = this._tokenCache.get(key);
1827
+ if (cached) return cached;
1828
+ await this.getMarkets();
1829
+ const fromApi = this._tokenCache.get(key);
1830
+ if (fromApi) return fromApi;
1831
+ throw new AgetherError(
1832
+ `Unknown token: ${symbolOrAddress}. No Morpho market found with this collateral.`,
1833
+ "UNKNOWN_COLLATERAL"
1834
+ );
1835
+ }
1836
+ /**
1837
+ * Get all discovered collateral tokens (for balance iteration, etc.).
1838
+ * Returns unique tokens from the Morpho API market discovery.
1839
+ */
1840
+ async _getDiscoveredTokens() {
1841
+ await this.getMarkets();
1842
+ const seen = /* @__PURE__ */ new Set();
1843
+ const tokens = [];
1844
+ for (const [key, info] of this._tokenCache) {
1845
+ if (key.startsWith("0x")) continue;
1846
+ if (seen.has(info.address.toLowerCase())) continue;
1847
+ seen.add(info.address.toLowerCase());
1848
+ if (info.symbol === "USDC" || info.symbol === "USDbC") continue;
1849
+ tokens.push(info);
1850
+ }
1851
+ return tokens;
1852
+ }
1853
+ /**
1854
+ * Compute net deposited amounts per market using Morpho GraphQL API.
1855
+ *
1856
+ * Fetches all MarketSupply and MarketWithdraw transactions for the account,
1857
+ * then computes: netDeposited[marketId] = Σ Supply.assets − Σ Withdraw.assets
1858
+ *
1859
+ * Returns a Map<marketId (lowercase), bigint>.
1860
+ *
1861
+ * Uses pagination (100 per page) for completeness, though agent accounts
1862
+ * typically have single-digit transaction counts.
1863
+ */
1864
+ async _computeNetDepositedAll(accountAddr, chainId) {
1865
+ const result = /* @__PURE__ */ new Map();
1866
+ let skip = 0;
1867
+ const pageSize = 100;
1868
+ let hasMore = true;
1869
+ while (hasMore) {
1870
+ const txQuery = `{
1871
+ transactions(
1872
+ first: ${pageSize}
1873
+ skip: ${skip}
1874
+ where: {
1875
+ userAddress_in: ["${accountAddr}"]
1876
+ type_in: [MarketSupply, MarketWithdraw]
1877
+ chainId_in: [${chainId}]
1878
+ }
1879
+ ) {
1880
+ pageInfo { count countTotal }
1881
+ items {
1882
+ type
1883
+ data {
1884
+ ... on MarketTransferTransactionData {
1885
+ assets
1886
+ market { uniqueKey }
1887
+ }
1888
+ }
1889
+ }
1890
+ }
1891
+ }`;
1892
+ const resp = await import_axios.default.post(MORPHO_API_URL, { query: txQuery }, { timeout: 15e3 });
1893
+ const txData = resp.data?.data?.transactions;
1894
+ if (!txData?.items) break;
1895
+ for (const tx of txData.items) {
1896
+ const marketKey = tx.data?.market?.uniqueKey?.toLowerCase();
1897
+ if (!marketKey || !tx.data?.assets) continue;
1898
+ const assets = BigInt(tx.data.assets);
1899
+ const current = result.get(marketKey) ?? 0n;
1900
+ if (tx.type === "MarketSupply") {
1901
+ result.set(marketKey, current + assets);
1902
+ } else if (tx.type === "MarketWithdraw") {
1903
+ const newVal = current - assets;
1904
+ result.set(marketKey, newVal > 0n ? newVal : 0n);
1905
+ }
1906
+ }
1907
+ const fetched = skip + txData.items.length;
1908
+ const total = txData.pageInfo?.countTotal ?? 0;
1909
+ hasMore = fetched < total;
1910
+ skip += pageSize;
1911
+ }
1912
+ return result;
1913
+ }
1504
1914
  };
1505
1915
  }
1506
1916
  });