@agether/sdk 2.12.2 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +291 -150
- package/dist/index.d.mts +92 -27
- package/dist/index.d.ts +92 -27
- package/dist/index.js +291 -150
- package/dist/index.mjs +291 -150
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -290,8 +290,9 @@ var init_MorphoClient = __esm({
|
|
|
290
290
|
erc20Iface = new import_ethers.ethers.Interface(ERC20_ABI);
|
|
291
291
|
MorphoClient = class {
|
|
292
292
|
constructor(config) {
|
|
293
|
+
/** Market params cache: keyed by market uniqueKey (bytes32 hash) */
|
|
293
294
|
this._marketCache = /* @__PURE__ */ new Map();
|
|
294
|
-
/** Dynamic token registry: symbol (uppercase) → { address, symbol, decimals } */
|
|
295
|
+
/** Dynamic token registry: symbol (uppercase) or address (lowercase) → { address, symbol, decimals } */
|
|
295
296
|
this._tokenCache = /* @__PURE__ */ new Map();
|
|
296
297
|
this._discoveredAt = 0;
|
|
297
298
|
if (!config.agentId) {
|
|
@@ -394,21 +395,23 @@ var init_MorphoClient = __esm({
|
|
|
394
395
|
// Market Discovery (Morpho GraphQL API)
|
|
395
396
|
// ════════════════════════════════════════════════════════
|
|
396
397
|
/**
|
|
397
|
-
* Fetch
|
|
398
|
-
* 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
|
|
399
403
|
*/
|
|
400
|
-
async getMarkets(forceRefresh = false) {
|
|
404
|
+
async getMarkets(forceRefresh = false, filter) {
|
|
401
405
|
if (!forceRefresh && this._discoveredMarkets && Date.now() - this._discoveredAt < 3e5) {
|
|
402
|
-
return this._discoveredMarkets;
|
|
406
|
+
return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
|
|
403
407
|
}
|
|
404
408
|
const chainId = this.config.chainId;
|
|
405
|
-
const usdcAddr = this.config.contracts.usdc.toLowerCase();
|
|
406
409
|
const query = `{
|
|
407
410
|
markets(
|
|
408
411
|
first: 50
|
|
409
412
|
orderBy: SupplyAssetsUsd
|
|
410
413
|
orderDirection: Desc
|
|
411
|
-
where: { chainId_in: [${chainId}]
|
|
414
|
+
where: { chainId_in: [${chainId}] }
|
|
412
415
|
) {
|
|
413
416
|
items {
|
|
414
417
|
uniqueKey
|
|
@@ -441,14 +444,14 @@ var init_MorphoClient = __esm({
|
|
|
441
444
|
}));
|
|
442
445
|
this._discoveredAt = Date.now();
|
|
443
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
|
+
});
|
|
444
454
|
if (mi.collateralAsset.address !== import_ethers.ethers.ZeroAddress) {
|
|
445
|
-
this._marketCache.set(mi.collateralAsset.address.toLowerCase(), {
|
|
446
|
-
loanToken: mi.loanAsset.address,
|
|
447
|
-
collateralToken: mi.collateralAsset.address,
|
|
448
|
-
oracle: mi.oracle,
|
|
449
|
-
irm: mi.irm,
|
|
450
|
-
lltv: mi.lltv
|
|
451
|
-
});
|
|
452
455
|
this._tokenCache.set(mi.collateralAsset.symbol.toUpperCase(), {
|
|
453
456
|
address: mi.collateralAsset.address,
|
|
454
457
|
symbol: mi.collateralAsset.symbol,
|
|
@@ -473,17 +476,24 @@ var init_MorphoClient = __esm({
|
|
|
473
476
|
});
|
|
474
477
|
}
|
|
475
478
|
}
|
|
476
|
-
return this._discoveredMarkets;
|
|
479
|
+
return filter ? this._applyMarketFilter(this._discoveredMarkets, filter) : this._discoveredMarkets;
|
|
477
480
|
} catch (e) {
|
|
478
481
|
console.warn("[agether] getMarkets failed, using cache:", e instanceof Error ? e.message : e);
|
|
479
|
-
|
|
482
|
+
const cached = this._discoveredMarkets ?? [];
|
|
483
|
+
return filter ? this._applyMarketFilter(cached, filter) : cached;
|
|
480
484
|
}
|
|
481
485
|
}
|
|
482
486
|
/**
|
|
483
|
-
* Get MarketParams for a collateral token.
|
|
484
|
-
* 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)
|
|
485
495
|
*/
|
|
486
|
-
async findMarketForCollateral(collateralSymbolOrAddress) {
|
|
496
|
+
async findMarketForCollateral(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
|
|
487
497
|
let colAddr;
|
|
488
498
|
if (collateralSymbolOrAddress.startsWith("0x")) {
|
|
489
499
|
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
@@ -495,13 +505,33 @@ var init_MorphoClient = __esm({
|
|
|
495
505
|
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
496
506
|
}
|
|
497
507
|
}
|
|
498
|
-
|
|
499
|
-
if (
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
+
}
|
|
503
533
|
throw new AgetherError(
|
|
504
|
-
`No Morpho market found for collateral ${collateralSymbolOrAddress}
|
|
534
|
+
`No Morpho market found for collateral ${collateralSymbolOrAddress}` + (loanTokenSymbolOrAddress ? ` with loan token ${loanTokenSymbolOrAddress}` : ""),
|
|
505
535
|
"MARKET_NOT_FOUND"
|
|
506
536
|
);
|
|
507
537
|
}
|
|
@@ -536,12 +566,13 @@ var init_MorphoClient = __esm({
|
|
|
536
566
|
const acctAddr = await this.getAccountAddress();
|
|
537
567
|
const markets = await this.getMarkets();
|
|
538
568
|
const positions = [];
|
|
539
|
-
let
|
|
569
|
+
let totalDebtFloat = 0;
|
|
540
570
|
for (const m of markets) {
|
|
541
571
|
if (!m.collateralAsset || m.collateralAsset.address === import_ethers.ethers.ZeroAddress) continue;
|
|
542
572
|
try {
|
|
543
573
|
const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
|
|
544
574
|
if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
|
|
575
|
+
const loanDecimals = m.loanAsset.decimals;
|
|
545
576
|
let debt = 0n;
|
|
546
577
|
if (pos.borrowShares > 0n) {
|
|
547
578
|
try {
|
|
@@ -549,7 +580,7 @@ var init_MorphoClient = __esm({
|
|
|
549
580
|
const totalBorrowShares = BigInt(mkt.totalBorrowShares);
|
|
550
581
|
const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
|
|
551
582
|
debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
552
|
-
|
|
583
|
+
totalDebtFloat += parseFloat(import_ethers.ethers.formatUnits(debt, loanDecimals));
|
|
553
584
|
} catch (e) {
|
|
554
585
|
console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
|
|
555
586
|
}
|
|
@@ -557,10 +588,11 @@ var init_MorphoClient = __esm({
|
|
|
557
588
|
positions.push({
|
|
558
589
|
marketId: m.uniqueKey,
|
|
559
590
|
collateralToken: m.collateralAsset.symbol,
|
|
591
|
+
loanToken: m.loanAsset.symbol,
|
|
560
592
|
collateral: import_ethers.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
|
|
561
593
|
borrowShares: pos.borrowShares.toString(),
|
|
562
594
|
supplyShares: pos.supplyShares.toString(),
|
|
563
|
-
debt: import_ethers.ethers.formatUnits(debt,
|
|
595
|
+
debt: import_ethers.ethers.formatUnits(debt, loanDecimals)
|
|
564
596
|
});
|
|
565
597
|
} catch (e) {
|
|
566
598
|
console.warn(`[agether] position read failed for market:`, e instanceof Error ? e.message : e);
|
|
@@ -570,16 +602,28 @@ var init_MorphoClient = __esm({
|
|
|
570
602
|
return {
|
|
571
603
|
agentId: this.agentId,
|
|
572
604
|
agentAccount: acctAddr,
|
|
573
|
-
totalDebt:
|
|
605
|
+
totalDebt: totalDebtFloat.toFixed(6),
|
|
574
606
|
positions
|
|
575
607
|
};
|
|
576
608
|
}
|
|
577
609
|
// ════════════════════════════════════════════════════════
|
|
578
610
|
// Balance & Borrowing Capacity
|
|
579
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
|
+
}
|
|
580
623
|
/**
|
|
581
624
|
* Get the USDC balance of the AgentAccount.
|
|
582
625
|
* @returns USDC balance in raw units (6 decimals)
|
|
626
|
+
* @deprecated Use `getTokenBalance('USDC')` instead.
|
|
583
627
|
*/
|
|
584
628
|
async getUsdcBalance() {
|
|
585
629
|
const acctAddr = await this.getAccountAddress();
|
|
@@ -587,7 +631,7 @@ var init_MorphoClient = __esm({
|
|
|
587
631
|
return usdc.balanceOf(acctAddr);
|
|
588
632
|
}
|
|
589
633
|
/**
|
|
590
|
-
* Calculate the maximum additional
|
|
634
|
+
* Calculate the maximum additional loan token that can be borrowed
|
|
591
635
|
* given the agent's current collateral and debt across all markets.
|
|
592
636
|
*
|
|
593
637
|
* For each market with collateral deposited:
|
|
@@ -595,7 +639,7 @@ var init_MorphoClient = __esm({
|
|
|
595
639
|
*
|
|
596
640
|
* Uses the Morpho oracle to price collateral → loan token.
|
|
597
641
|
*
|
|
598
|
-
* @returns Maximum additional
|
|
642
|
+
* @returns Maximum additional borrowable per market (raw units in each market's loan token)
|
|
599
643
|
*/
|
|
600
644
|
async getMaxBorrowable() {
|
|
601
645
|
const acctAddr = await this.getAccountAddress();
|
|
@@ -628,6 +672,8 @@ var init_MorphoClient = __esm({
|
|
|
628
672
|
totalAdditional += maxAdditional;
|
|
629
673
|
byMarket.push({
|
|
630
674
|
collateralToken: m.collateralAsset.symbol,
|
|
675
|
+
loanToken: m.loanAsset.symbol,
|
|
676
|
+
loanDecimals: m.loanAsset.decimals,
|
|
631
677
|
maxAdditional,
|
|
632
678
|
currentDebt,
|
|
633
679
|
collateralValue: collateralValueInLoan
|
|
@@ -643,14 +689,16 @@ var init_MorphoClient = __esm({
|
|
|
643
689
|
// Market Rates & Yield Estimation
|
|
644
690
|
// ════════════════════════════════════════════════════════
|
|
645
691
|
/**
|
|
646
|
-
* Fetch current supply/borrow APY for
|
|
692
|
+
* Fetch current supply/borrow APY for markets from Morpho GraphQL API.
|
|
647
693
|
*
|
|
648
694
|
* Note: On Morpho Blue, collateral does NOT earn yield directly. Supply APY
|
|
649
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.
|
|
650
699
|
*/
|
|
651
|
-
async getMarketRates(collateralSymbolOrAddress) {
|
|
700
|
+
async getMarketRates(collateralSymbolOrAddress, loanTokenSymbolOrAddress) {
|
|
652
701
|
const chainId = this.config.chainId;
|
|
653
|
-
const usdcAddr = this.config.contracts.usdc.toLowerCase();
|
|
654
702
|
let collateralFilter = "";
|
|
655
703
|
if (collateralSymbolOrAddress) {
|
|
656
704
|
let colAddr;
|
|
@@ -666,12 +714,27 @@ var init_MorphoClient = __esm({
|
|
|
666
714
|
}
|
|
667
715
|
collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
|
|
668
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
|
+
}
|
|
669
732
|
const query = `{
|
|
670
733
|
markets(
|
|
671
734
|
first: 50
|
|
672
735
|
orderBy: SupplyAssetsUsd
|
|
673
736
|
orderDirection: Desc
|
|
674
|
-
where: { chainId_in: [${chainId}]
|
|
737
|
+
where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter} }
|
|
675
738
|
) {
|
|
676
739
|
items {
|
|
677
740
|
uniqueKey
|
|
@@ -691,17 +754,21 @@ var init_MorphoClient = __esm({
|
|
|
691
754
|
try {
|
|
692
755
|
const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
|
|
693
756
|
const items = resp.data?.data?.markets?.items ?? [];
|
|
694
|
-
return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers.ethers.ZeroAddress).map((m) =>
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
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
|
+
});
|
|
705
772
|
} catch (e) {
|
|
706
773
|
console.warn("[agether] getMarketRates failed:", e instanceof Error ? e.message : e);
|
|
707
774
|
return [];
|
|
@@ -735,14 +802,15 @@ var init_MorphoClient = __esm({
|
|
|
735
802
|
} else {
|
|
736
803
|
try {
|
|
737
804
|
const params = await this.findMarketForCollateral(collateralSymbol);
|
|
805
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
738
806
|
const oracleContract = new import_ethers.Contract(params.oracle, [
|
|
739
807
|
"function price() view returns (uint256)"
|
|
740
808
|
], this.provider);
|
|
741
809
|
const oraclePrice = await oracleContract.price();
|
|
742
810
|
const ORACLE_PRICE_SCALE = 10n ** 36n;
|
|
743
811
|
const amountWei = import_ethers.ethers.parseUnits(amount, colInfo.decimals);
|
|
744
|
-
const
|
|
745
|
-
collateralValueUsd = Number(
|
|
812
|
+
const valueInLoan = amountWei * oraclePrice / ORACLE_PRICE_SCALE;
|
|
813
|
+
collateralValueUsd = Number(valueInLoan) / 10 ** loanDecimals;
|
|
746
814
|
} catch (e) {
|
|
747
815
|
console.warn("[agether] oracle price fetch for yield estimation failed:", e instanceof Error ? e.message : e);
|
|
748
816
|
throw new AgetherError("Cannot determine collateral value. Provide ethPriceUsd.", "PRICE_UNAVAILABLE");
|
|
@@ -763,61 +831,65 @@ var init_MorphoClient = __esm({
|
|
|
763
831
|
// Supply-Side (Lending) — earn yield by supplying USDC
|
|
764
832
|
// ════════════════════════════════════════════════════════
|
|
765
833
|
/**
|
|
766
|
-
* Supply
|
|
834
|
+
* Supply loan token to a Morpho Blue market as a lender (earn yield).
|
|
767
835
|
*
|
|
768
836
|
* Unlike `supplyCollateral` (borrower-side), this is the **lender-side**:
|
|
769
|
-
* you deposit the loanToken
|
|
837
|
+
* you deposit the loanToken into the market's supply pool and earn
|
|
770
838
|
* interest paid by borrowers.
|
|
771
839
|
*
|
|
772
|
-
* @param
|
|
840
|
+
* @param amount - Amount of loan token to supply (e.g. '500' for 500 USDC, '0.5' for 0.5 WETH)
|
|
773
841
|
* @param collateralSymbol - Market collateral token to identify which market (e.g. 'WETH')
|
|
774
842
|
* Optional — defaults to highest-APY market
|
|
843
|
+
* @param loanTokenSymbol - Loan token to filter market (e.g. 'USDC', 'WETH'). Optional.
|
|
775
844
|
*/
|
|
776
|
-
async supplyAsset(
|
|
845
|
+
async supplyAsset(amount, collateralSymbol, loanTokenSymbol) {
|
|
777
846
|
const acctAddr = await this.getAccountAddress();
|
|
778
|
-
const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
|
|
779
847
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
780
|
-
const usdcAddr = this.config.contracts.usdc;
|
|
781
848
|
let params;
|
|
782
849
|
let usedCollateral;
|
|
783
850
|
if (collateralSymbol) {
|
|
784
|
-
params = await this.findMarketForCollateral(collateralSymbol);
|
|
851
|
+
params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
|
|
785
852
|
usedCollateral = collateralSymbol;
|
|
786
853
|
} else {
|
|
787
|
-
const rates = await this.getMarketRates();
|
|
854
|
+
const rates = await this.getMarketRates(void 0, loanTokenSymbol);
|
|
788
855
|
if (rates.length === 0) throw new AgetherError("No markets available", "NO_MARKETS");
|
|
789
856
|
const best = rates.reduce((a, b) => a.supplyApy > b.supplyApy ? a : b);
|
|
790
|
-
params = await this.findMarketForCollateral(best.collateralToken);
|
|
857
|
+
params = await this.findMarketForCollateral(best.collateralToken, loanTokenSymbol);
|
|
791
858
|
usedCollateral = best.collateralToken;
|
|
792
859
|
}
|
|
860
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
861
|
+
const loanTokenAddr = params.loanToken;
|
|
862
|
+
const parsedAmount = import_ethers.ethers.parseUnits(amount, loanDecimals);
|
|
793
863
|
const marketId = import_ethers.ethers.keccak256(
|
|
794
864
|
import_ethers.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
795
865
|
["address", "address", "address", "address", "uint256"],
|
|
796
866
|
[params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
|
|
797
867
|
)
|
|
798
868
|
);
|
|
799
|
-
const
|
|
800
|
-
const acctBalance = await
|
|
801
|
-
if (acctBalance <
|
|
802
|
-
const shortfall =
|
|
803
|
-
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());
|
|
804
874
|
if (eoaBalance < shortfall) {
|
|
875
|
+
const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
|
|
876
|
+
const loanSymbol = loanInfo?.symbol ?? "loan token";
|
|
805
877
|
throw new AgetherError(
|
|
806
|
-
`Insufficient
|
|
878
|
+
`Insufficient ${loanSymbol}. Need ${amount}, AgentAccount has ${import_ethers.ethers.formatUnits(acctBalance, loanDecimals)}, EOA has ${import_ethers.ethers.formatUnits(eoaBalance, loanDecimals)}.`,
|
|
807
879
|
"INSUFFICIENT_BALANCE"
|
|
808
880
|
);
|
|
809
881
|
}
|
|
810
|
-
const transferTx = await
|
|
882
|
+
const transferTx = await loanContract.transfer(acctAddr, shortfall);
|
|
811
883
|
await transferTx.wait();
|
|
812
884
|
this._refreshSigner();
|
|
813
885
|
}
|
|
814
|
-
const targets = [
|
|
886
|
+
const targets = [loanTokenAddr, morphoAddr];
|
|
815
887
|
const values = [0n, 0n];
|
|
816
888
|
const datas = [
|
|
817
|
-
erc20Iface.encodeFunctionData("approve", [morphoAddr,
|
|
889
|
+
erc20Iface.encodeFunctionData("approve", [morphoAddr, parsedAmount]),
|
|
818
890
|
morphoIface.encodeFunctionData("supply", [
|
|
819
891
|
this._toTuple(params),
|
|
820
|
-
|
|
892
|
+
parsedAmount,
|
|
821
893
|
0n,
|
|
822
894
|
acctAddr,
|
|
823
895
|
"0x"
|
|
@@ -826,7 +898,7 @@ var init_MorphoClient = __esm({
|
|
|
826
898
|
const receipt = await this.batch(targets, values, datas);
|
|
827
899
|
return {
|
|
828
900
|
tx: receipt.hash,
|
|
829
|
-
amount
|
|
901
|
+
amount,
|
|
830
902
|
marketId,
|
|
831
903
|
collateralToken: usedCollateral,
|
|
832
904
|
agentAccount: acctAddr
|
|
@@ -839,17 +911,26 @@ var init_MorphoClient = __esm({
|
|
|
839
911
|
* @param collateralSymbol - Market collateral to identify which market
|
|
840
912
|
* @param receiver - Destination address (defaults to EOA)
|
|
841
913
|
*/
|
|
842
|
-
|
|
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) {
|
|
843
923
|
const acctAddr = await this.getAccountAddress();
|
|
844
924
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
845
925
|
const dest = receiver || await this.getSignerAddress();
|
|
846
926
|
let params;
|
|
847
927
|
if (collateralSymbol) {
|
|
848
|
-
params = await this.findMarketForCollateral(collateralSymbol);
|
|
928
|
+
params = await this.findMarketForCollateral(collateralSymbol, loanTokenSymbol);
|
|
849
929
|
} else {
|
|
850
930
|
const { params: p } = await this._findActiveSupplyMarket();
|
|
851
931
|
params = p;
|
|
852
932
|
}
|
|
933
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
853
934
|
const marketId = import_ethers.ethers.keccak256(
|
|
854
935
|
import_ethers.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
855
936
|
["address", "address", "address", "address", "uint256"],
|
|
@@ -858,13 +939,13 @@ var init_MorphoClient = __esm({
|
|
|
858
939
|
);
|
|
859
940
|
let withdrawAssets;
|
|
860
941
|
let withdrawShares;
|
|
861
|
-
if (
|
|
942
|
+
if (amount === "all") {
|
|
862
943
|
const pos = await this.morphoBlue.position(marketId, acctAddr);
|
|
863
944
|
withdrawShares = BigInt(pos.supplyShares);
|
|
864
945
|
withdrawAssets = 0n;
|
|
865
946
|
if (withdrawShares === 0n) throw new AgetherError("No supply position to withdraw", "NO_SUPPLY");
|
|
866
947
|
} else {
|
|
867
|
-
withdrawAssets = import_ethers.ethers.parseUnits(
|
|
948
|
+
withdrawAssets = import_ethers.ethers.parseUnits(amount, loanDecimals);
|
|
868
949
|
withdrawShares = 0n;
|
|
869
950
|
}
|
|
870
951
|
const data = morphoIface.encodeFunctionData("withdraw", [
|
|
@@ -882,13 +963,13 @@ var init_MorphoClient = __esm({
|
|
|
882
963
|
const totalSupplyAssets = BigInt(mkt.totalSupplyAssets);
|
|
883
964
|
const totalSupplyShares = BigInt(mkt.totalSupplyShares);
|
|
884
965
|
const currentAssets = totalSupplyShares > 0n ? BigInt(pos.supplyShares) * totalSupplyAssets / totalSupplyShares : 0n;
|
|
885
|
-
remainingSupply = import_ethers.ethers.formatUnits(currentAssets,
|
|
966
|
+
remainingSupply = import_ethers.ethers.formatUnits(currentAssets, loanDecimals);
|
|
886
967
|
} catch (e) {
|
|
887
968
|
console.warn("[agether] failed to read remaining supply:", e instanceof Error ? e.message : e);
|
|
888
969
|
}
|
|
889
970
|
return {
|
|
890
971
|
tx: receipt.hash,
|
|
891
|
-
amount
|
|
972
|
+
amount,
|
|
892
973
|
remainingSupply,
|
|
893
974
|
destination: dest
|
|
894
975
|
};
|
|
@@ -961,14 +1042,13 @@ var init_MorphoClient = __esm({
|
|
|
961
1042
|
* Computes available yield, verifies the requested amount doesn't exceed it,
|
|
962
1043
|
* then withdraws from the supply position and sends directly to the recipient.
|
|
963
1044
|
*
|
|
964
|
-
* @param recipient - Address to receive the
|
|
965
|
-
* @param
|
|
1045
|
+
* @param recipient - Address to receive the loan token
|
|
1046
|
+
* @param amount - Amount to pay from yield (e.g. '5.50')
|
|
966
1047
|
* @param collateralSymbol - Market collateral to identify which supply position
|
|
967
1048
|
*/
|
|
968
|
-
async payFromYield(recipient,
|
|
1049
|
+
async payFromYield(recipient, amount, collateralSymbol) {
|
|
969
1050
|
const acctAddr = await this.getAccountAddress();
|
|
970
1051
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
971
|
-
const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
|
|
972
1052
|
const positions = await this.getSupplyPositions(collateralSymbol);
|
|
973
1053
|
if (positions.length === 0) {
|
|
974
1054
|
throw new AgetherError("No supply position found", "NO_SUPPLY");
|
|
@@ -976,17 +1056,20 @@ var init_MorphoClient = __esm({
|
|
|
976
1056
|
const pos = positions.reduce(
|
|
977
1057
|
(a, b) => parseFloat(a.earnedYield) > parseFloat(b.earnedYield) ? a : b
|
|
978
1058
|
);
|
|
979
|
-
const
|
|
980
|
-
|
|
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;
|
|
981
1065
|
throw new AgetherError(
|
|
982
|
-
`Requested ${
|
|
1066
|
+
`Requested ${amount} ${loanSymbol} exceeds available yield of ${pos.earnedYield} ${loanSymbol}. Use withdrawSupply to withdraw principal.`,
|
|
983
1067
|
"EXCEEDS_YIELD"
|
|
984
1068
|
);
|
|
985
1069
|
}
|
|
986
|
-
const params = await this.findMarketForCollateral(pos.collateralToken);
|
|
987
1070
|
const data = morphoIface.encodeFunctionData("withdraw", [
|
|
988
1071
|
this._toTuple(params),
|
|
989
|
-
|
|
1072
|
+
parsedAmount,
|
|
990
1073
|
0n,
|
|
991
1074
|
acctAddr,
|
|
992
1075
|
recipient
|
|
@@ -1005,7 +1088,7 @@ var init_MorphoClient = __esm({
|
|
|
1005
1088
|
}
|
|
1006
1089
|
return {
|
|
1007
1090
|
tx: receipt.hash,
|
|
1008
|
-
yieldWithdrawn:
|
|
1091
|
+
yieldWithdrawn: amount,
|
|
1009
1092
|
recipient,
|
|
1010
1093
|
remainingYield,
|
|
1011
1094
|
remainingSupply
|
|
@@ -1063,28 +1146,31 @@ var init_MorphoClient = __esm({
|
|
|
1063
1146
|
};
|
|
1064
1147
|
}
|
|
1065
1148
|
/**
|
|
1066
|
-
* Borrow
|
|
1149
|
+
* Borrow loan token against existing collateral.
|
|
1067
1150
|
*
|
|
1068
1151
|
* AgentAccount.execute: Morpho.borrow(params, amount, 0, account, account)
|
|
1069
1152
|
*
|
|
1070
|
-
* @param
|
|
1153
|
+
* @param amount - Loan token amount (e.g. '100' for 100 USDC, '0.5' for 0.5 WETH)
|
|
1071
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')
|
|
1072
1157
|
*/
|
|
1073
|
-
async borrow(
|
|
1158
|
+
async borrow(amount, tokenSymbol, marketParams, loanTokenSymbol) {
|
|
1074
1159
|
const acctAddr = await this.getAccountAddress();
|
|
1075
|
-
const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
|
|
1076
1160
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1077
1161
|
let params;
|
|
1078
1162
|
let usedToken = tokenSymbol || "WETH";
|
|
1079
1163
|
if (marketParams) {
|
|
1080
1164
|
params = marketParams;
|
|
1081
1165
|
} else if (tokenSymbol) {
|
|
1082
|
-
params = await this.findMarketForCollateral(tokenSymbol);
|
|
1166
|
+
params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
|
|
1083
1167
|
} else {
|
|
1084
1168
|
const { params: p, symbol } = await this._findActiveMarket();
|
|
1085
1169
|
params = p;
|
|
1086
1170
|
usedToken = symbol;
|
|
1087
1171
|
}
|
|
1172
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
1173
|
+
const parsedAmount = import_ethers.ethers.parseUnits(amount, loanDecimals);
|
|
1088
1174
|
try {
|
|
1089
1175
|
const marketId = import_ethers.ethers.keccak256(
|
|
1090
1176
|
import_ethers.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
@@ -1108,11 +1194,14 @@ var init_MorphoClient = __esm({
|
|
|
1108
1194
|
const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
|
|
1109
1195
|
const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1110
1196
|
const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
|
|
1111
|
-
if (
|
|
1112
|
-
const
|
|
1113
|
-
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);
|
|
1114
1203
|
throw new AgetherError(
|
|
1115
|
-
`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.`,
|
|
1116
1205
|
"EXCEEDS_MAX_LTV"
|
|
1117
1206
|
);
|
|
1118
1207
|
}
|
|
@@ -1122,7 +1211,7 @@ var init_MorphoClient = __esm({
|
|
|
1122
1211
|
}
|
|
1123
1212
|
const data = morphoIface.encodeFunctionData("borrow", [
|
|
1124
1213
|
this._toTuple(params),
|
|
1125
|
-
|
|
1214
|
+
parsedAmount,
|
|
1126
1215
|
0n,
|
|
1127
1216
|
acctAddr,
|
|
1128
1217
|
acctAddr
|
|
@@ -1130,7 +1219,7 @@ var init_MorphoClient = __esm({
|
|
|
1130
1219
|
const receipt = await this.exec(morphoAddr, data);
|
|
1131
1220
|
return {
|
|
1132
1221
|
tx: receipt.hash,
|
|
1133
|
-
amount
|
|
1222
|
+
amount,
|
|
1134
1223
|
collateralToken: usedToken,
|
|
1135
1224
|
agentAccount: acctAddr
|
|
1136
1225
|
};
|
|
@@ -1138,17 +1227,27 @@ var init_MorphoClient = __esm({
|
|
|
1138
1227
|
/**
|
|
1139
1228
|
* Deposit collateral AND borrow USDC in one batched transaction.
|
|
1140
1229
|
*
|
|
1230
|
+
/**
|
|
1231
|
+
* Deposit collateral AND borrow loan token in one batched transaction.
|
|
1232
|
+
*
|
|
1141
1233
|
* AgentAccount.executeBatch:
|
|
1142
1234
|
* [collateral.approve, Morpho.supplyCollateral, Morpho.borrow]
|
|
1143
1235
|
*
|
|
1144
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)
|
|
1145
1243
|
*/
|
|
1146
|
-
async depositAndBorrow(tokenSymbol, collateralAmount,
|
|
1244
|
+
async depositAndBorrow(tokenSymbol, collateralAmount, borrowAmount, marketParams, loanTokenSymbol) {
|
|
1147
1245
|
const acctAddr = await this.getAccountAddress();
|
|
1148
1246
|
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1149
|
-
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1247
|
+
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
|
|
1248
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
1150
1249
|
const colWei = import_ethers.ethers.parseUnits(collateralAmount, colInfo.decimals);
|
|
1151
|
-
const borrowWei = import_ethers.ethers.parseUnits(
|
|
1250
|
+
const borrowWei = import_ethers.ethers.parseUnits(borrowAmount, loanDecimals);
|
|
1152
1251
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1153
1252
|
try {
|
|
1154
1253
|
const marketId = import_ethers.ethers.keccak256(
|
|
@@ -1169,9 +1268,11 @@ var init_MorphoClient = __esm({
|
|
|
1169
1268
|
const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1170
1269
|
const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
|
|
1171
1270
|
if (borrowWei > maxAdditional) {
|
|
1172
|
-
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);
|
|
1173
1274
|
throw new AgetherError(
|
|
1174
|
-
`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.`,
|
|
1175
1276
|
"EXCEEDS_MAX_LTV"
|
|
1176
1277
|
);
|
|
1177
1278
|
}
|
|
@@ -1217,36 +1318,42 @@ var init_MorphoClient = __esm({
|
|
|
1217
1318
|
tx: receipt.hash,
|
|
1218
1319
|
collateralToken: tokenSymbol,
|
|
1219
1320
|
collateralAmount,
|
|
1220
|
-
borrowAmount
|
|
1321
|
+
borrowAmount,
|
|
1221
1322
|
agentAccount: acctAddr
|
|
1222
1323
|
};
|
|
1223
1324
|
}
|
|
1224
1325
|
/**
|
|
1225
|
-
* Repay borrowed
|
|
1326
|
+
* Repay borrowed loan token from AgentAccount.
|
|
1226
1327
|
*
|
|
1227
1328
|
* AgentAccount.executeBatch:
|
|
1228
|
-
* [
|
|
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)
|
|
1229
1335
|
*/
|
|
1230
|
-
async repay(
|
|
1336
|
+
async repay(amount, tokenSymbol, marketParams, loanTokenSymbol) {
|
|
1231
1337
|
const acctAddr = await this.getAccountAddress();
|
|
1232
1338
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1233
|
-
const usdcAddr = this.config.contracts.usdc;
|
|
1234
1339
|
let params;
|
|
1235
1340
|
if (marketParams) {
|
|
1236
1341
|
params = marketParams;
|
|
1237
1342
|
} else if (tokenSymbol) {
|
|
1238
|
-
params = await this.findMarketForCollateral(tokenSymbol);
|
|
1343
|
+
params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
|
|
1239
1344
|
} else {
|
|
1240
1345
|
const { params: p } = await this._findActiveMarket();
|
|
1241
1346
|
params = p;
|
|
1242
1347
|
}
|
|
1348
|
+
const loanTokenAddr = params.loanToken;
|
|
1349
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
1243
1350
|
let repayAssets;
|
|
1244
1351
|
let repayShares;
|
|
1245
1352
|
let approveAmount;
|
|
1246
|
-
if (
|
|
1353
|
+
if (amount === "all") {
|
|
1247
1354
|
const markets = await this.getMarkets();
|
|
1248
1355
|
const mkt = markets.find(
|
|
1249
|
-
(m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase()
|
|
1356
|
+
(m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase() && m.loanAsset?.address.toLowerCase() === params.loanToken.toLowerCase()
|
|
1250
1357
|
);
|
|
1251
1358
|
if (mkt) {
|
|
1252
1359
|
const pos = await this.morphoBlue.position(mkt.uniqueKey, acctAddr);
|
|
@@ -1256,33 +1363,35 @@ var init_MorphoClient = __esm({
|
|
|
1256
1363
|
const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
|
|
1257
1364
|
const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
|
|
1258
1365
|
const estimated = totalBorrowShares > 0n ? repayShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
|
|
1259
|
-
approveAmount = estimated > 0n ? estimated : import_ethers.ethers.parseUnits("1",
|
|
1366
|
+
approveAmount = estimated > 0n ? estimated : import_ethers.ethers.parseUnits("1", loanDecimals);
|
|
1260
1367
|
} else {
|
|
1261
|
-
repayAssets = import_ethers.ethers.parseUnits("999999",
|
|
1368
|
+
repayAssets = import_ethers.ethers.parseUnits("999999", loanDecimals);
|
|
1262
1369
|
repayShares = 0n;
|
|
1263
1370
|
approveAmount = repayAssets;
|
|
1264
1371
|
}
|
|
1265
1372
|
} else {
|
|
1266
|
-
repayAssets = import_ethers.ethers.parseUnits(
|
|
1373
|
+
repayAssets = import_ethers.ethers.parseUnits(amount, loanDecimals);
|
|
1267
1374
|
repayShares = 0n;
|
|
1268
1375
|
approveAmount = repayAssets;
|
|
1269
1376
|
}
|
|
1270
|
-
const
|
|
1271
|
-
const acctBalance = await
|
|
1377
|
+
const loanContract = new import_ethers.Contract(loanTokenAddr, ERC20_ABI, this._signer);
|
|
1378
|
+
const acctBalance = await loanContract.balanceOf(acctAddr);
|
|
1272
1379
|
if (acctBalance < approveAmount) {
|
|
1273
1380
|
const shortfall = approveAmount - acctBalance;
|
|
1274
|
-
const eoaBalance = await
|
|
1381
|
+
const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
|
|
1275
1382
|
if (eoaBalance < shortfall) {
|
|
1383
|
+
const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
|
|
1384
|
+
const loanSymbol = loanInfo?.symbol ?? "loan token";
|
|
1276
1385
|
throw new AgetherError(
|
|
1277
|
-
`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)}.`,
|
|
1278
1387
|
"INSUFFICIENT_BALANCE"
|
|
1279
1388
|
);
|
|
1280
1389
|
}
|
|
1281
|
-
const transferTx = await
|
|
1390
|
+
const transferTx = await loanContract.transfer(acctAddr, shortfall);
|
|
1282
1391
|
await transferTx.wait();
|
|
1283
1392
|
this._refreshSigner();
|
|
1284
1393
|
}
|
|
1285
|
-
const targets = [
|
|
1394
|
+
const targets = [loanTokenAddr, morphoAddr];
|
|
1286
1395
|
const values = [0n, 0n];
|
|
1287
1396
|
const datas = [
|
|
1288
1397
|
erc20Iface.encodeFunctionData("approve", [morphoAddr, approveAmount]),
|
|
@@ -1302,7 +1411,7 @@ var init_MorphoClient = __esm({
|
|
|
1302
1411
|
} catch (e) {
|
|
1303
1412
|
console.warn("[agether] failed to read remaining debt after repay:", e instanceof Error ? e.message : e);
|
|
1304
1413
|
}
|
|
1305
|
-
return { tx: receipt.hash, amount
|
|
1414
|
+
return { tx: receipt.hash, amount, remainingDebt };
|
|
1306
1415
|
}
|
|
1307
1416
|
/**
|
|
1308
1417
|
* Withdraw collateral from Morpho Blue.
|
|
@@ -1316,12 +1425,13 @@ var init_MorphoClient = __esm({
|
|
|
1316
1425
|
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1317
1426
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1318
1427
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1319
|
-
const
|
|
1428
|
+
const loanTokenAddr = params.loanToken;
|
|
1429
|
+
const loanDecimals = await this._getLoanTokenDecimals(params);
|
|
1320
1430
|
const dest = receiver || await this.getSignerAddress();
|
|
1321
1431
|
let weiAmount;
|
|
1322
1432
|
const markets = await this.getMarkets();
|
|
1323
1433
|
const market = markets.find(
|
|
1324
|
-
(m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase()
|
|
1434
|
+
(m) => m.collateralAsset?.address.toLowerCase() === colInfo.address.toLowerCase() && m.loanAsset?.address.toLowerCase() === loanTokenAddr.toLowerCase()
|
|
1325
1435
|
);
|
|
1326
1436
|
if (amount === "all") {
|
|
1327
1437
|
if (!market) throw new AgetherError("Market not found", "MARKET_NOT_FOUND");
|
|
@@ -1344,8 +1454,10 @@ var init_MorphoClient = __esm({
|
|
|
1344
1454
|
const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
|
|
1345
1455
|
const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
|
|
1346
1456
|
const estimated = totalBorrowShares > 0n ? dustBorrowShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
|
|
1347
|
-
dustApproveAmount = estimated > 0n ? estimated : import_ethers.ethers.parseUnits("1",
|
|
1348
|
-
|
|
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`);
|
|
1349
1461
|
}
|
|
1350
1462
|
} catch (e) {
|
|
1351
1463
|
console.warn("[agether] failed to check borrow shares before withdraw:", e instanceof Error ? e.message : e);
|
|
@@ -1359,19 +1471,21 @@ var init_MorphoClient = __esm({
|
|
|
1359
1471
|
]);
|
|
1360
1472
|
let receipt;
|
|
1361
1473
|
if (hasDustDebt) {
|
|
1362
|
-
const
|
|
1363
|
-
const acctBalance = await
|
|
1474
|
+
const loanContract = new import_ethers.Contract(loanTokenAddr, ERC20_ABI, this._signer);
|
|
1475
|
+
const acctBalance = await loanContract.balanceOf(acctAddr);
|
|
1364
1476
|
if (acctBalance < dustApproveAmount) {
|
|
1365
1477
|
const shortfall = dustApproveAmount - acctBalance;
|
|
1366
|
-
const eoaBalance = await
|
|
1478
|
+
const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
|
|
1367
1479
|
if (eoaBalance >= shortfall) {
|
|
1368
|
-
|
|
1369
|
-
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);
|
|
1370
1484
|
await transferTx.wait();
|
|
1371
1485
|
this._refreshSigner();
|
|
1372
1486
|
}
|
|
1373
1487
|
}
|
|
1374
|
-
const targets = [
|
|
1488
|
+
const targets = [loanTokenAddr, morphoAddr, morphoAddr];
|
|
1375
1489
|
const values = [0n, 0n, 0n];
|
|
1376
1490
|
const datas = [
|
|
1377
1491
|
erc20Iface.encodeFunctionData("approve", [morphoAddr, dustApproveAmount]),
|
|
@@ -1616,6 +1730,37 @@ var init_MorphoClient = __esm({
|
|
|
1616
1730
|
}
|
|
1617
1731
|
throw new AgetherError("No active supply position found", "NO_SUPPLY");
|
|
1618
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
|
+
}
|
|
1619
1764
|
/**
|
|
1620
1765
|
* Resolve a token symbol or address to { address, symbol, decimals }.
|
|
1621
1766
|
*
|
|
@@ -1632,8 +1777,8 @@ var init_MorphoClient = __esm({
|
|
|
1632
1777
|
const fromApi = this._tokenCache.get(key);
|
|
1633
1778
|
if (fromApi) return fromApi;
|
|
1634
1779
|
throw new AgetherError(
|
|
1635
|
-
`Unknown token: ${symbolOrAddress}. No Morpho market found with this
|
|
1636
|
-
"
|
|
1780
|
+
`Unknown token: ${symbolOrAddress}. No Morpho market found with this token.`,
|
|
1781
|
+
"UNKNOWN_TOKEN"
|
|
1637
1782
|
);
|
|
1638
1783
|
}
|
|
1639
1784
|
/**
|
|
@@ -1839,25 +1984,21 @@ var init_AgetherClient = __esm({
|
|
|
1839
1984
|
const acctAddr = await this.agether4337Factory.getAccount(agentId);
|
|
1840
1985
|
this.accountAddress = acctAddr;
|
|
1841
1986
|
if (!acctExists) {
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
this._refreshSigner();
|
|
1858
|
-
} catch (e) {
|
|
1859
|
-
console.warn("[agether] setAgentURI failed (non-fatal):", e instanceof Error ? e.message : e);
|
|
1860
|
-
}
|
|
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();
|
|
1861
2002
|
}
|
|
1862
2003
|
const kyaRequired = await this.isKyaRequired();
|
|
1863
2004
|
return {
|
|
@@ -1875,7 +2016,7 @@ var init_AgetherClient = __esm({
|
|
|
1875
2016
|
async _mintNewIdentity(name, description) {
|
|
1876
2017
|
const registrationFile = JSON.stringify({
|
|
1877
2018
|
type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
|
|
1878
|
-
name: name || "
|
|
2019
|
+
name: name || "Agether Agent",
|
|
1879
2020
|
description: description || "AI agent registered via @agether/sdk",
|
|
1880
2021
|
active: true,
|
|
1881
2022
|
registrations: [{
|