@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/cli.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agether CLI — Direct Morpho Blue Credit for AI Agents
|
|
4
|
+
*
|
|
5
|
+
* Architecture (v2 — Safe + Safe7579):
|
|
6
|
+
* - All commands sign transactions directly with the agent's private key
|
|
7
|
+
* - Uses MorphoClient for lending (ERC-4337 UserOps through Safe account)
|
|
8
|
+
* - Uses X402Client for paid API calls
|
|
9
|
+
*
|
|
10
|
+
* Supported chains: Ethereum (1, default), Base (8453), Base Sepolia (84532)
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* agether init <private-key> [--agent-id <id>] [--chain <chainId>]
|
|
14
|
+
* agether register [--name <n>] Register ERC-8004 + Safe account
|
|
15
|
+
* agether balance Check balances
|
|
16
|
+
* agether status Show Morpho positions
|
|
17
|
+
* agether score Get credit score (x402-gated)
|
|
18
|
+
* agether markets List Morpho markets
|
|
19
|
+
* agether deposit --amount 0.05 --token WETH Deposit collateral
|
|
20
|
+
* agether borrow --amount 100 Borrow USDC
|
|
21
|
+
* agether deposit-and-borrow --amount 0.05 --token WETH --borrow 100
|
|
22
|
+
* agether repay --amount 50 Repay USDC
|
|
23
|
+
* agether withdraw --amount 0.05 --token WETH Withdraw collateral
|
|
24
|
+
* agether sponsor --amount 0.05 --token WETH --agent-id 123
|
|
25
|
+
* agether fund --amount 50 Fund Safe account with USDC
|
|
26
|
+
* agether x402 <url> [--method GET|POST] [--body <json>]
|
|
27
|
+
*/
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG"}
|
package/dist/cli.js
CHANGED
|
@@ -408,7 +408,7 @@ var init_MorphoClient = __esm({
|
|
|
408
408
|
const chainId = this.config.chainId;
|
|
409
409
|
const query = `{
|
|
410
410
|
markets(
|
|
411
|
-
first:
|
|
411
|
+
first: 500
|
|
412
412
|
orderBy: SupplyAssetsUsd
|
|
413
413
|
orderDirection: Desc
|
|
414
414
|
where: { chainId_in: [${chainId}] }
|
|
@@ -514,7 +514,7 @@ var init_MorphoClient = __esm({
|
|
|
514
514
|
const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
|
|
515
515
|
loanAddr = resolved.address.toLowerCase();
|
|
516
516
|
} catch {
|
|
517
|
-
loanAddr =
|
|
517
|
+
loanAddr = void 0;
|
|
518
518
|
}
|
|
519
519
|
}
|
|
520
520
|
}
|
|
@@ -522,6 +522,7 @@ var init_MorphoClient = __esm({
|
|
|
522
522
|
for (const m of this._discoveredMarkets ?? []) {
|
|
523
523
|
if (m.collateralAsset.address.toLowerCase() !== colAddr) continue;
|
|
524
524
|
if (loanAddr && m.loanAsset.address.toLowerCase() !== loanAddr) continue;
|
|
525
|
+
if (!loanAddr && loanTokenSymbolOrAddress && m.loanAsset.symbol.toUpperCase() !== loanTokenSymbolOrAddress.toUpperCase()) continue;
|
|
525
526
|
return {
|
|
526
527
|
loanToken: m.loanAsset.address,
|
|
527
528
|
collateralToken: m.collateralAsset.address,
|
|
@@ -530,6 +531,27 @@ var init_MorphoClient = __esm({
|
|
|
530
531
|
lltv: m.lltv
|
|
531
532
|
};
|
|
532
533
|
}
|
|
534
|
+
if (!collateralSymbolOrAddress.startsWith("0x")) {
|
|
535
|
+
const searched = await this.searchMarkets(collateralSymbolOrAddress, { asCollateral: true });
|
|
536
|
+
const allResults = [...searched];
|
|
537
|
+
if (loanTokenSymbolOrAddress && !loanTokenSymbolOrAddress.startsWith("0x")) {
|
|
538
|
+
const loanSearched = await this.searchMarkets(loanTokenSymbolOrAddress, { asLoanToken: true });
|
|
539
|
+
const seen = new Set(allResults.map((r) => r.marketId));
|
|
540
|
+
for (const m of loanSearched) {
|
|
541
|
+
if (!seen.has(m.marketId)) allResults.push(m);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
for (const m of allResults) {
|
|
545
|
+
if (colAddr.startsWith("0x") && m.collateralAddress.toLowerCase() !== colAddr) {
|
|
546
|
+
if (m.collateralToken.toUpperCase() !== collateralSymbolOrAddress.toUpperCase()) continue;
|
|
547
|
+
} else if (m.collateralToken.toUpperCase() !== collateralSymbolOrAddress.toUpperCase()) {
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
if (loanAddr && m.loanAddress.toLowerCase() !== loanAddr) continue;
|
|
551
|
+
if (!loanAddr && loanTokenSymbolOrAddress && m.loanToken.toUpperCase() !== loanTokenSymbolOrAddress.toUpperCase()) continue;
|
|
552
|
+
return this.getMarketParams(m.marketId);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
533
555
|
throw new AgetherError(
|
|
534
556
|
`No Morpho market found for collateral ${collateralSymbolOrAddress}` + (loanTokenSymbolOrAddress ? ` with loan token ${loanTokenSymbolOrAddress}` : ""),
|
|
535
557
|
"MARKET_NOT_FOUND"
|
|
@@ -560,26 +582,54 @@ var init_MorphoClient = __esm({
|
|
|
560
582
|
};
|
|
561
583
|
}
|
|
562
584
|
/**
|
|
563
|
-
* Full status: positions across all
|
|
585
|
+
* Full status: positions across all markets the user has interacted with.
|
|
586
|
+
*
|
|
587
|
+
* Uses Morpho GraphQL `marketPositions` to find ALL positions (not limited
|
|
588
|
+
* to the top-500 markets), then reads onchain data for accurate debt.
|
|
564
589
|
*/
|
|
565
590
|
async getStatus() {
|
|
566
591
|
const acctAddr = await this.getAccountAddress();
|
|
567
|
-
const
|
|
592
|
+
const chainId = this.config.chainId;
|
|
568
593
|
const positions = [];
|
|
569
594
|
let totalDebtFloat = 0;
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
595
|
+
try {
|
|
596
|
+
const posQuery = `{
|
|
597
|
+
marketPositions(
|
|
598
|
+
where: {
|
|
599
|
+
userAddress_in: ["${acctAddr}"]
|
|
600
|
+
chainId_in: [${chainId}]
|
|
601
|
+
}
|
|
602
|
+
first: 100
|
|
603
|
+
) {
|
|
604
|
+
items {
|
|
605
|
+
supplyShares
|
|
606
|
+
borrowShares
|
|
607
|
+
collateral
|
|
608
|
+
market {
|
|
609
|
+
uniqueKey
|
|
610
|
+
loanAsset { symbol address decimals }
|
|
611
|
+
collateralAsset { symbol address decimals }
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}`;
|
|
616
|
+
const resp = await import_axios.default.post(MORPHO_API_URL, { query: posQuery }, { timeout: 15e3 });
|
|
617
|
+
const items = resp.data?.data?.marketPositions?.items ?? [];
|
|
618
|
+
for (const item of items) {
|
|
619
|
+
const supplyShares = BigInt(item.supplyShares ?? "0");
|
|
620
|
+
const borrowShares = BigInt(item.borrowShares ?? "0");
|
|
621
|
+
const collateral = BigInt(item.collateral ?? "0");
|
|
622
|
+
if (collateral === 0n && borrowShares === 0n && supplyShares === 0n) continue;
|
|
623
|
+
const m = item.market;
|
|
624
|
+
if (!m?.collateralAsset || !m?.loanAsset) continue;
|
|
575
625
|
const loanDecimals = m.loanAsset.decimals;
|
|
576
626
|
let debt = 0n;
|
|
577
|
-
if (
|
|
627
|
+
if (borrowShares > 0n) {
|
|
578
628
|
try {
|
|
579
629
|
const mkt = await this.morphoBlue.market(m.uniqueKey);
|
|
580
630
|
const totalBorrowShares = BigInt(mkt.totalBorrowShares);
|
|
581
631
|
const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
|
|
582
|
-
debt = totalBorrowShares > 0n ? (
|
|
632
|
+
debt = totalBorrowShares > 0n ? (borrowShares * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
583
633
|
totalDebtFloat += parseFloat(import_ethers.ethers.formatUnits(debt, loanDecimals));
|
|
584
634
|
} catch (e) {
|
|
585
635
|
console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
|
|
@@ -589,14 +639,46 @@ var init_MorphoClient = __esm({
|
|
|
589
639
|
marketId: m.uniqueKey,
|
|
590
640
|
collateralToken: m.collateralAsset.symbol,
|
|
591
641
|
loanToken: m.loanAsset.symbol,
|
|
592
|
-
collateral: import_ethers.ethers.formatUnits(
|
|
593
|
-
borrowShares:
|
|
594
|
-
supplyShares:
|
|
642
|
+
collateral: import_ethers.ethers.formatUnits(collateral, m.collateralAsset.decimals),
|
|
643
|
+
borrowShares: borrowShares.toString(),
|
|
644
|
+
supplyShares: supplyShares.toString(),
|
|
595
645
|
debt: import_ethers.ethers.formatUnits(debt, loanDecimals)
|
|
596
646
|
});
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
|
|
647
|
+
}
|
|
648
|
+
} catch (e) {
|
|
649
|
+
console.warn("[agether] marketPositions API failed, falling back to market scan:", e instanceof Error ? e.message : e);
|
|
650
|
+
const markets = await this.getMarkets();
|
|
651
|
+
for (const m of markets) {
|
|
652
|
+
if (!m.collateralAsset || m.collateralAsset.address === import_ethers.ethers.ZeroAddress) continue;
|
|
653
|
+
try {
|
|
654
|
+
const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
|
|
655
|
+
if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
|
|
656
|
+
const loanDecimals = m.loanAsset.decimals;
|
|
657
|
+
let debt = 0n;
|
|
658
|
+
if (pos.borrowShares > 0n) {
|
|
659
|
+
try {
|
|
660
|
+
const mkt = await this.morphoBlue.market(m.uniqueKey);
|
|
661
|
+
const totalBorrowShares = BigInt(mkt.totalBorrowShares);
|
|
662
|
+
const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
|
|
663
|
+
debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
664
|
+
totalDebtFloat += parseFloat(import_ethers.ethers.formatUnits(debt, loanDecimals));
|
|
665
|
+
} catch (e2) {
|
|
666
|
+
console.warn(`[agether] debt calc failed:`, e2 instanceof Error ? e2.message : e2);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
positions.push({
|
|
670
|
+
marketId: m.uniqueKey,
|
|
671
|
+
collateralToken: m.collateralAsset.symbol,
|
|
672
|
+
loanToken: m.loanAsset.symbol,
|
|
673
|
+
collateral: import_ethers.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
|
|
674
|
+
borrowShares: pos.borrowShares.toString(),
|
|
675
|
+
supplyShares: pos.supplyShares.toString(),
|
|
676
|
+
debt: import_ethers.ethers.formatUnits(debt, loanDecimals)
|
|
677
|
+
});
|
|
678
|
+
} catch (e2) {
|
|
679
|
+
console.warn(`[agether] position read failed:`, e2 instanceof Error ? e2.message : e2);
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
600
682
|
}
|
|
601
683
|
}
|
|
602
684
|
return {
|
|
@@ -700,41 +782,39 @@ var init_MorphoClient = __esm({
|
|
|
700
782
|
async getMarketRates(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
|
|
701
783
|
const chainId = this.config.chainId;
|
|
702
784
|
let collateralFilter = "";
|
|
785
|
+
let loanFilter = "";
|
|
786
|
+
let searchTerm = "";
|
|
703
787
|
if (collateralSymbolOrAddress) {
|
|
704
|
-
let colAddr;
|
|
705
788
|
if (collateralSymbolOrAddress.startsWith("0x")) {
|
|
706
|
-
|
|
789
|
+
collateralFilter = `, collateralAssetAddress_in: ["${collateralSymbolOrAddress.toLowerCase()}"]`;
|
|
707
790
|
} else {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
}
|
|
712
|
-
|
|
791
|
+
const cached = this._tokenCache.get(collateralSymbolOrAddress.toUpperCase());
|
|
792
|
+
if (cached) {
|
|
793
|
+
collateralFilter = `, collateralAssetAddress_in: ["${cached.address.toLowerCase()}"]`;
|
|
794
|
+
} else {
|
|
795
|
+
searchTerm = collateralSymbolOrAddress;
|
|
713
796
|
}
|
|
714
797
|
}
|
|
715
|
-
collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
|
|
716
798
|
}
|
|
717
|
-
let loanFilter = "";
|
|
718
799
|
if (loanTokenSymbolOrAddress) {
|
|
719
|
-
let loanAddr;
|
|
720
800
|
if (loanTokenSymbolOrAddress.startsWith("0x")) {
|
|
721
|
-
|
|
801
|
+
loanFilter = `, loanAssetAddress_in: ["${loanTokenSymbolOrAddress.toLowerCase()}"]`;
|
|
722
802
|
} else {
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
}
|
|
727
|
-
|
|
803
|
+
const cached = this._tokenCache.get(loanTokenSymbolOrAddress.toUpperCase());
|
|
804
|
+
if (cached) {
|
|
805
|
+
loanFilter = `, loanAssetAddress_in: ["${cached.address.toLowerCase()}"]`;
|
|
806
|
+
} else {
|
|
807
|
+
searchTerm = searchTerm || loanTokenSymbolOrAddress;
|
|
728
808
|
}
|
|
729
809
|
}
|
|
730
|
-
loanFilter = `, loanAssetAddress_in: ["${loanAddr}"]`;
|
|
731
810
|
}
|
|
811
|
+
const searchClause = searchTerm ? `, search: "${searchTerm}"` : "";
|
|
732
812
|
const query = `{
|
|
733
813
|
markets(
|
|
734
|
-
first:
|
|
814
|
+
first: 100
|
|
735
815
|
orderBy: SupplyAssetsUsd
|
|
736
816
|
orderDirection: Desc
|
|
737
|
-
where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter} }
|
|
817
|
+
where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter}${searchClause} }
|
|
738
818
|
) {
|
|
739
819
|
items {
|
|
740
820
|
uniqueKey
|
|
@@ -753,7 +833,15 @@ var init_MorphoClient = __esm({
|
|
|
753
833
|
}`;
|
|
754
834
|
try {
|
|
755
835
|
const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
|
|
756
|
-
|
|
836
|
+
let items = resp.data?.data?.markets?.items ?? [];
|
|
837
|
+
if (searchTerm && collateralSymbolOrAddress && !collateralSymbolOrAddress.startsWith("0x")) {
|
|
838
|
+
const sym = collateralSymbolOrAddress.toUpperCase();
|
|
839
|
+
items = items.filter((m) => m.collateralAsset?.symbol?.toUpperCase() === sym);
|
|
840
|
+
}
|
|
841
|
+
if (searchTerm && loanTokenSymbolOrAddress && !loanTokenSymbolOrAddress.startsWith("0x")) {
|
|
842
|
+
const sym = loanTokenSymbolOrAddress.toUpperCase();
|
|
843
|
+
items = items.filter((m) => m.loanAsset?.symbol?.toUpperCase() === sym);
|
|
844
|
+
}
|
|
757
845
|
return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers.ethers.ZeroAddress).map((m) => {
|
|
758
846
|
const loanDecimals = m.loanAsset?.decimals ?? 18;
|
|
759
847
|
return {
|
|
@@ -774,6 +862,205 @@ var init_MorphoClient = __esm({
|
|
|
774
862
|
return [];
|
|
775
863
|
}
|
|
776
864
|
}
|
|
865
|
+
// ════════════════════════════════════════════════════════
|
|
866
|
+
// Market Search & Wallet Discovery
|
|
867
|
+
// ════════════════════════════════════════════════════════
|
|
868
|
+
/**
|
|
869
|
+
* Search Morpho markets by token name using the Morpho GraphQL API `search` field.
|
|
870
|
+
* No hardcoded token lists — uses Morpho's built-in fuzzy search.
|
|
871
|
+
*
|
|
872
|
+
* @param search - token name or symbol (e.g. 'WETH', 'staked ETH', 'ezETH')
|
|
873
|
+
* @param options.asCollateral - only return markets where the searched token is collateral
|
|
874
|
+
* @param options.asLoanToken - only return markets where the searched token is the loan asset
|
|
875
|
+
*/
|
|
876
|
+
async searchMarkets(search, options) {
|
|
877
|
+
const chainId = this.config.chainId;
|
|
878
|
+
const query = `{
|
|
879
|
+
markets(
|
|
880
|
+
first: 100
|
|
881
|
+
orderBy: SupplyAssetsUsd
|
|
882
|
+
orderDirection: Desc
|
|
883
|
+
where: { chainId_in: [${chainId}], search: "${search}" }
|
|
884
|
+
) {
|
|
885
|
+
items {
|
|
886
|
+
uniqueKey
|
|
887
|
+
lltv
|
|
888
|
+
loanAsset { address symbol decimals }
|
|
889
|
+
collateralAsset { address symbol decimals }
|
|
890
|
+
state {
|
|
891
|
+
borrowAssets
|
|
892
|
+
supplyAssets
|
|
893
|
+
utilization
|
|
894
|
+
supplyApy
|
|
895
|
+
borrowApy
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}`;
|
|
900
|
+
try {
|
|
901
|
+
const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
|
|
902
|
+
let items = resp.data?.data?.markets?.items ?? [];
|
|
903
|
+
items = items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers.ethers.ZeroAddress);
|
|
904
|
+
const searchUpper = search.toUpperCase();
|
|
905
|
+
if (options?.asCollateral) {
|
|
906
|
+
items = items.filter((m) => m.collateralAsset?.symbol?.toUpperCase() === searchUpper);
|
|
907
|
+
}
|
|
908
|
+
if (options?.asLoanToken) {
|
|
909
|
+
items = items.filter((m) => m.loanAsset?.symbol?.toUpperCase() === searchUpper);
|
|
910
|
+
}
|
|
911
|
+
return items.map((m) => {
|
|
912
|
+
const loanDecimals = m.loanAsset?.decimals ?? 18;
|
|
913
|
+
const collateralDecimals = m.collateralAsset?.decimals ?? 18;
|
|
914
|
+
return {
|
|
915
|
+
collateralToken: m.collateralAsset.symbol,
|
|
916
|
+
loanToken: m.loanAsset.symbol,
|
|
917
|
+
loanDecimals,
|
|
918
|
+
collateralDecimals,
|
|
919
|
+
supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
|
|
920
|
+
borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
|
|
921
|
+
utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
|
|
922
|
+
totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
|
|
923
|
+
totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
|
|
924
|
+
lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
|
|
925
|
+
marketId: m.uniqueKey,
|
|
926
|
+
collateralAddress: m.collateralAsset.address,
|
|
927
|
+
loanAddress: m.loanAsset.address
|
|
928
|
+
};
|
|
929
|
+
});
|
|
930
|
+
} catch (e) {
|
|
931
|
+
console.warn("[agether] searchMarkets failed:", e instanceof Error ? e.message : e);
|
|
932
|
+
return [];
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Scan the AgentAccount wallet for all ERC-20 tokens that appear in Morpho
|
|
937
|
+
* markets on the current chain. Returns tokens where balance > 0.
|
|
938
|
+
*
|
|
939
|
+
* Uses the full market list (500 markets) to discover all relevant tokens,
|
|
940
|
+
* then checks on-chain balance for each unique token address.
|
|
941
|
+
*
|
|
942
|
+
* @returns Array of tokens with non-zero balance, sorted by balance descending.
|
|
943
|
+
*/
|
|
944
|
+
async getWalletTokenBalances() {
|
|
945
|
+
const acctAddr = await this.getAccountAddress();
|
|
946
|
+
await this.getMarkets();
|
|
947
|
+
const uniqueTokens = /* @__PURE__ */ new Map();
|
|
948
|
+
for (const [key, info] of this._tokenCache.entries()) {
|
|
949
|
+
if (key.startsWith("0x") && !uniqueTokens.has(key)) {
|
|
950
|
+
uniqueTokens.set(key, info);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
const results = [];
|
|
954
|
+
try {
|
|
955
|
+
const ethBalance = await this.provider.getBalance(acctAddr);
|
|
956
|
+
if (ethBalance > 0n) {
|
|
957
|
+
results.push({
|
|
958
|
+
symbol: "ETH",
|
|
959
|
+
address: import_ethers.ethers.ZeroAddress,
|
|
960
|
+
decimals: 18,
|
|
961
|
+
balance: ethBalance,
|
|
962
|
+
balanceFormatted: import_ethers.ethers.formatEther(ethBalance)
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
} catch {
|
|
966
|
+
}
|
|
967
|
+
const tokenEntries = Array.from(uniqueTokens.values());
|
|
968
|
+
const batchSize = 20;
|
|
969
|
+
for (let i = 0; i < tokenEntries.length; i += batchSize) {
|
|
970
|
+
const batch = tokenEntries.slice(i, i + batchSize);
|
|
971
|
+
const checks = batch.map(async (info) => {
|
|
972
|
+
try {
|
|
973
|
+
const token = new import_ethers.Contract(info.address, ERC20_ABI, this.provider);
|
|
974
|
+
const balance = await token.balanceOf(acctAddr);
|
|
975
|
+
if (balance > 0n) {
|
|
976
|
+
return {
|
|
977
|
+
symbol: info.symbol,
|
|
978
|
+
address: info.address,
|
|
979
|
+
decimals: info.decimals,
|
|
980
|
+
balance,
|
|
981
|
+
balanceFormatted: import_ethers.ethers.formatUnits(balance, info.decimals)
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
} catch {
|
|
985
|
+
}
|
|
986
|
+
return null;
|
|
987
|
+
});
|
|
988
|
+
const batchResults = await Promise.all(checks);
|
|
989
|
+
for (const r of batchResults) {
|
|
990
|
+
if (r) results.push(r);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
results.sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
|
|
994
|
+
return results;
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Find borrowing opportunities for the agent.
|
|
998
|
+
*
|
|
999
|
+
* - If `collateralSymbol` is provided: find all markets where that token is collateral.
|
|
1000
|
+
* - If omitted: scan wallet balances and find markets for each token the agent holds.
|
|
1001
|
+
*
|
|
1002
|
+
* Returns markets grouped by collateral token with APY, liquidity, and balance info.
|
|
1003
|
+
*
|
|
1004
|
+
* @param collateralSymbol - optional, e.g. 'WETH'. If omitted, scans wallet.
|
|
1005
|
+
*/
|
|
1006
|
+
async findBorrowingOptions(collateralSymbol) {
|
|
1007
|
+
let tokensToCheck;
|
|
1008
|
+
if (collateralSymbol) {
|
|
1009
|
+
tokensToCheck = [{ symbol: collateralSymbol, balanceFormatted: "N/A" }];
|
|
1010
|
+
try {
|
|
1011
|
+
const balance = await this.getTokenBalance(collateralSymbol);
|
|
1012
|
+
const info = await this._resolveToken(collateralSymbol);
|
|
1013
|
+
tokensToCheck = [{ symbol: collateralSymbol, balanceFormatted: import_ethers.ethers.formatUnits(balance, info.decimals) }];
|
|
1014
|
+
} catch {
|
|
1015
|
+
}
|
|
1016
|
+
} else {
|
|
1017
|
+
const walletTokens = await this.getWalletTokenBalances();
|
|
1018
|
+
tokensToCheck = walletTokens.filter((t) => t.symbol !== "ETH").map((t) => ({ symbol: t.symbol, balanceFormatted: t.balanceFormatted }));
|
|
1019
|
+
if (tokensToCheck.length === 0) {
|
|
1020
|
+
return [];
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
const results = [];
|
|
1024
|
+
for (const token of tokensToCheck) {
|
|
1025
|
+
const markets = await this.searchMarkets(token.symbol, { asCollateral: true });
|
|
1026
|
+
if (markets.length === 0) continue;
|
|
1027
|
+
results.push({
|
|
1028
|
+
collateralToken: token.symbol,
|
|
1029
|
+
collateralBalance: token.balanceFormatted,
|
|
1030
|
+
markets: markets.map((m) => ({
|
|
1031
|
+
loanToken: m.loanToken,
|
|
1032
|
+
borrowApy: `${(m.borrowApy * 100).toFixed(2)}%`,
|
|
1033
|
+
supplyApy: `${(m.supplyApy * 100).toFixed(2)}%`,
|
|
1034
|
+
lltv: m.lltv,
|
|
1035
|
+
utilization: `${(m.utilization * 100).toFixed(1)}%`,
|
|
1036
|
+
availableLiquidity: `$${(m.totalSupplyUsd - m.totalBorrowUsd).toFixed(0)}`,
|
|
1037
|
+
marketId: m.marketId
|
|
1038
|
+
}))
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
return results;
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Find supply/lending opportunities for a specific loan token.
|
|
1045
|
+
*
|
|
1046
|
+
* "What can I supply to earn WETH?" → shows all markets where WETH is the loan token
|
|
1047
|
+
* (user supplies WETH to earn yield from borrowers).
|
|
1048
|
+
*
|
|
1049
|
+
* @param loanTokenSymbol - e.g. 'USDC', 'WETH'
|
|
1050
|
+
*/
|
|
1051
|
+
async findSupplyOptions(loanTokenSymbol) {
|
|
1052
|
+
const markets = await this.searchMarkets(loanTokenSymbol, { asLoanToken: true });
|
|
1053
|
+
return markets.map((m) => ({
|
|
1054
|
+
collateralToken: m.collateralToken,
|
|
1055
|
+
loanToken: m.loanToken,
|
|
1056
|
+
supplyApy: `${(m.supplyApy * 100).toFixed(2)}%`,
|
|
1057
|
+
borrowApy: `${(m.borrowApy * 100).toFixed(2)}%`,
|
|
1058
|
+
lltv: m.lltv,
|
|
1059
|
+
utilization: `${(m.utilization * 100).toFixed(1)}%`,
|
|
1060
|
+
totalSupply: `$${m.totalSupplyUsd.toFixed(0)}`,
|
|
1061
|
+
marketId: m.marketId
|
|
1062
|
+
}));
|
|
1063
|
+
}
|
|
777
1064
|
/**
|
|
778
1065
|
* Estimate theoretical yield for a given collateral amount over a period.
|
|
779
1066
|
*
|
|
@@ -1678,6 +1965,46 @@ var init_MorphoClient = __esm({
|
|
|
1678
1965
|
/** Find the first market where the agent has collateral deposited. */
|
|
1679
1966
|
async _findActiveMarket() {
|
|
1680
1967
|
const acctAddr = await this.getAccountAddress();
|
|
1968
|
+
const chainId = this.config.chainId;
|
|
1969
|
+
try {
|
|
1970
|
+
const posQuery = `{
|
|
1971
|
+
marketPositions(
|
|
1972
|
+
where: { userAddress_in: ["${acctAddr}"], chainId_in: [${chainId}] }
|
|
1973
|
+
first: 50
|
|
1974
|
+
) {
|
|
1975
|
+
items {
|
|
1976
|
+
collateral
|
|
1977
|
+
market {
|
|
1978
|
+
uniqueKey
|
|
1979
|
+
oracleAddress
|
|
1980
|
+
irmAddress
|
|
1981
|
+
lltv
|
|
1982
|
+
loanAsset { address symbol decimals }
|
|
1983
|
+
collateralAsset { address symbol decimals }
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
}`;
|
|
1988
|
+
const resp = await import_axios.default.post(MORPHO_API_URL, { query: posQuery }, { timeout: 1e4 });
|
|
1989
|
+
const items = resp.data?.data?.marketPositions?.items ?? [];
|
|
1990
|
+
for (const item of items) {
|
|
1991
|
+
if (BigInt(item.collateral ?? "0") > 0n && item.market?.collateralAsset) {
|
|
1992
|
+
const m = item.market;
|
|
1993
|
+
return {
|
|
1994
|
+
params: {
|
|
1995
|
+
loanToken: m.loanAsset.address,
|
|
1996
|
+
collateralToken: m.collateralAsset.address,
|
|
1997
|
+
oracle: m.oracleAddress,
|
|
1998
|
+
irm: m.irmAddress,
|
|
1999
|
+
lltv: BigInt(m.lltv)
|
|
2000
|
+
},
|
|
2001
|
+
symbol: m.collateralAsset.symbol
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
} catch (e) {
|
|
2006
|
+
console.warn("[agether] _findActiveMarket GraphQL failed, falling back:", e instanceof Error ? e.message : e);
|
|
2007
|
+
}
|
|
1681
2008
|
const markets = await this.getMarkets();
|
|
1682
2009
|
for (const m of markets) {
|
|
1683
2010
|
if (!m.collateralAsset || m.collateralAsset.address === import_ethers.ethers.ZeroAddress) continue;
|
|
@@ -1706,6 +2033,46 @@ var init_MorphoClient = __esm({
|
|
|
1706
2033
|
/** Find the first market where the agent has a supply (lending) position. */
|
|
1707
2034
|
async _findActiveSupplyMarket() {
|
|
1708
2035
|
const acctAddr = await this.getAccountAddress();
|
|
2036
|
+
const chainId = this.config.chainId;
|
|
2037
|
+
try {
|
|
2038
|
+
const posQuery = `{
|
|
2039
|
+
marketPositions(
|
|
2040
|
+
where: { userAddress_in: ["${acctAddr}"], chainId_in: [${chainId}] }
|
|
2041
|
+
first: 50
|
|
2042
|
+
) {
|
|
2043
|
+
items {
|
|
2044
|
+
supplyShares
|
|
2045
|
+
market {
|
|
2046
|
+
uniqueKey
|
|
2047
|
+
oracleAddress
|
|
2048
|
+
irmAddress
|
|
2049
|
+
lltv
|
|
2050
|
+
loanAsset { address symbol decimals }
|
|
2051
|
+
collateralAsset { address symbol decimals }
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
}`;
|
|
2056
|
+
const resp = await import_axios.default.post(MORPHO_API_URL, { query: posQuery }, { timeout: 1e4 });
|
|
2057
|
+
const items = resp.data?.data?.marketPositions?.items ?? [];
|
|
2058
|
+
for (const item of items) {
|
|
2059
|
+
if (BigInt(item.supplyShares ?? "0") > 0n && item.market?.collateralAsset) {
|
|
2060
|
+
const m = item.market;
|
|
2061
|
+
return {
|
|
2062
|
+
params: {
|
|
2063
|
+
loanToken: m.loanAsset.address,
|
|
2064
|
+
collateralToken: m.collateralAsset.address,
|
|
2065
|
+
oracle: m.oracleAddress,
|
|
2066
|
+
irm: m.irmAddress,
|
|
2067
|
+
lltv: BigInt(m.lltv)
|
|
2068
|
+
},
|
|
2069
|
+
symbol: m.collateralAsset.symbol
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
} catch (e) {
|
|
2074
|
+
console.warn("[agether] _findActiveSupplyMarket GraphQL failed, falling back:", e instanceof Error ? e.message : e);
|
|
2075
|
+
}
|
|
1709
2076
|
const markets = await this.getMarkets();
|
|
1710
2077
|
for (const m of markets) {
|
|
1711
2078
|
if (!m.collateralAsset || m.collateralAsset.address === import_ethers.ethers.ZeroAddress) continue;
|
|
@@ -1776,6 +2143,24 @@ var init_MorphoClient = __esm({
|
|
|
1776
2143
|
await this.getMarkets();
|
|
1777
2144
|
const fromApi = this._tokenCache.get(key);
|
|
1778
2145
|
if (fromApi) return fromApi;
|
|
2146
|
+
if (!symbolOrAddress.startsWith("0x")) {
|
|
2147
|
+
const searchResults = await this.searchMarkets(symbolOrAddress);
|
|
2148
|
+
const sym = symbolOrAddress.toUpperCase();
|
|
2149
|
+
for (const m of searchResults) {
|
|
2150
|
+
if (m.collateralToken.toUpperCase() === sym) {
|
|
2151
|
+
const info = { address: m.collateralAddress, symbol: m.collateralToken, decimals: m.collateralDecimals };
|
|
2152
|
+
this._tokenCache.set(sym, info);
|
|
2153
|
+
this._tokenCache.set(m.collateralAddress.toLowerCase(), info);
|
|
2154
|
+
return info;
|
|
2155
|
+
}
|
|
2156
|
+
if (m.loanToken.toUpperCase() === sym) {
|
|
2157
|
+
const info = { address: m.loanAddress, symbol: m.loanToken, decimals: m.loanDecimals };
|
|
2158
|
+
this._tokenCache.set(sym, info);
|
|
2159
|
+
this._tokenCache.set(m.loanAddress.toLowerCase(), info);
|
|
2160
|
+
return info;
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
1779
2164
|
throw new AgetherError(
|
|
1780
2165
|
`Unknown token: ${symbolOrAddress}. No Morpho market found with this token.`,
|
|
1781
2166
|
"UNKNOWN_TOKEN"
|