@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/cli.js +294 -131
- package/dist/index.d.mts +92 -27
- package/dist/index.d.ts +92 -27
- package/dist/index.js +294 -131
- package/dist/index.mjs +294 -131
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -58,6 +58,7 @@ var init_abis = __esm({
|
|
|
58
58
|
"function exists(uint256 agentId) view returns (bool)",
|
|
59
59
|
"function register() returns (uint256 agentId)",
|
|
60
60
|
"function register(string agentURI) returns (uint256 agentId)",
|
|
61
|
+
"function setAgentURI(uint256 agentId, string newURI)",
|
|
61
62
|
"event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
|
|
62
63
|
];
|
|
63
64
|
AGETHER_4337_FACTORY_ABI = [
|
|
@@ -289,8 +290,9 @@ var init_MorphoClient = __esm({
|
|
|
289
290
|
erc20Iface = new import_ethers.ethers.Interface(ERC20_ABI);
|
|
290
291
|
MorphoClient = class {
|
|
291
292
|
constructor(config) {
|
|
293
|
+
/** Market params cache: keyed by market uniqueKey (bytes32 hash) */
|
|
292
294
|
this._marketCache = /* @__PURE__ */ new Map();
|
|
293
|
-
/** Dynamic token registry: symbol (uppercase) → { address, symbol, decimals } */
|
|
295
|
+
/** Dynamic token registry: symbol (uppercase) or address (lowercase) → { address, symbol, decimals } */
|
|
294
296
|
this._tokenCache = /* @__PURE__ */ new Map();
|
|
295
297
|
this._discoveredAt = 0;
|
|
296
298
|
if (!config.agentId) {
|
|
@@ -393,21 +395,23 @@ var init_MorphoClient = __esm({
|
|
|
393
395
|
// Market Discovery (Morpho GraphQL API)
|
|
394
396
|
// ════════════════════════════════════════════════════════
|
|
395
397
|
/**
|
|
396
|
-
* Fetch
|
|
397
|
-
* Caches results for 5 minutes.
|
|
398
|
+
* Fetch available markets on the current chain from Morpho API.
|
|
399
|
+
* Caches results for 5 minutes. Supports all loan tokens (not just USDC).
|
|
400
|
+
*
|
|
401
|
+
* @param forceRefresh - bypass cache TTL
|
|
402
|
+
* @param filter - optional filter by loan token and/or collateral token
|
|
398
403
|
*/
|
|
399
|
-
async getMarkets(forceRefresh = false) {
|
|
404
|
+
async getMarkets(forceRefresh = false, filter) {
|
|
400
405
|
if (!forceRefresh && this._discoveredMarkets && Date.now() - this._discoveredAt < 3e5) {
|
|
401
|
-
return this._discoveredMarkets;
|
|
406
|
+
return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
|
|
402
407
|
}
|
|
403
408
|
const chainId = this.config.chainId;
|
|
404
|
-
const usdcAddr = this.config.contracts.usdc.toLowerCase();
|
|
405
409
|
const query = `{
|
|
406
410
|
markets(
|
|
407
411
|
first: 50
|
|
408
412
|
orderBy: SupplyAssetsUsd
|
|
409
413
|
orderDirection: Desc
|
|
410
|
-
where: { chainId_in: [${chainId}]
|
|
414
|
+
where: { chainId_in: [${chainId}] }
|
|
411
415
|
) {
|
|
412
416
|
items {
|
|
413
417
|
uniqueKey
|
|
@@ -440,14 +444,14 @@ var init_MorphoClient = __esm({
|
|
|
440
444
|
}));
|
|
441
445
|
this._discoveredAt = Date.now();
|
|
442
446
|
for (const mi of this._discoveredMarkets) {
|
|
447
|
+
this._marketCache.set(mi.uniqueKey.toLowerCase(), {
|
|
448
|
+
loanToken: mi.loanAsset.address,
|
|
449
|
+
collateralToken: mi.collateralAsset.address,
|
|
450
|
+
oracle: mi.oracle,
|
|
451
|
+
irm: mi.irm,
|
|
452
|
+
lltv: mi.lltv
|
|
453
|
+
});
|
|
443
454
|
if (mi.collateralAsset.address !== import_ethers.ethers.ZeroAddress) {
|
|
444
|
-
this._marketCache.set(mi.collateralAsset.address.toLowerCase(), {
|
|
445
|
-
loanToken: mi.loanAsset.address,
|
|
446
|
-
collateralToken: mi.collateralAsset.address,
|
|
447
|
-
oracle: mi.oracle,
|
|
448
|
-
irm: mi.irm,
|
|
449
|
-
lltv: mi.lltv
|
|
450
|
-
});
|
|
451
455
|
this._tokenCache.set(mi.collateralAsset.symbol.toUpperCase(), {
|
|
452
456
|
address: mi.collateralAsset.address,
|
|
453
457
|
symbol: mi.collateralAsset.symbol,
|
|
@@ -472,17 +476,24 @@ var init_MorphoClient = __esm({
|
|
|
472
476
|
});
|
|
473
477
|
}
|
|
474
478
|
}
|
|
475
|
-
return this._discoveredMarkets;
|
|
479
|
+
return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
|
|
476
480
|
} catch (e) {
|
|
477
481
|
console.warn("[agether] getMarkets failed, using cache:", e instanceof Error ? e.message : e);
|
|
478
|
-
|
|
482
|
+
const cached = this._discoveredMarkets ?? [];
|
|
483
|
+
return filter ? this._applyMarketFilter(cached, filter) : cached;
|
|
479
484
|
}
|
|
480
485
|
}
|
|
481
486
|
/**
|
|
482
|
-
* Get MarketParams for a collateral token.
|
|
483
|
-
* Tries cache → API
|
|
487
|
+
* Get MarketParams for a collateral token (and optionally a specific loan token).
|
|
488
|
+
* Tries cache → API discovery.
|
|
489
|
+
*
|
|
490
|
+
* When `loanTokenSymbolOrAddress` is omitted, returns the most liquid market
|
|
491
|
+
* for that collateral (sorted by supply, typically the USDC market).
|
|
492
|
+
*
|
|
493
|
+
* @param collateralSymbolOrAddress - e.g. 'WETH', 'wstETH', or '0x4200...'
|
|
494
|
+
* @param loanTokenSymbolOrAddress - e.g. 'USDC', 'WETH', or '0x833589...' (optional)
|
|
484
495
|
*/
|
|
485
|
-
async findMarketForCollateral(collateralSymbolOrAddress) {
|
|
496
|
+
async findMarketForCollateral(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
|
|
486
497
|
let colAddr;
|
|
487
498
|
if (collateralSymbolOrAddress.startsWith("0x")) {
|
|
488
499
|
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
@@ -494,13 +505,33 @@ var init_MorphoClient = __esm({
|
|
|
494
505
|
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
495
506
|
}
|
|
496
507
|
}
|
|
497
|
-
|
|
498
|
-
if (
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
508
|
+
let loanAddr;
|
|
509
|
+
if (loanTokenSymbolOrAddress) {
|
|
510
|
+
if (loanTokenSymbolOrAddress.startsWith("0x")) {
|
|
511
|
+
loanAddr = loanTokenSymbolOrAddress.toLowerCase();
|
|
512
|
+
} else {
|
|
513
|
+
try {
|
|
514
|
+
const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
|
|
515
|
+
loanAddr = resolved.address.toLowerCase();
|
|
516
|
+
} catch {
|
|
517
|
+
loanAddr = loanTokenSymbolOrAddress.toLowerCase();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
if (!this._discoveredMarkets) await this.getMarkets();
|
|
522
|
+
for (const m of this._discoveredMarkets ?? []) {
|
|
523
|
+
if (m.collateralAsset.address.toLowerCase() !== colAddr) continue;
|
|
524
|
+
if (loanAddr && m.loanAsset.address.toLowerCase() !== loanAddr) continue;
|
|
525
|
+
return {
|
|
526
|
+
loanToken: m.loanAsset.address,
|
|
527
|
+
collateralToken: m.collateralAsset.address,
|
|
528
|
+
oracle: m.oracle,
|
|
529
|
+
irm: m.irm,
|
|
530
|
+
lltv: m.lltv
|
|
531
|
+
};
|
|
532
|
+
}
|
|
502
533
|
throw new AgetherError(
|
|
503
|
-
`No Morpho market found for collateral ${collateralSymbolOrAddress}
|
|
534
|
+
`No Morpho market found for collateral ${collateralSymbolOrAddress}` + (loanTokenSymbolOrAddress ? ` with loan token ${loanTokenSymbolOrAddress}` : ""),
|
|
504
535
|
"MARKET_NOT_FOUND"
|
|
505
536
|
);
|
|
506
537
|
}
|
|
@@ -535,12 +566,13 @@ var init_MorphoClient = __esm({
|
|
|
535
566
|
const acctAddr = await this.getAccountAddress();
|
|
536
567
|
const markets = await this.getMarkets();
|
|
537
568
|
const positions = [];
|
|
538
|
-
let
|
|
569
|
+
let totalDebtFloat = 0;
|
|
539
570
|
for (const m of markets) {
|
|
540
571
|
if (!m.collateralAsset || m.collateralAsset.address === import_ethers.ethers.ZeroAddress) continue;
|
|
541
572
|
try {
|
|
542
573
|
const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
|
|
543
574
|
if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
|
|
575
|
+
const loanDecimals = m.loanAsset.decimals;
|
|
544
576
|
let debt = 0n;
|
|
545
577
|
if (pos.borrowShares > 0n) {
|
|
546
578
|
try {
|
|
@@ -548,7 +580,7 @@ var init_MorphoClient = __esm({
|
|
|
548
580
|
const totalBorrowShares = BigInt(mkt.totalBorrowShares);
|
|
549
581
|
const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
|
|
550
582
|
debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
551
|
-
|
|
583
|
+
totalDebtFloat += parseFloat(import_ethers.ethers.formatUnits(debt, loanDecimals));
|
|
552
584
|
} catch (e) {
|
|
553
585
|
console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
|
|
554
586
|
}
|
|
@@ -556,10 +588,11 @@ var init_MorphoClient = __esm({
|
|
|
556
588
|
positions.push({
|
|
557
589
|
marketId: m.uniqueKey,
|
|
558
590
|
collateralToken: m.collateralAsset.symbol,
|
|
591
|
+
loanToken: m.loanAsset.symbol,
|
|
559
592
|
collateral: import_ethers.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
|
|
560
593
|
borrowShares: pos.borrowShares.toString(),
|
|
561
594
|
supplyShares: pos.supplyShares.toString(),
|
|
562
|
-
debt: import_ethers.ethers.formatUnits(debt,
|
|
595
|
+
debt: import_ethers.ethers.formatUnits(debt, loanDecimals)
|
|
563
596
|
});
|
|
564
597
|
} catch (e) {
|
|
565
598
|
console.warn(`[agether] position read failed for market:`, e instanceof Error ? e.message : e);
|
|
@@ -569,16 +602,28 @@ var init_MorphoClient = __esm({
|
|
|
569
602
|
return {
|
|
570
603
|
agentId: this.agentId,
|
|
571
604
|
agentAccount: acctAddr,
|
|
572
|
-
totalDebt:
|
|
605
|
+
totalDebt: totalDebtFloat.toFixed(6),
|
|
573
606
|
positions
|
|
574
607
|
};
|
|
575
608
|
}
|
|
576
609
|
// ════════════════════════════════════════════════════════
|
|
577
610
|
// Balance & Borrowing Capacity
|
|
578
611
|
// ════════════════════════════════════════════════════════
|
|
612
|
+
/**
|
|
613
|
+
* Get the balance of any ERC-20 token in the AgentAccount.
|
|
614
|
+
* @param symbolOrAddress - token symbol (e.g. 'USDC', 'WETH') or address
|
|
615
|
+
* @returns balance in raw units
|
|
616
|
+
*/
|
|
617
|
+
async getTokenBalance(symbolOrAddress) {
|
|
618
|
+
const acctAddr = await this.getAccountAddress();
|
|
619
|
+
const tokenInfo = await this._resolveToken(symbolOrAddress);
|
|
620
|
+
const token = new import_ethers.Contract(tokenInfo.address, ERC20_ABI, this.provider);
|
|
621
|
+
return token.balanceOf(acctAddr);
|
|
622
|
+
}
|
|
579
623
|
/**
|
|
580
624
|
* Get the USDC balance of the AgentAccount.
|
|
581
625
|
* @returns USDC balance in raw units (6 decimals)
|
|
626
|
+
* @deprecated Use `getTokenBalance('USDC')` instead.
|
|
582
627
|
*/
|
|
583
628
|
async getUsdcBalance() {
|
|
584
629
|
const acctAddr = await this.getAccountAddress();
|
|
@@ -586,7 +631,7 @@ var init_MorphoClient = __esm({
|
|
|
586
631
|
return usdc.balanceOf(acctAddr);
|
|
587
632
|
}
|
|
588
633
|
/**
|
|
589
|
-
* Calculate the maximum additional
|
|
634
|
+
* Calculate the maximum additional loan token that can be borrowed
|
|
590
635
|
* given the agent's current collateral and debt across all markets.
|
|
591
636
|
*
|
|
592
637
|
* For each market with collateral deposited:
|
|
@@ -594,7 +639,7 @@ var init_MorphoClient = __esm({
|
|
|
594
639
|
*
|
|
595
640
|
* Uses the Morpho oracle to price collateral → loan token.
|
|
596
641
|
*
|
|
597
|
-
* @returns Maximum additional
|
|
642
|
+
* @returns Maximum additional borrowable per market (raw units in each market's loan token)
|
|
598
643
|
*/
|
|
599
644
|
async getMaxBorrowable() {
|
|
600
645
|
const acctAddr = await this.getAccountAddress();
|
|
@@ -627,6 +672,8 @@ var init_MorphoClient = __esm({
|
|
|
627
672
|
totalAdditional += maxAdditional;
|
|
628
673
|
byMarket.push({
|
|
629
674
|
collateralToken: m.collateralAsset.symbol,
|
|
675
|
+
loanToken: m.loanAsset.symbol,
|
|
676
|
+
loanDecimals: m.loanAsset.decimals,
|
|
630
677
|
maxAdditional,
|
|
631
678
|
currentDebt,
|
|
632
679
|
collateralValue: collateralValueInLoan
|
|
@@ -642,14 +689,16 @@ var init_MorphoClient = __esm({
|
|
|
642
689
|
// Market Rates & Yield Estimation
|
|
643
690
|
// ════════════════════════════════════════════════════════
|
|
644
691
|
/**
|
|
645
|
-
* Fetch current supply/borrow APY for
|
|
692
|
+
* Fetch current supply/borrow APY for markets from Morpho GraphQL API.
|
|
646
693
|
*
|
|
647
694
|
* Note: On Morpho Blue, collateral does NOT earn yield directly. Supply APY
|
|
648
695
|
* is what lenders earn; borrow APY is what borrowers pay.
|
|
696
|
+
*
|
|
697
|
+
* @param collateralSymbolOrAddress - filter by collateral token (optional)
|
|
698
|
+
* @param loanTokenSymbolOrAddress - filter by loan token (optional). Omit for all loan tokens.
|
|
649
699
|
*/
|
|
650
|
-
async getMarketRates(collateralSymbolOrAddress) {
|
|
700
|
+
async getMarketRates(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
|
|
651
701
|
const chainId = this.config.chainId;
|
|
652
|
-
const usdcAddr = this.config.contracts.usdc.toLowerCase();
|
|
653
702
|
let collateralFilter = "";
|
|
654
703
|
if (collateralSymbolOrAddress) {
|
|
655
704
|
let colAddr;
|
|
@@ -665,12 +714,27 @@ var init_MorphoClient = __esm({
|
|
|
665
714
|
}
|
|
666
715
|
collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
|
|
667
716
|
}
|
|
717
|
+
let loanFilter = "";
|
|
718
|
+
if (loanTokenSymbolOrAddress) {
|
|
719
|
+
let loanAddr;
|
|
720
|
+
if (loanTokenSymbolOrAddress.startsWith("0x")) {
|
|
721
|
+
loanAddr = loanTokenSymbolOrAddress.toLowerCase();
|
|
722
|
+
} else {
|
|
723
|
+
try {
|
|
724
|
+
const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
|
|
725
|
+
loanAddr = resolved.address.toLowerCase();
|
|
726
|
+
} catch {
|
|
727
|
+
loanAddr = loanTokenSymbolOrAddress.toLowerCase();
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
loanFilter = `, loanAssetAddress_in: ["${loanAddr}"]`;
|
|
731
|
+
}
|
|
668
732
|
const query = `{
|
|
669
733
|
markets(
|
|
670
734
|
first: 50
|
|
671
735
|
orderBy: SupplyAssetsUsd
|
|
672
736
|
orderDirection: Desc
|
|
673
|
-
where: { chainId_in: [${chainId}]
|
|
737
|
+
where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter} }
|
|
674
738
|
) {
|
|
675
739
|
items {
|
|
676
740
|
uniqueKey
|
|
@@ -690,17 +754,21 @@ var init_MorphoClient = __esm({
|
|
|
690
754
|
try {
|
|
691
755
|
const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
|
|
692
756
|
const items = resp.data?.data?.markets?.items ?? [];
|
|
693
|
-
return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers.ethers.ZeroAddress).map((m) =>
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
757
|
+
return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers.ethers.ZeroAddress).map((m) => {
|
|
758
|
+
const loanDecimals = m.loanAsset?.decimals ?? 18;
|
|
759
|
+
return {
|
|
760
|
+
collateralToken: m.collateralAsset.symbol,
|
|
761
|
+
loanToken: m.loanAsset.symbol,
|
|
762
|
+
loanDecimals,
|
|
763
|
+
supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
|
|
764
|
+
borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
|
|
765
|
+
utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
|
|
766
|
+
totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
|
|
767
|
+
totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
|
|
768
|
+
lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
|
|
769
|
+
marketId: m.uniqueKey
|
|
770
|
+
};
|
|
771
|
+
});
|
|
704
772
|
} catch (e) {
|
|
705
773
|
console.warn("[agether] getMarketRates failed:", e instanceof Error ? e.message : e);
|
|
706
774
|
return [];
|
|
@@ -734,14 +802,15 @@ var init_MorphoClient = __esm({
|
|
|
734
802
|
} else {
|
|
735
803
|
try {
|
|
736
804
|
const params = await this.findMarketForCollateral(collateralSymbol);
|
|
805
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
737
806
|
const oracleContract = new import_ethers.Contract(params.oracle, [
|
|
738
807
|
"function price() view returns (uint256)"
|
|
739
808
|
], this.provider);
|
|
740
809
|
const oraclePrice = await oracleContract.price();
|
|
741
810
|
const ORACLE_PRICE_SCALE = 10n ** 36n;
|
|
742
811
|
const amountWei = import_ethers.ethers.parseUnits(amount, colInfo.decimals);
|
|
743
|
-
const
|
|
744
|
-
collateralValueUsd = Number(
|
|
812
|
+
const valueInLoan = amountWei * oraclePrice / ORACLE_PRICE_SCALE;
|
|
813
|
+
collateralValueUsd = Number(valueInLoan) / 10 ** loanDecimals;
|
|
745
814
|
} catch (e) {
|
|
746
815
|
console.warn("[agether] oracle price fetch for yield estimation failed:", e instanceof Error ? e.message : e);
|
|
747
816
|
throw new AgetherError("Cannot determine collateral value. Provide ethPriceUsd.", "PRICE_UNAVAILABLE");
|
|
@@ -762,61 +831,65 @@ var init_MorphoClient = __esm({
|
|
|
762
831
|
// Supply-Side (Lending) — earn yield by supplying USDC
|
|
763
832
|
// ════════════════════════════════════════════════════════
|
|
764
833
|
/**
|
|
765
|
-
* Supply
|
|
834
|
+
* Supply loan token to a Morpho Blue market as a lender (earn yield).
|
|
766
835
|
*
|
|
767
836
|
* Unlike `supplyCollateral` (borrower-side), this is the **lender-side**:
|
|
768
|
-
* you deposit the loanToken
|
|
837
|
+
* you deposit the loanToken into the market's supply pool and earn
|
|
769
838
|
* interest paid by borrowers.
|
|
770
839
|
*
|
|
771
|
-
* @param
|
|
840
|
+
* @param amount - Amount of loan token to supply (e.g. '500' for 500 USDC, '0.5' for 0.5 WETH)
|
|
772
841
|
* @param collateralSymbol - Market collateral token to identify which market (e.g. 'WETH')
|
|
773
842
|
* Optional — defaults to highest-APY market
|
|
843
|
+
* @param loanTokenSymbol - Loan token to filter market (e.g. 'USDC', 'WETH'). Optional.
|
|
774
844
|
*/
|
|
775
|
-
async supplyAsset(
|
|
845
|
+
async supplyAsset(amount, collateralSymbol, loanTokenSymbol) {
|
|
776
846
|
const acctAddr = await this.getAccountAddress();
|
|
777
|
-
const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
|
|
778
847
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
779
|
-
const usdcAddr = this.config.contracts.usdc;
|
|
780
848
|
let params;
|
|
781
849
|
let usedCollateral;
|
|
782
850
|
if (collateralSymbol) {
|
|
783
|
-
params = await this.findMarketForCollateral(collateralSymbol);
|
|
851
|
+
params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
|
|
784
852
|
usedCollateral = collateralSymbol;
|
|
785
853
|
} else {
|
|
786
|
-
const rates = await this.getMarketRates();
|
|
854
|
+
const rates = await this.getMarketRates(void 0, loanTokenSymbol);
|
|
787
855
|
if (rates.length === 0) throw new AgetherError("No markets available", "NO_MARKETS");
|
|
788
856
|
const best = rates.reduce((a, b) => a.supplyApy > b.supplyApy ? a : b);
|
|
789
|
-
params = await this.findMarketForCollateral(best.collateralToken);
|
|
857
|
+
params = await this.findMarketForCollateral(best.collateralToken, loanTokenSymbol);
|
|
790
858
|
usedCollateral = best.collateralToken;
|
|
791
859
|
}
|
|
860
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
861
|
+
const loanTokenAddr = params.loanToken;
|
|
862
|
+
const parsedAmount = import_ethers.ethers.parseUnits(amount, loanDecimals);
|
|
792
863
|
const marketId = import_ethers.ethers.keccak256(
|
|
793
864
|
import_ethers.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
794
865
|
["address", "address", "address", "address", "uint256"],
|
|
795
866
|
[params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
|
|
796
867
|
)
|
|
797
868
|
);
|
|
798
|
-
const
|
|
799
|
-
const acctBalance = await
|
|
800
|
-
if (acctBalance <
|
|
801
|
-
const shortfall =
|
|
802
|
-
const eoaBalance = await
|
|
869
|
+
const loanContract = new import_ethers.Contract(loanTokenAddr, ERC20_ABI, this._signer);
|
|
870
|
+
const acctBalance = await loanContract.balanceOf(acctAddr);
|
|
871
|
+
if (acctBalance < parsedAmount) {
|
|
872
|
+
const shortfall = parsedAmount - acctBalance;
|
|
873
|
+
const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
|
|
803
874
|
if (eoaBalance < shortfall) {
|
|
875
|
+
const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
|
|
876
|
+
const loanSymbol = loanInfo?.symbol ?? "loan token";
|
|
804
877
|
throw new AgetherError(
|
|
805
|
-
`Insufficient
|
|
878
|
+
`Insufficient ${loanSymbol}. Need ${amount}, AgentAccount has ${import_ethers.ethers.formatUnits(acctBalance, loanDecimals)}, EOA has ${import_ethers.ethers.formatUnits(eoaBalance, loanDecimals)}.`,
|
|
806
879
|
"INSUFFICIENT_BALANCE"
|
|
807
880
|
);
|
|
808
881
|
}
|
|
809
|
-
const transferTx = await
|
|
882
|
+
const transferTx = await loanContract.transfer(acctAddr, shortfall);
|
|
810
883
|
await transferTx.wait();
|
|
811
884
|
this._refreshSigner();
|
|
812
885
|
}
|
|
813
|
-
const targets = [
|
|
886
|
+
const targets = [loanTokenAddr, morphoAddr];
|
|
814
887
|
const values = [0n, 0n];
|
|
815
888
|
const datas = [
|
|
816
|
-
erc20Iface.encodeFunctionData("approve", [morphoAddr,
|
|
889
|
+
erc20Iface.encodeFunctionData("approve", [morphoAddr, parsedAmount]),
|
|
817
890
|
morphoIface.encodeFunctionData("supply", [
|
|
818
891
|
this._toTuple(params),
|
|
819
|
-
|
|
892
|
+
parsedAmount,
|
|
820
893
|
0n,
|
|
821
894
|
acctAddr,
|
|
822
895
|
"0x"
|
|
@@ -825,7 +898,7 @@ var init_MorphoClient = __esm({
|
|
|
825
898
|
const receipt = await this.batch(targets, values, datas);
|
|
826
899
|
return {
|
|
827
900
|
tx: receipt.hash,
|
|
828
|
-
amount
|
|
901
|
+
amount,
|
|
829
902
|
marketId,
|
|
830
903
|
collateralToken: usedCollateral,
|
|
831
904
|
agentAccount: acctAddr
|
|
@@ -838,17 +911,26 @@ var init_MorphoClient = __esm({
|
|
|
838
911
|
* @param collateralSymbol - Market collateral to identify which market
|
|
839
912
|
* @param receiver - Destination address (defaults to EOA)
|
|
840
913
|
*/
|
|
841
|
-
|
|
914
|
+
/**
|
|
915
|
+
* Withdraw supplied loan token (+ earned interest) from a Morpho Blue market.
|
|
916
|
+
*
|
|
917
|
+
* @param amount - Amount to withdraw (e.g. '100' or 'all' for full position)
|
|
918
|
+
* @param collateralSymbol - Market collateral to identify which market
|
|
919
|
+
* @param receiver - Destination address (defaults to EOA)
|
|
920
|
+
* @param loanTokenSymbol - Loan token to filter market (optional)
|
|
921
|
+
*/
|
|
922
|
+
async withdrawSupply(amount, collateralSymbol, receiver, loanTokenSymbol) {
|
|
842
923
|
const acctAddr = await this.getAccountAddress();
|
|
843
924
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
844
925
|
const dest = receiver || await this.getSignerAddress();
|
|
845
926
|
let params;
|
|
846
927
|
if (collateralSymbol) {
|
|
847
|
-
params = await this.findMarketForCollateral(collateralSymbol);
|
|
928
|
+
params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
|
|
848
929
|
} else {
|
|
849
930
|
const { params: p } = await this._findActiveSupplyMarket();
|
|
850
931
|
params = p;
|
|
851
932
|
}
|
|
933
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
852
934
|
const marketId = import_ethers.ethers.keccak256(
|
|
853
935
|
import_ethers.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
854
936
|
["address", "address", "address", "address", "uint256"],
|
|
@@ -857,13 +939,13 @@ var init_MorphoClient = __esm({
|
|
|
857
939
|
);
|
|
858
940
|
let withdrawAssets;
|
|
859
941
|
let withdrawShares;
|
|
860
|
-
if (
|
|
942
|
+
if (amount === "all") {
|
|
861
943
|
const pos = await this.morphoBlue.position(marketId, acctAddr);
|
|
862
944
|
withdrawShares = BigInt(pos.supplyShares);
|
|
863
945
|
withdrawAssets = 0n;
|
|
864
946
|
if (withdrawShares === 0n) throw new AgetherError("No supply position to withdraw", "NO_SUPPLY");
|
|
865
947
|
} else {
|
|
866
|
-
withdrawAssets = import_ethers.ethers.parseUnits(
|
|
948
|
+
withdrawAssets = import_ethers.ethers.parseUnits(amount, loanDecimals);
|
|
867
949
|
withdrawShares = 0n;
|
|
868
950
|
}
|
|
869
951
|
const data = morphoIface.encodeFunctionData("withdraw", [
|
|
@@ -881,13 +963,13 @@ var init_MorphoClient = __esm({
|
|
|
881
963
|
const totalSupplyAssets = BigInt(mkt.totalSupplyAssets);
|
|
882
964
|
const totalSupplyShares = BigInt(mkt.totalSupplyShares);
|
|
883
965
|
const currentAssets = totalSupplyShares > 0n ? BigInt(pos.supplyShares) * totalSupplyAssets / totalSupplyShares : 0n;
|
|
884
|
-
remainingSupply = import_ethers.ethers.formatUnits(currentAssets,
|
|
966
|
+
remainingSupply = import_ethers.ethers.formatUnits(currentAssets, loanDecimals);
|
|
885
967
|
} catch (e) {
|
|
886
968
|
console.warn("[agether] failed to read remaining supply:", e instanceof Error ? e.message : e);
|
|
887
969
|
}
|
|
888
970
|
return {
|
|
889
971
|
tx: receipt.hash,
|
|
890
|
-
amount
|
|
972
|
+
amount,
|
|
891
973
|
remainingSupply,
|
|
892
974
|
destination: dest
|
|
893
975
|
};
|
|
@@ -960,14 +1042,13 @@ var init_MorphoClient = __esm({
|
|
|
960
1042
|
* Computes available yield, verifies the requested amount doesn't exceed it,
|
|
961
1043
|
* then withdraws from the supply position and sends directly to the recipient.
|
|
962
1044
|
*
|
|
963
|
-
* @param recipient - Address to receive the
|
|
964
|
-
* @param
|
|
1045
|
+
* @param recipient - Address to receive the loan token
|
|
1046
|
+
* @param amount - Amount to pay from yield (e.g. '5.50')
|
|
965
1047
|
* @param collateralSymbol - Market collateral to identify which supply position
|
|
966
1048
|
*/
|
|
967
|
-
async payFromYield(recipient,
|
|
1049
|
+
async payFromYield(recipient, amount, collateralSymbol) {
|
|
968
1050
|
const acctAddr = await this.getAccountAddress();
|
|
969
1051
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
970
|
-
const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
|
|
971
1052
|
const positions = await this.getSupplyPositions(collateralSymbol);
|
|
972
1053
|
if (positions.length === 0) {
|
|
973
1054
|
throw new AgetherError("No supply position found", "NO_SUPPLY");
|
|
@@ -975,17 +1056,20 @@ var init_MorphoClient = __esm({
|
|
|
975
1056
|
const pos = positions.reduce(
|
|
976
1057
|
(a, b) => parseFloat(a.earnedYield) > parseFloat(b.earnedYield) ? a : b
|
|
977
1058
|
);
|
|
978
|
-
const
|
|
979
|
-
|
|
1059
|
+
const params = await this.findMarketForCollateral(pos.collateralToken, pos.loanToken);
|
|
1060
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
1061
|
+
const parsedAmount = import_ethers.ethers.parseUnits(amount, loanDecimals);
|
|
1062
|
+
const availableYield = import_ethers.ethers.parseUnits(pos.earnedYield, loanDecimals);
|
|
1063
|
+
if (parsedAmount > availableYield) {
|
|
1064
|
+
const loanSymbol = pos.loanToken;
|
|
980
1065
|
throw new AgetherError(
|
|
981
|
-
`Requested ${
|
|
1066
|
+
`Requested ${amount} ${loanSymbol} exceeds available yield of ${pos.earnedYield} ${loanSymbol}. Use withdrawSupply to withdraw principal.`,
|
|
982
1067
|
"EXCEEDS_YIELD"
|
|
983
1068
|
);
|
|
984
1069
|
}
|
|
985
|
-
const params = await this.findMarketForCollateral(pos.collateralToken);
|
|
986
1070
|
const data = morphoIface.encodeFunctionData("withdraw", [
|
|
987
1071
|
this._toTuple(params),
|
|
988
|
-
|
|
1072
|
+
parsedAmount,
|
|
989
1073
|
0n,
|
|
990
1074
|
acctAddr,
|
|
991
1075
|
recipient
|
|
@@ -1004,7 +1088,7 @@ var init_MorphoClient = __esm({
|
|
|
1004
1088
|
}
|
|
1005
1089
|
return {
|
|
1006
1090
|
tx: receipt.hash,
|
|
1007
|
-
yieldWithdrawn:
|
|
1091
|
+
yieldWithdrawn: amount,
|
|
1008
1092
|
recipient,
|
|
1009
1093
|
remainingYield,
|
|
1010
1094
|
remainingSupply
|
|
@@ -1062,28 +1146,31 @@ var init_MorphoClient = __esm({
|
|
|
1062
1146
|
};
|
|
1063
1147
|
}
|
|
1064
1148
|
/**
|
|
1065
|
-
* Borrow
|
|
1149
|
+
* Borrow loan token against existing collateral.
|
|
1066
1150
|
*
|
|
1067
1151
|
* AgentAccount.execute: Morpho.borrow(params, amount, 0, account, account)
|
|
1068
1152
|
*
|
|
1069
|
-
* @param
|
|
1153
|
+
* @param amount - Loan token amount (e.g. '100' for 100 USDC, '0.5' for 0.5 WETH)
|
|
1070
1154
|
* @param tokenSymbol - collateral symbol to identify which market (default: first with collateral)
|
|
1155
|
+
* @param marketParams - explicit market params (optional)
|
|
1156
|
+
* @param loanTokenSymbol - loan token to filter market (optional, e.g. 'USDC', 'WETH')
|
|
1071
1157
|
*/
|
|
1072
|
-
async borrow(
|
|
1158
|
+
async borrow(amount, tokenSymbol, marketParams, loanTokenSymbol) {
|
|
1073
1159
|
const acctAddr = await this.getAccountAddress();
|
|
1074
|
-
const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
|
|
1075
1160
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1076
1161
|
let params;
|
|
1077
1162
|
let usedToken = tokenSymbol || "WETH";
|
|
1078
1163
|
if (marketParams) {
|
|
1079
1164
|
params = marketParams;
|
|
1080
1165
|
} else if (tokenSymbol) {
|
|
1081
|
-
params = await this.findMarketForCollateral(tokenSymbol);
|
|
1166
|
+
params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
|
|
1082
1167
|
} else {
|
|
1083
1168
|
const { params: p, symbol } = await this._findActiveMarket();
|
|
1084
1169
|
params = p;
|
|
1085
1170
|
usedToken = symbol;
|
|
1086
1171
|
}
|
|
1172
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
1173
|
+
const parsedAmount = import_ethers.ethers.parseUnits(amount, loanDecimals);
|
|
1087
1174
|
try {
|
|
1088
1175
|
const marketId = import_ethers.ethers.keccak256(
|
|
1089
1176
|
import_ethers.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
@@ -1107,11 +1194,14 @@ var init_MorphoClient = __esm({
|
|
|
1107
1194
|
const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
|
|
1108
1195
|
const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1109
1196
|
const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
|
|
1110
|
-
if (
|
|
1111
|
-
const
|
|
1112
|
-
const
|
|
1197
|
+
if (parsedAmount > maxAdditional) {
|
|
1198
|
+
const loanInfo = this._tokenCache.get(params.loanToken.toLowerCase());
|
|
1199
|
+
const loanSymbol = loanInfo?.symbol ?? "loan token";
|
|
1200
|
+
const colInfo = await this._resolveToken(usedToken);
|
|
1201
|
+
const maxFormatted = import_ethers.ethers.formatUnits(maxAdditional, loanDecimals);
|
|
1202
|
+
const colFormatted = import_ethers.ethers.formatUnits(pos.collateral, colInfo.decimals);
|
|
1113
1203
|
throw new AgetherError(
|
|
1114
|
-
`Borrow of
|
|
1204
|
+
`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.`,
|
|
1115
1205
|
"EXCEEDS_MAX_LTV"
|
|
1116
1206
|
);
|
|
1117
1207
|
}
|
|
@@ -1121,7 +1211,7 @@ var init_MorphoClient = __esm({
|
|
|
1121
1211
|
}
|
|
1122
1212
|
const data = morphoIface.encodeFunctionData("borrow", [
|
|
1123
1213
|
this._toTuple(params),
|
|
1124
|
-
|
|
1214
|
+
parsedAmount,
|
|
1125
1215
|
0n,
|
|
1126
1216
|
acctAddr,
|
|
1127
1217
|
acctAddr
|
|
@@ -1129,7 +1219,7 @@ var init_MorphoClient = __esm({
|
|
|
1129
1219
|
const receipt = await this.exec(morphoAddr, data);
|
|
1130
1220
|
return {
|
|
1131
1221
|
tx: receipt.hash,
|
|
1132
|
-
amount
|
|
1222
|
+
amount,
|
|
1133
1223
|
collateralToken: usedToken,
|
|
1134
1224
|
agentAccount: acctAddr
|
|
1135
1225
|
};
|
|
@@ -1137,17 +1227,27 @@ var init_MorphoClient = __esm({
|
|
|
1137
1227
|
/**
|
|
1138
1228
|
* Deposit collateral AND borrow USDC in one batched transaction.
|
|
1139
1229
|
*
|
|
1230
|
+
/**
|
|
1231
|
+
* Deposit collateral AND borrow loan token in one batched transaction.
|
|
1232
|
+
*
|
|
1140
1233
|
* AgentAccount.executeBatch:
|
|
1141
1234
|
* [collateral.approve, Morpho.supplyCollateral, Morpho.borrow]
|
|
1142
1235
|
*
|
|
1143
1236
|
* The collateral must be transferred to AgentAccount first.
|
|
1237
|
+
*
|
|
1238
|
+
* @param tokenSymbol - collateral token symbol (e.g. 'WETH')
|
|
1239
|
+
* @param collateralAmount - amount of collateral (e.g. '0.05')
|
|
1240
|
+
* @param borrowAmount - amount of loan token to borrow (e.g. '100')
|
|
1241
|
+
* @param marketParams - explicit market params (optional)
|
|
1242
|
+
* @param loanTokenSymbol - loan token to filter market (optional)
|
|
1144
1243
|
*/
|
|
1145
|
-
async depositAndBorrow(tokenSymbol, collateralAmount,
|
|
1244
|
+
async depositAndBorrow(tokenSymbol, collateralAmount, borrowAmount, marketParams, loanTokenSymbol) {
|
|
1146
1245
|
const acctAddr = await this.getAccountAddress();
|
|
1147
1246
|
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1148
|
-
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1247
|
+
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
|
|
1248
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
1149
1249
|
const colWei = import_ethers.ethers.parseUnits(collateralAmount, colInfo.decimals);
|
|
1150
|
-
const borrowWei = import_ethers.ethers.parseUnits(
|
|
1250
|
+
const borrowWei = import_ethers.ethers.parseUnits(borrowAmount, loanDecimals);
|
|
1151
1251
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1152
1252
|
try {
|
|
1153
1253
|
const marketId = import_ethers.ethers.keccak256(
|
|
@@ -1168,9 +1268,11 @@ var init_MorphoClient = __esm({
|
|
|
1168
1268
|
const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1169
1269
|
const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
|
|
1170
1270
|
if (borrowWei > maxAdditional) {
|
|
1171
|
-
const
|
|
1271
|
+
const loanInfo = this._tokenCache.get(params.loanToken.toLowerCase());
|
|
1272
|
+
const loanSymbol = loanInfo?.symbol ?? "loan token";
|
|
1273
|
+
const maxFormatted = import_ethers.ethers.formatUnits(maxAdditional, loanDecimals);
|
|
1172
1274
|
throw new AgetherError(
|
|
1173
|
-
`Borrow of
|
|
1275
|
+
`Borrow of ${borrowAmount} ${loanSymbol} exceeds max borrowable ${maxFormatted} ${loanSymbol} (total collateral: ${import_ethers.ethers.formatUnits(totalCollateral, colInfo.decimals)} ${tokenSymbol}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Reduce borrow or increase collateral.`,
|
|
1174
1276
|
"EXCEEDS_MAX_LTV"
|
|
1175
1277
|
);
|
|
1176
1278
|
}
|
|
@@ -1216,36 +1318,42 @@ var init_MorphoClient = __esm({
|
|
|
1216
1318
|
tx: receipt.hash,
|
|
1217
1319
|
collateralToken: tokenSymbol,
|
|
1218
1320
|
collateralAmount,
|
|
1219
|
-
borrowAmount
|
|
1321
|
+
borrowAmount,
|
|
1220
1322
|
agentAccount: acctAddr
|
|
1221
1323
|
};
|
|
1222
1324
|
}
|
|
1223
1325
|
/**
|
|
1224
|
-
* Repay borrowed
|
|
1326
|
+
* Repay borrowed loan token from AgentAccount.
|
|
1225
1327
|
*
|
|
1226
1328
|
* AgentAccount.executeBatch:
|
|
1227
|
-
* [
|
|
1329
|
+
* [loanToken.approve(MorphoBlue), Morpho.repay(params)]
|
|
1330
|
+
*
|
|
1331
|
+
* @param amount - loan token amount to repay (e.g. '50' or 'all' for full repayment)
|
|
1332
|
+
* @param tokenSymbol - collateral symbol to identify which market (optional)
|
|
1333
|
+
* @param marketParams - explicit market params (optional)
|
|
1334
|
+
* @param loanTokenSymbol - loan token to filter market (optional)
|
|
1228
1335
|
*/
|
|
1229
|
-
async repay(
|
|
1336
|
+
async repay(amount, tokenSymbol, marketParams, loanTokenSymbol) {
|
|
1230
1337
|
const acctAddr = await this.getAccountAddress();
|
|
1231
1338
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1232
|
-
const usdcAddr = this.config.contracts.usdc;
|
|
1233
1339
|
let params;
|
|
1234
1340
|
if (marketParams) {
|
|
1235
1341
|
params = marketParams;
|
|
1236
1342
|
} else if (tokenSymbol) {
|
|
1237
|
-
params = await this.findMarketForCollateral(tokenSymbol);
|
|
1343
|
+
params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
|
|
1238
1344
|
} else {
|
|
1239
1345
|
const { params: p } = await this._findActiveMarket();
|
|
1240
1346
|
params = p;
|
|
1241
1347
|
}
|
|
1348
|
+
const loanTokenAddr = params.loanToken;
|
|
1349
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
1242
1350
|
let repayAssets;
|
|
1243
1351
|
let repayShares;
|
|
1244
1352
|
let approveAmount;
|
|
1245
|
-
if (
|
|
1353
|
+
if (amount === "all") {
|
|
1246
1354
|
const markets = await this.getMarkets();
|
|
1247
1355
|
const mkt = markets.find(
|
|
1248
|
-
(m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase()
|
|
1356
|
+
(m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase() && m.loanAsset?.address.toLowerCase() === params.loanToken.toLowerCase()
|
|
1249
1357
|
);
|
|
1250
1358
|
if (mkt) {
|
|
1251
1359
|
const pos = await this.morphoBlue.position(mkt.uniqueKey, acctAddr);
|
|
@@ -1255,33 +1363,35 @@ var init_MorphoClient = __esm({
|
|
|
1255
1363
|
const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
|
|
1256
1364
|
const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
|
|
1257
1365
|
const estimated = totalBorrowShares > 0n ? repayShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
|
|
1258
|
-
approveAmount = estimated > 0n ? estimated : import_ethers.ethers.parseUnits("1",
|
|
1366
|
+
approveAmount = estimated > 0n ? estimated : import_ethers.ethers.parseUnits("1", loanDecimals);
|
|
1259
1367
|
} else {
|
|
1260
|
-
repayAssets = import_ethers.ethers.parseUnits("999999",
|
|
1368
|
+
repayAssets = import_ethers.ethers.parseUnits("999999", loanDecimals);
|
|
1261
1369
|
repayShares = 0n;
|
|
1262
1370
|
approveAmount = repayAssets;
|
|
1263
1371
|
}
|
|
1264
1372
|
} else {
|
|
1265
|
-
repayAssets = import_ethers.ethers.parseUnits(
|
|
1373
|
+
repayAssets = import_ethers.ethers.parseUnits(amount, loanDecimals);
|
|
1266
1374
|
repayShares = 0n;
|
|
1267
1375
|
approveAmount = repayAssets;
|
|
1268
1376
|
}
|
|
1269
|
-
const
|
|
1270
|
-
const acctBalance = await
|
|
1377
|
+
const loanContract = new import_ethers.Contract(loanTokenAddr, ERC20_ABI, this._signer);
|
|
1378
|
+
const acctBalance = await loanContract.balanceOf(acctAddr);
|
|
1271
1379
|
if (acctBalance < approveAmount) {
|
|
1272
1380
|
const shortfall = approveAmount - acctBalance;
|
|
1273
|
-
const eoaBalance = await
|
|
1381
|
+
const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
|
|
1274
1382
|
if (eoaBalance < shortfall) {
|
|
1383
|
+
const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
|
|
1384
|
+
const loanSymbol = loanInfo?.symbol ?? "loan token";
|
|
1275
1385
|
throw new AgetherError(
|
|
1276
|
-
`Insufficient
|
|
1386
|
+
`Insufficient ${loanSymbol} for repay. Need ${import_ethers.ethers.formatUnits(approveAmount, loanDecimals)}, AgentAccount has ${import_ethers.ethers.formatUnits(acctBalance, loanDecimals)}, EOA has ${import_ethers.ethers.formatUnits(eoaBalance, loanDecimals)}.`,
|
|
1277
1387
|
"INSUFFICIENT_BALANCE"
|
|
1278
1388
|
);
|
|
1279
1389
|
}
|
|
1280
|
-
const transferTx = await
|
|
1390
|
+
const transferTx = await loanContract.transfer(acctAddr, shortfall);
|
|
1281
1391
|
await transferTx.wait();
|
|
1282
1392
|
this._refreshSigner();
|
|
1283
1393
|
}
|
|
1284
|
-
const targets = [
|
|
1394
|
+
const targets = [loanTokenAddr, morphoAddr];
|
|
1285
1395
|
const values = [0n, 0n];
|
|
1286
1396
|
const datas = [
|
|
1287
1397
|
erc20Iface.encodeFunctionData("approve", [morphoAddr, approveAmount]),
|
|
@@ -1301,7 +1411,7 @@ var init_MorphoClient = __esm({
|
|
|
1301
1411
|
} catch (e) {
|
|
1302
1412
|
console.warn("[agether] failed to read remaining debt after repay:", e instanceof Error ? e.message : e);
|
|
1303
1413
|
}
|
|
1304
|
-
return { tx: receipt.hash, amount
|
|
1414
|
+
return { tx: receipt.hash, amount, remainingDebt };
|
|
1305
1415
|
}
|
|
1306
1416
|
/**
|
|
1307
1417
|
* Withdraw collateral from Morpho Blue.
|
|
@@ -1315,12 +1425,13 @@ var init_MorphoClient = __esm({
|
|
|
1315
1425
|
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1316
1426
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1317
1427
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1318
|
-
const
|
|
1428
|
+
const loanTokenAddr = params.loanToken;
|
|
1429
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
1319
1430
|
const dest = receiver || await this.getSignerAddress();
|
|
1320
1431
|
let weiAmount;
|
|
1321
1432
|
const markets = await this.getMarkets();
|
|
1322
1433
|
const market = markets.find(
|
|
1323
|
-
(m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase()
|
|
1434
|
+
(m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase() && m.loanAsset?.address.toLowerCase() === loanTokenAddr.toLowerCase()
|
|
1324
1435
|
);
|
|
1325
1436
|
if (amount === "all") {
|
|
1326
1437
|
if (!market) throw new AgetherError("Market not found", "MARKET_NOT_FOUND");
|
|
@@ -1343,8 +1454,10 @@ var init_MorphoClient = __esm({
|
|
|
1343
1454
|
const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
|
|
1344
1455
|
const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
|
|
1345
1456
|
const estimated = totalBorrowShares > 0n ? dustBorrowShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
|
|
1346
|
-
dustApproveAmount = estimated > 0n ? estimated : import_ethers.ethers.parseUnits("1",
|
|
1347
|
-
|
|
1457
|
+
dustApproveAmount = estimated > 0n ? estimated : import_ethers.ethers.parseUnits("1", loanDecimals);
|
|
1458
|
+
const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
|
|
1459
|
+
const loanSymbol = loanInfo?.symbol ?? "loan token";
|
|
1460
|
+
console.log(`[agether] dust borrow shares detected: ${dustBorrowShares} shares \u2248 ${import_ethers.ethers.formatUnits(dustApproveAmount, loanDecimals)} ${loanSymbol} \u2014 auto-repaying before withdraw`);
|
|
1348
1461
|
}
|
|
1349
1462
|
} catch (e) {
|
|
1350
1463
|
console.warn("[agether] failed to check borrow shares before withdraw:", e instanceof Error ? e.message : e);
|
|
@@ -1358,19 +1471,21 @@ var init_MorphoClient = __esm({
|
|
|
1358
1471
|
]);
|
|
1359
1472
|
let receipt;
|
|
1360
1473
|
if (hasDustDebt) {
|
|
1361
|
-
const
|
|
1362
|
-
const acctBalance = await
|
|
1474
|
+
const loanContract = new import_ethers.Contract(loanTokenAddr, ERC20_ABI, this._signer);
|
|
1475
|
+
const acctBalance = await loanContract.balanceOf(acctAddr);
|
|
1363
1476
|
if (acctBalance < dustApproveAmount) {
|
|
1364
1477
|
const shortfall = dustApproveAmount - acctBalance;
|
|
1365
|
-
const eoaBalance = await
|
|
1478
|
+
const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
|
|
1366
1479
|
if (eoaBalance >= shortfall) {
|
|
1367
|
-
|
|
1368
|
-
const
|
|
1480
|
+
const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
|
|
1481
|
+
const loanSymbol = loanInfo?.symbol ?? "loan token";
|
|
1482
|
+
console.log(`[agether] transferring ${import_ethers.ethers.formatUnits(shortfall, loanDecimals)} ${loanSymbol} from EOA \u2192 AgentAccount for dust repay`);
|
|
1483
|
+
const transferTx = await loanContract.transfer(acctAddr, shortfall);
|
|
1369
1484
|
await transferTx.wait();
|
|
1370
1485
|
this._refreshSigner();
|
|
1371
1486
|
}
|
|
1372
1487
|
}
|
|
1373
|
-
const targets = [
|
|
1488
|
+
const targets = [loanTokenAddr, morphoAddr, morphoAddr];
|
|
1374
1489
|
const values = [0n, 0n, 0n];
|
|
1375
1490
|
const datas = [
|
|
1376
1491
|
erc20Iface.encodeFunctionData("approve", [morphoAddr, dustApproveAmount]),
|
|
@@ -1615,6 +1730,37 @@ var init_MorphoClient = __esm({
|
|
|
1615
1730
|
}
|
|
1616
1731
|
throw new AgetherError("No active supply position found", "NO_SUPPLY");
|
|
1617
1732
|
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Resolve loan token decimals from market params.
|
|
1735
|
+
* Uses the `_tokenCache` populated by `getMarkets()`.
|
|
1736
|
+
*/
|
|
1737
|
+
async _getLoanTokenDecimals(params) {
|
|
1738
|
+
const tokenInfo = this._tokenCache.get(params.loanToken.toLowerCase());
|
|
1739
|
+
if (tokenInfo) return tokenInfo.decimals;
|
|
1740
|
+
await this.getMarkets();
|
|
1741
|
+
const fromApi = this._tokenCache.get(params.loanToken.toLowerCase());
|
|
1742
|
+
return fromApi?.decimals ?? 18;
|
|
1743
|
+
}
|
|
1744
|
+
/**
|
|
1745
|
+
* Apply client-side filter to discovered markets.
|
|
1746
|
+
*/
|
|
1747
|
+
_applyMarketFilter(markets, filter) {
|
|
1748
|
+
return markets.filter((m) => {
|
|
1749
|
+
if (filter.loanToken) {
|
|
1750
|
+
const loanAddr = filter.loanToken.toLowerCase();
|
|
1751
|
+
if (m.loanAsset.address.toLowerCase() !== loanAddr && m.loanAsset.symbol.toUpperCase() !== filter.loanToken.toUpperCase()) {
|
|
1752
|
+
return false;
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
if (filter.collateralToken) {
|
|
1756
|
+
const colAddr = filter.collateralToken.toLowerCase();
|
|
1757
|
+
if (m.collateralAsset.address.toLowerCase() !== colAddr && m.collateralAsset.symbol.toUpperCase() !== filter.collateralToken.toUpperCase()) {
|
|
1758
|
+
return false;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
return true;
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1618
1764
|
/**
|
|
1619
1765
|
* Resolve a token symbol or address to { address, symbol, decimals }.
|
|
1620
1766
|
*
|
|
@@ -1631,8 +1777,8 @@ var init_MorphoClient = __esm({
|
|
|
1631
1777
|
const fromApi = this._tokenCache.get(key);
|
|
1632
1778
|
if (fromApi) return fromApi;
|
|
1633
1779
|
throw new AgetherError(
|
|
1634
|
-
`Unknown token: ${symbolOrAddress}. No Morpho market found with this
|
|
1635
|
-
"
|
|
1780
|
+
`Unknown token: ${symbolOrAddress}. No Morpho market found with this token.`,
|
|
1781
|
+
"UNKNOWN_TOKEN"
|
|
1636
1782
|
);
|
|
1637
1783
|
}
|
|
1638
1784
|
/**
|
|
@@ -1837,6 +1983,23 @@ var init_AgetherClient = __esm({
|
|
|
1837
1983
|
}
|
|
1838
1984
|
const acctAddr = await this.agether4337Factory.getAccount(agentId);
|
|
1839
1985
|
this.accountAddress = acctAddr;
|
|
1986
|
+
if (!acctExists) {
|
|
1987
|
+
const updatedMeta = JSON.stringify({
|
|
1988
|
+
type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
|
|
1989
|
+
name: options?.name || "Unnamed Agent",
|
|
1990
|
+
description: options?.description || "AI agent registered via @agether/sdk",
|
|
1991
|
+
active: true,
|
|
1992
|
+
wallet: `eip155:${this.config.chainId}:${acctAddr}`,
|
|
1993
|
+
registrations: [{
|
|
1994
|
+
agentId: Number(agentId),
|
|
1995
|
+
agentRegistry: `eip155:${this.config.chainId}:${this.config.contracts.identityRegistry}`
|
|
1996
|
+
}]
|
|
1997
|
+
});
|
|
1998
|
+
const finalURI = `data:application/json;base64,${Buffer.from(updatedMeta).toString("base64")}`;
|
|
1999
|
+
const uriTx = await this.identityRegistry.setAgentURI(agentId, finalURI);
|
|
2000
|
+
await uriTx.wait();
|
|
2001
|
+
this._refreshSigner();
|
|
2002
|
+
}
|
|
1840
2003
|
const kyaRequired = await this.isKyaRequired();
|
|
1841
2004
|
return {
|
|
1842
2005
|
agentId: agentId.toString(),
|
|
@@ -1853,7 +2016,7 @@ var init_AgetherClient = __esm({
|
|
|
1853
2016
|
async _mintNewIdentity(name, description) {
|
|
1854
2017
|
const registrationFile = JSON.stringify({
|
|
1855
2018
|
type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
|
|
1856
|
-
name: name || "
|
|
2019
|
+
name: name || "Agether Agent",
|
|
1857
2020
|
description: description || "AI agent registered via @agether/sdk",
|
|
1858
2021
|
active: true,
|
|
1859
2022
|
registrations: [{
|