@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.mjs CHANGED
@@ -419,25 +419,21 @@ var AgetherClient = class _AgetherClient {
419
419
  const acctAddr = await this.agether4337Factory.getAccount(agentId);
420
420
  this.accountAddress = acctAddr;
421
421
  if (!acctExists) {
422
- try {
423
- const updatedMeta = JSON.stringify({
424
- type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
425
- name: options?.name || "Unnamed Agent",
426
- description: options?.description || "AI agent registered via @agether/sdk",
427
- active: true,
428
- wallet: `eip155:${this.config.chainId}:${acctAddr}`,
429
- registrations: [{
430
- agentId: Number(agentId),
431
- agentRegistry: `eip155:${this.config.chainId}:${this.config.contracts.identityRegistry}`
432
- }]
433
- });
434
- const finalURI = `data:application/json;base64,${Buffer.from(updatedMeta).toString("base64")}`;
435
- const uriTx = await this.identityRegistry.setAgentURI(agentId, finalURI);
436
- await uriTx.wait();
437
- this._refreshSigner();
438
- } catch (e) {
439
- console.warn("[agether] setAgentURI failed (non-fatal):", e instanceof Error ? e.message : e);
440
- }
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();
441
437
  }
442
438
  const kyaRequired = await this.isKyaRequired();
443
439
  return {
@@ -455,7 +451,7 @@ var AgetherClient = class _AgetherClient {
455
451
  async _mintNewIdentity(name, description) {
456
452
  const registrationFile = JSON.stringify({
457
453
  type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
458
- name: name || "Unnamed Agent",
454
+ name: name || "Agether Agent",
459
455
  description: description || "AI agent registered via @agether/sdk",
460
456
  active: true,
461
457
  registrations: [{
@@ -966,8 +962,9 @@ var morphoIface = new ethers2.Interface(MORPHO_BLUE_ABI);
966
962
  var erc20Iface2 = new ethers2.Interface(ERC20_ABI);
967
963
  var MorphoClient = class {
968
964
  constructor(config) {
965
+ /** Market params cache: keyed by market uniqueKey (bytes32 hash) */
969
966
  this._marketCache = /* @__PURE__ */ new Map();
970
- /** Dynamic token registry: symbol (uppercase) → { address, symbol, decimals } */
967
+ /** Dynamic token registry: symbol (uppercase) or address (lowercase) → { address, symbol, decimals } */
971
968
  this._tokenCache = /* @__PURE__ */ new Map();
972
969
  this._discoveredAt = 0;
973
970
  if (!config.agentId) {
@@ -1070,21 +1067,23 @@ var MorphoClient = class {
1070
1067
  // Market Discovery (Morpho GraphQL API)
1071
1068
  // ════════════════════════════════════════════════════════
1072
1069
  /**
1073
- * Fetch USDC borrow markets on Base from Morpho API.
1074
- * 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
1075
1075
  */
1076
- async getMarkets(forceRefresh = false) {
1076
+ async getMarkets(forceRefresh = false, filter) {
1077
1077
  if (!forceRefresh && this._discoveredMarkets && Date.now() - this._discoveredAt < 3e5) {
1078
- return this._discoveredMarkets;
1078
+ return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
1079
1079
  }
1080
1080
  const chainId = this.config.chainId;
1081
- const usdcAddr = this.config.contracts.usdc.toLowerCase();
1082
1081
  const query = `{
1083
1082
  markets(
1084
1083
  first: 50
1085
1084
  orderBy: SupplyAssetsUsd
1086
1085
  orderDirection: Desc
1087
- where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"] }
1086
+ where: { chainId_in: [${chainId}] }
1088
1087
  ) {
1089
1088
  items {
1090
1089
  uniqueKey
@@ -1117,14 +1116,14 @@ var MorphoClient = class {
1117
1116
  }));
1118
1117
  this._discoveredAt = Date.now();
1119
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
+ });
1120
1126
  if (mi.collateralAsset.address !== ethers2.ZeroAddress) {
1121
- this._marketCache.set(mi.collateralAsset.address.toLowerCase(), {
1122
- loanToken: mi.loanAsset.address,
1123
- collateralToken: mi.collateralAsset.address,
1124
- oracle: mi.oracle,
1125
- irm: mi.irm,
1126
- lltv: mi.lltv
1127
- });
1128
1127
  this._tokenCache.set(mi.collateralAsset.symbol.toUpperCase(), {
1129
1128
  address: mi.collateralAsset.address,
1130
1129
  symbol: mi.collateralAsset.symbol,
@@ -1149,17 +1148,24 @@ var MorphoClient = class {
1149
1148
  });
1150
1149
  }
1151
1150
  }
1152
- return this._discoveredMarkets;
1151
+ return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
1153
1152
  } catch (e) {
1154
1153
  console.warn("[agether] getMarkets failed, using cache:", e instanceof Error ? e.message : e);
1155
- return this._discoveredMarkets ?? [];
1154
+ const cached = this._discoveredMarkets ?? [];
1155
+ return filter ? this._applyMarketFilter(cached, filter) : cached;
1156
1156
  }
1157
1157
  }
1158
1158
  /**
1159
- * Get MarketParams for a collateral token.
1160
- * 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)
1161
1167
  */
1162
- async findMarketForCollateral(collateralSymbolOrAddress) {
1168
+ async findMarketForCollateral(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
1163
1169
  let colAddr;
1164
1170
  if (collateralSymbolOrAddress.startsWith("0x")) {
1165
1171
  colAddr = collateralSymbolOrAddress.toLowerCase();
@@ -1171,13 +1177,33 @@ var MorphoClient = class {
1171
1177
  colAddr = collateralSymbolOrAddress.toLowerCase();
1172
1178
  }
1173
1179
  }
1174
- const cached = this._marketCache.get(colAddr);
1175
- if (cached) return cached;
1176
- await this.getMarkets();
1177
- const fromApi = this._marketCache.get(colAddr);
1178
- 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
+ }
1179
1205
  throw new AgetherError(
1180
- `No Morpho market found for collateral ${collateralSymbolOrAddress}`,
1206
+ `No Morpho market found for collateral ${collateralSymbolOrAddress}` + (loanTokenSymbolOrAddress ? ` with loan token ${loanTokenSymbolOrAddress}` : ""),
1181
1207
  "MARKET_NOT_FOUND"
1182
1208
  );
1183
1209
  }
@@ -1212,12 +1238,13 @@ var MorphoClient = class {
1212
1238
  const acctAddr = await this.getAccountAddress();
1213
1239
  const markets = await this.getMarkets();
1214
1240
  const positions = [];
1215
- let totalDebt = 0n;
1241
+ let totalDebtFloat = 0;
1216
1242
  for (const m of markets) {
1217
1243
  if (!m.collateralAsset || m.collateralAsset.address === ethers2.ZeroAddress) continue;
1218
1244
  try {
1219
1245
  const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
1220
1246
  if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
1247
+ const loanDecimals = m.loanAsset.decimals;
1221
1248
  let debt = 0n;
1222
1249
  if (pos.borrowShares > 0n) {
1223
1250
  try {
@@ -1225,7 +1252,7 @@ var MorphoClient = class {
1225
1252
  const totalBorrowShares = BigInt(mkt.totalBorrowShares);
1226
1253
  const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
1227
1254
  debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1228
- totalDebt += debt;
1255
+ totalDebtFloat += parseFloat(ethers2.formatUnits(debt, loanDecimals));
1229
1256
  } catch (e) {
1230
1257
  console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
1231
1258
  }
@@ -1233,10 +1260,11 @@ var MorphoClient = class {
1233
1260
  positions.push({
1234
1261
  marketId: m.uniqueKey,
1235
1262
  collateralToken: m.collateralAsset.symbol,
1263
+ loanToken: m.loanAsset.symbol,
1236
1264
  collateral: ethers2.formatUnits(pos.collateral, m.collateralAsset.decimals),
1237
1265
  borrowShares: pos.borrowShares.toString(),
1238
1266
  supplyShares: pos.supplyShares.toString(),
1239
- debt: ethers2.formatUnits(debt, 6)
1267
+ debt: ethers2.formatUnits(debt, loanDecimals)
1240
1268
  });
1241
1269
  } catch (e) {
1242
1270
  console.warn(`[agether] position read failed for market:`, e instanceof Error ? e.message : e);
@@ -1246,16 +1274,28 @@ var MorphoClient = class {
1246
1274
  return {
1247
1275
  agentId: this.agentId,
1248
1276
  agentAccount: acctAddr,
1249
- totalDebt: ethers2.formatUnits(totalDebt, 6),
1277
+ totalDebt: totalDebtFloat.toFixed(6),
1250
1278
  positions
1251
1279
  };
1252
1280
  }
1253
1281
  // ════════════════════════════════════════════════════════
1254
1282
  // Balance & Borrowing Capacity
1255
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
+ }
1256
1295
  /**
1257
1296
  * Get the USDC balance of the AgentAccount.
1258
1297
  * @returns USDC balance in raw units (6 decimals)
1298
+ * @deprecated Use `getTokenBalance('USDC')` instead.
1259
1299
  */
1260
1300
  async getUsdcBalance() {
1261
1301
  const acctAddr = await this.getAccountAddress();
@@ -1263,7 +1303,7 @@ var MorphoClient = class {
1263
1303
  return usdc.balanceOf(acctAddr);
1264
1304
  }
1265
1305
  /**
1266
- * Calculate the maximum additional USDC that can be borrowed
1306
+ * Calculate the maximum additional loan token that can be borrowed
1267
1307
  * given the agent's current collateral and debt across all markets.
1268
1308
  *
1269
1309
  * For each market with collateral deposited:
@@ -1271,7 +1311,7 @@ var MorphoClient = class {
1271
1311
  *
1272
1312
  * Uses the Morpho oracle to price collateral → loan token.
1273
1313
  *
1274
- * @returns Maximum additional USDC borrowable (6 decimals)
1314
+ * @returns Maximum additional borrowable per market (raw units in each market's loan token)
1275
1315
  */
1276
1316
  async getMaxBorrowable() {
1277
1317
  const acctAddr = await this.getAccountAddress();
@@ -1304,6 +1344,8 @@ var MorphoClient = class {
1304
1344
  totalAdditional += maxAdditional;
1305
1345
  byMarket.push({
1306
1346
  collateralToken: m.collateralAsset.symbol,
1347
+ loanToken: m.loanAsset.symbol,
1348
+ loanDecimals: m.loanAsset.decimals,
1307
1349
  maxAdditional,
1308
1350
  currentDebt,
1309
1351
  collateralValue: collateralValueInLoan
@@ -1319,14 +1361,16 @@ var MorphoClient = class {
1319
1361
  // Market Rates & Yield Estimation
1320
1362
  // ════════════════════════════════════════════════════════
1321
1363
  /**
1322
- * 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.
1323
1365
  *
1324
1366
  * Note: On Morpho Blue, collateral does NOT earn yield directly. Supply APY
1325
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.
1326
1371
  */
1327
- async getMarketRates(collateralSymbolOrAddress) {
1372
+ async getMarketRates(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
1328
1373
  const chainId = this.config.chainId;
1329
- const usdcAddr = this.config.contracts.usdc.toLowerCase();
1330
1374
  let collateralFilter = "";
1331
1375
  if (collateralSymbolOrAddress) {
1332
1376
  let colAddr;
@@ -1342,12 +1386,27 @@ var MorphoClient = class {
1342
1386
  }
1343
1387
  collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
1344
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
+ }
1345
1404
  const query = `{
1346
1405
  markets(
1347
1406
  first: 50
1348
1407
  orderBy: SupplyAssetsUsd
1349
1408
  orderDirection: Desc
1350
- where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"]${collateralFilter} }
1409
+ where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter} }
1351
1410
  ) {
1352
1411
  items {
1353
1412
  uniqueKey
@@ -1367,17 +1426,21 @@ var MorphoClient = class {
1367
1426
  try {
1368
1427
  const resp = await axios.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
1369
1428
  const items = resp.data?.data?.markets?.items ?? [];
1370
- return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== ethers2.ZeroAddress).map((m) => ({
1371
- collateralToken: m.collateralAsset.symbol,
1372
- loanToken: m.loanAsset.symbol,
1373
- supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
1374
- borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
1375
- utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
1376
- totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 1e6 : 0,
1377
- totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 1e6 : 0,
1378
- lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
1379
- marketId: m.uniqueKey
1380
- }));
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
+ });
1381
1444
  } catch (e) {
1382
1445
  console.warn("[agether] getMarketRates failed:", e instanceof Error ? e.message : e);
1383
1446
  return [];
@@ -1411,14 +1474,15 @@ var MorphoClient = class {
1411
1474
  } else {
1412
1475
  try {
1413
1476
  const params = await this.findMarketForCollateral(collateralSymbol);
1477
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1414
1478
  const oracleContract = new Contract2(params.oracle, [
1415
1479
  "function price() view returns (uint256)"
1416
1480
  ], this.provider);
1417
1481
  const oraclePrice = await oracleContract.price();
1418
1482
  const ORACLE_PRICE_SCALE = 10n ** 36n;
1419
1483
  const amountWei = ethers2.parseUnits(amount, colInfo.decimals);
1420
- const valueInUsdc = amountWei * oraclePrice / ORACLE_PRICE_SCALE;
1421
- collateralValueUsd = Number(valueInUsdc) / 1e6;
1484
+ const valueInLoan = amountWei * oraclePrice / ORACLE_PRICE_SCALE;
1485
+ collateralValueUsd = Number(valueInLoan) / 10 ** loanDecimals;
1422
1486
  } catch (e) {
1423
1487
  console.warn("[agether] oracle price fetch for yield estimation failed:", e instanceof Error ? e.message : e);
1424
1488
  throw new AgetherError("Cannot determine collateral value. Provide ethPriceUsd.", "PRICE_UNAVAILABLE");
@@ -1439,61 +1503,65 @@ var MorphoClient = class {
1439
1503
  // Supply-Side (Lending) — earn yield by supplying USDC
1440
1504
  // ════════════════════════════════════════════════════════
1441
1505
  /**
1442
- * 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).
1443
1507
  *
1444
1508
  * Unlike `supplyCollateral` (borrower-side), this is the **lender-side**:
1445
- * 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
1446
1510
  * interest paid by borrowers.
1447
1511
  *
1448
- * @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)
1449
1513
  * @param collateralSymbol - Market collateral token to identify which market (e.g. 'WETH')
1450
1514
  * Optional — defaults to highest-APY market
1515
+ * @param loanTokenSymbol - Loan token to filter market (e.g. 'USDC', 'WETH'). Optional.
1451
1516
  */
1452
- async supplyAsset(usdcAmount, collateralSymbol) {
1517
+ async supplyAsset(amount, collateralSymbol, loanTokenSymbol) {
1453
1518
  const acctAddr = await this.getAccountAddress();
1454
- const amount = ethers2.parseUnits(usdcAmount, 6);
1455
1519
  const morphoAddr = this.config.contracts.morphoBlue;
1456
- const usdcAddr = this.config.contracts.usdc;
1457
1520
  let params;
1458
1521
  let usedCollateral;
1459
1522
  if (collateralSymbol) {
1460
- params = await this.findMarketForCollateral(collateralSymbol);
1523
+ params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
1461
1524
  usedCollateral = collateralSymbol;
1462
1525
  } else {
1463
- const rates = await this.getMarketRates();
1526
+ const rates = await this.getMarketRates(void 0, loanTokenSymbol);
1464
1527
  if (rates.length === 0) throw new AgetherError("No markets available", "NO_MARKETS");
1465
1528
  const best = rates.reduce((a, b) => a.supplyApy > b.supplyApy ? a : b);
1466
- params = await this.findMarketForCollateral(best.collateralToken);
1529
+ params = await this.findMarketForCollateral(best.collateralToken, loanTokenSymbol);
1467
1530
  usedCollateral = best.collateralToken;
1468
1531
  }
1532
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1533
+ const loanTokenAddr = params.loanToken;
1534
+ const parsedAmount = ethers2.parseUnits(amount, loanDecimals);
1469
1535
  const marketId = ethers2.keccak256(
1470
1536
  ethers2.AbiCoder.defaultAbiCoder().encode(
1471
1537
  ["address", "address", "address", "address", "uint256"],
1472
1538
  [params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
1473
1539
  )
1474
1540
  );
1475
- const usdcContract = new Contract2(usdcAddr, ERC20_ABI, this._signer);
1476
- const acctBalance = await usdcContract.balanceOf(acctAddr);
1477
- if (acctBalance < amount) {
1478
- const shortfall = amount - acctBalance;
1479
- 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());
1480
1546
  if (eoaBalance < shortfall) {
1547
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
1548
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
1481
1549
  throw new AgetherError(
1482
- `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)}.`,
1483
1551
  "INSUFFICIENT_BALANCE"
1484
1552
  );
1485
1553
  }
1486
- const transferTx = await usdcContract.transfer(acctAddr, shortfall);
1554
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
1487
1555
  await transferTx.wait();
1488
1556
  this._refreshSigner();
1489
1557
  }
1490
- const targets = [usdcAddr, morphoAddr];
1558
+ const targets = [loanTokenAddr, morphoAddr];
1491
1559
  const values = [0n, 0n];
1492
1560
  const datas = [
1493
- erc20Iface2.encodeFunctionData("approve", [morphoAddr, amount]),
1561
+ erc20Iface2.encodeFunctionData("approve", [morphoAddr, parsedAmount]),
1494
1562
  morphoIface.encodeFunctionData("supply", [
1495
1563
  this._toTuple(params),
1496
- amount,
1564
+ parsedAmount,
1497
1565
  0n,
1498
1566
  acctAddr,
1499
1567
  "0x"
@@ -1502,7 +1570,7 @@ var MorphoClient = class {
1502
1570
  const receipt = await this.batch(targets, values, datas);
1503
1571
  return {
1504
1572
  tx: receipt.hash,
1505
- amount: usdcAmount,
1573
+ amount,
1506
1574
  marketId,
1507
1575
  collateralToken: usedCollateral,
1508
1576
  agentAccount: acctAddr
@@ -1515,17 +1583,26 @@ var MorphoClient = class {
1515
1583
  * @param collateralSymbol - Market collateral to identify which market
1516
1584
  * @param receiver - Destination address (defaults to EOA)
1517
1585
  */
1518
- 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) {
1519
1595
  const acctAddr = await this.getAccountAddress();
1520
1596
  const morphoAddr = this.config.contracts.morphoBlue;
1521
1597
  const dest = receiver || await this.getSignerAddress();
1522
1598
  let params;
1523
1599
  if (collateralSymbol) {
1524
- params = await this.findMarketForCollateral(collateralSymbol);
1600
+ params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
1525
1601
  } else {
1526
1602
  const { params: p } = await this._findActiveSupplyMarket();
1527
1603
  params = p;
1528
1604
  }
1605
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1529
1606
  const marketId = ethers2.keccak256(
1530
1607
  ethers2.AbiCoder.defaultAbiCoder().encode(
1531
1608
  ["address", "address", "address", "address", "uint256"],
@@ -1534,13 +1611,13 @@ var MorphoClient = class {
1534
1611
  );
1535
1612
  let withdrawAssets;
1536
1613
  let withdrawShares;
1537
- if (usdcAmount === "all") {
1614
+ if (amount === "all") {
1538
1615
  const pos = await this.morphoBlue.position(marketId, acctAddr);
1539
1616
  withdrawShares = BigInt(pos.supplyShares);
1540
1617
  withdrawAssets = 0n;
1541
1618
  if (withdrawShares === 0n) throw new AgetherError("No supply position to withdraw", "NO_SUPPLY");
1542
1619
  } else {
1543
- withdrawAssets = ethers2.parseUnits(usdcAmount, 6);
1620
+ withdrawAssets = ethers2.parseUnits(amount, loanDecimals);
1544
1621
  withdrawShares = 0n;
1545
1622
  }
1546
1623
  const data = morphoIface.encodeFunctionData("withdraw", [
@@ -1558,13 +1635,13 @@ var MorphoClient = class {
1558
1635
  const totalSupplyAssets = BigInt(mkt.totalSupplyAssets);
1559
1636
  const totalSupplyShares = BigInt(mkt.totalSupplyShares);
1560
1637
  const currentAssets = totalSupplyShares > 0n ? BigInt(pos.supplyShares) * totalSupplyAssets / totalSupplyShares : 0n;
1561
- remainingSupply = ethers2.formatUnits(currentAssets, 6);
1638
+ remainingSupply = ethers2.formatUnits(currentAssets, loanDecimals);
1562
1639
  } catch (e) {
1563
1640
  console.warn("[agether] failed to read remaining supply:", e instanceof Error ? e.message : e);
1564
1641
  }
1565
1642
  return {
1566
1643
  tx: receipt.hash,
1567
- amount: usdcAmount,
1644
+ amount,
1568
1645
  remainingSupply,
1569
1646
  destination: dest
1570
1647
  };
@@ -1637,14 +1714,13 @@ var MorphoClient = class {
1637
1714
  * Computes available yield, verifies the requested amount doesn't exceed it,
1638
1715
  * then withdraws from the supply position and sends directly to the recipient.
1639
1716
  *
1640
- * @param recipient - Address to receive the USDC
1641
- * @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')
1642
1719
  * @param collateralSymbol - Market collateral to identify which supply position
1643
1720
  */
1644
- async payFromYield(recipient, usdcAmount, collateralSymbol) {
1721
+ async payFromYield(recipient, amount, collateralSymbol) {
1645
1722
  const acctAddr = await this.getAccountAddress();
1646
1723
  const morphoAddr = this.config.contracts.morphoBlue;
1647
- const amount = ethers2.parseUnits(usdcAmount, 6);
1648
1724
  const positions = await this.getSupplyPositions(collateralSymbol);
1649
1725
  if (positions.length === 0) {
1650
1726
  throw new AgetherError("No supply position found", "NO_SUPPLY");
@@ -1652,17 +1728,20 @@ var MorphoClient = class {
1652
1728
  const pos = positions.reduce(
1653
1729
  (a, b) => parseFloat(a.earnedYield) > parseFloat(b.earnedYield) ? a : b
1654
1730
  );
1655
- const availableYield = ethers2.parseUnits(pos.earnedYield, 6);
1656
- 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;
1657
1737
  throw new AgetherError(
1658
- `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.`,
1659
1739
  "EXCEEDS_YIELD"
1660
1740
  );
1661
1741
  }
1662
- const params = await this.findMarketForCollateral(pos.collateralToken);
1663
1742
  const data = morphoIface.encodeFunctionData("withdraw", [
1664
1743
  this._toTuple(params),
1665
- amount,
1744
+ parsedAmount,
1666
1745
  0n,
1667
1746
  acctAddr,
1668
1747
  recipient
@@ -1681,7 +1760,7 @@ var MorphoClient = class {
1681
1760
  }
1682
1761
  return {
1683
1762
  tx: receipt.hash,
1684
- yieldWithdrawn: usdcAmount,
1763
+ yieldWithdrawn: amount,
1685
1764
  recipient,
1686
1765
  remainingYield,
1687
1766
  remainingSupply
@@ -1739,28 +1818,31 @@ var MorphoClient = class {
1739
1818
  };
1740
1819
  }
1741
1820
  /**
1742
- * Borrow USDC against existing collateral.
1821
+ * Borrow loan token against existing collateral.
1743
1822
  *
1744
1823
  * AgentAccount.execute: Morpho.borrow(params, amount, 0, account, account)
1745
1824
  *
1746
- * @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)
1747
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')
1748
1829
  */
1749
- async borrow(usdcAmount, tokenSymbol, marketParams) {
1830
+ async borrow(amount, tokenSymbol, marketParams, loanTokenSymbol) {
1750
1831
  const acctAddr = await this.getAccountAddress();
1751
- const amount = ethers2.parseUnits(usdcAmount, 6);
1752
1832
  const morphoAddr = this.config.contracts.morphoBlue;
1753
1833
  let params;
1754
1834
  let usedToken = tokenSymbol || "WETH";
1755
1835
  if (marketParams) {
1756
1836
  params = marketParams;
1757
1837
  } else if (tokenSymbol) {
1758
- params = await this.findMarketForCollateral(tokenSymbol);
1838
+ params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1759
1839
  } else {
1760
1840
  const { params: p, symbol } = await this._findActiveMarket();
1761
1841
  params = p;
1762
1842
  usedToken = symbol;
1763
1843
  }
1844
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1845
+ const parsedAmount = ethers2.parseUnits(amount, loanDecimals);
1764
1846
  try {
1765
1847
  const marketId = ethers2.keccak256(
1766
1848
  ethers2.AbiCoder.defaultAbiCoder().encode(
@@ -1784,11 +1866,14 @@ var MorphoClient = class {
1784
1866
  const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
1785
1867
  const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1786
1868
  const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
1787
- if (amount > maxAdditional) {
1788
- const maxUsd = ethers2.formatUnits(maxAdditional, 6);
1789
- 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);
1790
1875
  throw new AgetherError(
1791
- `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.`,
1792
1877
  "EXCEEDS_MAX_LTV"
1793
1878
  );
1794
1879
  }
@@ -1798,7 +1883,7 @@ var MorphoClient = class {
1798
1883
  }
1799
1884
  const data = morphoIface.encodeFunctionData("borrow", [
1800
1885
  this._toTuple(params),
1801
- amount,
1886
+ parsedAmount,
1802
1887
  0n,
1803
1888
  acctAddr,
1804
1889
  acctAddr
@@ -1806,7 +1891,7 @@ var MorphoClient = class {
1806
1891
  const receipt = await this.exec(morphoAddr, data);
1807
1892
  return {
1808
1893
  tx: receipt.hash,
1809
- amount: usdcAmount,
1894
+ amount,
1810
1895
  collateralToken: usedToken,
1811
1896
  agentAccount: acctAddr
1812
1897
  };
@@ -1814,17 +1899,27 @@ var MorphoClient = class {
1814
1899
  /**
1815
1900
  * Deposit collateral AND borrow USDC in one batched transaction.
1816
1901
  *
1902
+ /**
1903
+ * Deposit collateral AND borrow loan token in one batched transaction.
1904
+ *
1817
1905
  * AgentAccount.executeBatch:
1818
1906
  * [collateral.approve, Morpho.supplyCollateral, Morpho.borrow]
1819
1907
  *
1820
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)
1821
1915
  */
1822
- async depositAndBorrow(tokenSymbol, collateralAmount, borrowUsdcAmount, marketParams) {
1916
+ async depositAndBorrow(tokenSymbol, collateralAmount, borrowAmount, marketParams, loanTokenSymbol) {
1823
1917
  const acctAddr = await this.getAccountAddress();
1824
1918
  const colInfo = await this._resolveToken(tokenSymbol);
1825
- const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
1919
+ const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1920
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1826
1921
  const colWei = ethers2.parseUnits(collateralAmount, colInfo.decimals);
1827
- const borrowWei = ethers2.parseUnits(borrowUsdcAmount, 6);
1922
+ const borrowWei = ethers2.parseUnits(borrowAmount, loanDecimals);
1828
1923
  const morphoAddr = this.config.contracts.morphoBlue;
1829
1924
  try {
1830
1925
  const marketId = ethers2.keccak256(
@@ -1845,9 +1940,11 @@ var MorphoClient = class {
1845
1940
  const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1846
1941
  const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
1847
1942
  if (borrowWei > maxAdditional) {
1848
- 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);
1849
1946
  throw new AgetherError(
1850
- `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.`,
1851
1948
  "EXCEEDS_MAX_LTV"
1852
1949
  );
1853
1950
  }
@@ -1893,36 +1990,42 @@ var MorphoClient = class {
1893
1990
  tx: receipt.hash,
1894
1991
  collateralToken: tokenSymbol,
1895
1992
  collateralAmount,
1896
- borrowAmount: borrowUsdcAmount,
1993
+ borrowAmount,
1897
1994
  agentAccount: acctAddr
1898
1995
  };
1899
1996
  }
1900
1997
  /**
1901
- * Repay borrowed USDC from AgentAccount.
1998
+ * Repay borrowed loan token from AgentAccount.
1902
1999
  *
1903
2000
  * AgentAccount.executeBatch:
1904
- * [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)
1905
2007
  */
1906
- async repay(usdcAmount, tokenSymbol, marketParams) {
2008
+ async repay(amount, tokenSymbol, marketParams, loanTokenSymbol) {
1907
2009
  const acctAddr = await this.getAccountAddress();
1908
2010
  const morphoAddr = this.config.contracts.morphoBlue;
1909
- const usdcAddr = this.config.contracts.usdc;
1910
2011
  let params;
1911
2012
  if (marketParams) {
1912
2013
  params = marketParams;
1913
2014
  } else if (tokenSymbol) {
1914
- params = await this.findMarketForCollateral(tokenSymbol);
2015
+ params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1915
2016
  } else {
1916
2017
  const { params: p } = await this._findActiveMarket();
1917
2018
  params = p;
1918
2019
  }
2020
+ const loanTokenAddr = params.loanToken;
2021
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1919
2022
  let repayAssets;
1920
2023
  let repayShares;
1921
2024
  let approveAmount;
1922
- if (usdcAmount === "all") {
2025
+ if (amount === "all") {
1923
2026
  const markets = await this.getMarkets();
1924
2027
  const mkt = markets.find(
1925
- (m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase()
2028
+ (m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase() && m.loanAsset?.address.toLowerCase() === params.loanToken.toLowerCase()
1926
2029
  );
1927
2030
  if (mkt) {
1928
2031
  const pos = await this.morphoBlue.position(mkt.uniqueKey, acctAddr);
@@ -1932,33 +2035,35 @@ var MorphoClient = class {
1932
2035
  const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
1933
2036
  const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
1934
2037
  const estimated = totalBorrowShares > 0n ? repayShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
1935
- approveAmount = estimated > 0n ? estimated : ethers2.parseUnits("1", 6);
2038
+ approveAmount = estimated > 0n ? estimated : ethers2.parseUnits("1", loanDecimals);
1936
2039
  } else {
1937
- repayAssets = ethers2.parseUnits("999999", 6);
2040
+ repayAssets = ethers2.parseUnits("999999", loanDecimals);
1938
2041
  repayShares = 0n;
1939
2042
  approveAmount = repayAssets;
1940
2043
  }
1941
2044
  } else {
1942
- repayAssets = ethers2.parseUnits(usdcAmount, 6);
2045
+ repayAssets = ethers2.parseUnits(amount, loanDecimals);
1943
2046
  repayShares = 0n;
1944
2047
  approveAmount = repayAssets;
1945
2048
  }
1946
- const usdcContract = new Contract2(usdcAddr, ERC20_ABI, this._signer);
1947
- const acctBalance = await usdcContract.balanceOf(acctAddr);
2049
+ const loanContract = new Contract2(loanTokenAddr, ERC20_ABI, this._signer);
2050
+ const acctBalance = await loanContract.balanceOf(acctAddr);
1948
2051
  if (acctBalance < approveAmount) {
1949
2052
  const shortfall = approveAmount - acctBalance;
1950
- const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
2053
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
1951
2054
  if (eoaBalance < shortfall) {
2055
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
2056
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
1952
2057
  throw new AgetherError(
1953
- `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)}.`,
1954
2059
  "INSUFFICIENT_BALANCE"
1955
2060
  );
1956
2061
  }
1957
- const transferTx = await usdcContract.transfer(acctAddr, shortfall);
2062
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
1958
2063
  await transferTx.wait();
1959
2064
  this._refreshSigner();
1960
2065
  }
1961
- const targets = [usdcAddr, morphoAddr];
2066
+ const targets = [loanTokenAddr, morphoAddr];
1962
2067
  const values = [0n, 0n];
1963
2068
  const datas = [
1964
2069
  erc20Iface2.encodeFunctionData("approve", [morphoAddr, approveAmount]),
@@ -1978,7 +2083,7 @@ var MorphoClient = class {
1978
2083
  } catch (e) {
1979
2084
  console.warn("[agether] failed to read remaining debt after repay:", e instanceof Error ? e.message : e);
1980
2085
  }
1981
- return { tx: receipt.hash, amount: usdcAmount, remainingDebt };
2086
+ return { tx: receipt.hash, amount, remainingDebt };
1982
2087
  }
1983
2088
  /**
1984
2089
  * Withdraw collateral from Morpho Blue.
@@ -1992,12 +2097,13 @@ var MorphoClient = class {
1992
2097
  const colInfo = await this._resolveToken(tokenSymbol);
1993
2098
  const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
1994
2099
  const morphoAddr = this.config.contracts.morphoBlue;
1995
- const usdcAddr = this.config.contracts.usdc;
2100
+ const loanTokenAddr = params.loanToken;
2101
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1996
2102
  const dest = receiver || await this.getSignerAddress();
1997
2103
  let weiAmount;
1998
2104
  const markets = await this.getMarkets();
1999
2105
  const market = markets.find(
2000
- (m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase()
2106
+ (m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase() && m.loanAsset?.address.toLowerCase() === loanTokenAddr.toLowerCase()
2001
2107
  );
2002
2108
  if (amount === "all") {
2003
2109
  if (!market) throw new AgetherError("Market not found", "MARKET_NOT_FOUND");
@@ -2020,8 +2126,10 @@ var MorphoClient = class {
2020
2126
  const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
2021
2127
  const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
2022
2128
  const estimated = totalBorrowShares > 0n ? dustBorrowShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
2023
- dustApproveAmount = estimated > 0n ? estimated : ethers2.parseUnits("1", 6);
2024
- 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`);
2025
2133
  }
2026
2134
  } catch (e) {
2027
2135
  console.warn("[agether] failed to check borrow shares before withdraw:", e instanceof Error ? e.message : e);
@@ -2035,19 +2143,21 @@ var MorphoClient = class {
2035
2143
  ]);
2036
2144
  let receipt;
2037
2145
  if (hasDustDebt) {
2038
- const usdcContract = new Contract2(usdcAddr, ERC20_ABI, this._signer);
2039
- const acctBalance = await usdcContract.balanceOf(acctAddr);
2146
+ const loanContract = new Contract2(loanTokenAddr, ERC20_ABI, this._signer);
2147
+ const acctBalance = await loanContract.balanceOf(acctAddr);
2040
2148
  if (acctBalance < dustApproveAmount) {
2041
2149
  const shortfall = dustApproveAmount - acctBalance;
2042
- const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
2150
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
2043
2151
  if (eoaBalance >= shortfall) {
2044
- console.log(`[agether] transferring ${ethers2.formatUnits(shortfall, 6)} USDC from EOA \u2192 AgentAccount for dust repay`);
2045
- 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);
2046
2156
  await transferTx.wait();
2047
2157
  this._refreshSigner();
2048
2158
  }
2049
2159
  }
2050
- const targets = [usdcAddr, morphoAddr, morphoAddr];
2160
+ const targets = [loanTokenAddr, morphoAddr, morphoAddr];
2051
2161
  const values = [0n, 0n, 0n];
2052
2162
  const datas = [
2053
2163
  erc20Iface2.encodeFunctionData("approve", [morphoAddr, dustApproveAmount]),
@@ -2292,6 +2402,37 @@ var MorphoClient = class {
2292
2402
  }
2293
2403
  throw new AgetherError("No active supply position found", "NO_SUPPLY");
2294
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
+ }
2295
2436
  /**
2296
2437
  * Resolve a token symbol or address to { address, symbol, decimals }.
2297
2438
  *
@@ -2308,8 +2449,8 @@ var MorphoClient = class {
2308
2449
  const fromApi = this._tokenCache.get(key);
2309
2450
  if (fromApi) return fromApi;
2310
2451
  throw new AgetherError(
2311
- `Unknown token: ${symbolOrAddress}. No Morpho market found with this collateral.`,
2312
- "UNKNOWN_COLLATERAL"
2452
+ `Unknown token: ${symbolOrAddress}. No Morpho market found with this token.`,
2453
+ "UNKNOWN_TOKEN"
2313
2454
  );
2314
2455
  }
2315
2456
  /**