@agether/sdk 2.12.2 → 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.js CHANGED
@@ -494,25 +494,21 @@ var AgetherClient = class _AgetherClient {
494
494
  const acctAddr = await this.agether4337Factory.getAccount(agentId);
495
495
  this.accountAddress = acctAddr;
496
496
  if (!acctExists) {
497
- try {
498
- const updatedMeta = JSON.stringify({
499
- type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
500
- name: options?.name || "Unnamed Agent",
501
- description: options?.description || "AI agent registered via @agether/sdk",
502
- active: true,
503
- wallet: `eip155:${this.config.chainId}:${acctAddr}`,
504
- registrations: [{
505
- agentId: Number(agentId),
506
- agentRegistry: `eip155:${this.config.chainId}:${this.config.contracts.identityRegistry}`
507
- }]
508
- });
509
- const finalURI = `data:application/json;base64,${Buffer.from(updatedMeta).toString("base64")}`;
510
- const uriTx = await this.identityRegistry.setAgentURI(agentId, finalURI);
511
- await uriTx.wait();
512
- this._refreshSigner();
513
- } catch (e) {
514
- console.warn("[agether] setAgentURI failed (non-fatal):", e instanceof Error ? e.message : e);
515
- }
497
+ const updatedMeta = JSON.stringify({
498
+ type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
499
+ name: options?.name || "Unnamed Agent",
500
+ description: options?.description || "AI agent registered via @agether/sdk",
501
+ active: true,
502
+ wallet: `eip155:${this.config.chainId}:${acctAddr}`,
503
+ registrations: [{
504
+ agentId: Number(agentId),
505
+ agentRegistry: `eip155:${this.config.chainId}:${this.config.contracts.identityRegistry}`
506
+ }]
507
+ });
508
+ const finalURI = `data:application/json;base64,${Buffer.from(updatedMeta).toString("base64")}`;
509
+ const uriTx = await this.identityRegistry.setAgentURI(agentId, finalURI);
510
+ await uriTx.wait();
511
+ this._refreshSigner();
516
512
  }
517
513
  const kyaRequired = await this.isKyaRequired();
518
514
  return {
@@ -530,7 +526,7 @@ var AgetherClient = class _AgetherClient {
530
526
  async _mintNewIdentity(name, description) {
531
527
  const registrationFile = JSON.stringify({
532
528
  type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
533
- name: name || "Unnamed Agent",
529
+ name: name || "Agether Agent",
534
530
  description: description || "AI agent registered via @agether/sdk",
535
531
  active: true,
536
532
  registrations: [{
@@ -1041,8 +1037,9 @@ var morphoIface = new import_ethers2.ethers.Interface(MORPHO_BLUE_ABI);
1041
1037
  var erc20Iface2 = new import_ethers2.ethers.Interface(ERC20_ABI);
1042
1038
  var MorphoClient = class {
1043
1039
  constructor(config) {
1040
+ /** Market params cache: keyed by market uniqueKey (bytes32 hash) */
1044
1041
  this._marketCache = /* @__PURE__ */ new Map();
1045
- /** Dynamic token registry: symbol (uppercase) → { address, symbol, decimals } */
1042
+ /** Dynamic token registry: symbol (uppercase) or address (lowercase) → { address, symbol, decimals } */
1046
1043
  this._tokenCache = /* @__PURE__ */ new Map();
1047
1044
  this._discoveredAt = 0;
1048
1045
  if (!config.agentId) {
@@ -1145,21 +1142,23 @@ var MorphoClient = class {
1145
1142
  // Market Discovery (Morpho GraphQL API)
1146
1143
  // ════════════════════════════════════════════════════════
1147
1144
  /**
1148
- * Fetch USDC borrow markets on Base from Morpho API.
1149
- * Caches results for 5 minutes.
1145
+ * Fetch available markets on the current chain from Morpho API.
1146
+ * Caches results for 5 minutes. Supports all loan tokens (not just USDC).
1147
+ *
1148
+ * @param forceRefresh - bypass cache TTL
1149
+ * @param filter - optional filter by loan token and/or collateral token
1150
1150
  */
1151
- async getMarkets(forceRefresh = false) {
1151
+ async getMarkets(forceRefresh = false, filter) {
1152
1152
  if (!forceRefresh && this._discoveredMarkets && Date.now() - this._discoveredAt < 3e5) {
1153
- return this._discoveredMarkets;
1153
+ return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
1154
1154
  }
1155
1155
  const chainId = this.config.chainId;
1156
- const usdcAddr = this.config.contracts.usdc.toLowerCase();
1157
1156
  const query = `{
1158
1157
  markets(
1159
1158
  first: 50
1160
1159
  orderBy: SupplyAssetsUsd
1161
1160
  orderDirection: Desc
1162
- where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"] }
1161
+ where: { chainId_in: [${chainId}] }
1163
1162
  ) {
1164
1163
  items {
1165
1164
  uniqueKey
@@ -1192,14 +1191,14 @@ var MorphoClient = class {
1192
1191
  }));
1193
1192
  this._discoveredAt = Date.now();
1194
1193
  for (const mi of this._discoveredMarkets) {
1194
+ this._marketCache.set(mi.uniqueKey.toLowerCase(), {
1195
+ loanToken: mi.loanAsset.address,
1196
+ collateralToken: mi.collateralAsset.address,
1197
+ oracle: mi.oracle,
1198
+ irm: mi.irm,
1199
+ lltv: mi.lltv
1200
+ });
1195
1201
  if (mi.collateralAsset.address !== import_ethers2.ethers.ZeroAddress) {
1196
- this._marketCache.set(mi.collateralAsset.address.toLowerCase(), {
1197
- loanToken: mi.loanAsset.address,
1198
- collateralToken: mi.collateralAsset.address,
1199
- oracle: mi.oracle,
1200
- irm: mi.irm,
1201
- lltv: mi.lltv
1202
- });
1203
1202
  this._tokenCache.set(mi.collateralAsset.symbol.toUpperCase(), {
1204
1203
  address: mi.collateralAsset.address,
1205
1204
  symbol: mi.collateralAsset.symbol,
@@ -1224,17 +1223,24 @@ var MorphoClient = class {
1224
1223
  });
1225
1224
  }
1226
1225
  }
1227
- return this._discoveredMarkets;
1226
+ return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
1228
1227
  } catch (e) {
1229
1228
  console.warn("[agether] getMarkets failed, using cache:", e instanceof Error ? e.message : e);
1230
- return this._discoveredMarkets ?? [];
1229
+ const cached = this._discoveredMarkets ?? [];
1230
+ return filter ? this._applyMarketFilter(cached, filter) : cached;
1231
1231
  }
1232
1232
  }
1233
1233
  /**
1234
- * Get MarketParams for a collateral token.
1235
- * Tries cache → API → onchain idToMarketParams.
1234
+ * Get MarketParams for a collateral token (and optionally a specific loan token).
1235
+ * Tries cache → API discovery.
1236
+ *
1237
+ * When `loanTokenSymbolOrAddress` is omitted, returns the most liquid market
1238
+ * for that collateral (sorted by supply, typically the USDC market).
1239
+ *
1240
+ * @param collateralSymbolOrAddress - e.g. 'WETH', 'wstETH', or '0x4200...'
1241
+ * @param loanTokenSymbolOrAddress - e.g. 'USDC', 'WETH', or '0x833589...' (optional)
1236
1242
  */
1237
- async findMarketForCollateral(collateralSymbolOrAddress) {
1243
+ async findMarketForCollateral(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
1238
1244
  let colAddr;
1239
1245
  if (collateralSymbolOrAddress.startsWith("0x")) {
1240
1246
  colAddr = collateralSymbolOrAddress.toLowerCase();
@@ -1246,13 +1252,33 @@ var MorphoClient = class {
1246
1252
  colAddr = collateralSymbolOrAddress.toLowerCase();
1247
1253
  }
1248
1254
  }
1249
- const cached = this._marketCache.get(colAddr);
1250
- if (cached) return cached;
1251
- await this.getMarkets();
1252
- const fromApi = this._marketCache.get(colAddr);
1253
- if (fromApi) return fromApi;
1255
+ let loanAddr;
1256
+ if (loanTokenSymbolOrAddress) {
1257
+ if (loanTokenSymbolOrAddress.startsWith("0x")) {
1258
+ loanAddr = loanTokenSymbolOrAddress.toLowerCase();
1259
+ } else {
1260
+ try {
1261
+ const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
1262
+ loanAddr = resolved.address.toLowerCase();
1263
+ } catch {
1264
+ loanAddr = loanTokenSymbolOrAddress.toLowerCase();
1265
+ }
1266
+ }
1267
+ }
1268
+ if (!this._discoveredMarkets) await this.getMarkets();
1269
+ for (const m of this._discoveredMarkets ?? []) {
1270
+ if (m.collateralAsset.address.toLowerCase() !== colAddr) continue;
1271
+ if (loanAddr && m.loanAsset.address.toLowerCase() !== loanAddr) continue;
1272
+ return {
1273
+ loanToken: m.loanAsset.address,
1274
+ collateralToken: m.collateralAsset.address,
1275
+ oracle: m.oracle,
1276
+ irm: m.irm,
1277
+ lltv: m.lltv
1278
+ };
1279
+ }
1254
1280
  throw new AgetherError(
1255
- `No Morpho market found for collateral ${collateralSymbolOrAddress}`,
1281
+ `No Morpho market found for collateral ${collateralSymbolOrAddress}` + (loanTokenSymbolOrAddress ? ` with loan token ${loanTokenSymbolOrAddress}` : ""),
1256
1282
  "MARKET_NOT_FOUND"
1257
1283
  );
1258
1284
  }
@@ -1287,12 +1313,13 @@ var MorphoClient = class {
1287
1313
  const acctAddr = await this.getAccountAddress();
1288
1314
  const markets = await this.getMarkets();
1289
1315
  const positions = [];
1290
- let totalDebt = 0n;
1316
+ let totalDebtFloat = 0;
1291
1317
  for (const m of markets) {
1292
1318
  if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
1293
1319
  try {
1294
1320
  const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
1295
1321
  if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
1322
+ const loanDecimals = m.loanAsset.decimals;
1296
1323
  let debt = 0n;
1297
1324
  if (pos.borrowShares > 0n) {
1298
1325
  try {
@@ -1300,7 +1327,7 @@ var MorphoClient = class {
1300
1327
  const totalBorrowShares = BigInt(mkt.totalBorrowShares);
1301
1328
  const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
1302
1329
  debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1303
- totalDebt += debt;
1330
+ totalDebtFloat += parseFloat(import_ethers2.ethers.formatUnits(debt, loanDecimals));
1304
1331
  } catch (e) {
1305
1332
  console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
1306
1333
  }
@@ -1308,10 +1335,11 @@ var MorphoClient = class {
1308
1335
  positions.push({
1309
1336
  marketId: m.uniqueKey,
1310
1337
  collateralToken: m.collateralAsset.symbol,
1338
+ loanToken: m.loanAsset.symbol,
1311
1339
  collateral: import_ethers2.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
1312
1340
  borrowShares: pos.borrowShares.toString(),
1313
1341
  supplyShares: pos.supplyShares.toString(),
1314
- debt: import_ethers2.ethers.formatUnits(debt, 6)
1342
+ debt: import_ethers2.ethers.formatUnits(debt, loanDecimals)
1315
1343
  });
1316
1344
  } catch (e) {
1317
1345
  console.warn(`[agether] position read failed for market:`, e instanceof Error ? e.message : e);
@@ -1321,16 +1349,28 @@ var MorphoClient = class {
1321
1349
  return {
1322
1350
  agentId: this.agentId,
1323
1351
  agentAccount: acctAddr,
1324
- totalDebt: import_ethers2.ethers.formatUnits(totalDebt, 6),
1352
+ totalDebt: totalDebtFloat.toFixed(6),
1325
1353
  positions
1326
1354
  };
1327
1355
  }
1328
1356
  // ════════════════════════════════════════════════════════
1329
1357
  // Balance & Borrowing Capacity
1330
1358
  // ════════════════════════════════════════════════════════
1359
+ /**
1360
+ * Get the balance of any ERC-20 token in the AgentAccount.
1361
+ * @param symbolOrAddress - token symbol (e.g. 'USDC', 'WETH') or address
1362
+ * @returns balance in raw units
1363
+ */
1364
+ async getTokenBalance(symbolOrAddress) {
1365
+ const acctAddr = await this.getAccountAddress();
1366
+ const tokenInfo = await this._resolveToken(symbolOrAddress);
1367
+ const token = new import_ethers2.Contract(tokenInfo.address, ERC20_ABI, this.provider);
1368
+ return token.balanceOf(acctAddr);
1369
+ }
1331
1370
  /**
1332
1371
  * Get the USDC balance of the AgentAccount.
1333
1372
  * @returns USDC balance in raw units (6 decimals)
1373
+ * @deprecated Use `getTokenBalance('USDC')` instead.
1334
1374
  */
1335
1375
  async getUsdcBalance() {
1336
1376
  const acctAddr = await this.getAccountAddress();
@@ -1338,7 +1378,7 @@ var MorphoClient = class {
1338
1378
  return usdc.balanceOf(acctAddr);
1339
1379
  }
1340
1380
  /**
1341
- * Calculate the maximum additional USDC that can be borrowed
1381
+ * Calculate the maximum additional loan token that can be borrowed
1342
1382
  * given the agent's current collateral and debt across all markets.
1343
1383
  *
1344
1384
  * For each market with collateral deposited:
@@ -1346,7 +1386,7 @@ var MorphoClient = class {
1346
1386
  *
1347
1387
  * Uses the Morpho oracle to price collateral → loan token.
1348
1388
  *
1349
- * @returns Maximum additional USDC borrowable (6 decimals)
1389
+ * @returns Maximum additional borrowable per market (raw units in each market's loan token)
1350
1390
  */
1351
1391
  async getMaxBorrowable() {
1352
1392
  const acctAddr = await this.getAccountAddress();
@@ -1379,6 +1419,8 @@ var MorphoClient = class {
1379
1419
  totalAdditional += maxAdditional;
1380
1420
  byMarket.push({
1381
1421
  collateralToken: m.collateralAsset.symbol,
1422
+ loanToken: m.loanAsset.symbol,
1423
+ loanDecimals: m.loanAsset.decimals,
1382
1424
  maxAdditional,
1383
1425
  currentDebt,
1384
1426
  collateralValue: collateralValueInLoan
@@ -1394,14 +1436,16 @@ var MorphoClient = class {
1394
1436
  // Market Rates & Yield Estimation
1395
1437
  // ════════════════════════════════════════════════════════
1396
1438
  /**
1397
- * Fetch current supply/borrow APY for a collateral market from Morpho GraphQL API.
1439
+ * Fetch current supply/borrow APY for markets from Morpho GraphQL API.
1398
1440
  *
1399
1441
  * Note: On Morpho Blue, collateral does NOT earn yield directly. Supply APY
1400
1442
  * is what lenders earn; borrow APY is what borrowers pay.
1443
+ *
1444
+ * @param collateralSymbolOrAddress - filter by collateral token (optional)
1445
+ * @param loanTokenSymbolOrAddress - filter by loan token (optional). Omit for all loan tokens.
1401
1446
  */
1402
- async getMarketRates(collateralSymbolOrAddress) {
1447
+ async getMarketRates(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
1403
1448
  const chainId = this.config.chainId;
1404
- const usdcAddr = this.config.contracts.usdc.toLowerCase();
1405
1449
  let collateralFilter = "";
1406
1450
  if (collateralSymbolOrAddress) {
1407
1451
  let colAddr;
@@ -1417,12 +1461,27 @@ var MorphoClient = class {
1417
1461
  }
1418
1462
  collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
1419
1463
  }
1464
+ let loanFilter = "";
1465
+ if (loanTokenSymbolOrAddress) {
1466
+ let loanAddr;
1467
+ if (loanTokenSymbolOrAddress.startsWith("0x")) {
1468
+ loanAddr = loanTokenSymbolOrAddress.toLowerCase();
1469
+ } else {
1470
+ try {
1471
+ const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
1472
+ loanAddr = resolved.address.toLowerCase();
1473
+ } catch {
1474
+ loanAddr = loanTokenSymbolOrAddress.toLowerCase();
1475
+ }
1476
+ }
1477
+ loanFilter = `, loanAssetAddress_in: ["${loanAddr}"]`;
1478
+ }
1420
1479
  const query = `{
1421
1480
  markets(
1422
1481
  first: 50
1423
1482
  orderBy: SupplyAssetsUsd
1424
1483
  orderDirection: Desc
1425
- where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"]${collateralFilter} }
1484
+ where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter} }
1426
1485
  ) {
1427
1486
  items {
1428
1487
  uniqueKey
@@ -1442,17 +1501,21 @@ var MorphoClient = class {
1442
1501
  try {
1443
1502
  const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
1444
1503
  const items = resp.data?.data?.markets?.items ?? [];
1445
- return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers2.ethers.ZeroAddress).map((m) => ({
1446
- collateralToken: m.collateralAsset.symbol,
1447
- loanToken: m.loanAsset.symbol,
1448
- supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
1449
- borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
1450
- utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
1451
- totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 1e6 : 0,
1452
- totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 1e6 : 0,
1453
- lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
1454
- marketId: m.uniqueKey
1455
- }));
1504
+ return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers2.ethers.ZeroAddress).map((m) => {
1505
+ const loanDecimals = m.loanAsset?.decimals ?? 18;
1506
+ return {
1507
+ collateralToken: m.collateralAsset.symbol,
1508
+ loanToken: m.loanAsset.symbol,
1509
+ loanDecimals,
1510
+ supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
1511
+ borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
1512
+ utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
1513
+ totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
1514
+ totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
1515
+ lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
1516
+ marketId: m.uniqueKey
1517
+ };
1518
+ });
1456
1519
  } catch (e) {
1457
1520
  console.warn("[agether] getMarketRates failed:", e instanceof Error ? e.message : e);
1458
1521
  return [];
@@ -1486,14 +1549,15 @@ var MorphoClient = class {
1486
1549
  } else {
1487
1550
  try {
1488
1551
  const params = await this.findMarketForCollateral(collateralSymbol);
1552
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1489
1553
  const oracleContract = new import_ethers2.Contract(params.oracle, [
1490
1554
  "function price() view returns (uint256)"
1491
1555
  ], this.provider);
1492
1556
  const oraclePrice = await oracleContract.price();
1493
1557
  const ORACLE_PRICE_SCALE = 10n ** 36n;
1494
1558
  const amountWei = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
1495
- const valueInUsdc = amountWei * oraclePrice / ORACLE_PRICE_SCALE;
1496
- collateralValueUsd = Number(valueInUsdc) / 1e6;
1559
+ const valueInLoan = amountWei * oraclePrice / ORACLE_PRICE_SCALE;
1560
+ collateralValueUsd = Number(valueInLoan) / 10 ** loanDecimals;
1497
1561
  } catch (e) {
1498
1562
  console.warn("[agether] oracle price fetch for yield estimation failed:", e instanceof Error ? e.message : e);
1499
1563
  throw new AgetherError("Cannot determine collateral value. Provide ethPriceUsd.", "PRICE_UNAVAILABLE");
@@ -1514,61 +1578,65 @@ var MorphoClient = class {
1514
1578
  // Supply-Side (Lending) — earn yield by supplying USDC
1515
1579
  // ════════════════════════════════════════════════════════
1516
1580
  /**
1517
- * Supply USDC to a Morpho Blue market as a lender (earn yield).
1581
+ * Supply loan token to a Morpho Blue market as a lender (earn yield).
1518
1582
  *
1519
1583
  * Unlike `supplyCollateral` (borrower-side), this is the **lender-side**:
1520
- * you deposit the loanToken (USDC) into the market's supply pool and earn
1584
+ * you deposit the loanToken into the market's supply pool and earn
1521
1585
  * interest paid by borrowers.
1522
1586
  *
1523
- * @param usdcAmount - Amount of USDC to supply (e.g. '500')
1587
+ * @param amount - Amount of loan token to supply (e.g. '500' for 500 USDC, '0.5' for 0.5 WETH)
1524
1588
  * @param collateralSymbol - Market collateral token to identify which market (e.g. 'WETH')
1525
1589
  * Optional — defaults to highest-APY market
1590
+ * @param loanTokenSymbol - Loan token to filter market (e.g. 'USDC', 'WETH'). Optional.
1526
1591
  */
1527
- async supplyAsset(usdcAmount, collateralSymbol) {
1592
+ async supplyAsset(amount, collateralSymbol, loanTokenSymbol) {
1528
1593
  const acctAddr = await this.getAccountAddress();
1529
- const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
1530
1594
  const morphoAddr = this.config.contracts.morphoBlue;
1531
- const usdcAddr = this.config.contracts.usdc;
1532
1595
  let params;
1533
1596
  let usedCollateral;
1534
1597
  if (collateralSymbol) {
1535
- params = await this.findMarketForCollateral(collateralSymbol);
1598
+ params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
1536
1599
  usedCollateral = collateralSymbol;
1537
1600
  } else {
1538
- const rates = await this.getMarketRates();
1601
+ const rates = await this.getMarketRates(void 0, loanTokenSymbol);
1539
1602
  if (rates.length === 0) throw new AgetherError("No markets available", "NO_MARKETS");
1540
1603
  const best = rates.reduce((a, b) => a.supplyApy > b.supplyApy ? a : b);
1541
- params = await this.findMarketForCollateral(best.collateralToken);
1604
+ params = await this.findMarketForCollateral(best.collateralToken, loanTokenSymbol);
1542
1605
  usedCollateral = best.collateralToken;
1543
1606
  }
1607
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1608
+ const loanTokenAddr = params.loanToken;
1609
+ const parsedAmount = import_ethers2.ethers.parseUnits(amount, loanDecimals);
1544
1610
  const marketId = import_ethers2.ethers.keccak256(
1545
1611
  import_ethers2.ethers.AbiCoder.defaultAbiCoder().encode(
1546
1612
  ["address", "address", "address", "address", "uint256"],
1547
1613
  [params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
1548
1614
  )
1549
1615
  );
1550
- const usdcContract = new import_ethers2.Contract(usdcAddr, ERC20_ABI, this._signer);
1551
- const acctBalance = await usdcContract.balanceOf(acctAddr);
1552
- if (acctBalance < amount) {
1553
- const shortfall = amount - acctBalance;
1554
- const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
1616
+ const loanContract = new import_ethers2.Contract(loanTokenAddr, ERC20_ABI, this._signer);
1617
+ const acctBalance = await loanContract.balanceOf(acctAddr);
1618
+ if (acctBalance < parsedAmount) {
1619
+ const shortfall = parsedAmount - acctBalance;
1620
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
1555
1621
  if (eoaBalance < shortfall) {
1622
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
1623
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
1556
1624
  throw new AgetherError(
1557
- `Insufficient USDC. Need ${usdcAmount}, AgentAccount has ${import_ethers2.ethers.formatUnits(acctBalance, 6)}, EOA has ${import_ethers2.ethers.formatUnits(eoaBalance, 6)}.`,
1625
+ `Insufficient ${loanSymbol}. Need ${amount}, AgentAccount has ${import_ethers2.ethers.formatUnits(acctBalance, loanDecimals)}, EOA has ${import_ethers2.ethers.formatUnits(eoaBalance, loanDecimals)}.`,
1558
1626
  "INSUFFICIENT_BALANCE"
1559
1627
  );
1560
1628
  }
1561
- const transferTx = await usdcContract.transfer(acctAddr, shortfall);
1629
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
1562
1630
  await transferTx.wait();
1563
1631
  this._refreshSigner();
1564
1632
  }
1565
- const targets = [usdcAddr, morphoAddr];
1633
+ const targets = [loanTokenAddr, morphoAddr];
1566
1634
  const values = [0n, 0n];
1567
1635
  const datas = [
1568
- erc20Iface2.encodeFunctionData("approve", [morphoAddr, amount]),
1636
+ erc20Iface2.encodeFunctionData("approve", [morphoAddr, parsedAmount]),
1569
1637
  morphoIface.encodeFunctionData("supply", [
1570
1638
  this._toTuple(params),
1571
- amount,
1639
+ parsedAmount,
1572
1640
  0n,
1573
1641
  acctAddr,
1574
1642
  "0x"
@@ -1577,7 +1645,7 @@ var MorphoClient = class {
1577
1645
  const receipt = await this.batch(targets, values, datas);
1578
1646
  return {
1579
1647
  tx: receipt.hash,
1580
- amount: usdcAmount,
1648
+ amount,
1581
1649
  marketId,
1582
1650
  collateralToken: usedCollateral,
1583
1651
  agentAccount: acctAddr
@@ -1590,17 +1658,26 @@ var MorphoClient = class {
1590
1658
  * @param collateralSymbol - Market collateral to identify which market
1591
1659
  * @param receiver - Destination address (defaults to EOA)
1592
1660
  */
1593
- async withdrawSupply(usdcAmount, collateralSymbol, receiver) {
1661
+ /**
1662
+ * Withdraw supplied loan token (+ earned interest) from a Morpho Blue market.
1663
+ *
1664
+ * @param amount - Amount to withdraw (e.g. '100' or 'all' for full position)
1665
+ * @param collateralSymbol - Market collateral to identify which market
1666
+ * @param receiver - Destination address (defaults to EOA)
1667
+ * @param loanTokenSymbol - Loan token to filter market (optional)
1668
+ */
1669
+ async withdrawSupply(amount, collateralSymbol, receiver, loanTokenSymbol) {
1594
1670
  const acctAddr = await this.getAccountAddress();
1595
1671
  const morphoAddr = this.config.contracts.morphoBlue;
1596
1672
  const dest = receiver || await this.getSignerAddress();
1597
1673
  let params;
1598
1674
  if (collateralSymbol) {
1599
- params = await this.findMarketForCollateral(collateralSymbol);
1675
+ params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
1600
1676
  } else {
1601
1677
  const { params: p } = await this._findActiveSupplyMarket();
1602
1678
  params = p;
1603
1679
  }
1680
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1604
1681
  const marketId = import_ethers2.ethers.keccak256(
1605
1682
  import_ethers2.ethers.AbiCoder.defaultAbiCoder().encode(
1606
1683
  ["address", "address", "address", "address", "uint256"],
@@ -1609,13 +1686,13 @@ var MorphoClient = class {
1609
1686
  );
1610
1687
  let withdrawAssets;
1611
1688
  let withdrawShares;
1612
- if (usdcAmount === "all") {
1689
+ if (amount === "all") {
1613
1690
  const pos = await this.morphoBlue.position(marketId, acctAddr);
1614
1691
  withdrawShares = BigInt(pos.supplyShares);
1615
1692
  withdrawAssets = 0n;
1616
1693
  if (withdrawShares === 0n) throw new AgetherError("No supply position to withdraw", "NO_SUPPLY");
1617
1694
  } else {
1618
- withdrawAssets = import_ethers2.ethers.parseUnits(usdcAmount, 6);
1695
+ withdrawAssets = import_ethers2.ethers.parseUnits(amount, loanDecimals);
1619
1696
  withdrawShares = 0n;
1620
1697
  }
1621
1698
  const data = morphoIface.encodeFunctionData("withdraw", [
@@ -1633,13 +1710,13 @@ var MorphoClient = class {
1633
1710
  const totalSupplyAssets = BigInt(mkt.totalSupplyAssets);
1634
1711
  const totalSupplyShares = BigInt(mkt.totalSupplyShares);
1635
1712
  const currentAssets = totalSupplyShares > 0n ? BigInt(pos.supplyShares) * totalSupplyAssets / totalSupplyShares : 0n;
1636
- remainingSupply = import_ethers2.ethers.formatUnits(currentAssets, 6);
1713
+ remainingSupply = import_ethers2.ethers.formatUnits(currentAssets, loanDecimals);
1637
1714
  } catch (e) {
1638
1715
  console.warn("[agether] failed to read remaining supply:", e instanceof Error ? e.message : e);
1639
1716
  }
1640
1717
  return {
1641
1718
  tx: receipt.hash,
1642
- amount: usdcAmount,
1719
+ amount,
1643
1720
  remainingSupply,
1644
1721
  destination: dest
1645
1722
  };
@@ -1712,14 +1789,13 @@ var MorphoClient = class {
1712
1789
  * Computes available yield, verifies the requested amount doesn't exceed it,
1713
1790
  * then withdraws from the supply position and sends directly to the recipient.
1714
1791
  *
1715
- * @param recipient - Address to receive the USDC
1716
- * @param usdcAmount - Amount to pay from yield (e.g. '5.50')
1792
+ * @param recipient - Address to receive the loan token
1793
+ * @param amount - Amount to pay from yield (e.g. '5.50')
1717
1794
  * @param collateralSymbol - Market collateral to identify which supply position
1718
1795
  */
1719
- async payFromYield(recipient, usdcAmount, collateralSymbol) {
1796
+ async payFromYield(recipient, amount, collateralSymbol) {
1720
1797
  const acctAddr = await this.getAccountAddress();
1721
1798
  const morphoAddr = this.config.contracts.morphoBlue;
1722
- const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
1723
1799
  const positions = await this.getSupplyPositions(collateralSymbol);
1724
1800
  if (positions.length === 0) {
1725
1801
  throw new AgetherError("No supply position found", "NO_SUPPLY");
@@ -1727,17 +1803,20 @@ var MorphoClient = class {
1727
1803
  const pos = positions.reduce(
1728
1804
  (a, b) => parseFloat(a.earnedYield) > parseFloat(b.earnedYield) ? a : b
1729
1805
  );
1730
- const availableYield = import_ethers2.ethers.parseUnits(pos.earnedYield, 6);
1731
- if (amount > availableYield) {
1806
+ const params = await this.findMarketForCollateral(pos.collateralToken, pos.loanToken);
1807
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1808
+ const parsedAmount = import_ethers2.ethers.parseUnits(amount, loanDecimals);
1809
+ const availableYield = import_ethers2.ethers.parseUnits(pos.earnedYield, loanDecimals);
1810
+ if (parsedAmount > availableYield) {
1811
+ const loanSymbol = pos.loanToken;
1732
1812
  throw new AgetherError(
1733
- `Requested ${usdcAmount} USDC exceeds available yield of ${pos.earnedYield} USDC. Use withdrawSupply to withdraw principal.`,
1813
+ `Requested ${amount} ${loanSymbol} exceeds available yield of ${pos.earnedYield} ${loanSymbol}. Use withdrawSupply to withdraw principal.`,
1734
1814
  "EXCEEDS_YIELD"
1735
1815
  );
1736
1816
  }
1737
- const params = await this.findMarketForCollateral(pos.collateralToken);
1738
1817
  const data = morphoIface.encodeFunctionData("withdraw", [
1739
1818
  this._toTuple(params),
1740
- amount,
1819
+ parsedAmount,
1741
1820
  0n,
1742
1821
  acctAddr,
1743
1822
  recipient
@@ -1756,7 +1835,7 @@ var MorphoClient = class {
1756
1835
  }
1757
1836
  return {
1758
1837
  tx: receipt.hash,
1759
- yieldWithdrawn: usdcAmount,
1838
+ yieldWithdrawn: amount,
1760
1839
  recipient,
1761
1840
  remainingYield,
1762
1841
  remainingSupply
@@ -1814,28 +1893,31 @@ var MorphoClient = class {
1814
1893
  };
1815
1894
  }
1816
1895
  /**
1817
- * Borrow USDC against existing collateral.
1896
+ * Borrow loan token against existing collateral.
1818
1897
  *
1819
1898
  * AgentAccount.execute: Morpho.borrow(params, amount, 0, account, account)
1820
1899
  *
1821
- * @param usdcAmount - USDC amount (e.g. '100')
1900
+ * @param amount - Loan token amount (e.g. '100' for 100 USDC, '0.5' for 0.5 WETH)
1822
1901
  * @param tokenSymbol - collateral symbol to identify which market (default: first with collateral)
1902
+ * @param marketParams - explicit market params (optional)
1903
+ * @param loanTokenSymbol - loan token to filter market (optional, e.g. 'USDC', 'WETH')
1823
1904
  */
1824
- async borrow(usdcAmount, tokenSymbol, marketParams) {
1905
+ async borrow(amount, tokenSymbol, marketParams, loanTokenSymbol) {
1825
1906
  const acctAddr = await this.getAccountAddress();
1826
- const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
1827
1907
  const morphoAddr = this.config.contracts.morphoBlue;
1828
1908
  let params;
1829
1909
  let usedToken = tokenSymbol || "WETH";
1830
1910
  if (marketParams) {
1831
1911
  params = marketParams;
1832
1912
  } else if (tokenSymbol) {
1833
- params = await this.findMarketForCollateral(tokenSymbol);
1913
+ params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1834
1914
  } else {
1835
1915
  const { params: p, symbol } = await this._findActiveMarket();
1836
1916
  params = p;
1837
1917
  usedToken = symbol;
1838
1918
  }
1919
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1920
+ const parsedAmount = import_ethers2.ethers.parseUnits(amount, loanDecimals);
1839
1921
  try {
1840
1922
  const marketId = import_ethers2.ethers.keccak256(
1841
1923
  import_ethers2.ethers.AbiCoder.defaultAbiCoder().encode(
@@ -1859,11 +1941,14 @@ var MorphoClient = class {
1859
1941
  const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
1860
1942
  const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1861
1943
  const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
1862
- if (amount > maxAdditional) {
1863
- const maxUsd = import_ethers2.ethers.formatUnits(maxAdditional, 6);
1864
- const colFormatted = import_ethers2.ethers.formatUnits(pos.collateral, 18);
1944
+ if (parsedAmount > maxAdditional) {
1945
+ const loanInfo = this._tokenCache.get(params.loanToken.toLowerCase());
1946
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
1947
+ const colInfo = await this._resolveToken(usedToken);
1948
+ const maxFormatted = import_ethers2.ethers.formatUnits(maxAdditional, loanDecimals);
1949
+ const colFormatted = import_ethers2.ethers.formatUnits(pos.collateral, colInfo.decimals);
1865
1950
  throw new AgetherError(
1866
- `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.`,
1951
+ `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.`,
1867
1952
  "EXCEEDS_MAX_LTV"
1868
1953
  );
1869
1954
  }
@@ -1873,7 +1958,7 @@ var MorphoClient = class {
1873
1958
  }
1874
1959
  const data = morphoIface.encodeFunctionData("borrow", [
1875
1960
  this._toTuple(params),
1876
- amount,
1961
+ parsedAmount,
1877
1962
  0n,
1878
1963
  acctAddr,
1879
1964
  acctAddr
@@ -1881,7 +1966,7 @@ var MorphoClient = class {
1881
1966
  const receipt = await this.exec(morphoAddr, data);
1882
1967
  return {
1883
1968
  tx: receipt.hash,
1884
- amount: usdcAmount,
1969
+ amount,
1885
1970
  collateralToken: usedToken,
1886
1971
  agentAccount: acctAddr
1887
1972
  };
@@ -1889,17 +1974,27 @@ var MorphoClient = class {
1889
1974
  /**
1890
1975
  * Deposit collateral AND borrow USDC in one batched transaction.
1891
1976
  *
1977
+ /**
1978
+ * Deposit collateral AND borrow loan token in one batched transaction.
1979
+ *
1892
1980
  * AgentAccount.executeBatch:
1893
1981
  * [collateral.approve, Morpho.supplyCollateral, Morpho.borrow]
1894
1982
  *
1895
1983
  * The collateral must be transferred to AgentAccount first.
1984
+ *
1985
+ * @param tokenSymbol - collateral token symbol (e.g. 'WETH')
1986
+ * @param collateralAmount - amount of collateral (e.g. '0.05')
1987
+ * @param borrowAmount - amount of loan token to borrow (e.g. '100')
1988
+ * @param marketParams - explicit market params (optional)
1989
+ * @param loanTokenSymbol - loan token to filter market (optional)
1896
1990
  */
1897
- async depositAndBorrow(tokenSymbol, collateralAmount, borrowUsdcAmount, marketParams) {
1991
+ async depositAndBorrow(tokenSymbol, collateralAmount, borrowAmount, marketParams, loanTokenSymbol) {
1898
1992
  const acctAddr = await this.getAccountAddress();
1899
1993
  const colInfo = await this._resolveToken(tokenSymbol);
1900
- const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
1994
+ const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1995
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1901
1996
  const colWei = import_ethers2.ethers.parseUnits(collateralAmount, colInfo.decimals);
1902
- const borrowWei = import_ethers2.ethers.parseUnits(borrowUsdcAmount, 6);
1997
+ const borrowWei = import_ethers2.ethers.parseUnits(borrowAmount, loanDecimals);
1903
1998
  const morphoAddr = this.config.contracts.morphoBlue;
1904
1999
  try {
1905
2000
  const marketId = import_ethers2.ethers.keccak256(
@@ -1920,9 +2015,11 @@ var MorphoClient = class {
1920
2015
  const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1921
2016
  const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
1922
2017
  if (borrowWei > maxAdditional) {
1923
- const maxUsd = import_ethers2.ethers.formatUnits(maxAdditional, 6);
2018
+ const loanInfo = this._tokenCache.get(params.loanToken.toLowerCase());
2019
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
2020
+ const maxFormatted = import_ethers2.ethers.formatUnits(maxAdditional, loanDecimals);
1924
2021
  throw new AgetherError(
1925
- `Borrow of $${borrowUsdcAmount} USDC exceeds max borrowable $${maxUsd} USDC (total collateral: ${import_ethers2.ethers.formatUnits(totalCollateral, colInfo.decimals)} ${tokenSymbol}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Reduce borrow or increase collateral.`,
2022
+ `Borrow of ${borrowAmount} ${loanSymbol} exceeds max borrowable ${maxFormatted} ${loanSymbol} (total collateral: ${import_ethers2.ethers.formatUnits(totalCollateral, colInfo.decimals)} ${tokenSymbol}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Reduce borrow or increase collateral.`,
1926
2023
  "EXCEEDS_MAX_LTV"
1927
2024
  );
1928
2025
  }
@@ -1968,36 +2065,42 @@ var MorphoClient = class {
1968
2065
  tx: receipt.hash,
1969
2066
  collateralToken: tokenSymbol,
1970
2067
  collateralAmount,
1971
- borrowAmount: borrowUsdcAmount,
2068
+ borrowAmount,
1972
2069
  agentAccount: acctAddr
1973
2070
  };
1974
2071
  }
1975
2072
  /**
1976
- * Repay borrowed USDC from AgentAccount.
2073
+ * Repay borrowed loan token from AgentAccount.
1977
2074
  *
1978
2075
  * AgentAccount.executeBatch:
1979
- * [USDC.approve(MorphoBlue), Morpho.repay(params)]
2076
+ * [loanToken.approve(MorphoBlue), Morpho.repay(params)]
2077
+ *
2078
+ * @param amount - loan token amount to repay (e.g. '50' or 'all' for full repayment)
2079
+ * @param tokenSymbol - collateral symbol to identify which market (optional)
2080
+ * @param marketParams - explicit market params (optional)
2081
+ * @param loanTokenSymbol - loan token to filter market (optional)
1980
2082
  */
1981
- async repay(usdcAmount, tokenSymbol, marketParams) {
2083
+ async repay(amount, tokenSymbol, marketParams, loanTokenSymbol) {
1982
2084
  const acctAddr = await this.getAccountAddress();
1983
2085
  const morphoAddr = this.config.contracts.morphoBlue;
1984
- const usdcAddr = this.config.contracts.usdc;
1985
2086
  let params;
1986
2087
  if (marketParams) {
1987
2088
  params = marketParams;
1988
2089
  } else if (tokenSymbol) {
1989
- params = await this.findMarketForCollateral(tokenSymbol);
2090
+ params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1990
2091
  } else {
1991
2092
  const { params: p } = await this._findActiveMarket();
1992
2093
  params = p;
1993
2094
  }
2095
+ const loanTokenAddr = params.loanToken;
2096
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1994
2097
  let repayAssets;
1995
2098
  let repayShares;
1996
2099
  let approveAmount;
1997
- if (usdcAmount === "all") {
2100
+ if (amount === "all") {
1998
2101
  const markets = await this.getMarkets();
1999
2102
  const mkt = markets.find(
2000
- (m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase()
2103
+ (m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase() && m.loanAsset?.address.toLowerCase() === params.loanToken.toLowerCase()
2001
2104
  );
2002
2105
  if (mkt) {
2003
2106
  const pos = await this.morphoBlue.position(mkt.uniqueKey, acctAddr);
@@ -2007,33 +2110,35 @@ var MorphoClient = class {
2007
2110
  const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
2008
2111
  const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
2009
2112
  const estimated = totalBorrowShares > 0n ? repayShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
2010
- approveAmount = estimated > 0n ? estimated : import_ethers2.ethers.parseUnits("1", 6);
2113
+ approveAmount = estimated > 0n ? estimated : import_ethers2.ethers.parseUnits("1", loanDecimals);
2011
2114
  } else {
2012
- repayAssets = import_ethers2.ethers.parseUnits("999999", 6);
2115
+ repayAssets = import_ethers2.ethers.parseUnits("999999", loanDecimals);
2013
2116
  repayShares = 0n;
2014
2117
  approveAmount = repayAssets;
2015
2118
  }
2016
2119
  } else {
2017
- repayAssets = import_ethers2.ethers.parseUnits(usdcAmount, 6);
2120
+ repayAssets = import_ethers2.ethers.parseUnits(amount, loanDecimals);
2018
2121
  repayShares = 0n;
2019
2122
  approveAmount = repayAssets;
2020
2123
  }
2021
- const usdcContract = new import_ethers2.Contract(usdcAddr, ERC20_ABI, this._signer);
2022
- const acctBalance = await usdcContract.balanceOf(acctAddr);
2124
+ const loanContract = new import_ethers2.Contract(loanTokenAddr, ERC20_ABI, this._signer);
2125
+ const acctBalance = await loanContract.balanceOf(acctAddr);
2023
2126
  if (acctBalance < approveAmount) {
2024
2127
  const shortfall = approveAmount - acctBalance;
2025
- const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
2128
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
2026
2129
  if (eoaBalance < shortfall) {
2130
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
2131
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
2027
2132
  throw new AgetherError(
2028
- `Insufficient USDC for repay. Need ${import_ethers2.ethers.formatUnits(approveAmount, 6)} USDC, AgentAccount has ${import_ethers2.ethers.formatUnits(acctBalance, 6)}, EOA has ${import_ethers2.ethers.formatUnits(eoaBalance, 6)}.`,
2133
+ `Insufficient ${loanSymbol} for repay. Need ${import_ethers2.ethers.formatUnits(approveAmount, loanDecimals)}, AgentAccount has ${import_ethers2.ethers.formatUnits(acctBalance, loanDecimals)}, EOA has ${import_ethers2.ethers.formatUnits(eoaBalance, loanDecimals)}.`,
2029
2134
  "INSUFFICIENT_BALANCE"
2030
2135
  );
2031
2136
  }
2032
- const transferTx = await usdcContract.transfer(acctAddr, shortfall);
2137
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
2033
2138
  await transferTx.wait();
2034
2139
  this._refreshSigner();
2035
2140
  }
2036
- const targets = [usdcAddr, morphoAddr];
2141
+ const targets = [loanTokenAddr, morphoAddr];
2037
2142
  const values = [0n, 0n];
2038
2143
  const datas = [
2039
2144
  erc20Iface2.encodeFunctionData("approve", [morphoAddr, approveAmount]),
@@ -2053,7 +2158,7 @@ var MorphoClient = class {
2053
2158
  } catch (e) {
2054
2159
  console.warn("[agether] failed to read remaining debt after repay:", e instanceof Error ? e.message : e);
2055
2160
  }
2056
- return { tx: receipt.hash, amount: usdcAmount, remainingDebt };
2161
+ return { tx: receipt.hash, amount, remainingDebt };
2057
2162
  }
2058
2163
  /**
2059
2164
  * Withdraw collateral from Morpho Blue.
@@ -2067,12 +2172,13 @@ var MorphoClient = class {
2067
2172
  const colInfo = await this._resolveToken(tokenSymbol);
2068
2173
  const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
2069
2174
  const morphoAddr = this.config.contracts.morphoBlue;
2070
- const usdcAddr = this.config.contracts.usdc;
2175
+ const loanTokenAddr = params.loanToken;
2176
+ const loanDecimals = await this._getLoanTokenDecimals(params);
2071
2177
  const dest = receiver || await this.getSignerAddress();
2072
2178
  let weiAmount;
2073
2179
  const markets = await this.getMarkets();
2074
2180
  const market = markets.find(
2075
- (m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase()
2181
+ (m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase() && m.loanAsset?.address.toLowerCase() === loanTokenAddr.toLowerCase()
2076
2182
  );
2077
2183
  if (amount === "all") {
2078
2184
  if (!market) throw new AgetherError("Market not found", "MARKET_NOT_FOUND");
@@ -2095,8 +2201,10 @@ var MorphoClient = class {
2095
2201
  const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
2096
2202
  const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
2097
2203
  const estimated = totalBorrowShares > 0n ? dustBorrowShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
2098
- dustApproveAmount = estimated > 0n ? estimated : import_ethers2.ethers.parseUnits("1", 6);
2099
- console.log(`[agether] dust borrow shares detected: ${dustBorrowShares} shares \u2248 ${import_ethers2.ethers.formatUnits(dustApproveAmount, 6)} USDC \u2014 auto-repaying before withdraw`);
2204
+ dustApproveAmount = estimated > 0n ? estimated : import_ethers2.ethers.parseUnits("1", loanDecimals);
2205
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
2206
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
2207
+ console.log(`[agether] dust borrow shares detected: ${dustBorrowShares} shares \u2248 ${import_ethers2.ethers.formatUnits(dustApproveAmount, loanDecimals)} ${loanSymbol} \u2014 auto-repaying before withdraw`);
2100
2208
  }
2101
2209
  } catch (e) {
2102
2210
  console.warn("[agether] failed to check borrow shares before withdraw:", e instanceof Error ? e.message : e);
@@ -2110,19 +2218,21 @@ var MorphoClient = class {
2110
2218
  ]);
2111
2219
  let receipt;
2112
2220
  if (hasDustDebt) {
2113
- const usdcContract = new import_ethers2.Contract(usdcAddr, ERC20_ABI, this._signer);
2114
- const acctBalance = await usdcContract.balanceOf(acctAddr);
2221
+ const loanContract = new import_ethers2.Contract(loanTokenAddr, ERC20_ABI, this._signer);
2222
+ const acctBalance = await loanContract.balanceOf(acctAddr);
2115
2223
  if (acctBalance < dustApproveAmount) {
2116
2224
  const shortfall = dustApproveAmount - acctBalance;
2117
- const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
2225
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
2118
2226
  if (eoaBalance >= shortfall) {
2119
- console.log(`[agether] transferring ${import_ethers2.ethers.formatUnits(shortfall, 6)} USDC from EOA \u2192 AgentAccount for dust repay`);
2120
- const transferTx = await usdcContract.transfer(acctAddr, shortfall);
2227
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
2228
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
2229
+ console.log(`[agether] transferring ${import_ethers2.ethers.formatUnits(shortfall, loanDecimals)} ${loanSymbol} from EOA \u2192 AgentAccount for dust repay`);
2230
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
2121
2231
  await transferTx.wait();
2122
2232
  this._refreshSigner();
2123
2233
  }
2124
2234
  }
2125
- const targets = [usdcAddr, morphoAddr, morphoAddr];
2235
+ const targets = [loanTokenAddr, morphoAddr, morphoAddr];
2126
2236
  const values = [0n, 0n, 0n];
2127
2237
  const datas = [
2128
2238
  erc20Iface2.encodeFunctionData("approve", [morphoAddr, dustApproveAmount]),
@@ -2367,6 +2477,37 @@ var MorphoClient = class {
2367
2477
  }
2368
2478
  throw new AgetherError("No active supply position found", "NO_SUPPLY");
2369
2479
  }
2480
+ /**
2481
+ * Resolve loan token decimals from market params.
2482
+ * Uses the `_tokenCache` populated by `getMarkets()`.
2483
+ */
2484
+ async _getLoanTokenDecimals(params) {
2485
+ const tokenInfo = this._tokenCache.get(params.loanToken.toLowerCase());
2486
+ if (tokenInfo) return tokenInfo.decimals;
2487
+ await this.getMarkets();
2488
+ const fromApi = this._tokenCache.get(params.loanToken.toLowerCase());
2489
+ return fromApi?.decimals ?? 18;
2490
+ }
2491
+ /**
2492
+ * Apply client-side filter to discovered markets.
2493
+ */
2494
+ _applyMarketFilter(markets, filter) {
2495
+ return markets.filter((m) => {
2496
+ if (filter.loanToken) {
2497
+ const loanAddr = filter.loanToken.toLowerCase();
2498
+ if (m.loanAsset.address.toLowerCase() !== loanAddr && m.loanAsset.symbol.toUpperCase() !== filter.loanToken.toUpperCase()) {
2499
+ return false;
2500
+ }
2501
+ }
2502
+ if (filter.collateralToken) {
2503
+ const colAddr = filter.collateralToken.toLowerCase();
2504
+ if (m.collateralAsset.address.toLowerCase() !== colAddr && m.collateralAsset.symbol.toUpperCase() !== filter.collateralToken.toUpperCase()) {
2505
+ return false;
2506
+ }
2507
+ }
2508
+ return true;
2509
+ });
2510
+ }
2370
2511
  /**
2371
2512
  * Resolve a token symbol or address to { address, symbol, decimals }.
2372
2513
  *
@@ -2383,8 +2524,8 @@ var MorphoClient = class {
2383
2524
  const fromApi = this._tokenCache.get(key);
2384
2525
  if (fromApi) return fromApi;
2385
2526
  throw new AgetherError(
2386
- `Unknown token: ${symbolOrAddress}. No Morpho market found with this collateral.`,
2387
- "UNKNOWN_COLLATERAL"
2527
+ `Unknown token: ${symbolOrAddress}. No Morpho market found with this token.`,
2528
+ "UNKNOWN_TOKEN"
2388
2529
  );
2389
2530
  }
2390
2531
  /**