@agether/sdk 2.13.0 → 2.14.1
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/cli.d.ts +29 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +422 -37
- package/dist/clients/AgentIdentityClient.d.ts +200 -0
- package/dist/clients/AgentIdentityClient.d.ts.map +1 -0
- package/dist/clients/AgentIdentityClient.js +351 -0
- package/dist/clients/AgetherClient.d.ts +242 -0
- package/dist/clients/AgetherClient.d.ts.map +1 -0
- package/dist/clients/AgetherClient.js +736 -0
- package/dist/clients/MorphoClient.d.ts +572 -0
- package/dist/clients/MorphoClient.d.ts.map +1 -0
- package/dist/clients/MorphoClient.js +1974 -0
- package/dist/clients/ScoringClient.d.ts +103 -0
- package/dist/clients/ScoringClient.d.ts.map +1 -0
- package/dist/clients/ScoringClient.js +112 -0
- package/dist/clients/X402Client.d.ts +198 -0
- package/dist/clients/X402Client.d.ts.map +1 -0
- package/dist/clients/X402Client.js +438 -0
- package/dist/index.d.mts +87 -1
- package/dist/index.d.ts +87 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +422 -37
- package/dist/index.mjs +422 -37
- package/dist/types/index.d.ts +132 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +46 -0
- package/dist/utils/abis.d.ts +29 -0
- package/dist/utils/abis.d.ts.map +1 -0
- package/dist/utils/abis.js +138 -0
- package/dist/utils/config.d.ts +36 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +168 -0
- package/dist/utils/format.d.ts +44 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +75 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1155,7 +1155,7 @@ var MorphoClient = class {
|
|
|
1155
1155
|
const chainId = this.config.chainId;
|
|
1156
1156
|
const query = `{
|
|
1157
1157
|
markets(
|
|
1158
|
-
first:
|
|
1158
|
+
first: 500
|
|
1159
1159
|
orderBy: SupplyAssetsUsd
|
|
1160
1160
|
orderDirection: Desc
|
|
1161
1161
|
where: { chainId_in: [${chainId}] }
|
|
@@ -1261,7 +1261,7 @@ var MorphoClient = class {
|
|
|
1261
1261
|
const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
|
|
1262
1262
|
loanAddr = resolved.address.toLowerCase();
|
|
1263
1263
|
} catch {
|
|
1264
|
-
loanAddr =
|
|
1264
|
+
loanAddr = void 0;
|
|
1265
1265
|
}
|
|
1266
1266
|
}
|
|
1267
1267
|
}
|
|
@@ -1269,6 +1269,7 @@ var MorphoClient = class {
|
|
|
1269
1269
|
for (const m of this._discoveredMarkets ?? []) {
|
|
1270
1270
|
if (m.collateralAsset.address.toLowerCase() !== colAddr) continue;
|
|
1271
1271
|
if (loanAddr && m.loanAsset.address.toLowerCase() !== loanAddr) continue;
|
|
1272
|
+
if (!loanAddr && loanTokenSymbolOrAddress && m.loanAsset.symbol.toUpperCase() !== loanTokenSymbolOrAddress.toUpperCase()) continue;
|
|
1272
1273
|
return {
|
|
1273
1274
|
loanToken: m.loanAsset.address,
|
|
1274
1275
|
collateralToken: m.collateralAsset.address,
|
|
@@ -1277,6 +1278,27 @@ var MorphoClient = class {
|
|
|
1277
1278
|
lltv: m.lltv
|
|
1278
1279
|
};
|
|
1279
1280
|
}
|
|
1281
|
+
if (!collateralSymbolOrAddress.startsWith("0x")) {
|
|
1282
|
+
const searched = await this.searchMarkets(collateralSymbolOrAddress, { asCollateral: true });
|
|
1283
|
+
const allResults = [...searched];
|
|
1284
|
+
if (loanTokenSymbolOrAddress && !loanTokenSymbolOrAddress.startsWith("0x")) {
|
|
1285
|
+
const loanSearched = await this.searchMarkets(loanTokenSymbolOrAddress, { asLoanToken: true });
|
|
1286
|
+
const seen = new Set(allResults.map((r) => r.marketId));
|
|
1287
|
+
for (const m of loanSearched) {
|
|
1288
|
+
if (!seen.has(m.marketId)) allResults.push(m);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
for (const m of allResults) {
|
|
1292
|
+
if (colAddr.startsWith("0x") && m.collateralAddress.toLowerCase() !== colAddr) {
|
|
1293
|
+
if (m.collateralToken.toUpperCase() !== collateralSymbolOrAddress.toUpperCase()) continue;
|
|
1294
|
+
} else if (m.collateralToken.toUpperCase() !== collateralSymbolOrAddress.toUpperCase()) {
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
if (loanAddr && m.loanAddress.toLowerCase() !== loanAddr) continue;
|
|
1298
|
+
if (!loanAddr && loanTokenSymbolOrAddress && m.loanToken.toUpperCase() !== loanTokenSymbolOrAddress.toUpperCase()) continue;
|
|
1299
|
+
return this.getMarketParams(m.marketId);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1280
1302
|
throw new AgetherError(
|
|
1281
1303
|
`No Morpho market found for collateral ${collateralSymbolOrAddress}` + (loanTokenSymbolOrAddress ? ` with loan token ${loanTokenSymbolOrAddress}` : ""),
|
|
1282
1304
|
"MARKET_NOT_FOUND"
|
|
@@ -1307,26 +1329,54 @@ var MorphoClient = class {
|
|
|
1307
1329
|
};
|
|
1308
1330
|
}
|
|
1309
1331
|
/**
|
|
1310
|
-
* Full status: positions across all
|
|
1332
|
+
* Full status: positions across all markets the user has interacted with.
|
|
1333
|
+
*
|
|
1334
|
+
* Uses Morpho GraphQL `marketPositions` to find ALL positions (not limited
|
|
1335
|
+
* to the top-500 markets), then reads onchain data for accurate debt.
|
|
1311
1336
|
*/
|
|
1312
1337
|
async getStatus() {
|
|
1313
1338
|
const acctAddr = await this.getAccountAddress();
|
|
1314
|
-
const
|
|
1339
|
+
const chainId = this.config.chainId;
|
|
1315
1340
|
const positions = [];
|
|
1316
1341
|
let totalDebtFloat = 0;
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1342
|
+
try {
|
|
1343
|
+
const posQuery = `{
|
|
1344
|
+
marketPositions(
|
|
1345
|
+
where: {
|
|
1346
|
+
userAddress_in: ["${acctAddr}"]
|
|
1347
|
+
chainId_in: [${chainId}]
|
|
1348
|
+
}
|
|
1349
|
+
first: 100
|
|
1350
|
+
) {
|
|
1351
|
+
items {
|
|
1352
|
+
supplyShares
|
|
1353
|
+
borrowShares
|
|
1354
|
+
collateral
|
|
1355
|
+
market {
|
|
1356
|
+
uniqueKey
|
|
1357
|
+
loanAsset { symbol address decimals }
|
|
1358
|
+
collateralAsset { symbol address decimals }
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}`;
|
|
1363
|
+
const resp = await import_axios.default.post(MORPHO_API_URL, { query: posQuery }, { timeout: 15e3 });
|
|
1364
|
+
const items = resp.data?.data?.marketPositions?.items ?? [];
|
|
1365
|
+
for (const item of items) {
|
|
1366
|
+
const supplyShares = BigInt(item.supplyShares ?? "0");
|
|
1367
|
+
const borrowShares = BigInt(item.borrowShares ?? "0");
|
|
1368
|
+
const collateral = BigInt(item.collateral ?? "0");
|
|
1369
|
+
if (collateral === 0n && borrowShares === 0n && supplyShares === 0n) continue;
|
|
1370
|
+
const m = item.market;
|
|
1371
|
+
if (!m?.collateralAsset || !m?.loanAsset) continue;
|
|
1322
1372
|
const loanDecimals = m.loanAsset.decimals;
|
|
1323
1373
|
let debt = 0n;
|
|
1324
|
-
if (
|
|
1374
|
+
if (borrowShares > 0n) {
|
|
1325
1375
|
try {
|
|
1326
1376
|
const mkt = await this.morphoBlue.market(m.uniqueKey);
|
|
1327
1377
|
const totalBorrowShares = BigInt(mkt.totalBorrowShares);
|
|
1328
1378
|
const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
|
|
1329
|
-
debt = totalBorrowShares > 0n ? (
|
|
1379
|
+
debt = totalBorrowShares > 0n ? (borrowShares * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1330
1380
|
totalDebtFloat += parseFloat(import_ethers2.ethers.formatUnits(debt, loanDecimals));
|
|
1331
1381
|
} catch (e) {
|
|
1332
1382
|
console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
|
|
@@ -1336,14 +1386,46 @@ var MorphoClient = class {
|
|
|
1336
1386
|
marketId: m.uniqueKey,
|
|
1337
1387
|
collateralToken: m.collateralAsset.symbol,
|
|
1338
1388
|
loanToken: m.loanAsset.symbol,
|
|
1339
|
-
collateral: import_ethers2.ethers.formatUnits(
|
|
1340
|
-
borrowShares:
|
|
1341
|
-
supplyShares:
|
|
1389
|
+
collateral: import_ethers2.ethers.formatUnits(collateral, m.collateralAsset.decimals),
|
|
1390
|
+
borrowShares: borrowShares.toString(),
|
|
1391
|
+
supplyShares: supplyShares.toString(),
|
|
1342
1392
|
debt: import_ethers2.ethers.formatUnits(debt, loanDecimals)
|
|
1343
1393
|
});
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1394
|
+
}
|
|
1395
|
+
} catch (e) {
|
|
1396
|
+
console.warn("[agether] marketPositions API failed, falling back to market scan:", e instanceof Error ? e.message : e);
|
|
1397
|
+
const markets = await this.getMarkets();
|
|
1398
|
+
for (const m of markets) {
|
|
1399
|
+
if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
|
|
1400
|
+
try {
|
|
1401
|
+
const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
|
|
1402
|
+
if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
|
|
1403
|
+
const loanDecimals = m.loanAsset.decimals;
|
|
1404
|
+
let debt = 0n;
|
|
1405
|
+
if (pos.borrowShares > 0n) {
|
|
1406
|
+
try {
|
|
1407
|
+
const mkt = await this.morphoBlue.market(m.uniqueKey);
|
|
1408
|
+
const totalBorrowShares = BigInt(mkt.totalBorrowShares);
|
|
1409
|
+
const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
|
|
1410
|
+
debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1411
|
+
totalDebtFloat += parseFloat(import_ethers2.ethers.formatUnits(debt, loanDecimals));
|
|
1412
|
+
} catch (e2) {
|
|
1413
|
+
console.warn(`[agether] debt calc failed:`, e2 instanceof Error ? e2.message : e2);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
positions.push({
|
|
1417
|
+
marketId: m.uniqueKey,
|
|
1418
|
+
collateralToken: m.collateralAsset.symbol,
|
|
1419
|
+
loanToken: m.loanAsset.symbol,
|
|
1420
|
+
collateral: import_ethers2.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
|
|
1421
|
+
borrowShares: pos.borrowShares.toString(),
|
|
1422
|
+
supplyShares: pos.supplyShares.toString(),
|
|
1423
|
+
debt: import_ethers2.ethers.formatUnits(debt, loanDecimals)
|
|
1424
|
+
});
|
|
1425
|
+
} catch (e2) {
|
|
1426
|
+
console.warn(`[agether] position read failed:`, e2 instanceof Error ? e2.message : e2);
|
|
1427
|
+
continue;
|
|
1428
|
+
}
|
|
1347
1429
|
}
|
|
1348
1430
|
}
|
|
1349
1431
|
return {
|
|
@@ -1447,41 +1529,39 @@ var MorphoClient = class {
|
|
|
1447
1529
|
async getMarketRates(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
|
|
1448
1530
|
const chainId = this.config.chainId;
|
|
1449
1531
|
let collateralFilter = "";
|
|
1532
|
+
let loanFilter = "";
|
|
1533
|
+
let searchTerm = "";
|
|
1450
1534
|
if (collateralSymbolOrAddress) {
|
|
1451
|
-
let colAddr;
|
|
1452
1535
|
if (collateralSymbolOrAddress.startsWith("0x")) {
|
|
1453
|
-
|
|
1536
|
+
collateralFilter = `, collateralAssetAddress_in: ["${collateralSymbolOrAddress.toLowerCase()}"]`;
|
|
1454
1537
|
} else {
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1538
|
+
const cached = this._tokenCache.get(collateralSymbolOrAddress.toUpperCase());
|
|
1539
|
+
if (cached) {
|
|
1540
|
+
collateralFilter = `, collateralAssetAddress_in: ["${cached.address.toLowerCase()}"]`;
|
|
1541
|
+
} else {
|
|
1542
|
+
searchTerm = collateralSymbolOrAddress;
|
|
1460
1543
|
}
|
|
1461
1544
|
}
|
|
1462
|
-
collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
|
|
1463
1545
|
}
|
|
1464
|
-
let loanFilter = "";
|
|
1465
1546
|
if (loanTokenSymbolOrAddress) {
|
|
1466
|
-
let loanAddr;
|
|
1467
1547
|
if (loanTokenSymbolOrAddress.startsWith("0x")) {
|
|
1468
|
-
|
|
1548
|
+
loanFilter = `, loanAssetAddress_in: ["${loanTokenSymbolOrAddress.toLowerCase()}"]`;
|
|
1469
1549
|
} else {
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1550
|
+
const cached = this._tokenCache.get(loanTokenSymbolOrAddress.toUpperCase());
|
|
1551
|
+
if (cached) {
|
|
1552
|
+
loanFilter = `, loanAssetAddress_in: ["${cached.address.toLowerCase()}"]`;
|
|
1553
|
+
} else {
|
|
1554
|
+
searchTerm = searchTerm || loanTokenSymbolOrAddress;
|
|
1475
1555
|
}
|
|
1476
1556
|
}
|
|
1477
|
-
loanFilter = `, loanAssetAddress_in: ["${loanAddr}"]`;
|
|
1478
1557
|
}
|
|
1558
|
+
const searchClause = searchTerm ? `, search: "${searchTerm}"` : "";
|
|
1479
1559
|
const query = `{
|
|
1480
1560
|
markets(
|
|
1481
|
-
first:
|
|
1561
|
+
first: 100
|
|
1482
1562
|
orderBy: SupplyAssetsUsd
|
|
1483
1563
|
orderDirection: Desc
|
|
1484
|
-
where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter} }
|
|
1564
|
+
where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter}${searchClause} }
|
|
1485
1565
|
) {
|
|
1486
1566
|
items {
|
|
1487
1567
|
uniqueKey
|
|
@@ -1500,7 +1580,15 @@ var MorphoClient = class {
|
|
|
1500
1580
|
}`;
|
|
1501
1581
|
try {
|
|
1502
1582
|
const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
|
|
1503
|
-
|
|
1583
|
+
let items = resp.data?.data?.markets?.items ?? [];
|
|
1584
|
+
if (searchTerm && collateralSymbolOrAddress && !collateralSymbolOrAddress.startsWith("0x")) {
|
|
1585
|
+
const sym = collateralSymbolOrAddress.toUpperCase();
|
|
1586
|
+
items = items.filter((m) => m.collateralAsset?.symbol?.toUpperCase() === sym);
|
|
1587
|
+
}
|
|
1588
|
+
if (searchTerm && loanTokenSymbolOrAddress && !loanTokenSymbolOrAddress.startsWith("0x")) {
|
|
1589
|
+
const sym = loanTokenSymbolOrAddress.toUpperCase();
|
|
1590
|
+
items = items.filter((m) => m.loanAsset?.symbol?.toUpperCase() === sym);
|
|
1591
|
+
}
|
|
1504
1592
|
return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers2.ethers.ZeroAddress).map((m) => {
|
|
1505
1593
|
const loanDecimals = m.loanAsset?.decimals ?? 18;
|
|
1506
1594
|
return {
|
|
@@ -1521,6 +1609,205 @@ var MorphoClient = class {
|
|
|
1521
1609
|
return [];
|
|
1522
1610
|
}
|
|
1523
1611
|
}
|
|
1612
|
+
// ════════════════════════════════════════════════════════
|
|
1613
|
+
// Market Search & Wallet Discovery
|
|
1614
|
+
// ════════════════════════════════════════════════════════
|
|
1615
|
+
/**
|
|
1616
|
+
* Search Morpho markets by token name using the Morpho GraphQL API `search` field.
|
|
1617
|
+
* No hardcoded token lists — uses Morpho's built-in fuzzy search.
|
|
1618
|
+
*
|
|
1619
|
+
* @param search - token name or symbol (e.g. 'WETH', 'staked ETH', 'ezETH')
|
|
1620
|
+
* @param options.asCollateral - only return markets where the searched token is collateral
|
|
1621
|
+
* @param options.asLoanToken - only return markets where the searched token is the loan asset
|
|
1622
|
+
*/
|
|
1623
|
+
async searchMarkets(search, options) {
|
|
1624
|
+
const chainId = this.config.chainId;
|
|
1625
|
+
const query = `{
|
|
1626
|
+
markets(
|
|
1627
|
+
first: 100
|
|
1628
|
+
orderBy: SupplyAssetsUsd
|
|
1629
|
+
orderDirection: Desc
|
|
1630
|
+
where: { chainId_in: [${chainId}], search: "${search}" }
|
|
1631
|
+
) {
|
|
1632
|
+
items {
|
|
1633
|
+
uniqueKey
|
|
1634
|
+
lltv
|
|
1635
|
+
loanAsset { address symbol decimals }
|
|
1636
|
+
collateralAsset { address symbol decimals }
|
|
1637
|
+
state {
|
|
1638
|
+
borrowAssets
|
|
1639
|
+
supplyAssets
|
|
1640
|
+
utilization
|
|
1641
|
+
supplyApy
|
|
1642
|
+
borrowApy
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
}`;
|
|
1647
|
+
try {
|
|
1648
|
+
const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
|
|
1649
|
+
let items = resp.data?.data?.markets?.items ?? [];
|
|
1650
|
+
items = items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers2.ethers.ZeroAddress);
|
|
1651
|
+
const searchUpper = search.toUpperCase();
|
|
1652
|
+
if (options?.asCollateral) {
|
|
1653
|
+
items = items.filter((m) => m.collateralAsset?.symbol?.toUpperCase() === searchUpper);
|
|
1654
|
+
}
|
|
1655
|
+
if (options?.asLoanToken) {
|
|
1656
|
+
items = items.filter((m) => m.loanAsset?.symbol?.toUpperCase() === searchUpper);
|
|
1657
|
+
}
|
|
1658
|
+
return items.map((m) => {
|
|
1659
|
+
const loanDecimals = m.loanAsset?.decimals ?? 18;
|
|
1660
|
+
const collateralDecimals = m.collateralAsset?.decimals ?? 18;
|
|
1661
|
+
return {
|
|
1662
|
+
collateralToken: m.collateralAsset.symbol,
|
|
1663
|
+
loanToken: m.loanAsset.symbol,
|
|
1664
|
+
loanDecimals,
|
|
1665
|
+
collateralDecimals,
|
|
1666
|
+
supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
|
|
1667
|
+
borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
|
|
1668
|
+
utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
|
|
1669
|
+
totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
|
|
1670
|
+
totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
|
|
1671
|
+
lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
|
|
1672
|
+
marketId: m.uniqueKey,
|
|
1673
|
+
collateralAddress: m.collateralAsset.address,
|
|
1674
|
+
loanAddress: m.loanAsset.address
|
|
1675
|
+
};
|
|
1676
|
+
});
|
|
1677
|
+
} catch (e) {
|
|
1678
|
+
console.warn("[agether] searchMarkets failed:", e instanceof Error ? e.message : e);
|
|
1679
|
+
return [];
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
/**
|
|
1683
|
+
* Scan the AgentAccount wallet for all ERC-20 tokens that appear in Morpho
|
|
1684
|
+
* markets on the current chain. Returns tokens where balance > 0.
|
|
1685
|
+
*
|
|
1686
|
+
* Uses the full market list (500 markets) to discover all relevant tokens,
|
|
1687
|
+
* then checks on-chain balance for each unique token address.
|
|
1688
|
+
*
|
|
1689
|
+
* @returns Array of tokens with non-zero balance, sorted by balance descending.
|
|
1690
|
+
*/
|
|
1691
|
+
async getWalletTokenBalances() {
|
|
1692
|
+
const acctAddr = await this.getAccountAddress();
|
|
1693
|
+
await this.getMarkets();
|
|
1694
|
+
const uniqueTokens = /* @__PURE__ */ new Map();
|
|
1695
|
+
for (const [key, info] of this._tokenCache.entries()) {
|
|
1696
|
+
if (key.startsWith("0x") && !uniqueTokens.has(key)) {
|
|
1697
|
+
uniqueTokens.set(key, info);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
const results = [];
|
|
1701
|
+
try {
|
|
1702
|
+
const ethBalance = await this.provider.getBalance(acctAddr);
|
|
1703
|
+
if (ethBalance > 0n) {
|
|
1704
|
+
results.push({
|
|
1705
|
+
symbol: "ETH",
|
|
1706
|
+
address: import_ethers2.ethers.ZeroAddress,
|
|
1707
|
+
decimals: 18,
|
|
1708
|
+
balance: ethBalance,
|
|
1709
|
+
balanceFormatted: import_ethers2.ethers.formatEther(ethBalance)
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
} catch {
|
|
1713
|
+
}
|
|
1714
|
+
const tokenEntries = Array.from(uniqueTokens.values());
|
|
1715
|
+
const batchSize = 20;
|
|
1716
|
+
for (let i = 0; i < tokenEntries.length; i += batchSize) {
|
|
1717
|
+
const batch = tokenEntries.slice(i, i + batchSize);
|
|
1718
|
+
const checks = batch.map(async (info) => {
|
|
1719
|
+
try {
|
|
1720
|
+
const token = new import_ethers2.Contract(info.address, ERC20_ABI, this.provider);
|
|
1721
|
+
const balance = await token.balanceOf(acctAddr);
|
|
1722
|
+
if (balance > 0n) {
|
|
1723
|
+
return {
|
|
1724
|
+
symbol: info.symbol,
|
|
1725
|
+
address: info.address,
|
|
1726
|
+
decimals: info.decimals,
|
|
1727
|
+
balance,
|
|
1728
|
+
balanceFormatted: import_ethers2.ethers.formatUnits(balance, info.decimals)
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
} catch {
|
|
1732
|
+
}
|
|
1733
|
+
return null;
|
|
1734
|
+
});
|
|
1735
|
+
const batchResults = await Promise.all(checks);
|
|
1736
|
+
for (const r of batchResults) {
|
|
1737
|
+
if (r) results.push(r);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
results.sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
1741
|
+
return results;
|
|
1742
|
+
}
|
|
1743
|
+
/**
|
|
1744
|
+
* Find borrowing opportunities for the agent.
|
|
1745
|
+
*
|
|
1746
|
+
* - If `collateralSymbol` is provided: find all markets where that token is collateral.
|
|
1747
|
+
* - If omitted: scan wallet balances and find markets for each token the agent holds.
|
|
1748
|
+
*
|
|
1749
|
+
* Returns markets grouped by collateral token with APY, liquidity, and balance info.
|
|
1750
|
+
*
|
|
1751
|
+
* @param collateralSymbol - optional, e.g. 'WETH'. If omitted, scans wallet.
|
|
1752
|
+
*/
|
|
1753
|
+
async findBorrowingOptions(collateralSymbol) {
|
|
1754
|
+
let tokensToCheck;
|
|
1755
|
+
if (collateralSymbol) {
|
|
1756
|
+
tokensToCheck = [{ symbol: collateralSymbol, balanceFormatted: "N/A" }];
|
|
1757
|
+
try {
|
|
1758
|
+
const balance = await this.getTokenBalance(collateralSymbol);
|
|
1759
|
+
const info = await this._resolveToken(collateralSymbol);
|
|
1760
|
+
tokensToCheck = [{ symbol: collateralSymbol, balanceFormatted: import_ethers2.ethers.formatUnits(balance, info.decimals) }];
|
|
1761
|
+
} catch {
|
|
1762
|
+
}
|
|
1763
|
+
} else {
|
|
1764
|
+
const walletTokens = await this.getWalletTokenBalances();
|
|
1765
|
+
tokensToCheck = walletTokens.filter((t) => t.symbol !== "ETH").map((t) => ({ symbol: t.symbol, balanceFormatted: t.balanceFormatted }));
|
|
1766
|
+
if (tokensToCheck.length === 0) {
|
|
1767
|
+
return [];
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
const results = [];
|
|
1771
|
+
for (const token of tokensToCheck) {
|
|
1772
|
+
const markets = await this.searchMarkets(token.symbol, { asCollateral: true });
|
|
1773
|
+
if (markets.length === 0) continue;
|
|
1774
|
+
results.push({
|
|
1775
|
+
collateralToken: token.symbol,
|
|
1776
|
+
collateralBalance: token.balanceFormatted,
|
|
1777
|
+
markets: markets.map((m) => ({
|
|
1778
|
+
loanToken: m.loanToken,
|
|
1779
|
+
borrowApy: `${(m.borrowApy * 100).toFixed(2)}%`,
|
|
1780
|
+
supplyApy: `${(m.supplyApy * 100).toFixed(2)}%`,
|
|
1781
|
+
lltv: m.lltv,
|
|
1782
|
+
utilization: `${(m.utilization * 100).toFixed(1)}%`,
|
|
1783
|
+
availableLiquidity: `$${(m.totalSupplyUsd - m.totalBorrowUsd).toFixed(0)}`,
|
|
1784
|
+
marketId: m.marketId
|
|
1785
|
+
}))
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
return results;
|
|
1789
|
+
}
|
|
1790
|
+
/**
|
|
1791
|
+
* Find supply/lending opportunities for a specific loan token.
|
|
1792
|
+
*
|
|
1793
|
+
* "What can I supply to earn WETH?" → shows all markets where WETH is the loan token
|
|
1794
|
+
* (user supplies WETH to earn yield from borrowers).
|
|
1795
|
+
*
|
|
1796
|
+
* @param loanTokenSymbol - e.g. 'USDC', 'WETH'
|
|
1797
|
+
*/
|
|
1798
|
+
async findSupplyOptions(loanTokenSymbol) {
|
|
1799
|
+
const markets = await this.searchMarkets(loanTokenSymbol, { asLoanToken: true });
|
|
1800
|
+
return markets.map((m) => ({
|
|
1801
|
+
collateralToken: m.collateralToken,
|
|
1802
|
+
loanToken: m.loanToken,
|
|
1803
|
+
supplyApy: `${(m.supplyApy * 100).toFixed(2)}%`,
|
|
1804
|
+
borrowApy: `${(m.borrowApy * 100).toFixed(2)}%`,
|
|
1805
|
+
lltv: m.lltv,
|
|
1806
|
+
utilization: `${(m.utilization * 100).toFixed(1)}%`,
|
|
1807
|
+
totalSupply: `$${m.totalSupplyUsd.toFixed(0)}`,
|
|
1808
|
+
marketId: m.marketId
|
|
1809
|
+
}));
|
|
1810
|
+
}
|
|
1524
1811
|
/**
|
|
1525
1812
|
* Estimate theoretical yield for a given collateral amount over a period.
|
|
1526
1813
|
*
|
|
@@ -2425,6 +2712,46 @@ var MorphoClient = class {
|
|
|
2425
2712
|
/** Find the first market where the agent has collateral deposited. */
|
|
2426
2713
|
async _findActiveMarket() {
|
|
2427
2714
|
const acctAddr = await this.getAccountAddress();
|
|
2715
|
+
const chainId = this.config.chainId;
|
|
2716
|
+
try {
|
|
2717
|
+
const posQuery = `{
|
|
2718
|
+
marketPositions(
|
|
2719
|
+
where: { userAddress_in: ["${acctAddr}"], chainId_in: [${chainId}] }
|
|
2720
|
+
first: 50
|
|
2721
|
+
) {
|
|
2722
|
+
items {
|
|
2723
|
+
collateral
|
|
2724
|
+
market {
|
|
2725
|
+
uniqueKey
|
|
2726
|
+
oracleAddress
|
|
2727
|
+
irmAddress
|
|
2728
|
+
lltv
|
|
2729
|
+
loanAsset { address symbol decimals }
|
|
2730
|
+
collateralAsset { address symbol decimals }
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
}`;
|
|
2735
|
+
const resp = await import_axios.default.post(MORPHO_API_URL, { query: posQuery }, { timeout: 1e4 });
|
|
2736
|
+
const items = resp.data?.data?.marketPositions?.items ?? [];
|
|
2737
|
+
for (const item of items) {
|
|
2738
|
+
if (BigInt(item.collateral ?? "0") > 0n && item.market?.collateralAsset) {
|
|
2739
|
+
const m = item.market;
|
|
2740
|
+
return {
|
|
2741
|
+
params: {
|
|
2742
|
+
loanToken: m.loanAsset.address,
|
|
2743
|
+
collateralToken: m.collateralAsset.address,
|
|
2744
|
+
oracle: m.oracleAddress,
|
|
2745
|
+
irm: m.irmAddress,
|
|
2746
|
+
lltv: BigInt(m.lltv)
|
|
2747
|
+
},
|
|
2748
|
+
symbol: m.collateralAsset.symbol
|
|
2749
|
+
};
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
} catch (e) {
|
|
2753
|
+
console.warn("[agether] _findActiveMarket GraphQL failed, falling back:", e instanceof Error ? e.message : e);
|
|
2754
|
+
}
|
|
2428
2755
|
const markets = await this.getMarkets();
|
|
2429
2756
|
for (const m of markets) {
|
|
2430
2757
|
if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
|
|
@@ -2453,6 +2780,46 @@ var MorphoClient = class {
|
|
|
2453
2780
|
/** Find the first market where the agent has a supply (lending) position. */
|
|
2454
2781
|
async _findActiveSupplyMarket() {
|
|
2455
2782
|
const acctAddr = await this.getAccountAddress();
|
|
2783
|
+
const chainId = this.config.chainId;
|
|
2784
|
+
try {
|
|
2785
|
+
const posQuery = `{
|
|
2786
|
+
marketPositions(
|
|
2787
|
+
where: { userAddress_in: ["${acctAddr}"], chainId_in: [${chainId}] }
|
|
2788
|
+
first: 50
|
|
2789
|
+
) {
|
|
2790
|
+
items {
|
|
2791
|
+
supplyShares
|
|
2792
|
+
market {
|
|
2793
|
+
uniqueKey
|
|
2794
|
+
oracleAddress
|
|
2795
|
+
irmAddress
|
|
2796
|
+
lltv
|
|
2797
|
+
loanAsset { address symbol decimals }
|
|
2798
|
+
collateralAsset { address symbol decimals }
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
}`;
|
|
2803
|
+
const resp = await import_axios.default.post(MORPHO_API_URL, { query: posQuery }, { timeout: 1e4 });
|
|
2804
|
+
const items = resp.data?.data?.marketPositions?.items ?? [];
|
|
2805
|
+
for (const item of items) {
|
|
2806
|
+
if (BigInt(item.supplyShares ?? "0") > 0n && item.market?.collateralAsset) {
|
|
2807
|
+
const m = item.market;
|
|
2808
|
+
return {
|
|
2809
|
+
params: {
|
|
2810
|
+
loanToken: m.loanAsset.address,
|
|
2811
|
+
collateralToken: m.collateralAsset.address,
|
|
2812
|
+
oracle: m.oracleAddress,
|
|
2813
|
+
irm: m.irmAddress,
|
|
2814
|
+
lltv: BigInt(m.lltv)
|
|
2815
|
+
},
|
|
2816
|
+
symbol: m.collateralAsset.symbol
|
|
2817
|
+
};
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
} catch (e) {
|
|
2821
|
+
console.warn("[agether] _findActiveSupplyMarket GraphQL failed, falling back:", e instanceof Error ? e.message : e);
|
|
2822
|
+
}
|
|
2456
2823
|
const markets = await this.getMarkets();
|
|
2457
2824
|
for (const m of markets) {
|
|
2458
2825
|
if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
|
|
@@ -2523,6 +2890,24 @@ var MorphoClient = class {
|
|
|
2523
2890
|
await this.getMarkets();
|
|
2524
2891
|
const fromApi = this._tokenCache.get(key);
|
|
2525
2892
|
if (fromApi) return fromApi;
|
|
2893
|
+
if (!symbolOrAddress.startsWith("0x")) {
|
|
2894
|
+
const searchResults = await this.searchMarkets(symbolOrAddress);
|
|
2895
|
+
const sym = symbolOrAddress.toUpperCase();
|
|
2896
|
+
for (const m of searchResults) {
|
|
2897
|
+
if (m.collateralToken.toUpperCase() === sym) {
|
|
2898
|
+
const info = { address: m.collateralAddress, symbol: m.collateralToken, decimals: m.collateralDecimals };
|
|
2899
|
+
this._tokenCache.set(sym, info);
|
|
2900
|
+
this._tokenCache.set(m.collateralAddress.toLowerCase(), info);
|
|
2901
|
+
return info;
|
|
2902
|
+
}
|
|
2903
|
+
if (m.loanToken.toUpperCase() === sym) {
|
|
2904
|
+
const info = { address: m.loanAddress, symbol: m.loanToken, decimals: m.loanDecimals };
|
|
2905
|
+
this._tokenCache.set(sym, info);
|
|
2906
|
+
this._tokenCache.set(m.loanAddress.toLowerCase(), info);
|
|
2907
|
+
return info;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2526
2911
|
throw new AgetherError(
|
|
2527
2912
|
`Unknown token: ${symbolOrAddress}. No Morpho market found with this token.`,
|
|
2528
2913
|
"UNKNOWN_TOKEN"
|