@agether/sdk 2.12.1 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -129,6 +129,7 @@ var IDENTITY_REGISTRY_ABI = [
129
129
  "function exists(uint256 agentId) view returns (bool)",
130
130
  "function register() returns (uint256 agentId)",
131
131
  "function register(string agentURI) returns (uint256 agentId)",
132
+ "function setAgentURI(uint256 agentId, string newURI)",
132
133
  "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
133
134
  ];
134
135
  var AGETHER_4337_FACTORY_ABI = [
@@ -492,6 +493,23 @@ var AgetherClient = class _AgetherClient {
492
493
  }
493
494
  const acctAddr = await this.agether4337Factory.getAccount(agentId);
494
495
  this.accountAddress = acctAddr;
496
+ if (!acctExists) {
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();
512
+ }
495
513
  const kyaRequired = await this.isKyaRequired();
496
514
  return {
497
515
  agentId: agentId.toString(),
@@ -508,7 +526,7 @@ var AgetherClient = class _AgetherClient {
508
526
  async _mintNewIdentity(name, description) {
509
527
  const registrationFile = JSON.stringify({
510
528
  type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
511
- name: name || "Unnamed Agent",
529
+ name: name || "Agether Agent",
512
530
  description: description || "AI agent registered via @agether/sdk",
513
531
  active: true,
514
532
  registrations: [{
@@ -1019,8 +1037,9 @@ var morphoIface = new import_ethers2.ethers.Interface(MORPHO_BLUE_ABI);
1019
1037
  var erc20Iface2 = new import_ethers2.ethers.Interface(ERC20_ABI);
1020
1038
  var MorphoClient = class {
1021
1039
  constructor(config) {
1040
+ /** Market params cache: keyed by market uniqueKey (bytes32 hash) */
1022
1041
  this._marketCache = /* @__PURE__ */ new Map();
1023
- /** Dynamic token registry: symbol (uppercase) → { address, symbol, decimals } */
1042
+ /** Dynamic token registry: symbol (uppercase) or address (lowercase) → { address, symbol, decimals } */
1024
1043
  this._tokenCache = /* @__PURE__ */ new Map();
1025
1044
  this._discoveredAt = 0;
1026
1045
  if (!config.agentId) {
@@ -1123,21 +1142,23 @@ var MorphoClient = class {
1123
1142
  // Market Discovery (Morpho GraphQL API)
1124
1143
  // ════════════════════════════════════════════════════════
1125
1144
  /**
1126
- * Fetch USDC borrow markets on Base from Morpho API.
1127
- * 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
1128
1150
  */
1129
- async getMarkets(forceRefresh = false) {
1151
+ async getMarkets(forceRefresh = false, filter) {
1130
1152
  if (!forceRefresh && this._discoveredMarkets && Date.now() - this._discoveredAt < 3e5) {
1131
- return this._discoveredMarkets;
1153
+ return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
1132
1154
  }
1133
1155
  const chainId = this.config.chainId;
1134
- const usdcAddr = this.config.contracts.usdc.toLowerCase();
1135
1156
  const query = `{
1136
1157
  markets(
1137
1158
  first: 50
1138
1159
  orderBy: SupplyAssetsUsd
1139
1160
  orderDirection: Desc
1140
- where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"] }
1161
+ where: { chainId_in: [${chainId}] }
1141
1162
  ) {
1142
1163
  items {
1143
1164
  uniqueKey
@@ -1170,14 +1191,14 @@ var MorphoClient = class {
1170
1191
  }));
1171
1192
  this._discoveredAt = Date.now();
1172
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
+ });
1173
1201
  if (mi.collateralAsset.address !== import_ethers2.ethers.ZeroAddress) {
1174
- this._marketCache.set(mi.collateralAsset.address.toLowerCase(), {
1175
- loanToken: mi.loanAsset.address,
1176
- collateralToken: mi.collateralAsset.address,
1177
- oracle: mi.oracle,
1178
- irm: mi.irm,
1179
- lltv: mi.lltv
1180
- });
1181
1202
  this._tokenCache.set(mi.collateralAsset.symbol.toUpperCase(), {
1182
1203
  address: mi.collateralAsset.address,
1183
1204
  symbol: mi.collateralAsset.symbol,
@@ -1202,17 +1223,24 @@ var MorphoClient = class {
1202
1223
  });
1203
1224
  }
1204
1225
  }
1205
- return this._discoveredMarkets;
1226
+ return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
1206
1227
  } catch (e) {
1207
1228
  console.warn("[agether] getMarkets failed, using cache:", e instanceof Error ? e.message : e);
1208
- return this._discoveredMarkets ?? [];
1229
+ const cached = this._discoveredMarkets ?? [];
1230
+ return filter ? this._applyMarketFilter(cached, filter) : cached;
1209
1231
  }
1210
1232
  }
1211
1233
  /**
1212
- * Get MarketParams for a collateral token.
1213
- * 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)
1214
1242
  */
1215
- async findMarketForCollateral(collateralSymbolOrAddress) {
1243
+ async findMarketForCollateral(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
1216
1244
  let colAddr;
1217
1245
  if (collateralSymbolOrAddress.startsWith("0x")) {
1218
1246
  colAddr = collateralSymbolOrAddress.toLowerCase();
@@ -1224,13 +1252,33 @@ var MorphoClient = class {
1224
1252
  colAddr = collateralSymbolOrAddress.toLowerCase();
1225
1253
  }
1226
1254
  }
1227
- const cached = this._marketCache.get(colAddr);
1228
- if (cached) return cached;
1229
- await this.getMarkets();
1230
- const fromApi = this._marketCache.get(colAddr);
1231
- 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
+ }
1232
1280
  throw new AgetherError(
1233
- `No Morpho market found for collateral ${collateralSymbolOrAddress}`,
1281
+ `No Morpho market found for collateral ${collateralSymbolOrAddress}` + (loanTokenSymbolOrAddress ? ` with loan token ${loanTokenSymbolOrAddress}` : ""),
1234
1282
  "MARKET_NOT_FOUND"
1235
1283
  );
1236
1284
  }
@@ -1265,12 +1313,13 @@ var MorphoClient = class {
1265
1313
  const acctAddr = await this.getAccountAddress();
1266
1314
  const markets = await this.getMarkets();
1267
1315
  const positions = [];
1268
- let totalDebt = 0n;
1316
+ let totalDebtFloat = 0;
1269
1317
  for (const m of markets) {
1270
1318
  if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
1271
1319
  try {
1272
1320
  const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
1273
1321
  if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
1322
+ const loanDecimals = m.loanAsset.decimals;
1274
1323
  let debt = 0n;
1275
1324
  if (pos.borrowShares > 0n) {
1276
1325
  try {
@@ -1278,7 +1327,7 @@ var MorphoClient = class {
1278
1327
  const totalBorrowShares = BigInt(mkt.totalBorrowShares);
1279
1328
  const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
1280
1329
  debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1281
- totalDebt += debt;
1330
+ totalDebtFloat += parseFloat(import_ethers2.ethers.formatUnits(debt, loanDecimals));
1282
1331
  } catch (e) {
1283
1332
  console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
1284
1333
  }
@@ -1286,10 +1335,11 @@ var MorphoClient = class {
1286
1335
  positions.push({
1287
1336
  marketId: m.uniqueKey,
1288
1337
  collateralToken: m.collateralAsset.symbol,
1338
+ loanToken: m.loanAsset.symbol,
1289
1339
  collateral: import_ethers2.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
1290
1340
  borrowShares: pos.borrowShares.toString(),
1291
1341
  supplyShares: pos.supplyShares.toString(),
1292
- debt: import_ethers2.ethers.formatUnits(debt, 6)
1342
+ debt: import_ethers2.ethers.formatUnits(debt, loanDecimals)
1293
1343
  });
1294
1344
  } catch (e) {
1295
1345
  console.warn(`[agether] position read failed for market:`, e instanceof Error ? e.message : e);
@@ -1299,16 +1349,28 @@ var MorphoClient = class {
1299
1349
  return {
1300
1350
  agentId: this.agentId,
1301
1351
  agentAccount: acctAddr,
1302
- totalDebt: import_ethers2.ethers.formatUnits(totalDebt, 6),
1352
+ totalDebt: totalDebtFloat.toFixed(6),
1303
1353
  positions
1304
1354
  };
1305
1355
  }
1306
1356
  // ════════════════════════════════════════════════════════
1307
1357
  // Balance & Borrowing Capacity
1308
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
+ }
1309
1370
  /**
1310
1371
  * Get the USDC balance of the AgentAccount.
1311
1372
  * @returns USDC balance in raw units (6 decimals)
1373
+ * @deprecated Use `getTokenBalance('USDC')` instead.
1312
1374
  */
1313
1375
  async getUsdcBalance() {
1314
1376
  const acctAddr = await this.getAccountAddress();
@@ -1316,7 +1378,7 @@ var MorphoClient = class {
1316
1378
  return usdc.balanceOf(acctAddr);
1317
1379
  }
1318
1380
  /**
1319
- * Calculate the maximum additional USDC that can be borrowed
1381
+ * Calculate the maximum additional loan token that can be borrowed
1320
1382
  * given the agent's current collateral and debt across all markets.
1321
1383
  *
1322
1384
  * For each market with collateral deposited:
@@ -1324,7 +1386,7 @@ var MorphoClient = class {
1324
1386
  *
1325
1387
  * Uses the Morpho oracle to price collateral → loan token.
1326
1388
  *
1327
- * @returns Maximum additional USDC borrowable (6 decimals)
1389
+ * @returns Maximum additional borrowable per market (raw units in each market's loan token)
1328
1390
  */
1329
1391
  async getMaxBorrowable() {
1330
1392
  const acctAddr = await this.getAccountAddress();
@@ -1357,6 +1419,8 @@ var MorphoClient = class {
1357
1419
  totalAdditional += maxAdditional;
1358
1420
  byMarket.push({
1359
1421
  collateralToken: m.collateralAsset.symbol,
1422
+ loanToken: m.loanAsset.symbol,
1423
+ loanDecimals: m.loanAsset.decimals,
1360
1424
  maxAdditional,
1361
1425
  currentDebt,
1362
1426
  collateralValue: collateralValueInLoan
@@ -1372,14 +1436,16 @@ var MorphoClient = class {
1372
1436
  // Market Rates & Yield Estimation
1373
1437
  // ════════════════════════════════════════════════════════
1374
1438
  /**
1375
- * 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.
1376
1440
  *
1377
1441
  * Note: On Morpho Blue, collateral does NOT earn yield directly. Supply APY
1378
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.
1379
1446
  */
1380
- async getMarketRates(collateralSymbolOrAddress) {
1447
+ async getMarketRates(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
1381
1448
  const chainId = this.config.chainId;
1382
- const usdcAddr = this.config.contracts.usdc.toLowerCase();
1383
1449
  let collateralFilter = "";
1384
1450
  if (collateralSymbolOrAddress) {
1385
1451
  let colAddr;
@@ -1395,12 +1461,27 @@ var MorphoClient = class {
1395
1461
  }
1396
1462
  collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
1397
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
+ }
1398
1479
  const query = `{
1399
1480
  markets(
1400
1481
  first: 50
1401
1482
  orderBy: SupplyAssetsUsd
1402
1483
  orderDirection: Desc
1403
- where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"]${collateralFilter} }
1484
+ where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter} }
1404
1485
  ) {
1405
1486
  items {
1406
1487
  uniqueKey
@@ -1420,17 +1501,21 @@ var MorphoClient = class {
1420
1501
  try {
1421
1502
  const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
1422
1503
  const items = resp.data?.data?.markets?.items ?? [];
1423
- return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers2.ethers.ZeroAddress).map((m) => ({
1424
- collateralToken: m.collateralAsset.symbol,
1425
- loanToken: m.loanAsset.symbol,
1426
- supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
1427
- borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
1428
- utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
1429
- totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 1e6 : 0,
1430
- totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 1e6 : 0,
1431
- lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
1432
- marketId: m.uniqueKey
1433
- }));
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
+ });
1434
1519
  } catch (e) {
1435
1520
  console.warn("[agether] getMarketRates failed:", e instanceof Error ? e.message : e);
1436
1521
  return [];
@@ -1464,14 +1549,15 @@ var MorphoClient = class {
1464
1549
  } else {
1465
1550
  try {
1466
1551
  const params = await this.findMarketForCollateral(collateralSymbol);
1552
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1467
1553
  const oracleContract = new import_ethers2.Contract(params.oracle, [
1468
1554
  "function price() view returns (uint256)"
1469
1555
  ], this.provider);
1470
1556
  const oraclePrice = await oracleContract.price();
1471
1557
  const ORACLE_PRICE_SCALE = 10n ** 36n;
1472
1558
  const amountWei = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
1473
- const valueInUsdc = amountWei * oraclePrice / ORACLE_PRICE_SCALE;
1474
- collateralValueUsd = Number(valueInUsdc) / 1e6;
1559
+ const valueInLoan = amountWei * oraclePrice / ORACLE_PRICE_SCALE;
1560
+ collateralValueUsd = Number(valueInLoan) / 10 ** loanDecimals;
1475
1561
  } catch (e) {
1476
1562
  console.warn("[agether] oracle price fetch for yield estimation failed:", e instanceof Error ? e.message : e);
1477
1563
  throw new AgetherError("Cannot determine collateral value. Provide ethPriceUsd.", "PRICE_UNAVAILABLE");
@@ -1492,61 +1578,65 @@ var MorphoClient = class {
1492
1578
  // Supply-Side (Lending) — earn yield by supplying USDC
1493
1579
  // ════════════════════════════════════════════════════════
1494
1580
  /**
1495
- * 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).
1496
1582
  *
1497
1583
  * Unlike `supplyCollateral` (borrower-side), this is the **lender-side**:
1498
- * 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
1499
1585
  * interest paid by borrowers.
1500
1586
  *
1501
- * @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)
1502
1588
  * @param collateralSymbol - Market collateral token to identify which market (e.g. 'WETH')
1503
1589
  * Optional — defaults to highest-APY market
1590
+ * @param loanTokenSymbol - Loan token to filter market (e.g. 'USDC', 'WETH'). Optional.
1504
1591
  */
1505
- async supplyAsset(usdcAmount, collateralSymbol) {
1592
+ async supplyAsset(amount, collateralSymbol, loanTokenSymbol) {
1506
1593
  const acctAddr = await this.getAccountAddress();
1507
- const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
1508
1594
  const morphoAddr = this.config.contracts.morphoBlue;
1509
- const usdcAddr = this.config.contracts.usdc;
1510
1595
  let params;
1511
1596
  let usedCollateral;
1512
1597
  if (collateralSymbol) {
1513
- params = await this.findMarketForCollateral(collateralSymbol);
1598
+ params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
1514
1599
  usedCollateral = collateralSymbol;
1515
1600
  } else {
1516
- const rates = await this.getMarketRates();
1601
+ const rates = await this.getMarketRates(void 0, loanTokenSymbol);
1517
1602
  if (rates.length === 0) throw new AgetherError("No markets available", "NO_MARKETS");
1518
1603
  const best = rates.reduce((a, b) => a.supplyApy > b.supplyApy ? a : b);
1519
- params = await this.findMarketForCollateral(best.collateralToken);
1604
+ params = await this.findMarketForCollateral(best.collateralToken, loanTokenSymbol);
1520
1605
  usedCollateral = best.collateralToken;
1521
1606
  }
1607
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1608
+ const loanTokenAddr = params.loanToken;
1609
+ const parsedAmount = import_ethers2.ethers.parseUnits(amount, loanDecimals);
1522
1610
  const marketId = import_ethers2.ethers.keccak256(
1523
1611
  import_ethers2.ethers.AbiCoder.defaultAbiCoder().encode(
1524
1612
  ["address", "address", "address", "address", "uint256"],
1525
1613
  [params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
1526
1614
  )
1527
1615
  );
1528
- const usdcContract = new import_ethers2.Contract(usdcAddr, ERC20_ABI, this._signer);
1529
- const acctBalance = await usdcContract.balanceOf(acctAddr);
1530
- if (acctBalance < amount) {
1531
- const shortfall = amount - acctBalance;
1532
- 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());
1533
1621
  if (eoaBalance < shortfall) {
1622
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
1623
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
1534
1624
  throw new AgetherError(
1535
- `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)}.`,
1536
1626
  "INSUFFICIENT_BALANCE"
1537
1627
  );
1538
1628
  }
1539
- const transferTx = await usdcContract.transfer(acctAddr, shortfall);
1629
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
1540
1630
  await transferTx.wait();
1541
1631
  this._refreshSigner();
1542
1632
  }
1543
- const targets = [usdcAddr, morphoAddr];
1633
+ const targets = [loanTokenAddr, morphoAddr];
1544
1634
  const values = [0n, 0n];
1545
1635
  const datas = [
1546
- erc20Iface2.encodeFunctionData("approve", [morphoAddr, amount]),
1636
+ erc20Iface2.encodeFunctionData("approve", [morphoAddr, parsedAmount]),
1547
1637
  morphoIface.encodeFunctionData("supply", [
1548
1638
  this._toTuple(params),
1549
- amount,
1639
+ parsedAmount,
1550
1640
  0n,
1551
1641
  acctAddr,
1552
1642
  "0x"
@@ -1555,7 +1645,7 @@ var MorphoClient = class {
1555
1645
  const receipt = await this.batch(targets, values, datas);
1556
1646
  return {
1557
1647
  tx: receipt.hash,
1558
- amount: usdcAmount,
1648
+ amount,
1559
1649
  marketId,
1560
1650
  collateralToken: usedCollateral,
1561
1651
  agentAccount: acctAddr
@@ -1568,17 +1658,26 @@ var MorphoClient = class {
1568
1658
  * @param collateralSymbol - Market collateral to identify which market
1569
1659
  * @param receiver - Destination address (defaults to EOA)
1570
1660
  */
1571
- 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) {
1572
1670
  const acctAddr = await this.getAccountAddress();
1573
1671
  const morphoAddr = this.config.contracts.morphoBlue;
1574
1672
  const dest = receiver || await this.getSignerAddress();
1575
1673
  let params;
1576
1674
  if (collateralSymbol) {
1577
- params = await this.findMarketForCollateral(collateralSymbol);
1675
+ params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
1578
1676
  } else {
1579
1677
  const { params: p } = await this._findActiveSupplyMarket();
1580
1678
  params = p;
1581
1679
  }
1680
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1582
1681
  const marketId = import_ethers2.ethers.keccak256(
1583
1682
  import_ethers2.ethers.AbiCoder.defaultAbiCoder().encode(
1584
1683
  ["address", "address", "address", "address", "uint256"],
@@ -1587,13 +1686,13 @@ var MorphoClient = class {
1587
1686
  );
1588
1687
  let withdrawAssets;
1589
1688
  let withdrawShares;
1590
- if (usdcAmount === "all") {
1689
+ if (amount === "all") {
1591
1690
  const pos = await this.morphoBlue.position(marketId, acctAddr);
1592
1691
  withdrawShares = BigInt(pos.supplyShares);
1593
1692
  withdrawAssets = 0n;
1594
1693
  if (withdrawShares === 0n) throw new AgetherError("No supply position to withdraw", "NO_SUPPLY");
1595
1694
  } else {
1596
- withdrawAssets = import_ethers2.ethers.parseUnits(usdcAmount, 6);
1695
+ withdrawAssets = import_ethers2.ethers.parseUnits(amount, loanDecimals);
1597
1696
  withdrawShares = 0n;
1598
1697
  }
1599
1698
  const data = morphoIface.encodeFunctionData("withdraw", [
@@ -1611,13 +1710,13 @@ var MorphoClient = class {
1611
1710
  const totalSupplyAssets = BigInt(mkt.totalSupplyAssets);
1612
1711
  const totalSupplyShares = BigInt(mkt.totalSupplyShares);
1613
1712
  const currentAssets = totalSupplyShares > 0n ? BigInt(pos.supplyShares) * totalSupplyAssets / totalSupplyShares : 0n;
1614
- remainingSupply = import_ethers2.ethers.formatUnits(currentAssets, 6);
1713
+ remainingSupply = import_ethers2.ethers.formatUnits(currentAssets, loanDecimals);
1615
1714
  } catch (e) {
1616
1715
  console.warn("[agether] failed to read remaining supply:", e instanceof Error ? e.message : e);
1617
1716
  }
1618
1717
  return {
1619
1718
  tx: receipt.hash,
1620
- amount: usdcAmount,
1719
+ amount,
1621
1720
  remainingSupply,
1622
1721
  destination: dest
1623
1722
  };
@@ -1690,14 +1789,13 @@ var MorphoClient = class {
1690
1789
  * Computes available yield, verifies the requested amount doesn't exceed it,
1691
1790
  * then withdraws from the supply position and sends directly to the recipient.
1692
1791
  *
1693
- * @param recipient - Address to receive the USDC
1694
- * @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')
1695
1794
  * @param collateralSymbol - Market collateral to identify which supply position
1696
1795
  */
1697
- async payFromYield(recipient, usdcAmount, collateralSymbol) {
1796
+ async payFromYield(recipient, amount, collateralSymbol) {
1698
1797
  const acctAddr = await this.getAccountAddress();
1699
1798
  const morphoAddr = this.config.contracts.morphoBlue;
1700
- const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
1701
1799
  const positions = await this.getSupplyPositions(collateralSymbol);
1702
1800
  if (positions.length === 0) {
1703
1801
  throw new AgetherError("No supply position found", "NO_SUPPLY");
@@ -1705,17 +1803,20 @@ var MorphoClient = class {
1705
1803
  const pos = positions.reduce(
1706
1804
  (a, b) => parseFloat(a.earnedYield) > parseFloat(b.earnedYield) ? a : b
1707
1805
  );
1708
- const availableYield = import_ethers2.ethers.parseUnits(pos.earnedYield, 6);
1709
- 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;
1710
1812
  throw new AgetherError(
1711
- `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.`,
1712
1814
  "EXCEEDS_YIELD"
1713
1815
  );
1714
1816
  }
1715
- const params = await this.findMarketForCollateral(pos.collateralToken);
1716
1817
  const data = morphoIface.encodeFunctionData("withdraw", [
1717
1818
  this._toTuple(params),
1718
- amount,
1819
+ parsedAmount,
1719
1820
  0n,
1720
1821
  acctAddr,
1721
1822
  recipient
@@ -1734,7 +1835,7 @@ var MorphoClient = class {
1734
1835
  }
1735
1836
  return {
1736
1837
  tx: receipt.hash,
1737
- yieldWithdrawn: usdcAmount,
1838
+ yieldWithdrawn: amount,
1738
1839
  recipient,
1739
1840
  remainingYield,
1740
1841
  remainingSupply
@@ -1792,28 +1893,31 @@ var MorphoClient = class {
1792
1893
  };
1793
1894
  }
1794
1895
  /**
1795
- * Borrow USDC against existing collateral.
1896
+ * Borrow loan token against existing collateral.
1796
1897
  *
1797
1898
  * AgentAccount.execute: Morpho.borrow(params, amount, 0, account, account)
1798
1899
  *
1799
- * @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)
1800
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')
1801
1904
  */
1802
- async borrow(usdcAmount, tokenSymbol, marketParams) {
1905
+ async borrow(amount, tokenSymbol, marketParams, loanTokenSymbol) {
1803
1906
  const acctAddr = await this.getAccountAddress();
1804
- const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
1805
1907
  const morphoAddr = this.config.contracts.morphoBlue;
1806
1908
  let params;
1807
1909
  let usedToken = tokenSymbol || "WETH";
1808
1910
  if (marketParams) {
1809
1911
  params = marketParams;
1810
1912
  } else if (tokenSymbol) {
1811
- params = await this.findMarketForCollateral(tokenSymbol);
1913
+ params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1812
1914
  } else {
1813
1915
  const { params: p, symbol } = await this._findActiveMarket();
1814
1916
  params = p;
1815
1917
  usedToken = symbol;
1816
1918
  }
1919
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1920
+ const parsedAmount = import_ethers2.ethers.parseUnits(amount, loanDecimals);
1817
1921
  try {
1818
1922
  const marketId = import_ethers2.ethers.keccak256(
1819
1923
  import_ethers2.ethers.AbiCoder.defaultAbiCoder().encode(
@@ -1837,11 +1941,14 @@ var MorphoClient = class {
1837
1941
  const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
1838
1942
  const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1839
1943
  const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
1840
- if (amount > maxAdditional) {
1841
- const maxUsd = import_ethers2.ethers.formatUnits(maxAdditional, 6);
1842
- 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);
1843
1950
  throw new AgetherError(
1844
- `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.`,
1845
1952
  "EXCEEDS_MAX_LTV"
1846
1953
  );
1847
1954
  }
@@ -1851,7 +1958,7 @@ var MorphoClient = class {
1851
1958
  }
1852
1959
  const data = morphoIface.encodeFunctionData("borrow", [
1853
1960
  this._toTuple(params),
1854
- amount,
1961
+ parsedAmount,
1855
1962
  0n,
1856
1963
  acctAddr,
1857
1964
  acctAddr
@@ -1859,7 +1966,7 @@ var MorphoClient = class {
1859
1966
  const receipt = await this.exec(morphoAddr, data);
1860
1967
  return {
1861
1968
  tx: receipt.hash,
1862
- amount: usdcAmount,
1969
+ amount,
1863
1970
  collateralToken: usedToken,
1864
1971
  agentAccount: acctAddr
1865
1972
  };
@@ -1867,17 +1974,27 @@ var MorphoClient = class {
1867
1974
  /**
1868
1975
  * Deposit collateral AND borrow USDC in one batched transaction.
1869
1976
  *
1977
+ /**
1978
+ * Deposit collateral AND borrow loan token in one batched transaction.
1979
+ *
1870
1980
  * AgentAccount.executeBatch:
1871
1981
  * [collateral.approve, Morpho.supplyCollateral, Morpho.borrow]
1872
1982
  *
1873
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)
1874
1990
  */
1875
- async depositAndBorrow(tokenSymbol, collateralAmount, borrowUsdcAmount, marketParams) {
1991
+ async depositAndBorrow(tokenSymbol, collateralAmount, borrowAmount, marketParams, loanTokenSymbol) {
1876
1992
  const acctAddr = await this.getAccountAddress();
1877
1993
  const colInfo = await this._resolveToken(tokenSymbol);
1878
- const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
1994
+ const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1995
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1879
1996
  const colWei = import_ethers2.ethers.parseUnits(collateralAmount, colInfo.decimals);
1880
- const borrowWei = import_ethers2.ethers.parseUnits(borrowUsdcAmount, 6);
1997
+ const borrowWei = import_ethers2.ethers.parseUnits(borrowAmount, loanDecimals);
1881
1998
  const morphoAddr = this.config.contracts.morphoBlue;
1882
1999
  try {
1883
2000
  const marketId = import_ethers2.ethers.keccak256(
@@ -1898,9 +2015,11 @@ var MorphoClient = class {
1898
2015
  const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
1899
2016
  const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
1900
2017
  if (borrowWei > maxAdditional) {
1901
- 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);
1902
2021
  throw new AgetherError(
1903
- `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.`,
1904
2023
  "EXCEEDS_MAX_LTV"
1905
2024
  );
1906
2025
  }
@@ -1946,36 +2065,42 @@ var MorphoClient = class {
1946
2065
  tx: receipt.hash,
1947
2066
  collateralToken: tokenSymbol,
1948
2067
  collateralAmount,
1949
- borrowAmount: borrowUsdcAmount,
2068
+ borrowAmount,
1950
2069
  agentAccount: acctAddr
1951
2070
  };
1952
2071
  }
1953
2072
  /**
1954
- * Repay borrowed USDC from AgentAccount.
2073
+ * Repay borrowed loan token from AgentAccount.
1955
2074
  *
1956
2075
  * AgentAccount.executeBatch:
1957
- * [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)
1958
2082
  */
1959
- async repay(usdcAmount, tokenSymbol, marketParams) {
2083
+ async repay(amount, tokenSymbol, marketParams, loanTokenSymbol) {
1960
2084
  const acctAddr = await this.getAccountAddress();
1961
2085
  const morphoAddr = this.config.contracts.morphoBlue;
1962
- const usdcAddr = this.config.contracts.usdc;
1963
2086
  let params;
1964
2087
  if (marketParams) {
1965
2088
  params = marketParams;
1966
2089
  } else if (tokenSymbol) {
1967
- params = await this.findMarketForCollateral(tokenSymbol);
2090
+ params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
1968
2091
  } else {
1969
2092
  const { params: p } = await this._findActiveMarket();
1970
2093
  params = p;
1971
2094
  }
2095
+ const loanTokenAddr = params.loanToken;
2096
+ const loanDecimals = await this._getLoanTokenDecimals(params);
1972
2097
  let repayAssets;
1973
2098
  let repayShares;
1974
2099
  let approveAmount;
1975
- if (usdcAmount === "all") {
2100
+ if (amount === "all") {
1976
2101
  const markets = await this.getMarkets();
1977
2102
  const mkt = markets.find(
1978
- (m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase()
2103
+ (m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase() && m.loanAsset?.address.toLowerCase() === params.loanToken.toLowerCase()
1979
2104
  );
1980
2105
  if (mkt) {
1981
2106
  const pos = await this.morphoBlue.position(mkt.uniqueKey, acctAddr);
@@ -1985,33 +2110,35 @@ var MorphoClient = class {
1985
2110
  const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
1986
2111
  const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
1987
2112
  const estimated = totalBorrowShares > 0n ? repayShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
1988
- approveAmount = estimated > 0n ? estimated : import_ethers2.ethers.parseUnits("1", 6);
2113
+ approveAmount = estimated > 0n ? estimated : import_ethers2.ethers.parseUnits("1", loanDecimals);
1989
2114
  } else {
1990
- repayAssets = import_ethers2.ethers.parseUnits("999999", 6);
2115
+ repayAssets = import_ethers2.ethers.parseUnits("999999", loanDecimals);
1991
2116
  repayShares = 0n;
1992
2117
  approveAmount = repayAssets;
1993
2118
  }
1994
2119
  } else {
1995
- repayAssets = import_ethers2.ethers.parseUnits(usdcAmount, 6);
2120
+ repayAssets = import_ethers2.ethers.parseUnits(amount, loanDecimals);
1996
2121
  repayShares = 0n;
1997
2122
  approveAmount = repayAssets;
1998
2123
  }
1999
- const usdcContract = new import_ethers2.Contract(usdcAddr, ERC20_ABI, this._signer);
2000
- 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);
2001
2126
  if (acctBalance < approveAmount) {
2002
2127
  const shortfall = approveAmount - acctBalance;
2003
- const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
2128
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
2004
2129
  if (eoaBalance < shortfall) {
2130
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
2131
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
2005
2132
  throw new AgetherError(
2006
- `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)}.`,
2007
2134
  "INSUFFICIENT_BALANCE"
2008
2135
  );
2009
2136
  }
2010
- const transferTx = await usdcContract.transfer(acctAddr, shortfall);
2137
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
2011
2138
  await transferTx.wait();
2012
2139
  this._refreshSigner();
2013
2140
  }
2014
- const targets = [usdcAddr, morphoAddr];
2141
+ const targets = [loanTokenAddr, morphoAddr];
2015
2142
  const values = [0n, 0n];
2016
2143
  const datas = [
2017
2144
  erc20Iface2.encodeFunctionData("approve", [morphoAddr, approveAmount]),
@@ -2031,7 +2158,7 @@ var MorphoClient = class {
2031
2158
  } catch (e) {
2032
2159
  console.warn("[agether] failed to read remaining debt after repay:", e instanceof Error ? e.message : e);
2033
2160
  }
2034
- return { tx: receipt.hash, amount: usdcAmount, remainingDebt };
2161
+ return { tx: receipt.hash, amount, remainingDebt };
2035
2162
  }
2036
2163
  /**
2037
2164
  * Withdraw collateral from Morpho Blue.
@@ -2045,12 +2172,13 @@ var MorphoClient = class {
2045
2172
  const colInfo = await this._resolveToken(tokenSymbol);
2046
2173
  const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
2047
2174
  const morphoAddr = this.config.contracts.morphoBlue;
2048
- const usdcAddr = this.config.contracts.usdc;
2175
+ const loanTokenAddr = params.loanToken;
2176
+ const loanDecimals = await this._getLoanTokenDecimals(params);
2049
2177
  const dest = receiver || await this.getSignerAddress();
2050
2178
  let weiAmount;
2051
2179
  const markets = await this.getMarkets();
2052
2180
  const market = markets.find(
2053
- (m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase()
2181
+ (m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase() && m.loanAsset?.address.toLowerCase() === loanTokenAddr.toLowerCase()
2054
2182
  );
2055
2183
  if (amount === "all") {
2056
2184
  if (!market) throw new AgetherError("Market not found", "MARKET_NOT_FOUND");
@@ -2073,8 +2201,10 @@ var MorphoClient = class {
2073
2201
  const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
2074
2202
  const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
2075
2203
  const estimated = totalBorrowShares > 0n ? dustBorrowShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
2076
- dustApproveAmount = estimated > 0n ? estimated : import_ethers2.ethers.parseUnits("1", 6);
2077
- 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`);
2078
2208
  }
2079
2209
  } catch (e) {
2080
2210
  console.warn("[agether] failed to check borrow shares before withdraw:", e instanceof Error ? e.message : e);
@@ -2088,19 +2218,21 @@ var MorphoClient = class {
2088
2218
  ]);
2089
2219
  let receipt;
2090
2220
  if (hasDustDebt) {
2091
- const usdcContract = new import_ethers2.Contract(usdcAddr, ERC20_ABI, this._signer);
2092
- 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);
2093
2223
  if (acctBalance < dustApproveAmount) {
2094
2224
  const shortfall = dustApproveAmount - acctBalance;
2095
- const eoaBalance = await usdcContract.balanceOf(await this.getSignerAddress());
2225
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
2096
2226
  if (eoaBalance >= shortfall) {
2097
- console.log(`[agether] transferring ${import_ethers2.ethers.formatUnits(shortfall, 6)} USDC from EOA \u2192 AgentAccount for dust repay`);
2098
- 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);
2099
2231
  await transferTx.wait();
2100
2232
  this._refreshSigner();
2101
2233
  }
2102
2234
  }
2103
- const targets = [usdcAddr, morphoAddr, morphoAddr];
2235
+ const targets = [loanTokenAddr, morphoAddr, morphoAddr];
2104
2236
  const values = [0n, 0n, 0n];
2105
2237
  const datas = [
2106
2238
  erc20Iface2.encodeFunctionData("approve", [morphoAddr, dustApproveAmount]),
@@ -2345,6 +2477,37 @@ var MorphoClient = class {
2345
2477
  }
2346
2478
  throw new AgetherError("No active supply position found", "NO_SUPPLY");
2347
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
+ }
2348
2511
  /**
2349
2512
  * Resolve a token symbol or address to { address, symbol, decimals }.
2350
2513
  *
@@ -2361,8 +2524,8 @@ var MorphoClient = class {
2361
2524
  const fromApi = this._tokenCache.get(key);
2362
2525
  if (fromApi) return fromApi;
2363
2526
  throw new AgetherError(
2364
- `Unknown token: ${symbolOrAddress}. No Morpho market found with this collateral.`,
2365
- "UNKNOWN_COLLATERAL"
2527
+ `Unknown token: ${symbolOrAddress}. No Morpho market found with this token.`,
2528
+ "UNKNOWN_TOKEN"
2366
2529
  );
2367
2530
  }
2368
2531
  /**