@agether/sdk 2.14.0 → 2.15.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/index.js CHANGED
@@ -75,6 +75,7 @@ module.exports = __toCommonJS(index_exports);
75
75
 
76
76
  // src/clients/AgetherClient.ts
77
77
  var import_ethers = require("ethers");
78
+ var import_axios = __toESM(require("axios"));
78
79
 
79
80
  // src/types/index.ts
80
81
  var ChainId = /* @__PURE__ */ ((ChainId3) => {
@@ -371,6 +372,7 @@ function getContractAddresses(chainId) {
371
372
  // src/clients/AgetherClient.ts
372
373
  var MODE_SINGLE = "0x0000000000000000000000000000000000000000000000000000000000000000";
373
374
  var erc20Iface = new import_ethers.ethers.Interface(ERC20_ABI);
375
+ var MORPHO_API_URL = "https://api.morpho.org/graphql";
374
376
  var KNOWN_TOKENS = {
375
377
  [8453 /* Base */]: {
376
378
  WETH: { address: "0x4200000000000000000000000000000000000006", symbol: "WETH", decimals: 18 },
@@ -385,6 +387,17 @@ var KNOWN_TOKENS = {
385
387
  };
386
388
  var AgetherClient = class _AgetherClient {
387
389
  constructor(options) {
390
+ /**
391
+ * Resolve a token symbol or address to { address, symbol, decimals }.
392
+ *
393
+ * Resolution order:
394
+ * 1. `'USDC'` → from chain config (instant)
395
+ * 2. Well-known symbols (`'WETH'`, `'wstETH'`, `'cbETH'`) → built-in per-chain registry (instant)
396
+ * 3. Dynamic cache populated by previous Morpho API lookups (instant)
397
+ * 4. `'0x...'` address → reads decimals and symbol onchain
398
+ * 5. Morpho GraphQL `search` API → discovers any token across all Morpho markets
399
+ */
400
+ this._dynamicTokenCache = /* @__PURE__ */ new Map();
388
401
  this.config = options.config;
389
402
  this.signer = options.signer;
390
403
  this.agentId = options.agentId;
@@ -669,21 +682,10 @@ var AgetherClient = class _AgetherClient {
669
682
  }
670
683
  /**
671
684
  * Fund the Safe account with USDC from EOA.
672
- * This is a simple ERC-20 transfer (does NOT require a UserOp).
685
+ * @deprecated Use `fundAccountToken('USDC', amount)` instead.
673
686
  */
674
687
  async fundAccount(usdcAmount) {
675
- const acctAddr = await this.getAccountAddress();
676
- const usdc = new import_ethers.Contract(this.config.contracts.usdc, ERC20_ABI, this.signer);
677
- const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
678
- const tx = await usdc.transfer(acctAddr, amount);
679
- const receipt = await tx.wait();
680
- this._refreshSigner();
681
- return {
682
- txHash: receipt.hash,
683
- blockNumber: receipt.blockNumber,
684
- status: receipt.status === 1 ? "success" : "failed",
685
- gasUsed: receipt.gasUsed
686
- };
688
+ return this.fundAccountToken("USDC", usdcAmount);
687
689
  }
688
690
  // ════════════════════════════════════════════════════════
689
691
  // Withdrawals (Safe → EOA via UserOps)
@@ -735,6 +737,138 @@ var AgetherClient = class _AgetherClient {
735
737
  return { tx: receipt.hash, token: "ETH", amount: actualAmount, destination: eoaAddr };
736
738
  }
737
739
  // ════════════════════════════════════════════════════════
740
+ // Token Transfers & Approvals (AgentAccount → any address)
741
+ // ════════════════════════════════════════════════════════
742
+ /**
743
+ * Fund the AgentAccount with any ERC-20 token from EOA.
744
+ * Simple ERC-20 transfer (does NOT require a UserOp).
745
+ *
746
+ * @param tokenSymbol - Token symbol (e.g. 'USDC', 'WETH') or 0x address
747
+ * @param amount - Amount to send (human-readable, e.g. '100')
748
+ */
749
+ async fundAccountToken(tokenSymbol, amount) {
750
+ const acctAddr = await this.getAccountAddress();
751
+ const tokenInfo = await this._resolveToken(tokenSymbol);
752
+ const tokenContract = new import_ethers.Contract(tokenInfo.address, ERC20_ABI, this.signer);
753
+ const weiAmount = import_ethers.ethers.parseUnits(amount, tokenInfo.decimals);
754
+ const eoaBalance = await tokenContract.balanceOf(await this._getSignerAddress());
755
+ if (eoaBalance < weiAmount) {
756
+ throw new AgetherError(
757
+ `Insufficient ${tokenInfo.symbol}. EOA has ${import_ethers.ethers.formatUnits(eoaBalance, tokenInfo.decimals)}, need ${amount}.`,
758
+ "INSUFFICIENT_BALANCE"
759
+ );
760
+ }
761
+ const tx = await tokenContract.transfer(acctAddr, weiAmount);
762
+ const receipt = await tx.wait();
763
+ this._refreshSigner();
764
+ return {
765
+ txHash: receipt.hash,
766
+ blockNumber: receipt.blockNumber,
767
+ status: receipt.status === 1 ? "success" : "failed",
768
+ gasUsed: receipt.gasUsed
769
+ };
770
+ }
771
+ /**
772
+ * Transfer any ERC-20 token from AgentAccount to any address or agent.
773
+ * Executes via Safe UserOp (ERC-7579 single execution).
774
+ *
775
+ * @param tokenSymbol - Token symbol (e.g. 'USDC', 'WETH') or 0x address
776
+ * @param amount - Amount to send (e.g. '100' or 'all')
777
+ * @param to - Destination: `{ address: '0x...' }` or `{ agentId: '42' }`
778
+ */
779
+ async transferToken(tokenSymbol, amount, to) {
780
+ const acctAddr = await this.getAccountAddress();
781
+ const tokenInfo = await this._resolveToken(tokenSymbol);
782
+ const tokenContract = new import_ethers.Contract(tokenInfo.address, ERC20_ABI, this.signer.provider);
783
+ let toAddr;
784
+ if (to.address) {
785
+ toAddr = to.address;
786
+ } else if (to.agentId) {
787
+ toAddr = await this.agether4337Factory.getAccount(BigInt(to.agentId));
788
+ if (toAddr === import_ethers.ethers.ZeroAddress) {
789
+ throw new AgetherError(`Agent ${to.agentId} has no account`, "NO_ACCOUNT");
790
+ }
791
+ } else {
792
+ throw new AgetherError("Provide address or agentId as destination", "INVALID_TARGET");
793
+ }
794
+ let weiAmount;
795
+ if (amount === "all") {
796
+ weiAmount = await tokenContract.balanceOf(acctAddr);
797
+ if (weiAmount === 0n) {
798
+ throw new AgetherError(`No ${tokenInfo.symbol} in AgentAccount`, "INSUFFICIENT_BALANCE");
799
+ }
800
+ } else {
801
+ weiAmount = import_ethers.ethers.parseUnits(amount, tokenInfo.decimals);
802
+ }
803
+ const data = erc20Iface.encodeFunctionData("transfer", [toAddr, weiAmount]);
804
+ const receipt = await this._exec(tokenInfo.address, data);
805
+ const actualAmount = amount === "all" ? import_ethers.ethers.formatUnits(weiAmount, tokenInfo.decimals) : amount;
806
+ return { tx: receipt.hash, token: tokenInfo.symbol, amount: actualAmount, destination: toAddr };
807
+ }
808
+ /**
809
+ * Transfer ETH from AgentAccount to any address or agent.
810
+ * Executes via Safe UserOp.
811
+ *
812
+ * @param amount - ETH amount (e.g. '0.01' or 'all')
813
+ * @param to - Destination: `{ address: '0x...' }` or `{ agentId: '42' }`
814
+ */
815
+ async transferEth(amount, to) {
816
+ const acctAddr = await this.getAccountAddress();
817
+ let toAddr;
818
+ if (to.address) {
819
+ toAddr = to.address;
820
+ } else if (to.agentId) {
821
+ toAddr = await this.agether4337Factory.getAccount(BigInt(to.agentId));
822
+ if (toAddr === import_ethers.ethers.ZeroAddress) {
823
+ throw new AgetherError(`Agent ${to.agentId} has no account`, "NO_ACCOUNT");
824
+ }
825
+ } else {
826
+ throw new AgetherError("Provide address or agentId as destination", "INVALID_TARGET");
827
+ }
828
+ let weiAmount;
829
+ if (amount === "all") {
830
+ weiAmount = await this.signer.provider.getBalance(acctAddr);
831
+ if (weiAmount === 0n) throw new AgetherError("No ETH in AgentAccount", "INSUFFICIENT_BALANCE");
832
+ } else {
833
+ weiAmount = import_ethers.ethers.parseEther(amount);
834
+ }
835
+ const receipt = await this._exec(toAddr, "0x", weiAmount);
836
+ const actualAmount = amount === "all" ? import_ethers.ethers.formatEther(weiAmount) : amount;
837
+ return { tx: receipt.hash, token: "ETH", amount: actualAmount, destination: toAddr };
838
+ }
839
+ /**
840
+ * Approve a spender to use ERC-20 tokens from the AgentAccount.
841
+ * Executes via Safe UserOp (ERC-7579 single execution).
842
+ *
843
+ * @param tokenSymbol - Token symbol (e.g. 'USDC', 'WETH') or 0x address
844
+ * @param amount - Allowance amount (e.g. '1000' or 'max' for uint256 max)
845
+ * @param spender - Spender: `{ address: '0x...' }` or `{ agentId: '42' }`
846
+ */
847
+ async approveToken(tokenSymbol, amount, spender) {
848
+ const tokenInfo = await this._resolveToken(tokenSymbol);
849
+ let spenderAddr;
850
+ if (spender.address) {
851
+ spenderAddr = spender.address;
852
+ } else if (spender.agentId) {
853
+ spenderAddr = await this.agether4337Factory.getAccount(BigInt(spender.agentId));
854
+ if (spenderAddr === import_ethers.ethers.ZeroAddress) {
855
+ throw new AgetherError(`Agent ${spender.agentId} has no account`, "NO_ACCOUNT");
856
+ }
857
+ } else {
858
+ throw new AgetherError("Provide address or agentId as spender", "INVALID_TARGET");
859
+ }
860
+ let weiAmount;
861
+ if (amount === "max") {
862
+ weiAmount = import_ethers.ethers.MaxUint256;
863
+ } else {
864
+ weiAmount = import_ethers.ethers.parseUnits(amount, tokenInfo.decimals);
865
+ }
866
+ const data = erc20Iface.encodeFunctionData("approve", [spenderAddr, weiAmount]);
867
+ const receipt = await this._exec(tokenInfo.address, data);
868
+ const actualAmount = amount === "max" ? "unlimited" : amount;
869
+ return { tx: receipt.hash, token: tokenInfo.symbol, amount: actualAmount, spender: spenderAddr };
870
+ }
871
+ // ════════════════════════════════════════════════════════
738
872
  // Sponsorship
739
873
  // ════════════════════════════════════════════════════════
740
874
  /**
@@ -863,14 +997,6 @@ var AgetherClient = class _AgetherClient {
863
997
  }
864
998
  return this._eoaAddress;
865
999
  }
866
- /**
867
- * Resolve a token symbol or address to { address, symbol, decimals }.
868
- *
869
- * Supports:
870
- * - `'USDC'` → from chain config
871
- * - Well-known symbols (`'WETH'`, `'wstETH'`, `'cbETH'`) → built-in per-chain registry
872
- * - `'0x...'` address → reads decimals and symbol onchain
873
- */
874
1000
  async _resolveToken(symbolOrAddress) {
875
1001
  if (symbolOrAddress.toUpperCase() === "USDC") {
876
1002
  return { address: this.config.contracts.usdc, symbol: "USDC", decimals: 6 };
@@ -878,6 +1004,9 @@ var AgetherClient = class _AgetherClient {
878
1004
  const chainTokens = KNOWN_TOKENS[this.config.chainId] ?? {};
879
1005
  const bySymbol = chainTokens[symbolOrAddress] || chainTokens[symbolOrAddress.toUpperCase()];
880
1006
  if (bySymbol) return bySymbol;
1007
+ const cacheKey = symbolOrAddress.startsWith("0x") ? symbolOrAddress.toLowerCase() : symbolOrAddress.toUpperCase();
1008
+ const cached = this._dynamicTokenCache.get(cacheKey);
1009
+ if (cached) return cached;
881
1010
  if (symbolOrAddress.startsWith("0x") && symbolOrAddress.length === 42) {
882
1011
  try {
883
1012
  const token = new import_ethers.Contract(
@@ -886,7 +1015,10 @@ var AgetherClient = class _AgetherClient {
886
1015
  this.signer.provider
887
1016
  );
888
1017
  const [decimals, symbol] = await Promise.all([token.decimals(), token.symbol()]);
889
- return { address: symbolOrAddress, symbol, decimals: Number(decimals) };
1018
+ const info = { address: symbolOrAddress, symbol, decimals: Number(decimals) };
1019
+ this._dynamicTokenCache.set(symbolOrAddress.toLowerCase(), info);
1020
+ this._dynamicTokenCache.set(symbol.toUpperCase(), info);
1021
+ return info;
890
1022
  } catch (e) {
891
1023
  throw new AgetherError(
892
1024
  `Failed to read token at ${symbolOrAddress}: ${e instanceof Error ? e.message : e}`,
@@ -894,8 +1026,53 @@ var AgetherClient = class _AgetherClient {
894
1026
  );
895
1027
  }
896
1028
  }
1029
+ try {
1030
+ const chainId = this.config.chainId;
1031
+ const query = `{
1032
+ markets(
1033
+ first: 20
1034
+ where: { chainId_in: [${chainId}], search: "${symbolOrAddress}" }
1035
+ ) {
1036
+ items {
1037
+ loanAsset { address symbol decimals }
1038
+ collateralAsset { address symbol decimals }
1039
+ }
1040
+ }
1041
+ }`;
1042
+ const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
1043
+ const items = resp.data?.data?.markets?.items ?? [];
1044
+ const sym = symbolOrAddress.toUpperCase();
1045
+ for (const m of items) {
1046
+ if (m.loanAsset?.symbol?.toUpperCase() === sym) {
1047
+ const info = { address: m.loanAsset.address, symbol: m.loanAsset.symbol, decimals: m.loanAsset.decimals };
1048
+ this._dynamicTokenCache.set(sym, info);
1049
+ this._dynamicTokenCache.set(m.loanAsset.address.toLowerCase(), info);
1050
+ return info;
1051
+ }
1052
+ if (m.collateralAsset?.symbol?.toUpperCase() === sym) {
1053
+ const info = { address: m.collateralAsset.address, symbol: m.collateralAsset.symbol, decimals: m.collateralAsset.decimals };
1054
+ this._dynamicTokenCache.set(sym, info);
1055
+ this._dynamicTokenCache.set(m.collateralAsset.address.toLowerCase(), info);
1056
+ return info;
1057
+ }
1058
+ }
1059
+ for (const m of items) {
1060
+ if (m.loanAsset?.symbol) {
1061
+ const info = { address: m.loanAsset.address, symbol: m.loanAsset.symbol, decimals: m.loanAsset.decimals };
1062
+ this._dynamicTokenCache.set(m.loanAsset.symbol.toUpperCase(), info);
1063
+ this._dynamicTokenCache.set(m.loanAsset.address.toLowerCase(), info);
1064
+ }
1065
+ if (m.collateralAsset?.symbol) {
1066
+ const info = { address: m.collateralAsset.address, symbol: m.collateralAsset.symbol, decimals: m.collateralAsset.decimals };
1067
+ this._dynamicTokenCache.set(m.collateralAsset.symbol.toUpperCase(), info);
1068
+ this._dynamicTokenCache.set(m.collateralAsset.address.toLowerCase(), info);
1069
+ }
1070
+ }
1071
+ } catch (e) {
1072
+ console.warn("[agether] Morpho token search failed:", e instanceof Error ? e.message : e);
1073
+ }
897
1074
  throw new AgetherError(
898
- `Unknown token: ${symbolOrAddress}. Use a known symbol (USDC, WETH, wstETH, cbETH) or a 0x address.`,
1075
+ `Unknown token: ${symbolOrAddress}. No Morpho market found with this token.`,
899
1076
  "UNKNOWN_TOKEN"
900
1077
  );
901
1078
  }
@@ -1029,8 +1206,8 @@ var AgetherClient = class _AgetherClient {
1029
1206
 
1030
1207
  // src/clients/MorphoClient.ts
1031
1208
  var import_ethers2 = require("ethers");
1032
- var import_axios = __toESM(require("axios"));
1033
- var MORPHO_API_URL = "https://api.morpho.org/graphql";
1209
+ var import_axios2 = __toESM(require("axios"));
1210
+ var MORPHO_API_URL2 = "https://api.morpho.org/graphql";
1034
1211
  var MODE_SINGLE2 = "0x0000000000000000000000000000000000000000000000000000000000000000";
1035
1212
  var MODE_BATCH = "0x0100000000000000000000000000000000000000000000000000000000000000";
1036
1213
  var morphoIface = new import_ethers2.ethers.Interface(MORPHO_BLUE_ABI);
@@ -1176,7 +1353,7 @@ var MorphoClient = class {
1176
1353
  }
1177
1354
  }`;
1178
1355
  try {
1179
- const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
1356
+ const resp = await import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 });
1180
1357
  const items = resp.data?.data?.markets?.items ?? [];
1181
1358
  this._discoveredMarkets = items.map((m) => ({
1182
1359
  uniqueKey: m.uniqueKey,
@@ -1261,7 +1438,7 @@ var MorphoClient = class {
1261
1438
  const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
1262
1439
  loanAddr = resolved.address.toLowerCase();
1263
1440
  } catch {
1264
- loanAddr = loanTokenSymbolOrAddress.toLowerCase();
1441
+ loanAddr = void 0;
1265
1442
  }
1266
1443
  }
1267
1444
  }
@@ -1269,6 +1446,7 @@ var MorphoClient = class {
1269
1446
  for (const m of this._discoveredMarkets ?? []) {
1270
1447
  if (m.collateralAsset.address.toLowerCase() !== colAddr) continue;
1271
1448
  if (loanAddr && m.loanAsset.address.toLowerCase() !== loanAddr) continue;
1449
+ if (!loanAddr && loanTokenSymbolOrAddress && m.loanAsset.symbol.toUpperCase() !== loanTokenSymbolOrAddress.toUpperCase()) continue;
1272
1450
  return {
1273
1451
  loanToken: m.loanAsset.address,
1274
1452
  collateralToken: m.collateralAsset.address,
@@ -1279,9 +1457,22 @@ var MorphoClient = class {
1279
1457
  }
1280
1458
  if (!collateralSymbolOrAddress.startsWith("0x")) {
1281
1459
  const searched = await this.searchMarkets(collateralSymbolOrAddress, { asCollateral: true });
1282
- for (const m of searched) {
1460
+ const allResults = [...searched];
1461
+ if (loanTokenSymbolOrAddress && !loanTokenSymbolOrAddress.startsWith("0x")) {
1462
+ const loanSearched = await this.searchMarkets(loanTokenSymbolOrAddress, { asLoanToken: true });
1463
+ const seen = new Set(allResults.map((r) => r.marketId));
1464
+ for (const m of loanSearched) {
1465
+ if (!seen.has(m.marketId)) allResults.push(m);
1466
+ }
1467
+ }
1468
+ for (const m of allResults) {
1469
+ if (colAddr.startsWith("0x") && m.collateralAddress.toLowerCase() !== colAddr) {
1470
+ if (m.collateralToken.toUpperCase() !== collateralSymbolOrAddress.toUpperCase()) continue;
1471
+ } else if (m.collateralToken.toUpperCase() !== collateralSymbolOrAddress.toUpperCase()) {
1472
+ continue;
1473
+ }
1283
1474
  if (loanAddr && m.loanAddress.toLowerCase() !== loanAddr) continue;
1284
- if (loanTokenSymbolOrAddress && !loanTokenSymbolOrAddress.startsWith("0x") && m.loanToken.toUpperCase() !== loanTokenSymbolOrAddress.toUpperCase()) continue;
1475
+ if (!loanAddr && loanTokenSymbolOrAddress && m.loanToken.toUpperCase() !== loanTokenSymbolOrAddress.toUpperCase()) continue;
1285
1476
  return this.getMarketParams(m.marketId);
1286
1477
  }
1287
1478
  }
@@ -1315,26 +1506,54 @@ var MorphoClient = class {
1315
1506
  };
1316
1507
  }
1317
1508
  /**
1318
- * Full status: positions across all discovered markets.
1509
+ * Full status: positions across all markets the user has interacted with.
1510
+ *
1511
+ * Uses Morpho GraphQL `marketPositions` to find ALL positions (not limited
1512
+ * to the top-500 markets), then reads onchain data for accurate debt.
1319
1513
  */
1320
1514
  async getStatus() {
1321
1515
  const acctAddr = await this.getAccountAddress();
1322
- const markets = await this.getMarkets();
1516
+ const chainId = this.config.chainId;
1323
1517
  const positions = [];
1324
1518
  let totalDebtFloat = 0;
1325
- for (const m of markets) {
1326
- if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
1327
- try {
1328
- const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
1329
- if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
1519
+ try {
1520
+ const posQuery = `{
1521
+ marketPositions(
1522
+ where: {
1523
+ userAddress_in: ["${acctAddr}"]
1524
+ chainId_in: [${chainId}]
1525
+ }
1526
+ first: 100
1527
+ ) {
1528
+ items {
1529
+ supplyShares
1530
+ borrowShares
1531
+ collateral
1532
+ market {
1533
+ uniqueKey
1534
+ loanAsset { symbol address decimals }
1535
+ collateralAsset { symbol address decimals }
1536
+ }
1537
+ }
1538
+ }
1539
+ }`;
1540
+ const resp = await import_axios2.default.post(MORPHO_API_URL2, { query: posQuery }, { timeout: 15e3 });
1541
+ const items = resp.data?.data?.marketPositions?.items ?? [];
1542
+ for (const item of items) {
1543
+ const supplyShares = BigInt(item.supplyShares ?? "0");
1544
+ const borrowShares = BigInt(item.borrowShares ?? "0");
1545
+ const collateral = BigInt(item.collateral ?? "0");
1546
+ if (collateral === 0n && borrowShares === 0n && supplyShares === 0n) continue;
1547
+ const m = item.market;
1548
+ if (!m?.collateralAsset || !m?.loanAsset) continue;
1330
1549
  const loanDecimals = m.loanAsset.decimals;
1331
1550
  let debt = 0n;
1332
- if (pos.borrowShares > 0n) {
1551
+ if (borrowShares > 0n) {
1333
1552
  try {
1334
1553
  const mkt = await this.morphoBlue.market(m.uniqueKey);
1335
1554
  const totalBorrowShares = BigInt(mkt.totalBorrowShares);
1336
1555
  const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
1337
- debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1556
+ debt = totalBorrowShares > 0n ? (borrowShares * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1338
1557
  totalDebtFloat += parseFloat(import_ethers2.ethers.formatUnits(debt, loanDecimals));
1339
1558
  } catch (e) {
1340
1559
  console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
@@ -1344,14 +1563,46 @@ var MorphoClient = class {
1344
1563
  marketId: m.uniqueKey,
1345
1564
  collateralToken: m.collateralAsset.symbol,
1346
1565
  loanToken: m.loanAsset.symbol,
1347
- collateral: import_ethers2.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
1348
- borrowShares: pos.borrowShares.toString(),
1349
- supplyShares: pos.supplyShares.toString(),
1566
+ collateral: import_ethers2.ethers.formatUnits(collateral, m.collateralAsset.decimals),
1567
+ borrowShares: borrowShares.toString(),
1568
+ supplyShares: supplyShares.toString(),
1350
1569
  debt: import_ethers2.ethers.formatUnits(debt, loanDecimals)
1351
1570
  });
1352
- } catch (e) {
1353
- console.warn(`[agether] position read failed for market:`, e instanceof Error ? e.message : e);
1354
- continue;
1571
+ }
1572
+ } catch (e) {
1573
+ console.warn("[agether] marketPositions API failed, falling back to market scan:", e instanceof Error ? e.message : e);
1574
+ const markets = await this.getMarkets();
1575
+ for (const m of markets) {
1576
+ if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
1577
+ try {
1578
+ const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
1579
+ if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
1580
+ const loanDecimals = m.loanAsset.decimals;
1581
+ let debt = 0n;
1582
+ if (pos.borrowShares > 0n) {
1583
+ try {
1584
+ const mkt = await this.morphoBlue.market(m.uniqueKey);
1585
+ const totalBorrowShares = BigInt(mkt.totalBorrowShares);
1586
+ const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
1587
+ debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1588
+ totalDebtFloat += parseFloat(import_ethers2.ethers.formatUnits(debt, loanDecimals));
1589
+ } catch (e2) {
1590
+ console.warn(`[agether] debt calc failed:`, e2 instanceof Error ? e2.message : e2);
1591
+ }
1592
+ }
1593
+ positions.push({
1594
+ marketId: m.uniqueKey,
1595
+ collateralToken: m.collateralAsset.symbol,
1596
+ loanToken: m.loanAsset.symbol,
1597
+ collateral: import_ethers2.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
1598
+ borrowShares: pos.borrowShares.toString(),
1599
+ supplyShares: pos.supplyShares.toString(),
1600
+ debt: import_ethers2.ethers.formatUnits(debt, loanDecimals)
1601
+ });
1602
+ } catch (e2) {
1603
+ console.warn(`[agether] position read failed:`, e2 instanceof Error ? e2.message : e2);
1604
+ continue;
1605
+ }
1355
1606
  }
1356
1607
  }
1357
1608
  return {
@@ -1505,7 +1756,7 @@ var MorphoClient = class {
1505
1756
  }
1506
1757
  }`;
1507
1758
  try {
1508
- const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
1759
+ const resp = await import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 });
1509
1760
  let items = resp.data?.data?.markets?.items ?? [];
1510
1761
  if (searchTerm && collateralSymbolOrAddress && !collateralSymbolOrAddress.startsWith("0x")) {
1511
1762
  const sym = collateralSymbolOrAddress.toUpperCase();
@@ -1571,7 +1822,7 @@ var MorphoClient = class {
1571
1822
  }
1572
1823
  }`;
1573
1824
  try {
1574
- const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
1825
+ const resp = await import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 });
1575
1826
  let items = resp.data?.data?.markets?.items ?? [];
1576
1827
  items = items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers2.ethers.ZeroAddress);
1577
1828
  const searchUpper = search.toUpperCase();
@@ -1964,7 +2215,7 @@ var MorphoClient = class {
1964
2215
  }
1965
2216
  }
1966
2217
  }`;
1967
- const posResp = await import_axios.default.post(MORPHO_API_URL, { query: positionsQuery }, { timeout: 15e3 });
2218
+ const posResp = await import_axios2.default.post(MORPHO_API_URL2, { query: positionsQuery }, { timeout: 15e3 });
1968
2219
  const user = posResp.data?.data?.userByAddress;
1969
2220
  if (!user?.marketPositions) return [];
1970
2221
  const activePositions = user.marketPositions.filter(
@@ -2638,6 +2889,46 @@ var MorphoClient = class {
2638
2889
  /** Find the first market where the agent has collateral deposited. */
2639
2890
  async _findActiveMarket() {
2640
2891
  const acctAddr = await this.getAccountAddress();
2892
+ const chainId = this.config.chainId;
2893
+ try {
2894
+ const posQuery = `{
2895
+ marketPositions(
2896
+ where: { userAddress_in: ["${acctAddr}"], chainId_in: [${chainId}] }
2897
+ first: 50
2898
+ ) {
2899
+ items {
2900
+ collateral
2901
+ market {
2902
+ uniqueKey
2903
+ oracleAddress
2904
+ irmAddress
2905
+ lltv
2906
+ loanAsset { address symbol decimals }
2907
+ collateralAsset { address symbol decimals }
2908
+ }
2909
+ }
2910
+ }
2911
+ }`;
2912
+ const resp = await import_axios2.default.post(MORPHO_API_URL2, { query: posQuery }, { timeout: 1e4 });
2913
+ const items = resp.data?.data?.marketPositions?.items ?? [];
2914
+ for (const item of items) {
2915
+ if (BigInt(item.collateral ?? "0") > 0n && item.market?.collateralAsset) {
2916
+ const m = item.market;
2917
+ return {
2918
+ params: {
2919
+ loanToken: m.loanAsset.address,
2920
+ collateralToken: m.collateralAsset.address,
2921
+ oracle: m.oracleAddress,
2922
+ irm: m.irmAddress,
2923
+ lltv: BigInt(m.lltv)
2924
+ },
2925
+ symbol: m.collateralAsset.symbol
2926
+ };
2927
+ }
2928
+ }
2929
+ } catch (e) {
2930
+ console.warn("[agether] _findActiveMarket GraphQL failed, falling back:", e instanceof Error ? e.message : e);
2931
+ }
2641
2932
  const markets = await this.getMarkets();
2642
2933
  for (const m of markets) {
2643
2934
  if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
@@ -2666,6 +2957,46 @@ var MorphoClient = class {
2666
2957
  /** Find the first market where the agent has a supply (lending) position. */
2667
2958
  async _findActiveSupplyMarket() {
2668
2959
  const acctAddr = await this.getAccountAddress();
2960
+ const chainId = this.config.chainId;
2961
+ try {
2962
+ const posQuery = `{
2963
+ marketPositions(
2964
+ where: { userAddress_in: ["${acctAddr}"], chainId_in: [${chainId}] }
2965
+ first: 50
2966
+ ) {
2967
+ items {
2968
+ supplyShares
2969
+ market {
2970
+ uniqueKey
2971
+ oracleAddress
2972
+ irmAddress
2973
+ lltv
2974
+ loanAsset { address symbol decimals }
2975
+ collateralAsset { address symbol decimals }
2976
+ }
2977
+ }
2978
+ }
2979
+ }`;
2980
+ const resp = await import_axios2.default.post(MORPHO_API_URL2, { query: posQuery }, { timeout: 1e4 });
2981
+ const items = resp.data?.data?.marketPositions?.items ?? [];
2982
+ for (const item of items) {
2983
+ if (BigInt(item.supplyShares ?? "0") > 0n && item.market?.collateralAsset) {
2984
+ const m = item.market;
2985
+ return {
2986
+ params: {
2987
+ loanToken: m.loanAsset.address,
2988
+ collateralToken: m.collateralAsset.address,
2989
+ oracle: m.oracleAddress,
2990
+ irm: m.irmAddress,
2991
+ lltv: BigInt(m.lltv)
2992
+ },
2993
+ symbol: m.collateralAsset.symbol
2994
+ };
2995
+ }
2996
+ }
2997
+ } catch (e) {
2998
+ console.warn("[agether] _findActiveSupplyMarket GraphQL failed, falling back:", e instanceof Error ? e.message : e);
2999
+ }
2669
3000
  const markets = await this.getMarkets();
2670
3001
  for (const m of markets) {
2671
3002
  if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
@@ -2798,7 +3129,7 @@ var MorphoClient = class {
2798
3129
  }
2799
3130
  }
2800
3131
  }`;
2801
- const resp = await import_axios.default.post(MORPHO_API_URL, { query: txQuery }, { timeout: 15e3 });
3132
+ const resp = await import_axios2.default.post(MORPHO_API_URL2, { query: txQuery }, { timeout: 15e3 });
2802
3133
  const txData = resp.data?.data?.transactions;
2803
3134
  if (!txData?.items) break;
2804
3135
  for (const tx of txData.items) {
@@ -2823,7 +3154,7 @@ var MorphoClient = class {
2823
3154
  };
2824
3155
 
2825
3156
  // src/clients/ScoringClient.ts
2826
- var import_axios2 = __toESM(require("axios"));
3157
+ var import_axios3 = __toESM(require("axios"));
2827
3158
 
2828
3159
  // src/clients/X402Client.ts
2829
3160
  var import_fetch = require("@x402/fetch");
@@ -3185,7 +3516,7 @@ var ScoringClient = class {
3185
3516
  constructor(config) {
3186
3517
  this.endpoint = config.endpoint;
3187
3518
  this.defaultChainId = config.chainId;
3188
- this.client = import_axios2.default.create({
3519
+ this.client = import_axios3.default.create({
3189
3520
  baseURL: config.endpoint,
3190
3521
  headers: { "Content-Type": "application/json" },
3191
3522
  timeout: 3e4