@agether/sdk 2.16.0 → 2.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -340,7 +340,7 @@ var init_MorphoClient = __esm({
340
340
  MODE_BATCH = "0x0100000000000000000000000000000000000000000000000000000000000000";
341
341
  morphoIface = new import_ethers.ethers.Interface(MORPHO_BLUE_ABI);
342
342
  erc20Iface = new import_ethers.ethers.Interface(ERC20_ABI);
343
- MorphoClient = class {
343
+ MorphoClient = class _MorphoClient {
344
344
  constructor(config) {
345
345
  /** Market params cache: keyed by market uniqueKey (bytes32 hash) */
346
346
  this._marketCache = /* @__PURE__ */ new Map();
@@ -1716,9 +1716,17 @@ var init_MorphoClient = __esm({
1716
1716
  const estimated = totalBorrowShares > 0n ? repayShares * totalBorrowAssets / totalBorrowShares + 10n : 0n;
1717
1717
  approveAmount = estimated > 0n ? estimated : import_ethers.ethers.parseUnits("1", loanDecimals);
1718
1718
  } else {
1719
- repayAssets = import_ethers.ethers.parseUnits("999999", loanDecimals);
1720
- repayShares = 0n;
1721
- approveAmount = repayAssets;
1719
+ const marketId = import_ethers.ethers.keccak256(import_ethers.ethers.AbiCoder.defaultAbiCoder().encode(
1720
+ ["address", "address", "address", "address", "uint256"],
1721
+ [params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
1722
+ ));
1723
+ const pos = await this.morphoBlue.position(marketId, acctAddr);
1724
+ repayShares = BigInt(pos.borrowShares);
1725
+ repayAssets = 0n;
1726
+ const onChainMkt = await this.morphoBlue.market(marketId);
1727
+ const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
1728
+ const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
1729
+ approveAmount = totalBorrowShares > 0n ? repayShares * totalBorrowAssets / totalBorrowShares + 10n : import_ethers.ethers.parseUnits("1", loanDecimals);
1722
1730
  }
1723
1731
  } else {
1724
1732
  repayAssets = import_ethers.ethers.parseUnits(amount, loanDecimals);
@@ -1727,20 +1735,32 @@ var init_MorphoClient = __esm({
1727
1735
  }
1728
1736
  const loanContract = new import_ethers.Contract(loanTokenAddr, ERC20_ABI, this._signer);
1729
1737
  const acctBalance = await loanContract.balanceOf(acctAddr);
1730
- if (acctBalance < approveAmount) {
1731
- const shortfall = approveAmount - acctBalance;
1738
+ const checkAmount = repayShares > 0n && repayAssets === 0n ? approveAmount * 1005n / 1000n : approveAmount;
1739
+ if (acctBalance < checkAmount) {
1740
+ const shortfall = checkAmount - acctBalance;
1732
1741
  const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
1733
1742
  if (eoaBalance < shortfall) {
1734
- const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
1735
- const loanSymbol = loanInfo?.symbol ?? "loan token";
1736
- throw new AgetherError(
1737
- `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)}.`,
1738
- "INSUFFICIENT_BALANCE"
1739
- );
1743
+ if (repayShares > 0n && acctBalance + eoaBalance > 0n) {
1744
+ if (eoaBalance > 0n) {
1745
+ const transferTx = await loanContract.transfer(acctAddr, eoaBalance);
1746
+ await transferTx.wait();
1747
+ this._refreshSigner();
1748
+ }
1749
+ const newBalance = await loanContract.balanceOf(acctAddr);
1750
+ approveAmount = newBalance;
1751
+ } else {
1752
+ const loanInfo = this._tokenCache.get(loanTokenAddr.toLowerCase());
1753
+ const loanSymbol = loanInfo?.symbol ?? "loan token";
1754
+ throw new AgetherError(
1755
+ `Insufficient ${loanSymbol} for repay. Need ~${import_ethers.ethers.formatUnits(checkAmount, loanDecimals)}, AgentAccount has ${import_ethers.ethers.formatUnits(acctBalance, loanDecimals)}, EOA has ${import_ethers.ethers.formatUnits(eoaBalance, loanDecimals)}.`,
1756
+ "INSUFFICIENT_BALANCE"
1757
+ );
1758
+ }
1759
+ } else {
1760
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
1761
+ await transferTx.wait();
1762
+ this._refreshSigner();
1740
1763
  }
1741
- const transferTx = await loanContract.transfer(acctAddr, shortfall);
1742
- await transferTx.wait();
1743
- this._refreshSigner();
1744
1764
  }
1745
1765
  const targets = [loanTokenAddr, morphoAddr];
1746
1766
  const values = [0n, 0n];
@@ -1757,20 +1777,250 @@ var init_MorphoClient = __esm({
1757
1777
  const receipt = await this.batch(targets, values, datas);
1758
1778
  let remainingDebt = "0";
1759
1779
  try {
1760
- const status = await this.getStatus();
1761
- remainingDebt = status.totalDebt;
1780
+ const markets = await this.getMarkets(true);
1781
+ const mkt = markets.find(
1782
+ (m) => m.collateralAsset?.address.toLowerCase() === params.collateralToken.toLowerCase() && m.loanAsset?.address.toLowerCase() === params.loanToken.toLowerCase()
1783
+ );
1784
+ if (mkt) {
1785
+ const pos = await this.morphoBlue.position(mkt.uniqueKey, acctAddr);
1786
+ const shares = BigInt(pos.borrowShares);
1787
+ if (shares > 0n) {
1788
+ const onChainMkt = await this.morphoBlue.market(mkt.uniqueKey);
1789
+ const totalAssets = BigInt(onChainMkt.totalBorrowAssets);
1790
+ const totalShares = BigInt(onChainMkt.totalBorrowShares);
1791
+ const debtWei = totalShares > 0n ? shares * totalAssets / totalShares : 0n;
1792
+ remainingDebt = import_ethers.ethers.formatUnits(debtWei, loanDecimals);
1793
+ }
1794
+ }
1762
1795
  } catch (e) {
1763
1796
  console.warn("[agether] failed to read remaining debt after repay:", e instanceof Error ? e.message : e);
1764
1797
  }
1765
1798
  return { tx: receipt.hash, amount, remainingDebt };
1766
1799
  }
1800
+ static {
1801
+ // ════════════════════════════════════════════════════════
1802
+ // Yield Spread Analysis & Leverage
1803
+ // ════════════════════════════════════════════════════════
1804
+ /**
1805
+ * Mapping from Morpho collateral token symbols to DeFi Llama project IDs.
1806
+ * Used to look up native staking/restaking yields from the DeFi Llama Yields API.
1807
+ */
1808
+ this.LST_PROJECT_MAP = {
1809
+ "wstETH": { project: "lido", symbol: "STETH" },
1810
+ "cbETH": { project: "coinbase-wrapped-staked-eth", symbol: "CBETH" },
1811
+ "rETH": { project: "rocket-pool", symbol: "RETH" },
1812
+ "weETH": { project: "ether.fi-stake", symbol: "WEETH" },
1813
+ "ezETH": { project: "renzo", symbol: "EZETH" },
1814
+ "rsETH": { project: "kelp", symbol: "RSETH" },
1815
+ "swETH": { project: "swell-liquid-staking", symbol: "SWETH" },
1816
+ "mETH": { project: "meth-protocol", symbol: "METH" },
1817
+ "sfrxETH": { project: "frax-ether", symbol: "SFRXETH" },
1818
+ "oETH": { project: "origin-ether", symbol: "OETH" },
1819
+ "ETHx": { project: "stader", symbol: "ETHX" },
1820
+ "wBETH": { project: "binance-staked-eth", symbol: "WBETH" }
1821
+ };
1822
+ }
1823
+ static {
1824
+ /** Cache for LST yields fetched from DeFi Llama. TTL: 30 minutes. */
1825
+ this._lstYieldCache = null;
1826
+ }
1827
+ static {
1828
+ this.LST_CACHE_TTL = 30 * 60 * 1e3;
1829
+ }
1830
+ // 30 min
1831
+ /**
1832
+ * Fetch native staking/restaking yields for all known LST/LRT tokens
1833
+ * from the DeFi Llama Yields API. Results are cached for 30 minutes.
1834
+ *
1835
+ * @returns Map of token symbol → APY (e.g. { wstETH: 4.56, cbETH: 3.61 })
1836
+ */
1837
+ async _getLstYields() {
1838
+ if (_MorphoClient._lstYieldCache && Date.now() - _MorphoClient._lstYieldCache.ts < _MorphoClient.LST_CACHE_TTL) {
1839
+ return _MorphoClient._lstYieldCache.data;
1840
+ }
1841
+ const yields = {};
1842
+ try {
1843
+ const resp = await fetch("https://yields.llama.fi/pools", {
1844
+ headers: { "User-Agent": "agether-sdk/1.0" },
1845
+ signal: AbortSignal.timeout(1e4)
1846
+ });
1847
+ if (!resp.ok) {
1848
+ console.warn("[agether] DeFi Llama API returned", resp.status);
1849
+ return yields;
1850
+ }
1851
+ const data = await resp.json();
1852
+ const pools = data?.data ?? [];
1853
+ for (const [morphoSymbol, mapping] of Object.entries(_MorphoClient.LST_PROJECT_MAP)) {
1854
+ const matchingPools = pools.filter(
1855
+ (p) => p.project === mapping.project && p.symbol?.toUpperCase() === mapping.symbol && ["Ethereum", "Base"].includes(p.chain)
1856
+ );
1857
+ if (matchingPools.length > 0) {
1858
+ const best = matchingPools.reduce(
1859
+ (a, b) => (b.tvlUsd ?? 0) > (a.tvlUsd ?? 0) ? b : a
1860
+ );
1861
+ if (best.apy !== void 0 && best.apy > 0) {
1862
+ yields[morphoSymbol] = parseFloat(best.apy.toFixed(4));
1863
+ }
1864
+ }
1865
+ }
1866
+ const knownProjects = new Set(Object.values(_MorphoClient.LST_PROJECT_MAP).map((m) => m.project));
1867
+ const stakingProjects = pools.filter(
1868
+ (p) => p.symbol?.match(/^(ST|WST|CB|R|WE|EZ|RS|SW|M|OS|SFR|O|W?B)ETH$/i) && !knownProjects.has(p.project) && p.tvlUsd > 1e7 && p.apy > 0.5 && ["Ethereum", "Base"].includes(p.chain)
1869
+ );
1870
+ for (const p of stakingProjects) {
1871
+ const sym = p.symbol;
1872
+ if (!yields[sym]) {
1873
+ yields[sym] = parseFloat(p.apy.toFixed(4));
1874
+ }
1875
+ }
1876
+ } catch (e) {
1877
+ console.warn("[agether] Failed to fetch LST yields:", e instanceof Error ? e.message : e);
1878
+ }
1879
+ _MorphoClient._lstYieldCache = { data: yields, ts: Date.now() };
1880
+ return yields;
1881
+ }
1882
+ /**
1883
+ * Analyze yield spread between LST/LRT collateral yield and Morpho borrow rates.
1884
+ * Positive spread = profitable carry trade (collateral earns more than debt costs).
1885
+ *
1886
+ * @returns Array of markets with yield analysis, sorted by net spread descending.
1887
+ */
1888
+ async getYieldSpread() {
1889
+ const lstYields = await this._getLstYields();
1890
+ if (Object.keys(lstYields).length === 0) {
1891
+ console.warn("[agether] No LST yield data available \u2014 DeFi Llama may be unreachable");
1892
+ return [];
1893
+ }
1894
+ const lstTokens = Object.keys(lstYields);
1895
+ const allMarkets = [];
1896
+ for (const token of lstTokens) {
1897
+ try {
1898
+ const markets2 = await this.getMarketRates(token);
1899
+ allMarkets.push(...markets2);
1900
+ } catch {
1901
+ }
1902
+ }
1903
+ const seen = /* @__PURE__ */ new Set();
1904
+ const markets = allMarkets.filter((m) => {
1905
+ if (seen.has(m.marketId)) return false;
1906
+ seen.add(m.marketId);
1907
+ return true;
1908
+ });
1909
+ const results = [];
1910
+ for (const mkt of markets) {
1911
+ const collateralYield = lstYields[mkt.collateralToken];
1912
+ if (collateralYield === void 0) continue;
1913
+ const borrowRate = mkt.borrowApy;
1914
+ const netSpread = collateralYield - borrowRate;
1915
+ const lltv = parseFloat(mkt.lltv) / 100;
1916
+ const safeLtv = lltv * 0.8;
1917
+ const maxLeverage = 1 / (1 - safeLtv);
1918
+ const leveragedNetApy = collateralYield * maxLeverage - borrowRate * (maxLeverage - 1);
1919
+ results.push({
1920
+ collateralToken: mkt.collateralToken,
1921
+ loanToken: mkt.loanToken,
1922
+ collateralYield,
1923
+ borrowRate,
1924
+ netSpread,
1925
+ profitable: netSpread > 0,
1926
+ lltv,
1927
+ maxSafeLeverage: parseFloat(maxLeverage.toFixed(2)),
1928
+ leveragedNetApy: parseFloat(leveragedNetApy.toFixed(2)),
1929
+ liquidity: mkt.totalSupplyUsd - mkt.totalBorrowUsd,
1930
+ marketId: mkt.marketId
1931
+ });
1932
+ }
1933
+ return results.sort((a, b) => b.netSpread - a.netSpread);
1934
+ }
1767
1935
  /**
1768
- * Withdraw collateral from Morpho Blue.
1936
+ * Execute a leverage loop: deposit collateral borrow → swap to collateral → deposit again.
1937
+ * Repeats N times to achieve target leverage.
1769
1938
  *
1770
- * AgentAccount.execute: Morpho.withdrawCollateral(params, amount, account, receiver)
1939
+ * Example: 1 wstETH at 3x leverage = deposit 1 → borrow → swap to ~0.77 wstETH deposit →
1940
+ * borrow → swap to ~0.6 wstETH → deposit. Total exposure: ~2.37 wstETH, debt: ~1.37 ETH worth.
1771
1941
  *
1772
- * @param receiver - defaults to EOA wallet
1942
+ * @param collateralToken - LST token to deposit (e.g. 'wstETH')
1943
+ * @param amount - Initial deposit amount
1944
+ * @param targetLeverage - Target leverage multiplier (e.g. 2.0 for 2x)
1945
+ * @param loanToken - Token to borrow (default: inferred from market)
1946
+ * @param maxIterations - Max loop iterations (default: 5, safety cap)
1773
1947
  */
1948
+ async leverageLoop(collateralToken, amount, targetLeverage, loanToken, maxIterations = 5) {
1949
+ if (targetLeverage < 1.1 || targetLeverage > 10) {
1950
+ throw new AgetherError(
1951
+ "Target leverage must be between 1.1x and 10x",
1952
+ "INVALID_LEVERAGE"
1953
+ );
1954
+ }
1955
+ const spreads = await this.getYieldSpread();
1956
+ const matchingMarket = spreads.find(
1957
+ (s) => s.collateralToken.toLowerCase() === collateralToken.toLowerCase() && (!loanToken || s.loanToken.toLowerCase() === loanToken.toLowerCase())
1958
+ );
1959
+ if (!matchingMarket) {
1960
+ throw new AgetherError(
1961
+ `No LST market found for ${collateralToken}`,
1962
+ "MARKET_NOT_FOUND"
1963
+ );
1964
+ }
1965
+ if (!matchingMarket) {
1966
+ throw new AgetherError(
1967
+ `No yield data available for ${collateralToken}. Ensure DeFi Llama API is reachable.`,
1968
+ "NO_YIELD_DATA"
1969
+ );
1970
+ }
1971
+ if (targetLeverage > matchingMarket.maxSafeLeverage) {
1972
+ throw new AgetherError(
1973
+ `Target leverage ${targetLeverage}x exceeds max safe leverage ${matchingMarket.maxSafeLeverage}x for ${collateralToken}. Max LLTV is ${(matchingMarket.lltv * 100).toFixed(0)}%.`,
1974
+ "LEVERAGE_TOO_HIGH"
1975
+ );
1976
+ }
1977
+ const initialAmount = parseFloat(amount);
1978
+ const iterations = [];
1979
+ let totalCollateral = initialAmount;
1980
+ let totalDebt = 0;
1981
+ let currentAmount = initialAmount;
1982
+ for (let i = 0; i < maxIterations; i++) {
1983
+ const currentLeverage = totalDebt > 0 ? totalCollateral / (totalCollateral - totalDebt) : 1;
1984
+ if (currentLeverage >= targetLeverage * 0.98) break;
1985
+ const borrowAmount = currentAmount * matchingMarket.lltv * 0.8;
1986
+ const swapOutput = borrowAmount * 0.997;
1987
+ iterations.push({
1988
+ iteration: i + 1,
1989
+ deposit: currentAmount,
1990
+ borrow: borrowAmount,
1991
+ swapOutput
1992
+ });
1993
+ totalDebt += borrowAmount;
1994
+ totalCollateral += swapOutput;
1995
+ currentAmount = swapOutput;
1996
+ }
1997
+ const finalLeverage = totalDebt > 0 ? totalCollateral / (totalCollateral - totalDebt) : 1;
1998
+ return {
1999
+ strategy: "leverage_loop",
2000
+ collateralToken: matchingMarket.collateralToken,
2001
+ loanToken: matchingMarket.loanToken,
2002
+ initialDeposit: amount,
2003
+ targetLeverage,
2004
+ achievedLeverage: parseFloat(finalLeverage.toFixed(2)),
2005
+ totalCollateral: parseFloat(totalCollateral.toFixed(6)),
2006
+ totalDebt: parseFloat(totalDebt.toFixed(6)),
2007
+ iterations,
2008
+ estimatedNetApy: parseFloat(
2009
+ (matchingMarket.collateralYield * finalLeverage - matchingMarket.borrowRate * (finalLeverage - 1)).toFixed(2)
2010
+ ),
2011
+ healthFactor: parseFloat(
2012
+ (totalCollateral * matchingMarket.lltv / totalDebt).toFixed(2)
2013
+ ),
2014
+ warning: "This is a simulation. Actual execution requires DEX swap integration. Real results may differ due to slippage and price movements."
2015
+ };
2016
+ }
2017
+ /**
2018
+ * Withdraw collateral from Morpho Blue.
2019
+ *
2020
+ * AgentAccount.execute: Morpho.withdrawCollateral(params, amount, account, receiver)
2021
+ *
2022
+ * @param receiver - defaults to EOA wallet
2023
+ */
1774
2024
  async withdrawCollateral(tokenSymbol, amount, marketParams, receiver) {
1775
2025
  const acctAddr = await this.getAccountAddress();
1776
2026
  const colInfo = await this._resolveToken(tokenSymbol);
@@ -2557,13 +2807,64 @@ var init_AgetherClient = __esm({
2557
2807
  return this.agether4337Factory.accountExists(id);
2558
2808
  }
2559
2809
  // ════════════════════════════════════════════════════════
2810
+ // Token Discovery (for dynamic balance queries)
2811
+ // ════════════════════════════════════════════════════════
2812
+ /**
2813
+ * Discover token addresses from active Morpho Blue positions.
2814
+ * Queries the Morpho GraphQL API for markets involving this agent's account,
2815
+ * then extracts collateral and loan token info.
2816
+ */
2817
+ async _discoverPositionTokens() {
2818
+ const tokens = {};
2819
+ try {
2820
+ const acctAddr = await this.getAccountAddress();
2821
+ const chainId = this.config.chainId;
2822
+ const query = `{
2823
+ marketPositions(
2824
+ where: { userAddress_in: ["${acctAddr.toLowerCase()}"], chainId_in: [${chainId}] }
2825
+ first: 20
2826
+ ) {
2827
+ items {
2828
+ market {
2829
+ collateralAsset { address symbol decimals }
2830
+ loanAsset { address symbol decimals }
2831
+ }
2832
+ }
2833
+ }
2834
+ }`;
2835
+ const resp = await fetch("https://blue-api.morpho.org/graphql", {
2836
+ method: "POST",
2837
+ headers: { "Content-Type": "application/json" },
2838
+ body: JSON.stringify({ query }),
2839
+ signal: AbortSignal.timeout(5e3)
2840
+ });
2841
+ if (!resp.ok) return tokens;
2842
+ const data = await resp.json();
2843
+ const items = data?.data?.marketPositions?.items ?? [];
2844
+ for (const item of items) {
2845
+ const col = item?.market?.collateralAsset;
2846
+ const loan = item?.market?.loanAsset;
2847
+ if (col?.symbol && col?.address) {
2848
+ tokens[col.symbol] = { address: col.address, symbol: col.symbol, decimals: col.decimals ?? 18 };
2849
+ }
2850
+ if (loan?.symbol && loan?.address) {
2851
+ tokens[loan.symbol] = { address: loan.address, symbol: loan.symbol, decimals: loan.decimals ?? 18 };
2852
+ }
2853
+ }
2854
+ } catch {
2855
+ }
2856
+ return tokens;
2857
+ }
2858
+ // ════════════════════════════════════════════════════════
2560
2859
  // Balances
2561
2860
  // ════════════════════════════════════════════════════════
2562
2861
  /**
2563
- * Get ETH, USDC, and collateral token balances for EOA and Safe account.
2862
+ * Get ETH, USDC, and all token balances for EOA and Safe account.
2564
2863
  *
2565
- * Collateral tokens are resolved from a built-in registry of well-known
2566
- * tokens per chain (WETH, wstETH, cbETH).
2864
+ * Tokens are resolved from:
2865
+ * 1. Built-in registry of well-known tokens (WETH, wstETH, cbETH)
2866
+ * 2. Dynamic discovery from active Morpho positions (loan + collateral tokens)
2867
+ * This ensures tokens like LCAP or any new Morpho market tokens appear in balances.
2567
2868
  */
2568
2869
  async getBalances() {
2569
2870
  const provider = this.signer.provider;
@@ -2571,7 +2872,18 @@ var init_AgetherClient = __esm({
2571
2872
  const usdc = new import_ethers2.Contract(this.config.contracts.usdc, ERC20_ABI, provider);
2572
2873
  const ethBal = await provider.getBalance(eoaAddr);
2573
2874
  const usdcBal = await usdc.balanceOf(eoaAddr);
2574
- const knownTokens = KNOWN_TOKENS[this.config.chainId] ?? {};
2875
+ const knownTokens = {
2876
+ ...KNOWN_TOKENS[this.config.chainId] ?? {}
2877
+ };
2878
+ try {
2879
+ const positions = await this._discoverPositionTokens();
2880
+ for (const [symbol, info] of Object.entries(positions)) {
2881
+ if (!knownTokens[symbol]) {
2882
+ knownTokens[symbol] = info;
2883
+ }
2884
+ }
2885
+ } catch {
2886
+ }
2575
2887
  const eoaCollateral = {};
2576
2888
  for (const [symbol, info] of Object.entries(knownTokens)) {
2577
2889
  try {
package/dist/index.d.mts CHANGED
@@ -244,10 +244,18 @@ declare class AgetherClient {
244
244
  /** Check whether the Safe account has been deployed. */
245
245
  accountExists(): Promise<boolean>;
246
246
  /**
247
- * Get ETH, USDC, and collateral token balances for EOA and Safe account.
247
+ * Discover token addresses from active Morpho Blue positions.
248
+ * Queries the Morpho GraphQL API for markets involving this agent's account,
249
+ * then extracts collateral and loan token info.
250
+ */
251
+ private _discoverPositionTokens;
252
+ /**
253
+ * Get ETH, USDC, and all token balances for EOA and Safe account.
248
254
  *
249
- * Collateral tokens are resolved from a built-in registry of well-known
250
- * tokens per chain (WETH, wstETH, cbETH).
255
+ * Tokens are resolved from:
256
+ * 1. Built-in registry of well-known tokens (WETH, wstETH, cbETH)
257
+ * 2. Dynamic discovery from active Morpho positions (loan + collateral tokens)
258
+ * This ensures tokens like LCAP or any new Morpho market tokens appear in balances.
251
259
  */
252
260
  getBalances(): Promise<BalancesResult>;
253
261
  /**
@@ -561,6 +569,42 @@ interface PayFromYieldResult {
561
569
  remainingYield: string;
562
570
  remainingSupply: string;
563
571
  }
572
+ /** Yield spread analysis result for a single market. */
573
+ interface YieldSpreadResult {
574
+ collateralToken: string;
575
+ loanToken: string;
576
+ collateralYield: number;
577
+ borrowRate: number;
578
+ netSpread: number;
579
+ profitable: boolean;
580
+ lltv: number;
581
+ maxSafeLeverage: number;
582
+ leveragedNetApy: number;
583
+ liquidity: number;
584
+ marketId: string;
585
+ }
586
+ /** Single iteration in a leverage loop. */
587
+ interface LeverageIteration {
588
+ iteration: number;
589
+ deposit: number;
590
+ borrow: number;
591
+ swapOutput: number;
592
+ }
593
+ /** Result of a leverage loop simulation. */
594
+ interface LeverageResult {
595
+ strategy: string;
596
+ collateralToken: string;
597
+ loanToken: string;
598
+ initialDeposit: string;
599
+ targetLeverage: number;
600
+ achievedLeverage: number;
601
+ totalCollateral: number;
602
+ totalDebt: number;
603
+ iterations: LeverageIteration[];
604
+ estimatedNetApy: number;
605
+ healthFactor: number;
606
+ warning: string;
607
+ }
564
608
  declare class MorphoClient {
565
609
  private _signer;
566
610
  private provider;
@@ -890,12 +934,48 @@ declare class MorphoClient {
890
934
  */
891
935
  repay(amount: string, tokenSymbol?: string, marketParams?: MorphoMarketParams, loanTokenSymbol?: string): Promise<RepayResult>;
892
936
  /**
893
- * Withdraw collateral from Morpho Blue.
937
+ * Mapping from Morpho collateral token symbols to DeFi Llama project IDs.
938
+ * Used to look up native staking/restaking yields from the DeFi Llama Yields API.
939
+ */
940
+ private static readonly LST_PROJECT_MAP;
941
+ /** Cache for LST yields fetched from DeFi Llama. TTL: 30 minutes. */
942
+ private static _lstYieldCache;
943
+ private static readonly LST_CACHE_TTL;
944
+ /**
945
+ * Fetch native staking/restaking yields for all known LST/LRT tokens
946
+ * from the DeFi Llama Yields API. Results are cached for 30 minutes.
894
947
  *
895
- * AgentAccount.execute: Morpho.withdrawCollateral(params, amount, account, receiver)
948
+ * @returns Map of token symbol → APY (e.g. { wstETH: 4.56, cbETH: 3.61 })
949
+ */
950
+ private _getLstYields;
951
+ /**
952
+ * Analyze yield spread between LST/LRT collateral yield and Morpho borrow rates.
953
+ * Positive spread = profitable carry trade (collateral earns more than debt costs).
896
954
  *
897
- * @param receiver - defaults to EOA wallet
955
+ * @returns Array of markets with yield analysis, sorted by net spread descending.
898
956
  */
957
+ getYieldSpread(): Promise<YieldSpreadResult[]>;
958
+ /**
959
+ * Execute a leverage loop: deposit collateral → borrow → swap to collateral → deposit again.
960
+ * Repeats N times to achieve target leverage.
961
+ *
962
+ * Example: 1 wstETH at 3x leverage = deposit 1 → borrow → swap to ~0.77 wstETH → deposit →
963
+ * borrow → swap to ~0.6 wstETH → deposit. Total exposure: ~2.37 wstETH, debt: ~1.37 ETH worth.
964
+ *
965
+ * @param collateralToken - LST token to deposit (e.g. 'wstETH')
966
+ * @param amount - Initial deposit amount
967
+ * @param targetLeverage - Target leverage multiplier (e.g. 2.0 for 2x)
968
+ * @param loanToken - Token to borrow (default: inferred from market)
969
+ * @param maxIterations - Max loop iterations (default: 5, safety cap)
970
+ */
971
+ leverageLoop(collateralToken: string, amount: string, targetLeverage: number, loanToken?: string, maxIterations?: number): Promise<LeverageResult>;
972
+ /**
973
+ * Withdraw collateral from Morpho Blue.
974
+ *
975
+ * AgentAccount.execute: Morpho.withdrawCollateral(params, amount, account, receiver)
976
+ *
977
+ * @param receiver - defaults to EOA wallet
978
+ */
899
979
  withdrawCollateral(tokenSymbol: string, amount: string, marketParams?: MorphoMarketParams, receiver?: string): Promise<WithdrawResult>;
900
980
  /**
901
981
  * Refresh the signer and re-bind contract instances.
@@ -1589,4 +1669,4 @@ interface RetryOptions {
1589
1669
  */
1590
1670
  declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
1591
1671
 
1592
- export { ACCOUNT_FACTORY_ABI, AGENT_REPUTATION_ABI, AGETHER_4337_FACTORY_ABI, AGETHER_8004_SCORER_ABI, AGETHER_8004_VALIDATION_MODULE_ABI, AGETHER_HOOK_MULTIPLEXER_ABI, AgentIdentityClient, type AgentIdentityClientOptions, AgentNotApprovedError, AgetherClient, type AgetherClientOptions, type AgetherConfig, AgetherError, type AgetherSigner, type AgetherViemWallet, type BalancesResult, type BorrowResult, ChainId, type ContractAddresses, type DepositAndBorrowResult, type DepositResult, ENTRYPOINT_V07_ABI, ERC20_ABI, ERC8004_VALIDATION_MODULE_ABI, HOOK_MULTIPLEXER_ABI, IDENTITY_REGISTRY_ABI, InsufficientBalanceError, MORPHO_BLUE_ABI, type MarketFilter, MorphoClient, type MorphoClientConfig, type MorphoMarketInfo, type MorphoMarketParams, type MorphoPosition, type PayFromYieldResult, type PaymentRequirements, type PositionResult, type RegisterResult, type RepayResult, type RetryOptions, SAFE7579_ACCOUNT_ABI, SAFE_AGENT_FACTORY_ABI, type ScoreAttestation, type ScoreResult, ScoringClient, type ScoringClientConfig, ScoringRejectedError, type SpendingTracker, type StatusResult, type SupplyAssetResult, type SupplyPositionResult, type TransactionResult, VALIDATION_REGISTRY_ABI, type WithdrawFromAccountResult, type WithdrawResult, type WithdrawSupplyResult, X402Client, type X402Config, type X402PaymentRequest, type X402PaymentResult, type X402Response, bpsToRate, createConfig, formatAPR, formatAddress, formatHealthFactor, formatPercent, formatTimestamp, formatUSD, formatUnits, getContractAddresses, getDefaultConfig, getMorphoBlueAddress, getUSDCAddress, parseUnits, rateToBps, withRetry };
1672
+ export { ACCOUNT_FACTORY_ABI, AGENT_REPUTATION_ABI, AGETHER_4337_FACTORY_ABI, AGETHER_8004_SCORER_ABI, AGETHER_8004_VALIDATION_MODULE_ABI, AGETHER_HOOK_MULTIPLEXER_ABI, AgentIdentityClient, type AgentIdentityClientOptions, AgentNotApprovedError, AgetherClient, type AgetherClientOptions, type AgetherConfig, AgetherError, type AgetherSigner, type AgetherViemWallet, type BalancesResult, type BorrowResult, ChainId, type ContractAddresses, type DepositAndBorrowResult, type DepositResult, ENTRYPOINT_V07_ABI, ERC20_ABI, ERC8004_VALIDATION_MODULE_ABI, HOOK_MULTIPLEXER_ABI, IDENTITY_REGISTRY_ABI, InsufficientBalanceError, type LeverageIteration, type LeverageResult, MORPHO_BLUE_ABI, type MarketFilter, MorphoClient, type MorphoClientConfig, type MorphoMarketInfo, type MorphoMarketParams, type MorphoPosition, type PayFromYieldResult, type PaymentRequirements, type PositionResult, type RegisterResult, type RepayResult, type RetryOptions, SAFE7579_ACCOUNT_ABI, SAFE_AGENT_FACTORY_ABI, type ScoreAttestation, type ScoreResult, ScoringClient, type ScoringClientConfig, ScoringRejectedError, type SpendingTracker, type StatusResult, type SupplyAssetResult, type SupplyPositionResult, type TransactionResult, VALIDATION_REGISTRY_ABI, type WithdrawFromAccountResult, type WithdrawResult, type WithdrawSupplyResult, X402Client, type X402Config, type X402PaymentRequest, type X402PaymentResult, type X402Response, type YieldSpreadResult, bpsToRate, createConfig, formatAPR, formatAddress, formatHealthFactor, formatPercent, formatTimestamp, formatUSD, formatUnits, getContractAddresses, getDefaultConfig, getMorphoBlueAddress, getUSDCAddress, parseUnits, rateToBps, withRetry };