@agether/sdk 2.12.1 → 2.13.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.mjs CHANGED
@@ -54,6 +54,7 @@ var IDENTITY_REGISTRY_ABI = [
54
54
  "function exists(uint256 agentId) view returns (bool)",
55
55
  "function register() returns (uint256 agentId)",
56
56
  "function register(string agentURI) returns (uint256 agentId)",
57
+ "function setAgentURI(uint256 agentId, string newURI)",
57
58
  "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
58
59
  ];
59
60
  var AGETHER_4337_FACTORY_ABI = [
@@ -417,6 +418,23 @@ var AgetherClient = class _AgetherClient {
417
418
  }
418
419
  const acctAddr = await this.agether4337Factory.getAccount(agentId);
419
420
  this.accountAddress = acctAddr;
421
+ if (!acctExists) {
422
+ const updatedMeta = JSON.stringify({
423
+ type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
424
+ name: options?.name || "Unnamed Agent",
425
+ description: options?.description || "AI agent registered via @agether/sdk",
426
+ active: true,
427
+ wallet: `eip155:${this.config.chainId}:${acctAddr}`,
428
+ registrations: [{
429
+ agentId: Number(agentId),
430
+ agentRegistry: `eip155:${this.config.chainId}:${this.config.contracts.identityRegistry}`
431
+ }]
432
+ });
433
+ const finalURI = `data:application/json;base64,${Buffer.from(updatedMeta).toString("base64")}`;
434
+ const uriTx = await this.identityRegistry.setAgentURI(agentId, finalURI);
435
+ await uriTx.wait();
436
+ this._refreshSigner();
437
+ }
420
438
  const kyaRequired = await this.isKyaRequired();
421
439
  return {
422
440
  agentId: agentId.toString(),
@@ -433,7 +451,7 @@ var AgetherClient = class _AgetherClient {
433
451
  async _mintNewIdentity(name, description) {
434
452
  const registrationFile = JSON.stringify({
435
453
  type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
436
- name: name || "Unnamed Agent",
454
+ name: name || "Agether Agent",
437
455
  description: description || "AI agent registered via @agether/sdk",
438
456
  active: true,
439
457
  registrations: [{
@@ -944,8 +962,9 @@ var morphoIface = new ethers2.Interface(MORPHO_BLUE_ABI);
944
962
  var erc20Iface2 = new ethers2.Interface(ERC20_ABI);
945
963
  var MorphoClient = class {
946
964
  constructor(config) {
965
+ /** Market params cache: keyed by market uniqueKey (bytes32 hash) */
947
966
  this._marketCache = /* @__PURE__ */ new Map();
948
- /** Dynamic token registry: symbol (uppercase) → { address, symbol, decimals } */
967
+ /** Dynamic token registry: symbol (uppercase) or address (lowercase) → { address, symbol, decimals } */
949
968
  this._tokenCache = /* @__PURE__ */ new Map();
950
969
  this._discoveredAt = 0;
951
970
  if (!config.agentId) {
@@ -1048,21 +1067,23 @@ var MorphoClient = class {
1048
1067
  // Market Discovery (Morpho GraphQL API)
1049
1068
  // ════════════════════════════════════════════════════════
1050
1069
  /**
1051
- * Fetch USDC borrow markets on Base from Morpho API.
1052
- * Caches results for 5 minutes.
1070
+ * Fetch available markets on the current chain from Morpho API.
1071
+ * Caches results for 5 minutes. Supports all loan tokens (not just USDC).
1072
+ *
1073
+ * @param forceRefresh - bypass cache TTL
1074
+ * @param filter - optional filter by loan token and/or collateral token
1053
1075
  */
1054
- async getMarkets(forceRefresh = false) {
1076
+ async getMarkets(forceRefresh = false, filter) {
1055
1077
  if (!forceRefresh && this._discoveredMarkets && Date.now() - this._discoveredAt < 3e5) {
1056
- return this._discoveredMarkets;
1078
+ return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
1057
1079
  }
1058
1080
  const chainId = this.config.chainId;
1059
- const usdcAddr = this.config.contracts.usdc.toLowerCase();
1060
1081
  const query = `{
1061
1082
  markets(
1062
1083
  first: 50
1063
1084
  orderBy: SupplyAssetsUsd
1064
1085
  orderDirection: Desc
1065
- where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"] }
1086
+ where: { chainId_in: [${chainId}] }
1066
1087
  ) {
1067
1088
  items {
1068
1089
  uniqueKey
@@ -1095,14 +1116,14 @@ var MorphoClient = class {
1095
1116
  }));
1096
1117
  this._discoveredAt = Date.now();
1097
1118
  for (const mi of this._discoveredMarkets) {
1119
+ this._marketCache.set(mi.uniqueKey.toLowerCase(), {
1120
+ loanToken: mi.loanAsset.address,
1121
+ collateralToken: mi.collateralAsset.address,
1122
+ oracle: mi.oracle,
1123
+ irm: mi.irm,
1124
+ lltv: mi.lltv
1125
+ });
1098
1126
  if (mi.collateralAsset.address !== ethers2.ZeroAddress) {
1099
- this._marketCache.set(mi.collateralAsset.address.toLowerCase(), {
1100
- loanToken: mi.loanAsset.address,
1101
- collateralToken: mi.collateralAsset.address,
1102
- oracle: mi.oracle,
1103
- irm: mi.irm,
1104
- lltv: mi.lltv
1105
- });
1106
1127
  this._tokenCache.set(mi.collateralAsset.symbol.toUpperCase(), {
1107
1128
  address: mi.collateralAsset.address,
1108
1129
  symbol: mi.collateralAsset.symbol,
@@ -1127,17 +1148,24 @@ var MorphoClient = class {
1127
1148
  });
1128
1149
  }
1129
1150
  }
1130
- return this._discoveredMarkets;
1151
+ return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
1131
1152
  } catch (e) {
1132
1153
  console.warn("[agether] getMarkets failed, using cache:", e instanceof Error ? e.message : e);
1133
- return this._discoveredMarkets ?? [];
1154
+ const cached = this._discoveredMarkets ?? [];
1155
+ return filter ? this._applyMarketFilter(cached, filter) : cached;
1134
1156
  }
1135
1157
  }
1136
1158
  /**
1137
- * Get MarketParams for a collateral token.
1138
- * Tries cache → API → onchain idToMarketParams.
1159
+ * Get MarketParams for a collateral token (and optionally a specific loan token).
1160
+ * Tries cache → API discovery.
1161
+ *
1162
+ * When `loanTokenSymbolOrAddress` is omitted, returns the most liquid market
1163
+ * for that collateral (sorted by supply, typically the USDC market).
1164
+ *
1165
+ * @param collateralSymbolOrAddress - e.g. 'WETH', 'wstETH', or '0x4200...'
1166
+ * @param loanTokenSymbolOrAddress - e.g. 'USDC', 'WETH', or '0x833589...' (optional)
1139
1167
  */
1140
- async findMarketForCollateral(collateralSymbolOrAddress) {
1168
+ async findMarketForCollateral(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
1141
1169
  let colAddr;
1142
1170
  if (collateralSymbolOrAddress.startsWith("0x")) {
1143
1171
  colAddr = collateralSymbolOrAddress.toLowerCase();
@@ -1149,13 +1177,33 @@ var MorphoClient = class {
1149
1177
  colAddr = collateralSymbolOrAddress.toLowerCase();
1150
1178
  }
1151
1179
  }
1152
- const cached = this._marketCache.get(colAddr);
1153
- if (cached) return cached;
1154
- await this.getMarkets();
1155
- const fromApi = this._marketCache.get(colAddr);
1156
- if (fromApi) return fromApi;
1180
+ let loanAddr;
1181
+ if (loanTokenSymbolOrAddress) {
1182
+ if (loanTokenSymbolOrAddress.startsWith("0x")) {
1183
+ loanAddr = loanTokenSymbolOrAddress.toLowerCase();
1184
+ } else {
1185
+ try {
1186
+ const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
1187
+ loanAddr = resolved.address.toLowerCase();
1188
+ } catch {
1189
+ loanAddr = loanTokenSymbolOrAddress.toLowerCase();
1190
+ }
1191
+ }
1192
+ }
1193
+ if (!this._discoveredMarkets) await this.getMarkets();
1194
+ for (const m of this._discoveredMarkets ?? []) {
1195
+ if (m.collateralAsset.address.toLowerCase() !== colAddr) continue;
1196
+ if (loanAddr && m.loanAsset.address.toLowerCase() !== loanAddr) continue;
1197
+ return {
1198
+ loanToken: m.loanAsset.address,
1199
+ collateralToken: m.collateralAsset.address,
1200
+ oracle: m.oracle,
1201
+ irm: m.irm,
1202
+ lltv: m.lltv
1203
+ };
1204
+ }
1157
1205
  throw new AgetherError(
1158
- `No Morpho market found for collateral ${collateralSymbolOrAddress}`,
1206
+ `No Morpho market found for collateral ${collateralSymbolOrAddress}` + (loanTokenSymbolOrAddress ? ` with loan token ${loanTokenSymbolOrAddress}` : ""),
1159
1207
  "MARKET_NOT_FOUND"
1160
1208
  );
1161
1209
  }
@@ -1190,12 +1238,13 @@ var MorphoClient = class {
1190
1238
  const acctAddr = await this.getAccountAddress();
1191
1239
  const markets = await this.getMarkets();
1192
1240
  const positions = [];
1193
- let totalDebt = 0n;
1241
+ let totalDebtFloat = 0;
1194
1242
  for (const m of markets) {
1195
1243
  if (!m.collateralAsset || m.collateralAsset.address === ethers2.ZeroAddress) continue;
1196
1244
  try {
1197
1245
  const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
1198
1246
  if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
1247
+ const loanDecimals = m.loanAsset.decimals;
1199
1248
  let debt = 0n;
1200
1249
  if (pos.borrowShares > 0n) {
1201
1250
  try {
@@ -1203,7 +1252,7 @@ var MorphoClient = class {
1203
1252
  const totalBorrowShares = BigInt(mkt.totalBorrowShares);
1204
1253
  const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
1205
1254
  debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1206
- totalDebt += debt;
1255
+ totalDebtFloat += parseFloat(ethers2.formatUnits(debt, loanDecimals));
1207
1256
  } catch (e) {
1208
1257
  console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
1209
1258
  }
@@ -1211,10 +1260,11 @@ var MorphoClient = class {
1211
1260
  positions.push({
1212
1261
  marketId: m.uniqueKey,
1213
1262
  collateralToken: m.collateralAsset.symbol,
1263
+ loanToken: m.loanAsset.symbol,
1214
1264
  collateral: ethers2.formatUnits(pos.collateral, m.collateralAsset.decimals),
1215
1265
  borrowShares: pos.borrowShares.toString(),
1216
1266
  supplyShares: pos.supplyShares.toString(),
1217
- debt: ethers2.formatUnits(debt, 6)
1267
+ debt: ethers2.formatUnits(debt, loanDecimals)
1218
1268
  });
1219
1269
  } catch (e) {
1220
1270
  console.warn(`[agether] position read failed for market:`, e instanceof Error ? e.message : e);
@@ -1224,16 +1274,28 @@ var MorphoClient = class {
1224
1274
  return {
1225
1275
  agentId: this.agentId,
1226
1276
  agentAccount: acctAddr,
1227
- totalDebt: ethers2.formatUnits(totalDebt, 6),
1277
+ totalDebt: totalDebtFloat.toFixed(6),
1228
1278
  positions
1229
1279
  };
1230
1280
  }
1231
1281
  // ════════════════════════════════════════════════════════
1232
1282
  // Balance & Borrowing Capacity
1233
1283
  // ════════════════════════════════════════════════════════
1284
+ /**
1285
+ * Get the balance of any ERC-20 token in the AgentAccount.
1286
+ * @param symbolOrAddress - token symbol (e.g. 'USDC', 'WETH') or address
1287
+ * @returns balance in raw units
1288
+ */
1289
+ async getTokenBalance(symbolOrAddress) {
1290
+ const acctAddr = await this.getAccountAddress();
1291
+ const tokenInfo = await this._resolveToken(symbolOrAddress);
1292
+ const token = new Contract2(tokenInfo.address, ERC20_ABI, this.provider);
1293
+ return token.balanceOf(acctAddr);
1294
+ }
1234
1295
  /**
1235
1296
  * Get the USDC balance of the AgentAccount.
1236
1297
  * @returns USDC balance in raw units (6 decimals)
1298
+ * @deprecated Use `getTokenBalance('USDC')` instead.
1237
1299
  */
1238
1300
  async getUsdcBalance() {
1239
1301
  const acctAddr = await this.getAccountAddress();
@@ -1241,7 +1303,7 @@ var MorphoClient = class {
1241
1303
  return usdc.balanceOf(acctAddr);
1242
1304
  }
1243
1305
  /**
1244
- * Calculate the maximum additional USDC that can be borrowed
1306
+ * Calculate the maximum additional loan token that can be borrowed
1245
1307
  * given the agent's current collateral and debt across all markets.
1246
1308
  *
1247
1309
  * For each market with collateral deposited:
@@ -1249,7 +1311,7 @@ var MorphoClient = class {
1249
1311
  *
1250
1312
  * Uses the Morpho oracle to price collateral → loan token.
1251
1313
  *
1252
- * @returns Maximum additional USDC borrowable (6 decimals)
1314
+ * @returns Maximum additional borrowable per market (raw units in each market's loan token)
1253
1315
  */
1254
1316
  async getMaxBorrowable() {
1255
1317
  const acctAddr = await this.getAccountAddress();
@@ -1282,6 +1344,8 @@ var MorphoClient = class {
1282
1344
  totalAdditional += maxAdditional;
1283
1345
  byMarket.push({
1284
1346
  collateralToken: m.collateralAsset.symbol,
1347
+ loanToken: m.loanAsset.symbol,
1348
+ loanDecimals: m.loanAsset.decimals,
1285
1349
  maxAdditional,
1286
1350
  currentDebt,
1287
1351
  collateralValue: collateralValueInLoan
@@ -1297,14 +1361,16 @@ var MorphoClient = class {
1297
1361
  // Market Rates & Yield Estimation
1298
1362
  // ════════════════════════════════════════════════════════
1299
1363
  /**
1300
- * Fetch current supply/borrow APY for a collateral market from Morpho GraphQL API.
1364
+ * Fetch current supply/borrow APY for markets from Morpho GraphQL API.
1301
1365
  *
1302
1366
  * Note: On Morpho Blue, collateral does NOT earn yield directly. Supply APY
1303
1367
  * is what lenders earn; borrow APY is what borrowers pay.
1368
+ *
1369
+ * @param collateralSymbolOrAddress - filter by collateral token (optional)
1370
+ * @param loanTokenSymbolOrAddress - filter by loan token (optional). Omit for all loan tokens.
1304
1371
  */
1305
- async getMarketRates(collateralSymbolOrAddress) {
1372
+ async getMarketRates(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
1306
1373
  const chainId = this.config.chainId;
1307
- const usdcAddr = this.config.contracts.usdc.toLowerCase();
1308
1374
  let collateralFilter = "";
1309
1375
  if (collateralSymbolOrAddress) {
1310
1376
  let colAddr;
@@ -1320,12 +1386,27 @@ var MorphoClient = class {
1320
1386
  }
1321
1387
  collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
1322
1388
  }
1389
+ let loanFilter = "";
1390
+ if (loanTokenSymbolOrAddress) {
1391
+ let loanAddr;
1392
+ if (loanTokenSymbolOrAddress.startsWith("0x")) {
1393
+ loanAddr = loanTokenSymbolOrAddress.toLowerCase();
1394
+ } else {
1395
+ try {
1396
+ const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
1397
+ loanAddr = resolved.address.toLowerCase();
1398
+ } catch {
1399
+ loanAddr = loanTokenSymbolOrAddress.toLowerCase();
1400
+ }
1401
+ }
1402
+ loanFilter = `, loanAssetAddress_in: ["${loanAddr}"]`;
1403
+ }
1323
1404
  const query = `{
1324
1405
  markets(
1325
1406
  first: 50
1326
1407
  orderBy: SupplyAssetsUsd
1327
1408
  orderDirection: Desc
1328
- where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"]${collateralFilter} }
1409
+ where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter} }
1329
1410
  ) {
1330
1411
  items {
1331
1412
  uniqueKey
@@ -1345,17 +1426,21 @@ var MorphoClient = class {
1345
1426
  try {
1346
1427
  const resp = await axios.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
1347
1428
  const items = resp.data?.data?.markets?.items ?? [];
1348
- return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== ethers2.ZeroAddress).map((m) => ({
1349
- collateralToken: m.collateralAsset.symbol,
1350
- loanToken: m.loanAsset.symbol,
1351
- supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
1352
- borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
1353
- utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
1354
- totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 1e6 : 0,
1355
- totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 1e6 : 0,
1356
- lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
1357
- marketId: m.uniqueKey
1358
- }));
1429
+ return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== ethers2.ZeroAddress).map((m) => {
1430
+ const loanDecimals = m.loanAsset?.decimals ?? 18;
1431
+ return {
1432
+ collateralToken: m.collateralAsset.symbol,
1433
+ loanToken: m.loanAsset.symbol,
1434
+ loanDecimals,
1435
+ supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
1436
+ borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
1437
+ utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
1438
+ totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
1439
+ totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
1440
+ lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
1441
+ marketId: m.uniqueKey
1442
+ };
1443
+ });
1359
1444
  } catch (e) {
1360
1445
  console.warn("[agether] getMarketRates failed:", e instanceof Error ? e.message : e);
1361
1446
  return [];
@@ -1389,14 +1474,15 @@ var MorphoClient = class {
1389
1474
  } else {
1390
1475
  try {
1391
1476
  const params = await this.findMarketForCollateral(collateralSymbol);
1477
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1392
1478
  const oracleContract = new Contract2(params.oracle, [
1393
1479
  "function price() view returns (uint256)"
1394
1480
  ], this.provider);
1395
1481
  const oraclePrice = await oracleContract.price();
1396
1482
  const ORACLE_PRICE_SCALE = 10n ** 36n;
1397
1483
  const amountWei = ethers2.parseUnits(amount, colInfo.decimals);
1398
- const valueInUsdc = amountWei * oraclePrice / ORACLE_PRICE_SCALE;
1399
- collateralValueUsd = Number(valueInUsdc) / 1e6;
1484
+ const valueInLoan = amountWei * oraclePrice / ORACLE_PRICE_SCALE;
1485
+ collateralValueUsd = Number(valueInLoan) / 10 ** loanDecimals;
1400
1486
  } catch (e) {
1401
1487
  console.warn("[agether] oracle price fetch for yield estimation failed:", e instanceof Error ? e.message : e);
1402
1488
  throw new AgetherError("Cannot determine collateral value. Provide ethPriceUsd.", "PRICE_UNAVAILABLE");
@@ -1417,61 +1503,65 @@ var MorphoClient = class {
1417
1503
  // Supply-Side (Lending) — earn yield by supplying USDC
1418
1504
  // ════════════════════════════════════════════════════════
1419
1505
  /**
1420
- * Supply USDC to a Morpho Blue market as a lender (earn yield).
1506
+ * Supply loan token to a Morpho Blue market as a lender (earn yield).
1421
1507
  *
1422
1508
  * Unlike `supplyCollateral` (borrower-side), this is the **lender-side**:
1423
- * you deposit the loanToken (USDC) into the market's supply pool and earn
1509
+ * you deposit the loanToken into the market's supply pool and earn
1424
1510
  * interest paid by borrowers.
1425
1511
  *
1426
- * @param usdcAmount - Amount of USDC to supply (e.g. '500')
1512
+ * @param amount - Amount of loan token to supply (e.g. '500' for 500 USDC, '0.5' for 0.5 WETH)
1427
1513
  * @param collateralSymbol - Market collateral token to identify which market (e.g. 'WETH')
1428
1514
  * Optional — defaults to highest-APY market
1515
+ * @param loanTokenSymbol - Loan token to filter market (e.g. 'USDC', 'WETH'). Optional.
1429
1516
  */
1430
- async supplyAsset(usdcAmount, collateralSymbol) {
1517
+ async supplyAsset(amount, collateralSymbol, loanTokenSymbol) {
1431
1518
  const acctAddr = await this.getAccountAddress();
1432
- const amount = ethers2.parseUnits(usdcAmount, 6);
1433
1519
  const morphoAddr = this.config.contracts.morphoBlue;
1434
- const usdcAddr = this.config.contracts.usdc;
1435
1520
  let params;
1436
1521
  let usedCollateral;
1437
1522
  if (collateralSymbol) {
1438
- params = await this.findMarketForCollateral(collateralSymbol);
1523
+ params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
1439
1524
  usedCollateral = collateralSymbol;
1440
1525
  } else {
1441
- const rates = await this.getMarketRates();
1526
+ const rates = await this.getMarketRates(void 0, loanTokenSymbol);
1442
1527
  if (rates.length === 0) throw new AgetherError("No markets available", "NO_MARKETS");
1443
1528
  const best = rates.reduce((a, b) => a.supplyApy > b.supplyApy ? a : b);
1444
- params = await this.findMarketForCollateral(best.collateralToken);
1529
+ params = await this.findMarketForCollateral(best.collateralToken, loanTokenSymbol);
1445
1530
  usedCollateral = best.collateralToken;
1446
1531
  }
1532
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1533
+ const loanTokenAddr = params.loanToken;
1534
+ const parsedAmount = ethers2.parseUnits(amount, loanDecimals);
1447
1535
  const marketId = ethers2.keccak256(
1448
1536
  ethers2.AbiCoder.defaultAbiCoder().encode(
1449
1537
  ["address", "address", "address", "address", "uint256"],
1450
1538
  [params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
1451
1539
  )
1452
1540
  );
1453
- const usdcContract = new Contract2(usdcAddr, ERC20_ABI, this._signer);
1454
- const acctBalance = await usdcContract.balanceOf(acctAddr);
1455
- if (acctBalance < amount) {
1456
- const shortfall = amount - acctBalance;
1457
- const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
1541
+ const loanContract = new Contract2(loanTokenAddr, ERC20_ABI, this._signer);
1542
+ const acctBalance = await loanContract.balanceOf(acctAddr);
1543
+ if (acctBalance < parsedAmount) {
1544
+ const shortfall = parsedAmount - acctBalance;
1545
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
1458
1546
  if (eoaBalance < shortfall) {
1547
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
1548
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
1459
1549
  throw new AgetherError(
1460
- `Insufficient USDC. Need ${usdcAmount}, AgentAccount has ${ethers2.formatUnits(acctBalance, 6)}, EOA has ${ethers2.formatUnits(eoaBalance, 6)}.`,
1550
+ `Insufficient ${loanSymbol}. Need ${amount}, AgentAccount has ${ethers2.formatUnits(acctBalance, loanDecimals)}, EOA has ${ethers2.formatUnits(eoaBalance, loanDecimals)}.`,
1461
1551
  "INSUFFICIENT_BALANCE"
1462
1552
  );
1463
1553
  }
1464
- const transferTx = await usdcContract.transfer(acctAddr, shortfall);
1554
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
1465
1555
  await transferTx.wait();
1466
1556
  this._refreshSigner();
1467
1557
  }
1468
- const targets = [usdcAddr, morphoAddr];
1558
+ const targets = [loanTokenAddr, morphoAddr];
1469
1559
  const values = [0n, 0n];
1470
1560
  const datas = [
1471
- erc20Iface2.encodeFunctionData("approve", [morphoAddr, amount]),
1561
+ erc20Iface2.encodeFunctionData("approve", [morphoAddr, parsedAmount]),
1472
1562
  morphoIface.encodeFunctionData("supply", [
1473
1563
  this._toTuple(params),
1474
- amount,
1564
+ parsedAmount,
1475
1565
  0n,
1476
1566
  acctAddr,
1477
1567
  "0x"
@@ -1480,7 +1570,7 @@ var MorphoClient = class {
1480
1570
  const receipt = await this.batch(targets, values, datas);
1481
1571
  return {
1482
1572
  tx: receipt.hash,
1483
- amount: usdcAmount,
1573
+ amount,
1484
1574
  marketId,
1485
1575
  collateralToken: usedCollateral,
1486
1576
  agentAccount: acctAddr
@@ -1493,17 +1583,26 @@ var MorphoClient = class {
1493
1583
  * @param collateralSymbol - Market collateral to identify which market
1494
1584
  * @param receiver - Destination address (defaults to EOA)
1495
1585
  */
1496
- async withdrawSupply(usdcAmount, collateralSymbol, receiver) {
1586
+ /**
1587
+ * Withdraw supplied loan token (+ earned interest) from a Morpho Blue market.
1588
+ *
1589
+ * @param amount - Amount to withdraw (e.g. '100' or 'all' for full position)
1590
+ * @param collateralSymbol - Market collateral to identify which market
1591
+ * @param receiver - Destination address (defaults to EOA)
1592
+ * @param loanTokenSymbol - Loan token to filter market (optional)
1593
+ */
1594
+ async withdrawSupply(amount, collateralSymbol, receiver, loanTokenSymbol) {
1497
1595
  const acctAddr = await this.getAccountAddress();
1498
1596
  const morphoAddr = this.config.contracts.morphoBlue;
1499
1597
  const dest = receiver || await this.getSignerAddress();
1500
1598
  let params;
1501
1599
  if (collateralSymbol) {
1502
- params = await this.findMarketForCollateral(collateralSymbol);
1600
+ params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
1503
1601
  } else {
1504
1602
  const { params: p } = await this._findActiveSupplyMarket();
1505
1603
  params = p;
1506
1604
  }
1605
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1507
1606
  const marketId = ethers2.keccak256(
1508
1607
  ethers2.AbiCoder.defaultAbiCoder().encode(
1509
1608
  ["address", "address", "address", "address", "uint256"],
@@ -1512,13 +1611,13 @@ var MorphoClient = class {
1512
1611
  );
1513
1612
  let withdrawAssets;
1514
1613
  let withdrawShares;
1515
- if (usdcAmount === "all") {
1614
+ if (amount === "all") {
1516
1615
  const pos = await this.morphoBlue.position(marketId, acctAddr);
1517
1616
  withdrawShares = BigInt(pos.supplyShares);
1518
1617
  withdrawAssets = 0n;
1519
1618
  if (withdrawShares === 0n) throw new AgetherError("No supply position to withdraw", "NO_SUPPLY");
1520
1619
  } else {
1521
- withdrawAssets = ethers2.parseUnits(usdcAmount, 6);
1620
+ withdrawAssets = ethers2.parseUnits(amount, loanDecimals);
1522
1621
  withdrawShares = 0n;
1523
1622
  }
1524
1623
  const data = morphoIface.encodeFunctionData("withdraw", [
@@ -1536,13 +1635,13 @@ var MorphoClient = class {
1536
1635
  const totalSupplyAssets = BigInt(mkt.totalSupplyAssets);
1537
1636
  const totalSupplyShares = BigInt(mkt.totalSupplyShares);
1538
1637
  const currentAssets = totalSupplyShares > 0n ? BigInt(pos.supplyShares) * totalSupplyAssets / totalSupplyShares : 0n;
1539
- remainingSupply = ethers2.formatUnits(currentAssets, 6);
1638
+ remainingSupply = ethers2.formatUnits(currentAssets, loanDecimals);
1540
1639
  } catch (e) {
1541
1640
  console.warn("[agether] failed to read remaining supply:", e instanceof Error ? e.message : e);
1542
1641
  }
1543
1642
  return {
1544
1643
  tx: receipt.hash,
1545
- amount: usdcAmount,
1644
+ amount,
1546
1645
  remainingSupply,
1547
1646
  destination: dest
1548
1647
  };
@@ -1615,14 +1714,13 @@ var MorphoClient = class {
1615
1714
  * Computes available yield, verifies the requested amount doesn't exceed it,
1616
1715
  * then withdraws from the supply position and sends directly to the recipient.
1617
1716
  *
1618
- * @param recipient - Address to receive the USDC
1619
- * @param usdcAmount - Amount to pay from yield (e.g. '5.50')
1717
+ * @param recipient - Address to receive the loan token
1718
+ * @param amount - Amount to pay from yield (e.g. '5.50')
1620
1719
  * @param collateralSymbol - Market collateral to identify which supply position
1621
1720
  */
1622
- async payFromYield(recipient, usdcAmount, collateralSymbol) {
1721
+ async payFromYield(recipient, amount, collateralSymbol) {
1623
1722
  const acctAddr = await this.getAccountAddress();
1624
1723
  const morphoAddr = this.config.contracts.morphoBlue;
1625
- const amount = ethers2.parseUnits(usdcAmount, 6);
1626
1724
  const positions = await this.getSupplyPositions(collateralSymbol);
1627
1725
  if (positions.length === 0) {
1628
1726
  throw new AgetherError("No supply position found", "NO_SUPPLY");
@@ -1630,17 +1728,20 @@ var MorphoClient = class {
1630
1728
  const pos = positions.reduce(
1631
1729
  (a, b) => parseFloat(a.earnedYield) > parseFloat(b.earnedYield) ? a : b
1632
1730
  );
1633
- const availableYield = ethers2.parseUnits(pos.earnedYield, 6);
1634
- if (amount > availableYield) {
1731
+ const params = await this.findMarketForCollateral(pos.collateralToken, pos.loanToken);
1732
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1733
+ const parsedAmount = ethers2.parseUnits(amount, loanDecimals);
1734
+ const availableYield = ethers2.parseUnits(pos.earnedYield, loanDecimals);
1735
+ if (parsedAmount > availableYield) {
1736
+ const loanSymbol = pos.loanToken;
1635
1737
  throw new AgetherError(
1636
- `Requested ${usdcAmount} USDC exceeds available yield of ${pos.earnedYield} USDC. Use withdrawSupply to withdraw principal.`,
1738
+ `Requested ${amount} ${loanSymbol} exceeds available yield of ${pos.earnedYield} ${loanSymbol}. Use withdrawSupply to withdraw principal.`,
1637
1739
  "EXCEEDS_YIELD"
1638
1740
  );
1639
1741
  }
1640
- const params = await this.findMarketForCollateral(pos.collateralToken);
1641
1742
  const data = morphoIface.encodeFunctionData("withdraw", [
1642
1743
  this._toTuple(params),
1643
- amount,
1744
+ parsedAmount,
1644
1745
  0n,
1645
1746
  acctAddr,
1646
1747
  recipient
@@ -1659,7 +1760,7 @@ var MorphoClient = class {
1659
1760
  }
1660
1761
  return {
1661
1762
  tx: receipt.hash,
1662
- yieldWithdrawn: usdcAmount,
1763
+ yieldWithdrawn: amount,
1663
1764
  recipient,
1664
1765
  remainingYield,
1665
1766
  remainingSupply
@@ -1717,28 +1818,31 @@ var MorphoClient = class {
1717
1818
  };
1718
1819
  }
1719
1820
  /**
1720
- * Borrow USDC against existing collateral.
1821
+ * Borrow loan token against existing collateral.
1721
1822
  *
1722
1823
  * AgentAccount.execute: Morpho.borrow(params, amount, 0, account, account)
1723
1824
  *
1724
- * @param usdcAmount - USDC amount (e.g. '100')
1825
+ * @param amount - Loan token amount (e.g. '100' for 100 USDC, '0.5' for 0.5 WETH)
1725
1826
  * @param tokenSymbol - collateral symbol to identify which market (default: first with collateral)
1827
+ * @param marketParams - explicit market params (optional)
1828
+ * @param loanTokenSymbol - loan token to filter market (optional, e.g. 'USDC', 'WETH')
1726
1829
  */
1727
- async borrow(usdcAmount, tokenSymbol, marketParams) {
1830
+ async borrow(amount, tokenSymbol, marketParams, loanTokenSymbol) {
1728
1831
  const acctAddr = await this.getAccountAddress();
1729
- const amount = ethers2.parseUnits(usdcAmount, 6);
1730
1832
  const morphoAddr = this.config.contracts.morphoBlue;
1731
1833
  let params;
1732
1834
  let usedToken = tokenSymbol || "WETH";
1733
1835
  if (marketParams) {
1734
1836
  params = marketParams;
1735
1837
  } else if (tokenSymbol) {
1736
- params = await this.findMarketForCollateral(tokenSymbol);
1838
+ params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1737
1839
  } else {
1738
1840
  const { params: p, symbol } = await this._findActiveMarket();
1739
1841
  params = p;
1740
1842
  usedToken = symbol;
1741
1843
  }
1844
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1845
+ const parsedAmount = ethers2.parseUnits(amount, loanDecimals);
1742
1846
  try {
1743
1847
  const marketId = ethers2.keccak256(
1744
1848
  ethers2.AbiCoder.defaultAbiCoder().encode(
@@ -1762,11 +1866,14 @@ var MorphoClient = class {
1762
1866
  const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
1763
1867
  const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1764
1868
  const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
1765
- if (amount > maxAdditional) {
1766
- const maxUsd = ethers2.formatUnits(maxAdditional, 6);
1767
- const colFormatted = ethers2.formatUnits(pos.collateral, 18);
1869
+ if (parsedAmount > maxAdditional) {
1870
+ const loanInfo = this._tokenCache.get(params.loanToken.toLowerCase());
1871
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
1872
+ const colInfo = await this._resolveToken(usedToken);
1873
+ const maxFormatted = ethers2.formatUnits(maxAdditional, loanDecimals);
1874
+ const colFormatted = ethers2.formatUnits(pos.collateral, colInfo.decimals);
1768
1875
  throw new AgetherError(
1769
- `Borrow of $${usdcAmount} USDC exceeds max borrowable $${maxUsd} USDC (collateral: ${colFormatted} ${usedToken}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Deposit more collateral or reduce borrow amount.`,
1876
+ `Borrow of ${amount} ${loanSymbol} exceeds max borrowable ${maxFormatted} ${loanSymbol} (collateral: ${colFormatted} ${usedToken}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Deposit more collateral or reduce borrow amount.`,
1770
1877
  "EXCEEDS_MAX_LTV"
1771
1878
  );
1772
1879
  }
@@ -1776,7 +1883,7 @@ var MorphoClient = class {
1776
1883
  }
1777
1884
  const data = morphoIface.encodeFunctionData("borrow", [
1778
1885
  this._toTuple(params),
1779
- amount,
1886
+ parsedAmount,
1780
1887
  0n,
1781
1888
  acctAddr,
1782
1889
  acctAddr
@@ -1784,7 +1891,7 @@ var MorphoClient = class {
1784
1891
  const receipt = await this.exec(morphoAddr, data);
1785
1892
  return {
1786
1893
  tx: receipt.hash,
1787
- amount: usdcAmount,
1894
+ amount,
1788
1895
  collateralToken: usedToken,
1789
1896
  agentAccount: acctAddr
1790
1897
  };
@@ -1792,17 +1899,27 @@ var MorphoClient = class {
1792
1899
  /**
1793
1900
  * Deposit collateral AND borrow USDC in one batched transaction.
1794
1901
  *
1902
+ /**
1903
+ * Deposit collateral AND borrow loan token in one batched transaction.
1904
+ *
1795
1905
  * AgentAccount.executeBatch:
1796
1906
  * [collateral.approve, Morpho.supplyCollateral, Morpho.borrow]
1797
1907
  *
1798
1908
  * The collateral must be transferred to AgentAccount first.
1909
+ *
1910
+ * @param tokenSymbol - collateral token symbol (e.g. 'WETH')
1911
+ * @param collateralAmount - amount of collateral (e.g. '0.05')
1912
+ * @param borrowAmount - amount of loan token to borrow (e.g. '100')
1913
+ * @param marketParams - explicit market params (optional)
1914
+ * @param loanTokenSymbol - loan token to filter market (optional)
1799
1915
  */
1800
- async depositAndBorrow(tokenSymbol, collateralAmount, borrowUsdcAmount, marketParams) {
1916
+ async depositAndBorrow(tokenSymbol, collateralAmount, borrowAmount, marketParams, loanTokenSymbol) {
1801
1917
  const acctAddr = await this.getAccountAddress();
1802
1918
  const colInfo = await this._resolveToken(tokenSymbol);
1803
- const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
1919
+ const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1920
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1804
1921
  const colWei = ethers2.parseUnits(collateralAmount, colInfo.decimals);
1805
- const borrowWei = ethers2.parseUnits(borrowUsdcAmount, 6);
1922
+ const borrowWei = ethers2.parseUnits(borrowAmount, loanDecimals);
1806
1923
  const morphoAddr = this.config.contracts.morphoBlue;
1807
1924
  try {
1808
1925
  const marketId = ethers2.keccak256(
@@ -1823,9 +1940,11 @@ var MorphoClient = class {
1823
1940
  const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1824
1941
  const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
1825
1942
  if (borrowWei > maxAdditional) {
1826
- const maxUsd = ethers2.formatUnits(maxAdditional, 6);
1943
+ const loanInfo = this._tokenCache.get(params.loanToken.toLowerCase());
1944
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
1945
+ const maxFormatted = ethers2.formatUnits(maxAdditional, loanDecimals);
1827
1946
  throw new AgetherError(
1828
- `Borrow of $${borrowUsdcAmount} USDC exceeds max borrowable $${maxUsd} USDC (total collateral: ${ethers2.formatUnits(totalCollateral, colInfo.decimals)} ${tokenSymbol}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Reduce borrow or increase collateral.`,
1947
+ `Borrow of ${borrowAmount} ${loanSymbol} exceeds max borrowable ${maxFormatted} ${loanSymbol} (total collateral: ${ethers2.formatUnits(totalCollateral, colInfo.decimals)} ${tokenSymbol}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Reduce borrow or increase collateral.`,
1829
1948
  "EXCEEDS_MAX_LTV"
1830
1949
  );
1831
1950
  }
@@ -1871,36 +1990,42 @@ var MorphoClient = class {
1871
1990
  tx: receipt.hash,
1872
1991
  collateralToken: tokenSymbol,
1873
1992
  collateralAmount,
1874
- borrowAmount: borrowUsdcAmount,
1993
+ borrowAmount,
1875
1994
  agentAccount: acctAddr
1876
1995
  };
1877
1996
  }
1878
1997
  /**
1879
- * Repay borrowed USDC from AgentAccount.
1998
+ * Repay borrowed loan token from AgentAccount.
1880
1999
  *
1881
2000
  * AgentAccount.executeBatch:
1882
- * [USDC.approve(MorphoBlue), Morpho.repay(params)]
2001
+ * [loanToken.approve(MorphoBlue), Morpho.repay(params)]
2002
+ *
2003
+ * @param amount - loan token amount to repay (e.g. '50' or 'all' for full repayment)
2004
+ * @param tokenSymbol - collateral symbol to identify which market (optional)
2005
+ * @param marketParams - explicit market params (optional)
2006
+ * @param loanTokenSymbol - loan token to filter market (optional)
1883
2007
  */
1884
- async repay(usdcAmount, tokenSymbol, marketParams) {
2008
+ async repay(amount, tokenSymbol, marketParams, loanTokenSymbol) {
1885
2009
  const acctAddr = await this.getAccountAddress();
1886
2010
  const morphoAddr = this.config.contracts.morphoBlue;
1887
- const usdcAddr = this.config.contracts.usdc;
1888
2011
  let params;
1889
2012
  if (marketParams) {
1890
2013
  params = marketParams;
1891
2014
  } else if (tokenSymbol) {
1892
- params = await this.findMarketForCollateral(tokenSymbol);
2015
+ params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1893
2016
  } else {
1894
2017
  const { params: p } = await this._findActiveMarket();
1895
2018
  params = p;
1896
2019
  }
2020
+ const loanTokenAddr = params.loanToken;
2021
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1897
2022
  let repayAssets;
1898
2023
  let repayShares;
1899
2024
  let approveAmount;
1900
- if (usdcAmount === "all") {
2025
+ if (amount === "all") {
1901
2026
  const markets = await this.getMarkets();
1902
2027
  const mkt = markets.find(
1903
- (m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase()
2028
+ (m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase() && m.loanAsset?.address.toLowerCase() === params.loanToken.toLowerCase()
1904
2029
  );
1905
2030
  if (mkt) {
1906
2031
  const pos = await this.morphoBlue.position(mkt.uniqueKey, acctAddr);
@@ -1910,33 +2035,35 @@ var MorphoClient = class {
1910
2035
  const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
1911
2036
  const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
1912
2037
  const estimated = totalBorrowShares > 0n ? repayShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
1913
- approveAmount = estimated > 0n ? estimated : ethers2.parseUnits("1", 6);
2038
+ approveAmount = estimated > 0n ? estimated : ethers2.parseUnits("1", loanDecimals);
1914
2039
  } else {
1915
- repayAssets = ethers2.parseUnits("999999", 6);
2040
+ repayAssets = ethers2.parseUnits("999999", loanDecimals);
1916
2041
  repayShares = 0n;
1917
2042
  approveAmount = repayAssets;
1918
2043
  }
1919
2044
  } else {
1920
- repayAssets = ethers2.parseUnits(usdcAmount, 6);
2045
+ repayAssets = ethers2.parseUnits(amount, loanDecimals);
1921
2046
  repayShares = 0n;
1922
2047
  approveAmount = repayAssets;
1923
2048
  }
1924
- const usdcContract = new Contract2(usdcAddr, ERC20_ABI, this._signer);
1925
- const acctBalance = await usdcContract.balanceOf(acctAddr);
2049
+ const loanContract = new Contract2(loanTokenAddr, ERC20_ABI, this._signer);
2050
+ const acctBalance = await loanContract.balanceOf(acctAddr);
1926
2051
  if (acctBalance < approveAmount) {
1927
2052
  const shortfall = approveAmount - acctBalance;
1928
- const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
2053
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
1929
2054
  if (eoaBalance < shortfall) {
2055
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
2056
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
1930
2057
  throw new AgetherError(
1931
- `Insufficient USDC for repay. Need ${ethers2.formatUnits(approveAmount, 6)} USDC, AgentAccount has ${ethers2.formatUnits(acctBalance, 6)}, EOA has ${ethers2.formatUnits(eoaBalance, 6)}.`,
2058
+ `Insufficient ${loanSymbol} for repay. Need ${ethers2.formatUnits(approveAmount, loanDecimals)}, AgentAccount has ${ethers2.formatUnits(acctBalance, loanDecimals)}, EOA has ${ethers2.formatUnits(eoaBalance, loanDecimals)}.`,
1932
2059
  "INSUFFICIENT_BALANCE"
1933
2060
  );
1934
2061
  }
1935
- const transferTx = await usdcContract.transfer(acctAddr, shortfall);
2062
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
1936
2063
  await transferTx.wait();
1937
2064
  this._refreshSigner();
1938
2065
  }
1939
- const targets = [usdcAddr, morphoAddr];
2066
+ const targets = [loanTokenAddr, morphoAddr];
1940
2067
  const values = [0n, 0n];
1941
2068
  const datas = [
1942
2069
  erc20Iface2.encodeFunctionData("approve", [morphoAddr, approveAmount]),
@@ -1956,7 +2083,7 @@ var MorphoClient = class {
1956
2083
  } catch (e) {
1957
2084
  console.warn("[agether] failed to read remaining debt after repay:", e instanceof Error ? e.message : e);
1958
2085
  }
1959
- return { tx: receipt.hash, amount: usdcAmount, remainingDebt };
2086
+ return { tx: receipt.hash, amount, remainingDebt };
1960
2087
  }
1961
2088
  /**
1962
2089
  * Withdraw collateral from Morpho Blue.
@@ -1970,12 +2097,13 @@ var MorphoClient = class {
1970
2097
  const colInfo = await this._resolveToken(tokenSymbol);
1971
2098
  const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
1972
2099
  const morphoAddr = this.config.contracts.morphoBlue;
1973
- const usdcAddr = this.config.contracts.usdc;
2100
+ const loanTokenAddr = params.loanToken;
2101
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1974
2102
  const dest = receiver || await this.getSignerAddress();
1975
2103
  let weiAmount;
1976
2104
  const markets = await this.getMarkets();
1977
2105
  const market = markets.find(
1978
- (m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase()
2106
+ (m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase() && m.loanAsset?.address.toLowerCase() === loanTokenAddr.toLowerCase()
1979
2107
  );
1980
2108
  if (amount === "all") {
1981
2109
  if (!market) throw new AgetherError("Market not found", "MARKET_NOT_FOUND");
@@ -1998,8 +2126,10 @@ var MorphoClient = class {
1998
2126
  const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
1999
2127
  const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
2000
2128
  const estimated = totalBorrowShares > 0n ? dustBorrowShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
2001
- dustApproveAmount = estimated > 0n ? estimated : ethers2.parseUnits("1", 6);
2002
- console.log(`[agether] dust borrow shares detected: ${dustBorrowShares} shares \u2248 ${ethers2.formatUnits(dustApproveAmount, 6)} USDC \u2014 auto-repaying before withdraw`);
2129
+ dustApproveAmount = estimated > 0n ? estimated : ethers2.parseUnits("1", loanDecimals);
2130
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
2131
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
2132
+ console.log(`[agether] dust borrow shares detected: ${dustBorrowShares} shares \u2248 ${ethers2.formatUnits(dustApproveAmount, loanDecimals)} ${loanSymbol} \u2014 auto-repaying before withdraw`);
2003
2133
  }
2004
2134
  } catch (e) {
2005
2135
  console.warn("[agether] failed to check borrow shares before withdraw:", e instanceof Error ? e.message : e);
@@ -2013,19 +2143,21 @@ var MorphoClient = class {
2013
2143
  ]);
2014
2144
  let receipt;
2015
2145
  if (hasDustDebt) {
2016
- const usdcContract = new Contract2(usdcAddr, ERC20_ABI, this._signer);
2017
- const acctBalance = await usdcContract.balanceOf(acctAddr);
2146
+ const loanContract = new Contract2(loanTokenAddr, ERC20_ABI, this._signer);
2147
+ const acctBalance = await loanContract.balanceOf(acctAddr);
2018
2148
  if (acctBalance < dustApproveAmount) {
2019
2149
  const shortfall = dustApproveAmount - acctBalance;
2020
- const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
2150
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
2021
2151
  if (eoaBalance >= shortfall) {
2022
- console.log(`[agether] transferring ${ethers2.formatUnits(shortfall, 6)} USDC from EOA \u2192 AgentAccount for dust repay`);
2023
- const transferTx = await usdcContract.transfer(acctAddr, shortfall);
2152
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
2153
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
2154
+ console.log(`[agether] transferring ${ethers2.formatUnits(shortfall, loanDecimals)} ${loanSymbol} from EOA \u2192 AgentAccount for dust repay`);
2155
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
2024
2156
  await transferTx.wait();
2025
2157
  this._refreshSigner();
2026
2158
  }
2027
2159
  }
2028
- const targets = [usdcAddr, morphoAddr, morphoAddr];
2160
+ const targets = [loanTokenAddr, morphoAddr, morphoAddr];
2029
2161
  const values = [0n, 0n, 0n];
2030
2162
  const datas = [
2031
2163
  erc20Iface2.encodeFunctionData("approve", [morphoAddr, dustApproveAmount]),
@@ -2270,6 +2402,37 @@ var MorphoClient = class {
2270
2402
  }
2271
2403
  throw new AgetherError("No active supply position found", "NO_SUPPLY");
2272
2404
  }
2405
+ /**
2406
+ * Resolve loan token decimals from market params.
2407
+ * Uses the `_tokenCache` populated by `getMarkets()`.
2408
+ */
2409
+ async _getLoanTokenDecimals(params) {
2410
+ const tokenInfo = this._tokenCache.get(params.loanToken.toLowerCase());
2411
+ if (tokenInfo) return tokenInfo.decimals;
2412
+ await this.getMarkets();
2413
+ const fromApi = this._tokenCache.get(params.loanToken.toLowerCase());
2414
+ return fromApi?.decimals ?? 18;
2415
+ }
2416
+ /**
2417
+ * Apply client-side filter to discovered markets.
2418
+ */
2419
+ _applyMarketFilter(markets, filter) {
2420
+ return markets.filter((m) => {
2421
+ if (filter.loanToken) {
2422
+ const loanAddr = filter.loanToken.toLowerCase();
2423
+ if (m.loanAsset.address.toLowerCase() !== loanAddr && m.loanAsset.symbol.toUpperCase() !== filter.loanToken.toUpperCase()) {
2424
+ return false;
2425
+ }
2426
+ }
2427
+ if (filter.collateralToken) {
2428
+ const colAddr = filter.collateralToken.toLowerCase();
2429
+ if (m.collateralAsset.address.toLowerCase() !== colAddr && m.collateralAsset.symbol.toUpperCase() !== filter.collateralToken.toUpperCase()) {
2430
+ return false;
2431
+ }
2432
+ }
2433
+ return true;
2434
+ });
2435
+ }
2273
2436
  /**
2274
2437
  * Resolve a token symbol or address to { address, symbol, decimals }.
2275
2438
  *
@@ -2286,8 +2449,8 @@ var MorphoClient = class {
2286
2449
  const fromApi = this._tokenCache.get(key);
2287
2450
  if (fromApi) return fromApi;
2288
2451
  throw new AgetherError(
2289
- `Unknown token: ${symbolOrAddress}. No Morpho market found with this collateral.`,
2290
- "UNKNOWN_COLLATERAL"
2452
+ `Unknown token: ${symbolOrAddress}. No Morpho market found with this token.`,
2453
+ "UNKNOWN_TOKEN"
2291
2454
  );
2292
2455
  }
2293
2456
  /**