@agether/sdk 2.18.0 → 2.18.2

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
@@ -600,7 +600,7 @@ var init_MorphoClient = __esm({
600
600
  init_retry();
601
601
  init_types();
602
602
  init_abis();
603
- MORPHO_API_URL = "https://api.morpho.org/graphql";
603
+ MORPHO_API_URL = "https://blue-api.morpho.org/graphql";
604
604
  morphoIface = new import_ethers2.ethers.Interface(MORPHO_BLUE_ABI);
605
605
  erc20Iface = new import_ethers2.ethers.Interface(ERC20_ABI);
606
606
  MorphoClient = class _MorphoClient extends AgentAccountClient {
@@ -648,7 +648,7 @@ var init_MorphoClient = __esm({
648
648
  first: 500
649
649
  orderBy: SupplyAssetsUsd
650
650
  orderDirection: Desc
651
- where: { chainId_in: [${chainId}] }
651
+ where: { chainId_in: [${chainId}], whitelisted: true }
652
652
  ) {
653
653
  items {
654
654
  uniqueKey
@@ -1057,7 +1057,7 @@ var init_MorphoClient = __esm({
1057
1057
  first: 100
1058
1058
  orderBy: SupplyAssetsUsd
1059
1059
  orderDirection: Desc
1060
- where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter}${searchClause} }
1060
+ where: { chainId_in: [${chainId}], whitelisted: true${loanFilter}${collateralFilter}${searchClause} }
1061
1061
  ) {
1062
1062
  items {
1063
1063
  uniqueKey
@@ -1067,6 +1067,8 @@ var init_MorphoClient = __esm({
1067
1067
  state {
1068
1068
  borrowAssets
1069
1069
  supplyAssets
1070
+ supplyAssetsUsd
1071
+ borrowAssetsUsd
1070
1072
  utilization
1071
1073
  supplyApy
1072
1074
  borrowApy
@@ -1097,8 +1099,8 @@ var init_MorphoClient = __esm({
1097
1099
  supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
1098
1100
  borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
1099
1101
  utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
1100
- totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
1101
- totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
1102
+ totalSupplyUsd: m.state?.supplyAssetsUsd ? Number(m.state.supplyAssetsUsd) : m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
1103
+ totalBorrowUsd: m.state?.borrowAssetsUsd ? Number(m.state.borrowAssetsUsd) : m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
1102
1104
  lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
1103
1105
  marketId: m.uniqueKey
1104
1106
  };
@@ -1136,6 +1138,8 @@ var init_MorphoClient = __esm({
1136
1138
  state {
1137
1139
  borrowAssets
1138
1140
  supplyAssets
1141
+ supplyAssetsUsd
1142
+ borrowAssetsUsd
1139
1143
  utilization
1140
1144
  supplyApy
1141
1145
  borrowApy
@@ -1338,7 +1342,7 @@ var init_MorphoClient = __esm({
1338
1342
  } else {
1339
1343
  try {
1340
1344
  const params = await this.findMarketForCollateral(collateralSymbol);
1341
- const loanDecimals = await this._getLoanTokenDecimals(params);
1345
+ const loanDecimals = market.loanDecimals;
1342
1346
  const oracleContract = new import_ethers2.Contract(params.oracle, [
1343
1347
  "function price() view returns (uint256)"
1344
1348
  ], this.provider);
@@ -1881,8 +1885,8 @@ var init_MorphoClient = __esm({
1881
1885
  const { params: p } = await this._findActiveMarket();
1882
1886
  params = p;
1883
1887
  }
1884
- const loanTokenAddr = params.loanToken;
1885
1888
  const loanDecimals = await this._getLoanTokenDecimals(params);
1889
+ const loanTokenAddr = params.loanToken;
1886
1890
  let repayAssets;
1887
1891
  let repayShares;
1888
1892
  let approveAmount;
@@ -2260,6 +2264,84 @@ var init_MorphoClient = __esm({
2260
2264
  // ────────────────────────────────────────────────────────────
2261
2265
  // ERC-4337 UserOp helpers (Safe + Safe7579 + EntryPoint v0.7)
2262
2266
  // ────────────────────────────────────────────────────────────
2267
+ /**
2268
+ * Repay all debt and withdraw all collateral in one atomic batch.
2269
+ * Full position exit: approve + repay(shares) + withdrawCollateral → 1 UserOp.
2270
+ */
2271
+ async repayAndWithdraw(tokenSymbol, loanTokenSymbol, toEoa = true) {
2272
+ const acctAddr = await this.getAccountAddress();
2273
+ const morphoAddr = this.config.contracts.morphoBlue;
2274
+ let params;
2275
+ let colSymbol;
2276
+ if (tokenSymbol) {
2277
+ params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
2278
+ colSymbol = tokenSymbol;
2279
+ } else {
2280
+ const active = await this._findActiveMarket();
2281
+ params = active.params;
2282
+ colSymbol = active.symbol;
2283
+ }
2284
+ const colInfo = await this._resolveToken(colSymbol);
2285
+ const dest = toEoa ? await this.getSignerAddress() : acctAddr;
2286
+ const marketId = import_ethers2.ethers.keccak256(import_ethers2.ethers.AbiCoder.defaultAbiCoder().encode(
2287
+ ["address", "address", "address", "address", "uint256"],
2288
+ [params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
2289
+ ));
2290
+ const pos = await this.morphoBlue.position(marketId, acctAddr);
2291
+ const borrowShares = BigInt(pos.borrowShares);
2292
+ const collateralAmount = BigInt(pos.collateral);
2293
+ if (collateralAmount === 0n) {
2294
+ throw new AgetherError("No collateral to withdraw", "NO_COLLATERAL");
2295
+ }
2296
+ const targets = [];
2297
+ const values = [];
2298
+ const datas = [];
2299
+ if (borrowShares > 0n) {
2300
+ const onChainMkt = await this.morphoBlue.market(marketId);
2301
+ const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
2302
+ const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
2303
+ const estimatedDebt = totalBorrowShares > 0n ? borrowShares * totalBorrowAssets / totalBorrowShares + 100n : 0n;
2304
+ const loanContract = new import_ethers2.Contract(params.loanToken, ERC20_ABI, this._signer);
2305
+ const acctBalance = await loanContract.balanceOf(acctAddr);
2306
+ if (acctBalance < estimatedDebt) {
2307
+ const shortfall = estimatedDebt - acctBalance;
2308
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
2309
+ if (eoaBalance >= shortfall) {
2310
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
2311
+ await transferTx.wait();
2312
+ this._refreshSigner();
2313
+ }
2314
+ }
2315
+ targets.push(params.loanToken);
2316
+ values.push(0n);
2317
+ datas.push(erc20Iface.encodeFunctionData("approve", [morphoAddr, estimatedDebt]));
2318
+ targets.push(morphoAddr);
2319
+ values.push(0n);
2320
+ datas.push(morphoIface.encodeFunctionData("repay", [
2321
+ this._toTuple(params),
2322
+ 0n,
2323
+ borrowShares,
2324
+ acctAddr,
2325
+ "0x"
2326
+ ]));
2327
+ }
2328
+ targets.push(morphoAddr);
2329
+ values.push(0n);
2330
+ datas.push(morphoIface.encodeFunctionData("withdrawCollateral", [
2331
+ this._toTuple(params),
2332
+ collateralAmount,
2333
+ acctAddr,
2334
+ dest
2335
+ ]));
2336
+ const receipt = await this.executeBatch(targets, values, datas);
2337
+ return {
2338
+ tx: receipt.hash,
2339
+ repaid: borrowShares > 0n ? "all" : "0",
2340
+ withdrawn: import_ethers2.ethers.formatUnits(collateralAmount, colInfo.decimals),
2341
+ collateralToken: colSymbol,
2342
+ loanToken: this._tokenCache.get(params.loanToken.toLowerCase())?.symbol ?? "unknown"
2343
+ };
2344
+ }
2263
2345
  /** Convert MorphoMarketParams to Solidity tuple. */
2264
2346
  _toTuple(p) {
2265
2347
  return [p.loanToken, p.collateralToken, p.oracle, p.irm, p.lltv];
package/dist/index.d.mts CHANGED
@@ -1002,6 +1002,17 @@ declare class MorphoClient extends AgentAccountClient {
1002
1002
  * responsibility.
1003
1003
  */
1004
1004
  private _refreshSigner;
1005
+ /**
1006
+ * Repay all debt and withdraw all collateral in one atomic batch.
1007
+ * Full position exit: approve + repay(shares) + withdrawCollateral → 1 UserOp.
1008
+ */
1009
+ repayAndWithdraw(tokenSymbol?: string, loanTokenSymbol?: string, toEoa?: boolean): Promise<{
1010
+ tx: string;
1011
+ repaid: string;
1012
+ withdrawn: string;
1013
+ collateralToken: string;
1014
+ loanToken: string;
1015
+ }>;
1005
1016
  /** Convert MorphoMarketParams to Solidity tuple. */
1006
1017
  private _toTuple;
1007
1018
  /** Find the first market where the agent has collateral deposited. */
@@ -1128,6 +1139,67 @@ declare class AaveClient extends AgentAccountClient {
1128
1139
  * Supply an asset and borrow in one atomic batch operation.
1129
1140
  */
1130
1141
  supplyAndBorrow(supplyAsset: string, supplyAmount: string, borrowAsset: string, borrowAmount: string): Promise<ethers.TransactionReceipt>;
1142
+ /**
1143
+ * Repay all debt and withdraw all collateral in one atomic batch.
1144
+ * Full position exit: approve + repay + withdraw → 1 UserOp.
1145
+ */
1146
+ repayAndWithdraw(repayAsset: string, withdrawAsset: string, to?: string): Promise<ethers.TransactionReceipt>;
1147
+ /**
1148
+ * Search reserves by token symbol (case-insensitive partial match).
1149
+ */
1150
+ searchReserves(query: string): Promise<AaveReserveInfo[]>;
1151
+ /**
1152
+ * Get borrowing options: which tokens can be borrowed, with what collateral.
1153
+ * If collateral symbol given, shows what you can borrow using that as collateral.
1154
+ */
1155
+ getBorrowingOptions(collateral?: string): Promise<Array<{
1156
+ collateral: string;
1157
+ borrowable: AaveReserveInfo[];
1158
+ collateralBalance: number;
1159
+ collateralValueUsd: number;
1160
+ maxBorrowUsd: number;
1161
+ }>>;
1162
+ /**
1163
+ * Calculate max additional borrowable in USD given current positions.
1164
+ */
1165
+ getMaxBorrowable(): Promise<{
1166
+ availableBorrowUsd: number;
1167
+ currentLtv: number;
1168
+ liquidationThreshold: number;
1169
+ totalCollateralUsd: number;
1170
+ totalDebtUsd: number;
1171
+ }>;
1172
+ /**
1173
+ * Scan wallet for tokens that exist as Aave reserves.
1174
+ */
1175
+ getWalletTokens(): Promise<Array<{
1176
+ symbol: string;
1177
+ address: string;
1178
+ eoaBalance: number;
1179
+ safeBalance: number;
1180
+ priceUsd: number;
1181
+ valueUsd: number;
1182
+ canSupply: boolean;
1183
+ canBorrow: boolean;
1184
+ }>>;
1185
+ /**
1186
+ * Get yield spread for LST carry trades on Aave.
1187
+ * Compares LST native yield vs Aave borrow rates.
1188
+ */
1189
+ getYieldSpread(_minLiquidity?: number): Promise<Array<{
1190
+ collateralToken: string;
1191
+ loanToken: string;
1192
+ collateralYield: number;
1193
+ borrowRate: number;
1194
+ netSpread: number;
1195
+ profitable: boolean;
1196
+ collateralLtv: number;
1197
+ maxSafeLeverage: number;
1198
+ }>>;
1199
+ private static readonly LST_PROJECT_MAP;
1200
+ private static _lstYieldCache;
1201
+ private static readonly LST_CACHE_TTL;
1202
+ private _getLstYields;
1131
1203
  private _resolveReserve;
1132
1204
  }
1133
1205
 
package/dist/index.d.ts CHANGED
@@ -1002,6 +1002,17 @@ declare class MorphoClient extends AgentAccountClient {
1002
1002
  * responsibility.
1003
1003
  */
1004
1004
  private _refreshSigner;
1005
+ /**
1006
+ * Repay all debt and withdraw all collateral in one atomic batch.
1007
+ * Full position exit: approve + repay(shares) + withdrawCollateral → 1 UserOp.
1008
+ */
1009
+ repayAndWithdraw(tokenSymbol?: string, loanTokenSymbol?: string, toEoa?: boolean): Promise<{
1010
+ tx: string;
1011
+ repaid: string;
1012
+ withdrawn: string;
1013
+ collateralToken: string;
1014
+ loanToken: string;
1015
+ }>;
1005
1016
  /** Convert MorphoMarketParams to Solidity tuple. */
1006
1017
  private _toTuple;
1007
1018
  /** Find the first market where the agent has collateral deposited. */
@@ -1128,6 +1139,67 @@ declare class AaveClient extends AgentAccountClient {
1128
1139
  * Supply an asset and borrow in one atomic batch operation.
1129
1140
  */
1130
1141
  supplyAndBorrow(supplyAsset: string, supplyAmount: string, borrowAsset: string, borrowAmount: string): Promise<ethers.TransactionReceipt>;
1142
+ /**
1143
+ * Repay all debt and withdraw all collateral in one atomic batch.
1144
+ * Full position exit: approve + repay + withdraw → 1 UserOp.
1145
+ */
1146
+ repayAndWithdraw(repayAsset: string, withdrawAsset: string, to?: string): Promise<ethers.TransactionReceipt>;
1147
+ /**
1148
+ * Search reserves by token symbol (case-insensitive partial match).
1149
+ */
1150
+ searchReserves(query: string): Promise<AaveReserveInfo[]>;
1151
+ /**
1152
+ * Get borrowing options: which tokens can be borrowed, with what collateral.
1153
+ * If collateral symbol given, shows what you can borrow using that as collateral.
1154
+ */
1155
+ getBorrowingOptions(collateral?: string): Promise<Array<{
1156
+ collateral: string;
1157
+ borrowable: AaveReserveInfo[];
1158
+ collateralBalance: number;
1159
+ collateralValueUsd: number;
1160
+ maxBorrowUsd: number;
1161
+ }>>;
1162
+ /**
1163
+ * Calculate max additional borrowable in USD given current positions.
1164
+ */
1165
+ getMaxBorrowable(): Promise<{
1166
+ availableBorrowUsd: number;
1167
+ currentLtv: number;
1168
+ liquidationThreshold: number;
1169
+ totalCollateralUsd: number;
1170
+ totalDebtUsd: number;
1171
+ }>;
1172
+ /**
1173
+ * Scan wallet for tokens that exist as Aave reserves.
1174
+ */
1175
+ getWalletTokens(): Promise<Array<{
1176
+ symbol: string;
1177
+ address: string;
1178
+ eoaBalance: number;
1179
+ safeBalance: number;
1180
+ priceUsd: number;
1181
+ valueUsd: number;
1182
+ canSupply: boolean;
1183
+ canBorrow: boolean;
1184
+ }>>;
1185
+ /**
1186
+ * Get yield spread for LST carry trades on Aave.
1187
+ * Compares LST native yield vs Aave borrow rates.
1188
+ */
1189
+ getYieldSpread(_minLiquidity?: number): Promise<Array<{
1190
+ collateralToken: string;
1191
+ loanToken: string;
1192
+ collateralYield: number;
1193
+ borrowRate: number;
1194
+ netSpread: number;
1195
+ profitable: boolean;
1196
+ collateralLtv: number;
1197
+ maxSafeLeverage: number;
1198
+ }>>;
1199
+ private static readonly LST_PROJECT_MAP;
1200
+ private static _lstYieldCache;
1201
+ private static readonly LST_CACHE_TTL;
1202
+ private _getLstYields;
1131
1203
  private _resolveReserve;
1132
1204
  }
1133
1205
 
package/dist/index.js CHANGED
@@ -1570,7 +1570,7 @@ async function withRetry(fn, options = {}) {
1570
1570
  }
1571
1571
 
1572
1572
  // src/clients/MorphoClient.ts
1573
- var MORPHO_API_URL2 = "https://api.morpho.org/graphql";
1573
+ var MORPHO_API_URL2 = "https://blue-api.morpho.org/graphql";
1574
1574
  var morphoIface = new import_ethers3.ethers.Interface(MORPHO_BLUE_ABI);
1575
1575
  var erc20Iface2 = new import_ethers3.ethers.Interface(ERC20_ABI);
1576
1576
  var _MorphoClient = class _MorphoClient extends AgentAccountClient {
@@ -1618,7 +1618,7 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
1618
1618
  first: 500
1619
1619
  orderBy: SupplyAssetsUsd
1620
1620
  orderDirection: Desc
1621
- where: { chainId_in: [${chainId}] }
1621
+ where: { chainId_in: [${chainId}], whitelisted: true }
1622
1622
  ) {
1623
1623
  items {
1624
1624
  uniqueKey
@@ -2027,7 +2027,7 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
2027
2027
  first: 100
2028
2028
  orderBy: SupplyAssetsUsd
2029
2029
  orderDirection: Desc
2030
- where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter}${searchClause} }
2030
+ where: { chainId_in: [${chainId}], whitelisted: true${loanFilter}${collateralFilter}${searchClause} }
2031
2031
  ) {
2032
2032
  items {
2033
2033
  uniqueKey
@@ -2037,6 +2037,8 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
2037
2037
  state {
2038
2038
  borrowAssets
2039
2039
  supplyAssets
2040
+ supplyAssetsUsd
2041
+ borrowAssetsUsd
2040
2042
  utilization
2041
2043
  supplyApy
2042
2044
  borrowApy
@@ -2067,8 +2069,8 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
2067
2069
  supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
2068
2070
  borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
2069
2071
  utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
2070
- totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
2071
- totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
2072
+ totalSupplyUsd: m.state?.supplyAssetsUsd ? Number(m.state.supplyAssetsUsd) : m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
2073
+ totalBorrowUsd: m.state?.borrowAssetsUsd ? Number(m.state.borrowAssetsUsd) : m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
2072
2074
  lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
2073
2075
  marketId: m.uniqueKey
2074
2076
  };
@@ -2106,6 +2108,8 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
2106
2108
  state {
2107
2109
  borrowAssets
2108
2110
  supplyAssets
2111
+ supplyAssetsUsd
2112
+ borrowAssetsUsd
2109
2113
  utilization
2110
2114
  supplyApy
2111
2115
  borrowApy
@@ -2308,7 +2312,7 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
2308
2312
  } else {
2309
2313
  try {
2310
2314
  const params = await this.findMarketForCollateral(collateralSymbol);
2311
- const loanDecimals = await this._getLoanTokenDecimals(params);
2315
+ const loanDecimals = market.loanDecimals;
2312
2316
  const oracleContract = new import_ethers3.Contract(params.oracle, [
2313
2317
  "function price() view returns (uint256)"
2314
2318
  ], this.provider);
@@ -2851,8 +2855,8 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
2851
2855
  const { params: p } = await this._findActiveMarket();
2852
2856
  params = p;
2853
2857
  }
2854
- const loanTokenAddr = params.loanToken;
2855
2858
  const loanDecimals = await this._getLoanTokenDecimals(params);
2859
+ const loanTokenAddr = params.loanToken;
2856
2860
  let repayAssets;
2857
2861
  let repayShares;
2858
2862
  let approveAmount;
@@ -3200,6 +3204,84 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
3200
3204
  // ────────────────────────────────────────────────────────────
3201
3205
  // ERC-4337 UserOp helpers (Safe + Safe7579 + EntryPoint v0.7)
3202
3206
  // ────────────────────────────────────────────────────────────
3207
+ /**
3208
+ * Repay all debt and withdraw all collateral in one atomic batch.
3209
+ * Full position exit: approve + repay(shares) + withdrawCollateral → 1 UserOp.
3210
+ */
3211
+ async repayAndWithdraw(tokenSymbol, loanTokenSymbol, toEoa = true) {
3212
+ const acctAddr = await this.getAccountAddress();
3213
+ const morphoAddr = this.config.contracts.morphoBlue;
3214
+ let params;
3215
+ let colSymbol;
3216
+ if (tokenSymbol) {
3217
+ params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
3218
+ colSymbol = tokenSymbol;
3219
+ } else {
3220
+ const active = await this._findActiveMarket();
3221
+ params = active.params;
3222
+ colSymbol = active.symbol;
3223
+ }
3224
+ const colInfo = await this._resolveToken(colSymbol);
3225
+ const dest = toEoa ? await this.getSignerAddress() : acctAddr;
3226
+ const marketId = import_ethers3.ethers.keccak256(import_ethers3.ethers.AbiCoder.defaultAbiCoder().encode(
3227
+ ["address", "address", "address", "address", "uint256"],
3228
+ [params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
3229
+ ));
3230
+ const pos = await this.morphoBlue.position(marketId, acctAddr);
3231
+ const borrowShares = BigInt(pos.borrowShares);
3232
+ const collateralAmount = BigInt(pos.collateral);
3233
+ if (collateralAmount === 0n) {
3234
+ throw new AgetherError("No collateral to withdraw", "NO_COLLATERAL");
3235
+ }
3236
+ const targets = [];
3237
+ const values = [];
3238
+ const datas = [];
3239
+ if (borrowShares > 0n) {
3240
+ const onChainMkt = await this.morphoBlue.market(marketId);
3241
+ const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
3242
+ const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
3243
+ const estimatedDebt = totalBorrowShares > 0n ? borrowShares * totalBorrowAssets / totalBorrowShares + 100n : 0n;
3244
+ const loanContract = new import_ethers3.Contract(params.loanToken, ERC20_ABI, this._signer);
3245
+ const acctBalance = await loanContract.balanceOf(acctAddr);
3246
+ if (acctBalance < estimatedDebt) {
3247
+ const shortfall = estimatedDebt - acctBalance;
3248
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
3249
+ if (eoaBalance >= shortfall) {
3250
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
3251
+ await transferTx.wait();
3252
+ this._refreshSigner();
3253
+ }
3254
+ }
3255
+ targets.push(params.loanToken);
3256
+ values.push(0n);
3257
+ datas.push(erc20Iface2.encodeFunctionData("approve", [morphoAddr, estimatedDebt]));
3258
+ targets.push(morphoAddr);
3259
+ values.push(0n);
3260
+ datas.push(morphoIface.encodeFunctionData("repay", [
3261
+ this._toTuple(params),
3262
+ 0n,
3263
+ borrowShares,
3264
+ acctAddr,
3265
+ "0x"
3266
+ ]));
3267
+ }
3268
+ targets.push(morphoAddr);
3269
+ values.push(0n);
3270
+ datas.push(morphoIface.encodeFunctionData("withdrawCollateral", [
3271
+ this._toTuple(params),
3272
+ collateralAmount,
3273
+ acctAddr,
3274
+ dest
3275
+ ]));
3276
+ const receipt = await this.executeBatch(targets, values, datas);
3277
+ return {
3278
+ tx: receipt.hash,
3279
+ repaid: borrowShares > 0n ? "all" : "0",
3280
+ withdrawn: import_ethers3.ethers.formatUnits(collateralAmount, colInfo.decimals),
3281
+ collateralToken: colSymbol,
3282
+ loanToken: this._tokenCache.get(params.loanToken.toLowerCase())?.symbol ?? "unknown"
3283
+ };
3284
+ }
3203
3285
  /** Convert MorphoMarketParams to Solidity tuple. */
3204
3286
  _toTuple(p) {
3205
3287
  return [p.loanToken, p.collateralToken, p.oracle, p.irm, p.lltv];
@@ -3791,6 +3873,199 @@ var _AaveClient = class _AaveClient extends AgentAccountClient {
3791
3873
  );
3792
3874
  }
3793
3875
  // ─── Helpers ──────────────────────────────────────────────────────────
3876
+ /**
3877
+ * Repay all debt and withdraw all collateral in one atomic batch.
3878
+ * Full position exit: approve + repay + withdraw → 1 UserOp.
3879
+ */
3880
+ async repayAndWithdraw(repayAsset, withdrawAsset, to) {
3881
+ const repayReserve = this._resolveReserve(repayAsset);
3882
+ const withdrawReserve = this._resolveReserve(withdrawAsset);
3883
+ const safeAddr = await this.getAccountAddress();
3884
+ const poolAddr = await this._pool.getAddress();
3885
+ const recipient = to ?? safeAddr;
3886
+ const targets = [];
3887
+ const values = [];
3888
+ const datas = [];
3889
+ targets.push(repayReserve.address);
3890
+ values.push(0n);
3891
+ datas.push(this._erc20Iface.encodeFunctionData("approve", [poolAddr, import_ethers4.ethers.MaxUint256]));
3892
+ targets.push(poolAddr);
3893
+ values.push(0n);
3894
+ datas.push(this._poolIface.encodeFunctionData("repay", [
3895
+ repayReserve.address,
3896
+ import_ethers4.ethers.MaxUint256,
3897
+ 2,
3898
+ safeAddr
3899
+ ]));
3900
+ targets.push(poolAddr);
3901
+ values.push(0n);
3902
+ datas.push(this._poolIface.encodeFunctionData("withdraw", [
3903
+ withdrawReserve.address,
3904
+ import_ethers4.ethers.MaxUint256,
3905
+ recipient
3906
+ ]));
3907
+ return this.executeBatch(targets, values, datas);
3908
+ }
3909
+ // ─── Market Discovery ─────────────────────────────────────────────────
3910
+ /**
3911
+ * Search reserves by token symbol (case-insensitive partial match).
3912
+ */
3913
+ async searchReserves(query) {
3914
+ const reserves = await this.getReserves();
3915
+ const q = query.toLowerCase();
3916
+ return reserves.filter(
3917
+ (r) => r.symbol.toLowerCase().includes(q) || r.address.toLowerCase() === q
3918
+ );
3919
+ }
3920
+ /**
3921
+ * Get borrowing options: which tokens can be borrowed, with what collateral.
3922
+ * If collateral symbol given, shows what you can borrow using that as collateral.
3923
+ */
3924
+ async getBorrowingOptions(collateral) {
3925
+ const reserves = await this.getReserves();
3926
+ const safeAddr = await this.getAccountAddress();
3927
+ const results = [];
3928
+ const eoaAddr = this.getWalletAddress();
3929
+ for (const reserve of reserves) {
3930
+ if (!reserve.isActive || reserve.ltv === 0) continue;
3931
+ if (collateral && reserve.symbol.toLowerCase() !== collateral.toLowerCase()) continue;
3932
+ const token = new import_ethers4.Contract(reserve.address, [
3933
+ "function balanceOf(address) view returns (uint256)"
3934
+ ], this.provider);
3935
+ const [eoaBal, safeBal] = await Promise.all([
3936
+ token.balanceOf(eoaAddr).catch(() => 0n),
3937
+ token.balanceOf(safeAddr).catch(() => 0n)
3938
+ ]);
3939
+ const totalBal = Number(eoaBal + safeBal) / 10 ** reserve.decimals;
3940
+ if (totalBal <= 0 && !collateral) continue;
3941
+ const collateralValueUsd = totalBal * reserve.priceUsd;
3942
+ const maxBorrowUsd = collateralValueUsd * (reserve.ltv / 100);
3943
+ const borrowable = reserves.filter((r) => r.borrowingEnabled && r.isActive && r.symbol !== reserve.symbol);
3944
+ results.push({
3945
+ collateral: reserve.symbol,
3946
+ borrowable,
3947
+ collateralBalance: totalBal,
3948
+ collateralValueUsd,
3949
+ maxBorrowUsd
3950
+ });
3951
+ }
3952
+ return results;
3953
+ }
3954
+ /**
3955
+ * Calculate max additional borrowable in USD given current positions.
3956
+ */
3957
+ async getMaxBorrowable() {
3958
+ const data = await this.getAccountData();
3959
+ return {
3960
+ availableBorrowUsd: data.availableBorrowUsd,
3961
+ currentLtv: data.currentLtv,
3962
+ liquidationThreshold: data.liquidationThreshold,
3963
+ totalCollateralUsd: data.totalCollateralUsd,
3964
+ totalDebtUsd: data.totalDebtUsd
3965
+ };
3966
+ }
3967
+ /**
3968
+ * Scan wallet for tokens that exist as Aave reserves.
3969
+ */
3970
+ async getWalletTokens() {
3971
+ const reserves = await this.getReserves();
3972
+ const safeAddr = await this.getAccountAddress();
3973
+ const eoaAddr = this.getWalletAddress();
3974
+ const results = [];
3975
+ const promises = reserves.map(async (reserve) => {
3976
+ const token = new import_ethers4.Contract(reserve.address, [
3977
+ "function balanceOf(address) view returns (uint256)"
3978
+ ], this.provider);
3979
+ const [eoaBal, safeBal] = await Promise.all([
3980
+ token.balanceOf(eoaAddr).catch(() => 0n),
3981
+ token.balanceOf(safeAddr).catch(() => 0n)
3982
+ ]);
3983
+ const eoaBalance = Number(eoaBal) / 10 ** reserve.decimals;
3984
+ const safeBalance = Number(safeBal) / 10 ** reserve.decimals;
3985
+ if (eoaBalance > 0 || safeBalance > 0) {
3986
+ return {
3987
+ symbol: reserve.symbol,
3988
+ address: reserve.address,
3989
+ eoaBalance,
3990
+ safeBalance,
3991
+ priceUsd: reserve.priceUsd,
3992
+ valueUsd: (eoaBalance + safeBalance) * reserve.priceUsd,
3993
+ canSupply: reserve.isActive,
3994
+ canBorrow: reserve.borrowingEnabled
3995
+ };
3996
+ }
3997
+ return null;
3998
+ });
3999
+ const settled = await Promise.all(promises);
4000
+ for (const r of settled) {
4001
+ if (r) results.push(r);
4002
+ }
4003
+ return results;
4004
+ }
4005
+ /**
4006
+ * Get yield spread for LST carry trades on Aave.
4007
+ * Compares LST native yield vs Aave borrow rates.
4008
+ */
4009
+ async getYieldSpread(_minLiquidity = 0) {
4010
+ const reserves = await this.getReserves();
4011
+ const lstYields = await this._getLstYields();
4012
+ if (Object.keys(lstYields).length === 0) return [];
4013
+ const results = [];
4014
+ for (const [symbol, yieldApy] of Object.entries(lstYields)) {
4015
+ const collateralReserve = reserves.find((r) => r.symbol.toLowerCase() === symbol.toLowerCase());
4016
+ if (!collateralReserve || !collateralReserve.isActive || collateralReserve.ltv === 0) continue;
4017
+ for (const loanReserve of reserves) {
4018
+ if (!loanReserve.borrowingEnabled || !loanReserve.isActive) continue;
4019
+ if (loanReserve.symbol === collateralReserve.symbol) continue;
4020
+ const netSpread = yieldApy - loanReserve.borrowApy;
4021
+ const safeLtv = collateralReserve.ltv / 100 * 0.8;
4022
+ const maxLeverage = 1 / (1 - safeLtv);
4023
+ results.push({
4024
+ collateralToken: collateralReserve.symbol,
4025
+ loanToken: loanReserve.symbol,
4026
+ collateralYield: yieldApy,
4027
+ borrowRate: loanReserve.borrowApy,
4028
+ netSpread,
4029
+ profitable: netSpread > 0,
4030
+ collateralLtv: collateralReserve.ltv,
4031
+ maxSafeLeverage: parseFloat(maxLeverage.toFixed(2))
4032
+ });
4033
+ }
4034
+ }
4035
+ return results.sort((a, b) => b.netSpread - a.netSpread);
4036
+ }
4037
+ async _getLstYields() {
4038
+ if (_AaveClient._lstYieldCache && Date.now() - _AaveClient._lstYieldCache.ts < _AaveClient.LST_CACHE_TTL) {
4039
+ return _AaveClient._lstYieldCache.data;
4040
+ }
4041
+ const yields = {};
4042
+ try {
4043
+ const resp = await fetch("https://yields.llama.fi/pools", {
4044
+ headers: { "User-Agent": "agether-sdk/1.0" },
4045
+ signal: AbortSignal.timeout(1e4)
4046
+ });
4047
+ if (!resp.ok) return yields;
4048
+ const data = await resp.json();
4049
+ const pools = data?.data ?? [];
4050
+ for (const [morphoSymbol, mapping] of Object.entries(_AaveClient.LST_PROJECT_MAP)) {
4051
+ const matchingPools = pools.filter(
4052
+ (p) => p.project === mapping.project && p.symbol?.toUpperCase() === mapping.symbol && ["Ethereum", "Base"].includes(p.chain)
4053
+ );
4054
+ if (matchingPools.length > 0) {
4055
+ const best = matchingPools.reduce(
4056
+ (a, b) => (b.tvlUsd ?? 0) > (a.tvlUsd ?? 0) ? b : a
4057
+ );
4058
+ if (best.apy !== void 0 && best.apy > 0) {
4059
+ yields[morphoSymbol] = parseFloat(best.apy.toFixed(4));
4060
+ }
4061
+ }
4062
+ }
4063
+ } catch (e) {
4064
+ console.warn("[aave] Failed to fetch LST yields:", e instanceof Error ? e.message : e);
4065
+ }
4066
+ _AaveClient._lstYieldCache = { data: yields, ts: Date.now() };
4067
+ return yields;
4068
+ }
3794
4069
  _resolveReserve(symbolOrAddress) {
3795
4070
  const reserves = KNOWN_RESERVES[this._aaveChainId] ?? [];
3796
4071
  const bySymbol = reserves.find((r) => r.symbol.toLowerCase() === symbolOrAddress.toLowerCase());
@@ -3804,6 +4079,23 @@ var _AaveClient = class _AaveClient extends AgentAccountClient {
3804
4079
  }
3805
4080
  };
3806
4081
  _AaveClient.CACHE_TTL = 6e4;
4082
+ // ─── DeFi Llama LST Yields (shared logic) ─────────────────────────────
4083
+ _AaveClient.LST_PROJECT_MAP = {
4084
+ "wstETH": { project: "lido", symbol: "STETH" },
4085
+ "cbETH": { project: "coinbase-wrapped-staked-eth", symbol: "CBETH" },
4086
+ "rETH": { project: "rocket-pool", symbol: "RETH" },
4087
+ "weETH": { project: "ether.fi-stake", symbol: "WEETH" },
4088
+ "ezETH": { project: "renzo", symbol: "EZETH" },
4089
+ "rsETH": { project: "kelp", symbol: "RSETH" },
4090
+ "swETH": { project: "swell-liquid-staking", symbol: "SWETH" },
4091
+ "mETH": { project: "meth-protocol", symbol: "METH" },
4092
+ "sfrxETH": { project: "frax-ether", symbol: "SFRXETH" },
4093
+ "oETH": { project: "origin-ether", symbol: "OETH" },
4094
+ "ETHx": { project: "stader", symbol: "ETHX" },
4095
+ "wBETH": { project: "binance-staked-eth", symbol: "WBETH" }
4096
+ };
4097
+ _AaveClient._lstYieldCache = null;
4098
+ _AaveClient.LST_CACHE_TTL = 30 * 60 * 1e3;
3807
4099
  var AaveClient = _AaveClient;
3808
4100
 
3809
4101
  // src/clients/ScoringClient.ts
package/dist/index.mjs CHANGED
@@ -1492,7 +1492,7 @@ async function withRetry(fn, options = {}) {
1492
1492
  }
1493
1493
 
1494
1494
  // src/clients/MorphoClient.ts
1495
- var MORPHO_API_URL2 = "https://api.morpho.org/graphql";
1495
+ var MORPHO_API_URL2 = "https://blue-api.morpho.org/graphql";
1496
1496
  var morphoIface = new ethers3.Interface(MORPHO_BLUE_ABI);
1497
1497
  var erc20Iface2 = new ethers3.Interface(ERC20_ABI);
1498
1498
  var _MorphoClient = class _MorphoClient extends AgentAccountClient {
@@ -1540,7 +1540,7 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
1540
1540
  first: 500
1541
1541
  orderBy: SupplyAssetsUsd
1542
1542
  orderDirection: Desc
1543
- where: { chainId_in: [${chainId}] }
1543
+ where: { chainId_in: [${chainId}], whitelisted: true }
1544
1544
  ) {
1545
1545
  items {
1546
1546
  uniqueKey
@@ -1949,7 +1949,7 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
1949
1949
  first: 100
1950
1950
  orderBy: SupplyAssetsUsd
1951
1951
  orderDirection: Desc
1952
- where: { chainId_in: [${chainId}]${loanFilter}${collateralFilter}${searchClause} }
1952
+ where: { chainId_in: [${chainId}], whitelisted: true${loanFilter}${collateralFilter}${searchClause} }
1953
1953
  ) {
1954
1954
  items {
1955
1955
  uniqueKey
@@ -1959,6 +1959,8 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
1959
1959
  state {
1960
1960
  borrowAssets
1961
1961
  supplyAssets
1962
+ supplyAssetsUsd
1963
+ borrowAssetsUsd
1962
1964
  utilization
1963
1965
  supplyApy
1964
1966
  borrowApy
@@ -1989,8 +1991,8 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
1989
1991
  supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
1990
1992
  borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
1991
1993
  utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
1992
- totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
1993
- totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
1994
+ totalSupplyUsd: m.state?.supplyAssetsUsd ? Number(m.state.supplyAssetsUsd) : m.state?.supplyAssets ? Number(m.state.supplyAssets) / 10 ** loanDecimals : 0,
1995
+ totalBorrowUsd: m.state?.borrowAssetsUsd ? Number(m.state.borrowAssetsUsd) : m.state?.borrowAssets ? Number(m.state.borrowAssets) / 10 ** loanDecimals : 0,
1994
1996
  lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
1995
1997
  marketId: m.uniqueKey
1996
1998
  };
@@ -2028,6 +2030,8 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
2028
2030
  state {
2029
2031
  borrowAssets
2030
2032
  supplyAssets
2033
+ supplyAssetsUsd
2034
+ borrowAssetsUsd
2031
2035
  utilization
2032
2036
  supplyApy
2033
2037
  borrowApy
@@ -2230,7 +2234,7 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
2230
2234
  } else {
2231
2235
  try {
2232
2236
  const params = await this.findMarketForCollateral(collateralSymbol);
2233
- const loanDecimals = await this._getLoanTokenDecimals(params);
2237
+ const loanDecimals = market.loanDecimals;
2234
2238
  const oracleContract = new Contract3(params.oracle, [
2235
2239
  "function price() view returns (uint256)"
2236
2240
  ], this.provider);
@@ -2773,8 +2777,8 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
2773
2777
  const { params: p } = await this._findActiveMarket();
2774
2778
  params = p;
2775
2779
  }
2776
- const loanTokenAddr = params.loanToken;
2777
2780
  const loanDecimals = await this._getLoanTokenDecimals(params);
2781
+ const loanTokenAddr = params.loanToken;
2778
2782
  let repayAssets;
2779
2783
  let repayShares;
2780
2784
  let approveAmount;
@@ -3122,6 +3126,84 @@ var _MorphoClient = class _MorphoClient extends AgentAccountClient {
3122
3126
  // ────────────────────────────────────────────────────────────
3123
3127
  // ERC-4337 UserOp helpers (Safe + Safe7579 + EntryPoint v0.7)
3124
3128
  // ────────────────────────────────────────────────────────────
3129
+ /**
3130
+ * Repay all debt and withdraw all collateral in one atomic batch.
3131
+ * Full position exit: approve + repay(shares) + withdrawCollateral → 1 UserOp.
3132
+ */
3133
+ async repayAndWithdraw(tokenSymbol, loanTokenSymbol, toEoa = true) {
3134
+ const acctAddr = await this.getAccountAddress();
3135
+ const morphoAddr = this.config.contracts.morphoBlue;
3136
+ let params;
3137
+ let colSymbol;
3138
+ if (tokenSymbol) {
3139
+ params = await this.findMarketForCollateral(tokenSymbol, loanTokenSymbol);
3140
+ colSymbol = tokenSymbol;
3141
+ } else {
3142
+ const active = await this._findActiveMarket();
3143
+ params = active.params;
3144
+ colSymbol = active.symbol;
3145
+ }
3146
+ const colInfo = await this._resolveToken(colSymbol);
3147
+ const dest = toEoa ? await this.getSignerAddress() : acctAddr;
3148
+ const marketId = ethers3.keccak256(ethers3.AbiCoder.defaultAbiCoder().encode(
3149
+ ["address", "address", "address", "address", "uint256"],
3150
+ [params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
3151
+ ));
3152
+ const pos = await this.morphoBlue.position(marketId, acctAddr);
3153
+ const borrowShares = BigInt(pos.borrowShares);
3154
+ const collateralAmount = BigInt(pos.collateral);
3155
+ if (collateralAmount === 0n) {
3156
+ throw new AgetherError("No collateral to withdraw", "NO_COLLATERAL");
3157
+ }
3158
+ const targets = [];
3159
+ const values = [];
3160
+ const datas = [];
3161
+ if (borrowShares > 0n) {
3162
+ const onChainMkt = await this.morphoBlue.market(marketId);
3163
+ const totalBorrowAssets = BigInt(onChainMkt.totalBorrowAssets);
3164
+ const totalBorrowShares = BigInt(onChainMkt.totalBorrowShares);
3165
+ const estimatedDebt = totalBorrowShares > 0n ? borrowShares * totalBorrowAssets / totalBorrowShares + 100n : 0n;
3166
+ const loanContract = new Contract3(params.loanToken, ERC20_ABI, this._signer);
3167
+ const acctBalance = await loanContract.balanceOf(acctAddr);
3168
+ if (acctBalance < estimatedDebt) {
3169
+ const shortfall = estimatedDebt - acctBalance;
3170
+ const eoaBalance = await loanContract.balanceOf(await this.getSignerAddress());
3171
+ if (eoaBalance >= shortfall) {
3172
+ const transferTx = await loanContract.transfer(acctAddr, shortfall);
3173
+ await transferTx.wait();
3174
+ this._refreshSigner();
3175
+ }
3176
+ }
3177
+ targets.push(params.loanToken);
3178
+ values.push(0n);
3179
+ datas.push(erc20Iface2.encodeFunctionData("approve", [morphoAddr, estimatedDebt]));
3180
+ targets.push(morphoAddr);
3181
+ values.push(0n);
3182
+ datas.push(morphoIface.encodeFunctionData("repay", [
3183
+ this._toTuple(params),
3184
+ 0n,
3185
+ borrowShares,
3186
+ acctAddr,
3187
+ "0x"
3188
+ ]));
3189
+ }
3190
+ targets.push(morphoAddr);
3191
+ values.push(0n);
3192
+ datas.push(morphoIface.encodeFunctionData("withdrawCollateral", [
3193
+ this._toTuple(params),
3194
+ collateralAmount,
3195
+ acctAddr,
3196
+ dest
3197
+ ]));
3198
+ const receipt = await this.executeBatch(targets, values, datas);
3199
+ return {
3200
+ tx: receipt.hash,
3201
+ repaid: borrowShares > 0n ? "all" : "0",
3202
+ withdrawn: ethers3.formatUnits(collateralAmount, colInfo.decimals),
3203
+ collateralToken: colSymbol,
3204
+ loanToken: this._tokenCache.get(params.loanToken.toLowerCase())?.symbol ?? "unknown"
3205
+ };
3206
+ }
3125
3207
  /** Convert MorphoMarketParams to Solidity tuple. */
3126
3208
  _toTuple(p) {
3127
3209
  return [p.loanToken, p.collateralToken, p.oracle, p.irm, p.lltv];
@@ -3713,6 +3795,199 @@ var _AaveClient = class _AaveClient extends AgentAccountClient {
3713
3795
  );
3714
3796
  }
3715
3797
  // ─── Helpers ──────────────────────────────────────────────────────────
3798
+ /**
3799
+ * Repay all debt and withdraw all collateral in one atomic batch.
3800
+ * Full position exit: approve + repay + withdraw → 1 UserOp.
3801
+ */
3802
+ async repayAndWithdraw(repayAsset, withdrawAsset, to) {
3803
+ const repayReserve = this._resolveReserve(repayAsset);
3804
+ const withdrawReserve = this._resolveReserve(withdrawAsset);
3805
+ const safeAddr = await this.getAccountAddress();
3806
+ const poolAddr = await this._pool.getAddress();
3807
+ const recipient = to ?? safeAddr;
3808
+ const targets = [];
3809
+ const values = [];
3810
+ const datas = [];
3811
+ targets.push(repayReserve.address);
3812
+ values.push(0n);
3813
+ datas.push(this._erc20Iface.encodeFunctionData("approve", [poolAddr, ethers4.MaxUint256]));
3814
+ targets.push(poolAddr);
3815
+ values.push(0n);
3816
+ datas.push(this._poolIface.encodeFunctionData("repay", [
3817
+ repayReserve.address,
3818
+ ethers4.MaxUint256,
3819
+ 2,
3820
+ safeAddr
3821
+ ]));
3822
+ targets.push(poolAddr);
3823
+ values.push(0n);
3824
+ datas.push(this._poolIface.encodeFunctionData("withdraw", [
3825
+ withdrawReserve.address,
3826
+ ethers4.MaxUint256,
3827
+ recipient
3828
+ ]));
3829
+ return this.executeBatch(targets, values, datas);
3830
+ }
3831
+ // ─── Market Discovery ─────────────────────────────────────────────────
3832
+ /**
3833
+ * Search reserves by token symbol (case-insensitive partial match).
3834
+ */
3835
+ async searchReserves(query) {
3836
+ const reserves = await this.getReserves();
3837
+ const q = query.toLowerCase();
3838
+ return reserves.filter(
3839
+ (r) => r.symbol.toLowerCase().includes(q) || r.address.toLowerCase() === q
3840
+ );
3841
+ }
3842
+ /**
3843
+ * Get borrowing options: which tokens can be borrowed, with what collateral.
3844
+ * If collateral symbol given, shows what you can borrow using that as collateral.
3845
+ */
3846
+ async getBorrowingOptions(collateral) {
3847
+ const reserves = await this.getReserves();
3848
+ const safeAddr = await this.getAccountAddress();
3849
+ const results = [];
3850
+ const eoaAddr = this.getWalletAddress();
3851
+ for (const reserve of reserves) {
3852
+ if (!reserve.isActive || reserve.ltv === 0) continue;
3853
+ if (collateral && reserve.symbol.toLowerCase() !== collateral.toLowerCase()) continue;
3854
+ const token = new Contract4(reserve.address, [
3855
+ "function balanceOf(address) view returns (uint256)"
3856
+ ], this.provider);
3857
+ const [eoaBal, safeBal] = await Promise.all([
3858
+ token.balanceOf(eoaAddr).catch(() => 0n),
3859
+ token.balanceOf(safeAddr).catch(() => 0n)
3860
+ ]);
3861
+ const totalBal = Number(eoaBal + safeBal) / 10 ** reserve.decimals;
3862
+ if (totalBal <= 0 && !collateral) continue;
3863
+ const collateralValueUsd = totalBal * reserve.priceUsd;
3864
+ const maxBorrowUsd = collateralValueUsd * (reserve.ltv / 100);
3865
+ const borrowable = reserves.filter((r) => r.borrowingEnabled && r.isActive && r.symbol !== reserve.symbol);
3866
+ results.push({
3867
+ collateral: reserve.symbol,
3868
+ borrowable,
3869
+ collateralBalance: totalBal,
3870
+ collateralValueUsd,
3871
+ maxBorrowUsd
3872
+ });
3873
+ }
3874
+ return results;
3875
+ }
3876
+ /**
3877
+ * Calculate max additional borrowable in USD given current positions.
3878
+ */
3879
+ async getMaxBorrowable() {
3880
+ const data = await this.getAccountData();
3881
+ return {
3882
+ availableBorrowUsd: data.availableBorrowUsd,
3883
+ currentLtv: data.currentLtv,
3884
+ liquidationThreshold: data.liquidationThreshold,
3885
+ totalCollateralUsd: data.totalCollateralUsd,
3886
+ totalDebtUsd: data.totalDebtUsd
3887
+ };
3888
+ }
3889
+ /**
3890
+ * Scan wallet for tokens that exist as Aave reserves.
3891
+ */
3892
+ async getWalletTokens() {
3893
+ const reserves = await this.getReserves();
3894
+ const safeAddr = await this.getAccountAddress();
3895
+ const eoaAddr = this.getWalletAddress();
3896
+ const results = [];
3897
+ const promises = reserves.map(async (reserve) => {
3898
+ const token = new Contract4(reserve.address, [
3899
+ "function balanceOf(address) view returns (uint256)"
3900
+ ], this.provider);
3901
+ const [eoaBal, safeBal] = await Promise.all([
3902
+ token.balanceOf(eoaAddr).catch(() => 0n),
3903
+ token.balanceOf(safeAddr).catch(() => 0n)
3904
+ ]);
3905
+ const eoaBalance = Number(eoaBal) / 10 ** reserve.decimals;
3906
+ const safeBalance = Number(safeBal) / 10 ** reserve.decimals;
3907
+ if (eoaBalance > 0 || safeBalance > 0) {
3908
+ return {
3909
+ symbol: reserve.symbol,
3910
+ address: reserve.address,
3911
+ eoaBalance,
3912
+ safeBalance,
3913
+ priceUsd: reserve.priceUsd,
3914
+ valueUsd: (eoaBalance + safeBalance) * reserve.priceUsd,
3915
+ canSupply: reserve.isActive,
3916
+ canBorrow: reserve.borrowingEnabled
3917
+ };
3918
+ }
3919
+ return null;
3920
+ });
3921
+ const settled = await Promise.all(promises);
3922
+ for (const r of settled) {
3923
+ if (r) results.push(r);
3924
+ }
3925
+ return results;
3926
+ }
3927
+ /**
3928
+ * Get yield spread for LST carry trades on Aave.
3929
+ * Compares LST native yield vs Aave borrow rates.
3930
+ */
3931
+ async getYieldSpread(_minLiquidity = 0) {
3932
+ const reserves = await this.getReserves();
3933
+ const lstYields = await this._getLstYields();
3934
+ if (Object.keys(lstYields).length === 0) return [];
3935
+ const results = [];
3936
+ for (const [symbol, yieldApy] of Object.entries(lstYields)) {
3937
+ const collateralReserve = reserves.find((r) => r.symbol.toLowerCase() === symbol.toLowerCase());
3938
+ if (!collateralReserve || !collateralReserve.isActive || collateralReserve.ltv === 0) continue;
3939
+ for (const loanReserve of reserves) {
3940
+ if (!loanReserve.borrowingEnabled || !loanReserve.isActive) continue;
3941
+ if (loanReserve.symbol === collateralReserve.symbol) continue;
3942
+ const netSpread = yieldApy - loanReserve.borrowApy;
3943
+ const safeLtv = collateralReserve.ltv / 100 * 0.8;
3944
+ const maxLeverage = 1 / (1 - safeLtv);
3945
+ results.push({
3946
+ collateralToken: collateralReserve.symbol,
3947
+ loanToken: loanReserve.symbol,
3948
+ collateralYield: yieldApy,
3949
+ borrowRate: loanReserve.borrowApy,
3950
+ netSpread,
3951
+ profitable: netSpread > 0,
3952
+ collateralLtv: collateralReserve.ltv,
3953
+ maxSafeLeverage: parseFloat(maxLeverage.toFixed(2))
3954
+ });
3955
+ }
3956
+ }
3957
+ return results.sort((a, b) => b.netSpread - a.netSpread);
3958
+ }
3959
+ async _getLstYields() {
3960
+ if (_AaveClient._lstYieldCache && Date.now() - _AaveClient._lstYieldCache.ts < _AaveClient.LST_CACHE_TTL) {
3961
+ return _AaveClient._lstYieldCache.data;
3962
+ }
3963
+ const yields = {};
3964
+ try {
3965
+ const resp = await fetch("https://yields.llama.fi/pools", {
3966
+ headers: { "User-Agent": "agether-sdk/1.0" },
3967
+ signal: AbortSignal.timeout(1e4)
3968
+ });
3969
+ if (!resp.ok) return yields;
3970
+ const data = await resp.json();
3971
+ const pools = data?.data ?? [];
3972
+ for (const [morphoSymbol, mapping] of Object.entries(_AaveClient.LST_PROJECT_MAP)) {
3973
+ const matchingPools = pools.filter(
3974
+ (p) => p.project === mapping.project && p.symbol?.toUpperCase() === mapping.symbol && ["Ethereum", "Base"].includes(p.chain)
3975
+ );
3976
+ if (matchingPools.length > 0) {
3977
+ const best = matchingPools.reduce(
3978
+ (a, b) => (b.tvlUsd ?? 0) > (a.tvlUsd ?? 0) ? b : a
3979
+ );
3980
+ if (best.apy !== void 0 && best.apy > 0) {
3981
+ yields[morphoSymbol] = parseFloat(best.apy.toFixed(4));
3982
+ }
3983
+ }
3984
+ }
3985
+ } catch (e) {
3986
+ console.warn("[aave] Failed to fetch LST yields:", e instanceof Error ? e.message : e);
3987
+ }
3988
+ _AaveClient._lstYieldCache = { data: yields, ts: Date.now() };
3989
+ return yields;
3990
+ }
3716
3991
  _resolveReserve(symbolOrAddress) {
3717
3992
  const reserves = KNOWN_RESERVES[this._aaveChainId] ?? [];
3718
3993
  const bySymbol = reserves.find((r) => r.symbol.toLowerCase() === symbolOrAddress.toLowerCase());
@@ -3726,6 +4001,23 @@ var _AaveClient = class _AaveClient extends AgentAccountClient {
3726
4001
  }
3727
4002
  };
3728
4003
  _AaveClient.CACHE_TTL = 6e4;
4004
+ // ─── DeFi Llama LST Yields (shared logic) ─────────────────────────────
4005
+ _AaveClient.LST_PROJECT_MAP = {
4006
+ "wstETH": { project: "lido", symbol: "STETH" },
4007
+ "cbETH": { project: "coinbase-wrapped-staked-eth", symbol: "CBETH" },
4008
+ "rETH": { project: "rocket-pool", symbol: "RETH" },
4009
+ "weETH": { project: "ether.fi-stake", symbol: "WEETH" },
4010
+ "ezETH": { project: "renzo", symbol: "EZETH" },
4011
+ "rsETH": { project: "kelp", symbol: "RSETH" },
4012
+ "swETH": { project: "swell-liquid-staking", symbol: "SWETH" },
4013
+ "mETH": { project: "meth-protocol", symbol: "METH" },
4014
+ "sfrxETH": { project: "frax-ether", symbol: "SFRXETH" },
4015
+ "oETH": { project: "origin-ether", symbol: "OETH" },
4016
+ "ETHx": { project: "stader", symbol: "ETHX" },
4017
+ "wBETH": { project: "binance-staked-eth", symbol: "WBETH" }
4018
+ };
4019
+ _AaveClient._lstYieldCache = null;
4020
+ _AaveClient.LST_CACHE_TTL = 30 * 60 * 1e3;
3729
4021
  var AaveClient = _AaveClient;
3730
4022
 
3731
4023
  // src/clients/ScoringClient.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agether/sdk",
3
- "version": "2.18.0",
3
+ "version": "2.18.2",
4
4
  "description": "TypeScript SDK for Agether - autonomous credit for AI agents on Ethereum & Base",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",