@agether/sdk 2.4.0 → 2.6.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 +250 -100
- package/dist/index.d.mts +95 -22
- package/dist/index.d.ts +95 -22
- package/dist/index.js +249 -99
- package/dist/index.mjs +249 -99
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -460,11 +460,6 @@ var AgetherClient = class _AgetherClient {
|
|
|
460
460
|
// src/clients/MorphoClient.ts
|
|
461
461
|
import { ethers as ethers2, Contract as Contract2 } from "ethers";
|
|
462
462
|
import axios from "axios";
|
|
463
|
-
var BASE_COLLATERALS = {
|
|
464
|
-
WETH: { address: "0x4200000000000000000000000000000000000006", symbol: "WETH", decimals: 18 },
|
|
465
|
-
wstETH: { address: "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", symbol: "wstETH", decimals: 18 },
|
|
466
|
-
cbETH: { address: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", symbol: "cbETH", decimals: 18 }
|
|
467
|
-
};
|
|
468
463
|
var MORPHO_API_URL = "https://api.morpho.org/graphql";
|
|
469
464
|
var MODE_SINGLE = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
470
465
|
var MODE_BATCH = "0x0100000000000000000000000000000000000000000000000000000000000000";
|
|
@@ -473,6 +468,8 @@ var erc20Iface = new ethers2.Interface(ERC20_ABI);
|
|
|
473
468
|
var MorphoClient = class {
|
|
474
469
|
constructor(config) {
|
|
475
470
|
this._marketCache = /* @__PURE__ */ new Map();
|
|
471
|
+
/** Dynamic token registry: symbol (uppercase) → { address, symbol, decimals } */
|
|
472
|
+
this._tokenCache = /* @__PURE__ */ new Map();
|
|
476
473
|
this._discoveredAt = 0;
|
|
477
474
|
const chainId = config.chainId ?? 8453 /* Base */;
|
|
478
475
|
const defaultCfg = getDefaultConfig(chainId);
|
|
@@ -659,15 +656,16 @@ var MorphoClient = class {
|
|
|
659
656
|
const usdc = new Contract2(this.config.contracts.usdc, ERC20_ABI, this.provider);
|
|
660
657
|
const ethBal = await this.provider.getBalance(eoaAddr);
|
|
661
658
|
const usdcBal = await usdc.balanceOf(eoaAddr);
|
|
659
|
+
const discoveredTokens = await this._getDiscoveredTokens();
|
|
662
660
|
const eoaCollateral = {};
|
|
663
|
-
for (const
|
|
661
|
+
for (const info of discoveredTokens) {
|
|
664
662
|
try {
|
|
665
663
|
const token = new Contract2(info.address, ERC20_ABI, this.provider);
|
|
666
664
|
const bal = await token.balanceOf(eoaAddr);
|
|
667
|
-
eoaCollateral[symbol] = ethers2.formatUnits(bal, info.decimals);
|
|
665
|
+
eoaCollateral[info.symbol] = ethers2.formatUnits(bal, info.decimals);
|
|
668
666
|
} catch (e) {
|
|
669
|
-
console.warn(`[agether] EOA collateral fetch failed for ${symbol}:`, e instanceof Error ? e.message : e);
|
|
670
|
-
eoaCollateral[symbol] = "0";
|
|
667
|
+
console.warn(`[agether] EOA collateral fetch failed for ${info.symbol}:`, e instanceof Error ? e.message : e);
|
|
668
|
+
eoaCollateral[info.symbol] = "0";
|
|
671
669
|
}
|
|
672
670
|
}
|
|
673
671
|
const result = {
|
|
@@ -682,14 +680,14 @@ var MorphoClient = class {
|
|
|
682
680
|
const acctEth = await this.provider.getBalance(acctAddr);
|
|
683
681
|
const acctUsdc = await usdc.balanceOf(acctAddr);
|
|
684
682
|
const acctCollateral = {};
|
|
685
|
-
for (const
|
|
683
|
+
for (const info of discoveredTokens) {
|
|
686
684
|
try {
|
|
687
685
|
const token = new Contract2(info.address, ERC20_ABI, this.provider);
|
|
688
686
|
const bal = await token.balanceOf(acctAddr);
|
|
689
|
-
acctCollateral[symbol] = ethers2.formatUnits(bal, info.decimals);
|
|
687
|
+
acctCollateral[info.symbol] = ethers2.formatUnits(bal, info.decimals);
|
|
690
688
|
} catch (e) {
|
|
691
|
-
console.warn(`[agether] AgentAccount collateral fetch failed for ${symbol}:`, e instanceof Error ? e.message : e);
|
|
692
|
-
acctCollateral[symbol] = "0";
|
|
689
|
+
console.warn(`[agether] AgentAccount collateral fetch failed for ${info.symbol}:`, e instanceof Error ? e.message : e);
|
|
690
|
+
acctCollateral[info.symbol] = "0";
|
|
693
691
|
}
|
|
694
692
|
}
|
|
695
693
|
result.agentAccount = {
|
|
@@ -716,6 +714,50 @@ var MorphoClient = class {
|
|
|
716
714
|
this._refreshSigner();
|
|
717
715
|
return { tx: receipt.hash, amount: usdcAmount, agentAccount: acctAddr };
|
|
718
716
|
}
|
|
717
|
+
/**
|
|
718
|
+
* Withdraw (transfer) a token from AgentAccount to EOA.
|
|
719
|
+
* Executes an ERC-20 transfer via Safe UserOp.
|
|
720
|
+
*
|
|
721
|
+
* @param tokenSymbol - Token to withdraw (e.g. 'USDC', 'WETH', 'wstETH')
|
|
722
|
+
* @param amount - Amount to withdraw (human-readable, e.g. '100' for 100 USDC, or 'all')
|
|
723
|
+
*/
|
|
724
|
+
async withdrawToken(tokenSymbol, amount) {
|
|
725
|
+
const acctAddr = await this.getAccountAddress();
|
|
726
|
+
const eoaAddr = await this.getSignerAddress();
|
|
727
|
+
const tokenInfo = await this._resolveToken(tokenSymbol);
|
|
728
|
+
const tokenContract = new Contract2(tokenInfo.address, ERC20_ABI, this.provider);
|
|
729
|
+
let weiAmount;
|
|
730
|
+
if (amount === "all") {
|
|
731
|
+
weiAmount = await tokenContract.balanceOf(acctAddr);
|
|
732
|
+
if (weiAmount === 0n) throw new AgetherError(`No ${tokenSymbol} in AgentAccount`, "INSUFFICIENT_BALANCE");
|
|
733
|
+
} else {
|
|
734
|
+
weiAmount = ethers2.parseUnits(amount, tokenInfo.decimals);
|
|
735
|
+
}
|
|
736
|
+
const data = erc20Iface.encodeFunctionData("transfer", [eoaAddr, weiAmount]);
|
|
737
|
+
const receipt = await this.exec(tokenInfo.address, data);
|
|
738
|
+
const actualAmount = amount === "all" ? ethers2.formatUnits(weiAmount, tokenInfo.decimals) : amount;
|
|
739
|
+
return { tx: receipt.hash, token: tokenSymbol, amount: actualAmount, destination: eoaAddr };
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Withdraw ETH from AgentAccount to EOA.
|
|
743
|
+
* Executes a native ETH transfer via Safe UserOp.
|
|
744
|
+
*
|
|
745
|
+
* @param amount - ETH amount (e.g. '0.01' or 'all')
|
|
746
|
+
*/
|
|
747
|
+
async withdrawEth(amount) {
|
|
748
|
+
const acctAddr = await this.getAccountAddress();
|
|
749
|
+
const eoaAddr = await this.getSignerAddress();
|
|
750
|
+
let weiAmount;
|
|
751
|
+
if (amount === "all") {
|
|
752
|
+
weiAmount = await this.provider.getBalance(acctAddr);
|
|
753
|
+
if (weiAmount === 0n) throw new AgetherError("No ETH in AgentAccount", "INSUFFICIENT_BALANCE");
|
|
754
|
+
} else {
|
|
755
|
+
weiAmount = ethers2.parseEther(amount);
|
|
756
|
+
}
|
|
757
|
+
const receipt = await this.exec(eoaAddr, "0x", weiAmount);
|
|
758
|
+
const actualAmount = amount === "all" ? ethers2.formatEther(weiAmount) : amount;
|
|
759
|
+
return { tx: receipt.hash, token: "ETH", amount: actualAmount, destination: eoaAddr };
|
|
760
|
+
}
|
|
719
761
|
// ════════════════════════════════════════════════════════
|
|
720
762
|
// Market Discovery (Morpho GraphQL API)
|
|
721
763
|
// ════════════════════════════════════════════════════════
|
|
@@ -775,6 +817,28 @@ var MorphoClient = class {
|
|
|
775
817
|
irm: mi.irm,
|
|
776
818
|
lltv: mi.lltv
|
|
777
819
|
});
|
|
820
|
+
this._tokenCache.set(mi.collateralAsset.symbol.toUpperCase(), {
|
|
821
|
+
address: mi.collateralAsset.address,
|
|
822
|
+
symbol: mi.collateralAsset.symbol,
|
|
823
|
+
decimals: mi.collateralAsset.decimals
|
|
824
|
+
});
|
|
825
|
+
this._tokenCache.set(mi.collateralAsset.address.toLowerCase(), {
|
|
826
|
+
address: mi.collateralAsset.address,
|
|
827
|
+
symbol: mi.collateralAsset.symbol,
|
|
828
|
+
decimals: mi.collateralAsset.decimals
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
if (mi.loanAsset.address !== ethers2.ZeroAddress) {
|
|
832
|
+
this._tokenCache.set(mi.loanAsset.symbol.toUpperCase(), {
|
|
833
|
+
address: mi.loanAsset.address,
|
|
834
|
+
symbol: mi.loanAsset.symbol,
|
|
835
|
+
decimals: mi.loanAsset.decimals
|
|
836
|
+
});
|
|
837
|
+
this._tokenCache.set(mi.loanAsset.address.toLowerCase(), {
|
|
838
|
+
address: mi.loanAsset.address,
|
|
839
|
+
symbol: mi.loanAsset.symbol,
|
|
840
|
+
decimals: mi.loanAsset.decimals
|
|
841
|
+
});
|
|
778
842
|
}
|
|
779
843
|
}
|
|
780
844
|
return this._discoveredMarkets;
|
|
@@ -788,8 +852,17 @@ var MorphoClient = class {
|
|
|
788
852
|
* Tries cache → API → onchain idToMarketParams.
|
|
789
853
|
*/
|
|
790
854
|
async findMarketForCollateral(collateralSymbolOrAddress) {
|
|
791
|
-
|
|
792
|
-
|
|
855
|
+
let colAddr;
|
|
856
|
+
if (collateralSymbolOrAddress.startsWith("0x")) {
|
|
857
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
858
|
+
} else {
|
|
859
|
+
try {
|
|
860
|
+
const resolved = await this._resolveToken(collateralSymbolOrAddress);
|
|
861
|
+
colAddr = resolved.address.toLowerCase();
|
|
862
|
+
} catch {
|
|
863
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
864
|
+
}
|
|
865
|
+
}
|
|
793
866
|
const cached = this._marketCache.get(colAddr);
|
|
794
867
|
if (cached) return cached;
|
|
795
868
|
await this.getMarkets();
|
|
@@ -948,8 +1021,17 @@ var MorphoClient = class {
|
|
|
948
1021
|
const usdcAddr = this.config.contracts.usdc.toLowerCase();
|
|
949
1022
|
let collateralFilter = "";
|
|
950
1023
|
if (collateralSymbolOrAddress) {
|
|
951
|
-
|
|
952
|
-
|
|
1024
|
+
let colAddr;
|
|
1025
|
+
if (collateralSymbolOrAddress.startsWith("0x")) {
|
|
1026
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
1027
|
+
} else {
|
|
1028
|
+
try {
|
|
1029
|
+
const resolved = await this._resolveToken(collateralSymbolOrAddress);
|
|
1030
|
+
colAddr = resolved.address.toLowerCase();
|
|
1031
|
+
} catch {
|
|
1032
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
953
1035
|
collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
|
|
954
1036
|
}
|
|
955
1037
|
const query = `{
|
|
@@ -1008,8 +1090,7 @@ var MorphoClient = class {
|
|
|
1008
1090
|
* @returns Estimated yield in USD for the period
|
|
1009
1091
|
*/
|
|
1010
1092
|
async getYieldEstimate(collateralSymbol, amount, periodDays = 1, ethPriceUsd) {
|
|
1011
|
-
const colInfo =
|
|
1012
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${collateralSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1093
|
+
const colInfo = await this._resolveToken(collateralSymbol);
|
|
1013
1094
|
const rates = await this.getMarketRates(collateralSymbol);
|
|
1014
1095
|
if (rates.length === 0) {
|
|
1015
1096
|
throw new AgetherError(`No market found for ${collateralSymbol}`, "MARKET_NOT_FOUND");
|
|
@@ -1311,8 +1392,7 @@ var MorphoClient = class {
|
|
|
1311
1392
|
*/
|
|
1312
1393
|
async supplyCollateral(tokenSymbol, amount, marketParams) {
|
|
1313
1394
|
const acctAddr = await this.getAccountAddress();
|
|
1314
|
-
const colInfo =
|
|
1315
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1395
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1316
1396
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1317
1397
|
const weiAmount = ethers2.parseUnits(amount, colInfo.decimals);
|
|
1318
1398
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
@@ -1433,8 +1513,7 @@ var MorphoClient = class {
|
|
|
1433
1513
|
*/
|
|
1434
1514
|
async depositAndBorrow(tokenSymbol, collateralAmount, borrowUsdcAmount, marketParams) {
|
|
1435
1515
|
const acctAddr = await this.getAccountAddress();
|
|
1436
|
-
const colInfo =
|
|
1437
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1516
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1438
1517
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1439
1518
|
const colWei = ethers2.parseUnits(collateralAmount, colInfo.decimals);
|
|
1440
1519
|
const borrowWei = ethers2.parseUnits(borrowUsdcAmount, 6);
|
|
@@ -1602,8 +1681,7 @@ var MorphoClient = class {
|
|
|
1602
1681
|
*/
|
|
1603
1682
|
async withdrawCollateral(tokenSymbol, amount, marketParams, receiver) {
|
|
1604
1683
|
const acctAddr = await this.getAccountAddress();
|
|
1605
|
-
const colInfo =
|
|
1606
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1684
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1607
1685
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1608
1686
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1609
1687
|
const usdcAddr = this.config.contracts.usdc;
|
|
@@ -1700,8 +1778,7 @@ var MorphoClient = class {
|
|
|
1700
1778
|
* (The agent must then supplyCollateral themselves via their own account.)
|
|
1701
1779
|
*/
|
|
1702
1780
|
async sponsor(target, tokenSymbol, amount) {
|
|
1703
|
-
const colInfo =
|
|
1704
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1781
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1705
1782
|
let targetAddr;
|
|
1706
1783
|
if (target.address) {
|
|
1707
1784
|
targetAddr = target.address;
|
|
@@ -1957,6 +2034,43 @@ var MorphoClient = class {
|
|
|
1957
2034
|
}
|
|
1958
2035
|
throw new AgetherError("No active supply position found", "NO_SUPPLY");
|
|
1959
2036
|
}
|
|
2037
|
+
/**
|
|
2038
|
+
* Resolve a token symbol or address to { address, symbol, decimals }.
|
|
2039
|
+
*
|
|
2040
|
+
* Uses the dynamic `_tokenCache` populated by `getMarkets()` from the
|
|
2041
|
+
* Morpho GraphQL API — no hardcoded token list needed.
|
|
2042
|
+
*
|
|
2043
|
+
* @param symbolOrAddress - e.g. 'WETH', 'wstETH', or '0x4200...'
|
|
2044
|
+
*/
|
|
2045
|
+
async _resolveToken(symbolOrAddress) {
|
|
2046
|
+
const key = symbolOrAddress.startsWith("0x") ? symbolOrAddress.toLowerCase() : symbolOrAddress.toUpperCase();
|
|
2047
|
+
const cached = this._tokenCache.get(key);
|
|
2048
|
+
if (cached) return cached;
|
|
2049
|
+
await this.getMarkets();
|
|
2050
|
+
const fromApi = this._tokenCache.get(key);
|
|
2051
|
+
if (fromApi) return fromApi;
|
|
2052
|
+
throw new AgetherError(
|
|
2053
|
+
`Unknown token: ${symbolOrAddress}. No Morpho market found with this collateral.`,
|
|
2054
|
+
"UNKNOWN_COLLATERAL"
|
|
2055
|
+
);
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Get all discovered collateral tokens (for balance iteration, etc.).
|
|
2059
|
+
* Returns unique tokens from the Morpho API market discovery.
|
|
2060
|
+
*/
|
|
2061
|
+
async _getDiscoveredTokens() {
|
|
2062
|
+
await this.getMarkets();
|
|
2063
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2064
|
+
const tokens = [];
|
|
2065
|
+
for (const [key, info] of this._tokenCache) {
|
|
2066
|
+
if (key.startsWith("0x")) continue;
|
|
2067
|
+
if (seen.has(info.address.toLowerCase())) continue;
|
|
2068
|
+
seen.add(info.address.toLowerCase());
|
|
2069
|
+
if (info.symbol === "USDC" || info.symbol === "USDbC") continue;
|
|
2070
|
+
tokens.push(info);
|
|
2071
|
+
}
|
|
2072
|
+
return tokens;
|
|
2073
|
+
}
|
|
1960
2074
|
/**
|
|
1961
2075
|
* Compute net deposited amounts per market using Morpho GraphQL API.
|
|
1962
2076
|
*
|
|
@@ -2098,7 +2212,9 @@ var X402Client = class {
|
|
|
2098
2212
|
this.paidFetch = wrapFetchWithPayment(fetch, client);
|
|
2099
2213
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2100
2214
|
const dailyLimit = config.dailySpendLimitUsdc ? BigInt(Math.round(parseFloat(config.dailySpendLimitUsdc) * 1e6)) : 0n;
|
|
2101
|
-
|
|
2215
|
+
const restored = config.initialSpendingState;
|
|
2216
|
+
const restoredBorrowed = restored && restored.date === today ? BigInt(restored.totalBorrowed) : 0n;
|
|
2217
|
+
this._spendingTracker = { date: today, totalBorrowed: restoredBorrowed, dailyLimit };
|
|
2102
2218
|
}
|
|
2103
2219
|
async get(url, opts) {
|
|
2104
2220
|
return this.request(url, { ...opts, method: "GET" });
|
|
@@ -2127,66 +2243,122 @@ var X402Client = class {
|
|
|
2127
2243
|
return (Number(remaining > 0n ? remaining : 0n) / 1e6).toFixed(2);
|
|
2128
2244
|
}
|
|
2129
2245
|
/**
|
|
2130
|
-
* Pay with auto-
|
|
2246
|
+
* Pay with auto-funding: Make an x402 request with automatic USDC sourcing.
|
|
2131
2247
|
*
|
|
2132
|
-
*
|
|
2133
|
-
*
|
|
2134
|
-
*
|
|
2135
|
-
*
|
|
2136
|
-
*
|
|
2137
|
-
*
|
|
2138
|
-
*
|
|
2248
|
+
* Uses a **plan-then-execute** approach: all reads happen first, and if the
|
|
2249
|
+
* full deficit can't be covered the method fails with NO side-effects
|
|
2250
|
+
* (no yield withdrawn, no USDC borrowed).
|
|
2251
|
+
*
|
|
2252
|
+
* Waterfall (when both autoYield + autoDraw enabled):
|
|
2253
|
+
* 1. Check USDC balance on AgentAccount
|
|
2254
|
+
* 2. Probe the URL to discover payment amount (if 402)
|
|
2255
|
+
* 3. If insufficient USDC — PLANNING PHASE (read-only):
|
|
2256
|
+
* a. Calculate total available yield across supply positions
|
|
2257
|
+
* b. Calculate max borrowable from Morpho markets
|
|
2258
|
+
* c. Verify yield + borrow can cover full deficit — if not, fail immediately
|
|
2259
|
+
* 4. EXECUTION PHASE (only if plan is feasible):
|
|
2260
|
+
* a. Withdraw needed yield from supply positions
|
|
2261
|
+
* b. Borrow remaining deficit from Morpho
|
|
2262
|
+
* 5. Proceed with x402 payment
|
|
2139
2263
|
*/
|
|
2140
2264
|
async payWithAutoDraw(url, opts) {
|
|
2141
2265
|
const { morphoClient, ...fetchOpts } = opts || {};
|
|
2142
|
-
if (!this.config.autoDraw || !morphoClient) {
|
|
2266
|
+
if (!this.config.autoDraw && !this.config.autoYield || !morphoClient) {
|
|
2143
2267
|
return this.request(url, fetchOpts);
|
|
2144
2268
|
}
|
|
2145
2269
|
try {
|
|
2146
2270
|
const usdcBalance = await morphoClient.getUsdcBalance();
|
|
2147
|
-
console.log(` [auto-
|
|
2271
|
+
console.log(` [auto-fund] AgentAccount USDC balance: ${(Number(usdcBalance) / 1e6).toFixed(2)}`);
|
|
2148
2272
|
const paymentAmount = await this._probePaymentAmount(url, fetchOpts);
|
|
2149
2273
|
if (paymentAmount !== null) {
|
|
2150
|
-
console.log(` [auto-
|
|
2274
|
+
console.log(` [auto-fund] Payment required: ${(Number(paymentAmount) / 1e6).toFixed(6)} USDC`);
|
|
2151
2275
|
const bufferStr = this.config.autoDrawBuffer || "0.5";
|
|
2152
2276
|
const buffer = BigInt(Math.round(parseFloat(bufferStr) * 1e6));
|
|
2153
2277
|
const needed = paymentAmount + buffer;
|
|
2154
2278
|
if (usdcBalance < needed) {
|
|
2155
|
-
const
|
|
2156
|
-
console.log(` [auto-
|
|
2157
|
-
const
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2279
|
+
const totalDeficit = needed - usdcBalance;
|
|
2280
|
+
console.log(` [auto-fund] Insufficient balance. Deficit: ${(Number(totalDeficit) / 1e6).toFixed(2)} USDC`);
|
|
2281
|
+
const yieldPlan = [];
|
|
2282
|
+
let totalYieldAvailable = 0n;
|
|
2283
|
+
if (this.config.autoYield) {
|
|
2284
|
+
try {
|
|
2285
|
+
const positions = await morphoClient.getSupplyPositions();
|
|
2286
|
+
const withYield = positions.filter((p) => parseFloat(p.earnedYield) > 1e-6).sort((a, b) => parseFloat(b.earnedYield) - parseFloat(a.earnedYield));
|
|
2287
|
+
for (const pos of withYield) {
|
|
2288
|
+
const yieldRaw = BigInt(Math.floor(parseFloat(pos.earnedYield) * 1e6));
|
|
2289
|
+
if (yieldRaw > 0n) {
|
|
2290
|
+
yieldPlan.push({ collateralToken: pos.collateralToken, amount: yieldRaw });
|
|
2291
|
+
totalYieldAvailable += yieldRaw;
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
console.log(` [auto-fund] Plan: ${(Number(totalYieldAvailable) / 1e6).toFixed(6)} USDC available from yield`);
|
|
2295
|
+
} catch (e) {
|
|
2296
|
+
console.warn(` [auto-fund] Plan: yield check failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
2297
|
+
}
|
|
2163
2298
|
}
|
|
2164
|
-
const
|
|
2165
|
-
|
|
2299
|
+
const yieldToUse = totalYieldAvailable < totalDeficit ? totalYieldAvailable : totalDeficit;
|
|
2300
|
+
const deficitAfterYield = totalDeficit - yieldToUse;
|
|
2301
|
+
let canBorrow = 0n;
|
|
2302
|
+
if (this.config.autoDraw && deficitAfterYield > 0n) {
|
|
2303
|
+
const limitCheck = await this._checkSpendingLimit(deficitAfterYield, morphoClient);
|
|
2304
|
+
if (!limitCheck.allowed) {
|
|
2305
|
+
return { success: false, error: `Auto-fund blocked: ${limitCheck.reason}` };
|
|
2306
|
+
}
|
|
2307
|
+
const maxBorrowable = await morphoClient.getMaxBorrowable();
|
|
2308
|
+
canBorrow = maxBorrowable.total;
|
|
2309
|
+
console.log(` [auto-fund] Plan: ${(Number(canBorrow) / 1e6).toFixed(2)} USDC borrowable from Morpho`);
|
|
2310
|
+
}
|
|
2311
|
+
const totalAvailable = yieldToUse + canBorrow;
|
|
2312
|
+
if (totalAvailable < totalDeficit) {
|
|
2313
|
+
const parts = [];
|
|
2314
|
+
if (totalYieldAvailable > 0n) parts.push(`yield: $${(Number(totalYieldAvailable) / 1e6).toFixed(2)}`);
|
|
2315
|
+
if (canBorrow > 0n) parts.push(`borrowable: $${(Number(canBorrow) / 1e6).toFixed(2)}`);
|
|
2316
|
+
if (!this.config.autoYield) parts.push("autoYield: off");
|
|
2317
|
+
if (!this.config.autoDraw) parts.push("autoDraw: off");
|
|
2166
2318
|
return {
|
|
2167
2319
|
success: false,
|
|
2168
|
-
error: `
|
|
2320
|
+
error: `Cannot cover deficit of $${(Number(totalDeficit) / 1e6).toFixed(2)} USDC. Available: ${parts.join(", ")}. Balance: $${(Number(usdcBalance) / 1e6).toFixed(2)}, needed: $${(Number(needed) / 1e6).toFixed(2)}.`
|
|
2169
2321
|
};
|
|
2170
2322
|
}
|
|
2171
|
-
const
|
|
2172
|
-
|
|
2173
|
-
const borrowResult = await morphoClient.borrow(borrowAmount);
|
|
2174
|
-
console.log(` [auto-draw] Borrow tx: ${borrowResult.tx}`);
|
|
2175
|
-
this._trackSpending(deficit);
|
|
2176
|
-
const result = await this.request(url, fetchOpts);
|
|
2177
|
-
return {
|
|
2178
|
-
...result,
|
|
2179
|
-
autoDrawInfo: {
|
|
2180
|
-
borrowed: borrowAmount,
|
|
2181
|
-
borrowTx: borrowResult.tx,
|
|
2182
|
-
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2183
|
-
}
|
|
2323
|
+
const drawInfo = {
|
|
2324
|
+
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2184
2325
|
};
|
|
2326
|
+
if (yieldToUse > 0n) {
|
|
2327
|
+
let remaining = yieldToUse;
|
|
2328
|
+
let totalWithdrawn = 0n;
|
|
2329
|
+
let lastTx = "";
|
|
2330
|
+
for (const plan of yieldPlan) {
|
|
2331
|
+
if (remaining <= 0n) break;
|
|
2332
|
+
const toWithdraw = plan.amount < remaining ? plan.amount : remaining;
|
|
2333
|
+
const withdrawStr = (Number(toWithdraw) / 1e6).toFixed(6);
|
|
2334
|
+
console.log(` [auto-yield] Withdrawing $${withdrawStr} yield from ${plan.collateralToken} market`);
|
|
2335
|
+
const wr = await morphoClient.withdrawSupply(withdrawStr, plan.collateralToken);
|
|
2336
|
+
lastTx = wr.tx;
|
|
2337
|
+
totalWithdrawn += toWithdraw;
|
|
2338
|
+
remaining -= toWithdraw;
|
|
2339
|
+
}
|
|
2340
|
+
if (totalWithdrawn > 0n) {
|
|
2341
|
+
drawInfo.yieldWithdrawn = (Number(totalWithdrawn) / 1e6).toFixed(6);
|
|
2342
|
+
drawInfo.yieldTx = lastTx;
|
|
2343
|
+
console.log(` [auto-yield] Withdrawn: $${drawInfo.yieldWithdrawn}`);
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
if (deficitAfterYield > 0n && this.config.autoDraw) {
|
|
2347
|
+
const borrowAmount = (Number(deficitAfterYield) / 1e6).toFixed(6);
|
|
2348
|
+
console.log(` [auto-draw] Borrowing ${borrowAmount} USDC from Morpho...`);
|
|
2349
|
+
const borrowResult = await morphoClient.borrow(borrowAmount);
|
|
2350
|
+
console.log(` [auto-draw] Borrow tx: ${borrowResult.tx}`);
|
|
2351
|
+
drawInfo.borrowed = borrowAmount;
|
|
2352
|
+
drawInfo.borrowTx = borrowResult.tx;
|
|
2353
|
+
this._trackSpending(deficitAfterYield);
|
|
2354
|
+
}
|
|
2355
|
+
const result = await this.request(url, fetchOpts);
|
|
2356
|
+
return { ...result, autoDrawInfo: drawInfo };
|
|
2185
2357
|
}
|
|
2186
2358
|
}
|
|
2187
2359
|
return this.request(url, fetchOpts);
|
|
2188
2360
|
} catch (error) {
|
|
2189
|
-
console.log(` [auto-
|
|
2361
|
+
console.log(` [auto-fund] Failed: ${error instanceof Error ? error.message : String(error)}. Proceeding with normal request.`);
|
|
2190
2362
|
return this.request(url, fetchOpts);
|
|
2191
2363
|
}
|
|
2192
2364
|
}
|
|
@@ -2287,48 +2459,26 @@ var X402Client = class {
|
|
|
2287
2459
|
};
|
|
2288
2460
|
}
|
|
2289
2461
|
}
|
|
2290
|
-
/** Track a new spending amount */
|
|
2462
|
+
/** Track a new spending amount and notify the caller for persistence */
|
|
2291
2463
|
_trackSpending(amount) {
|
|
2292
2464
|
this._resetTrackerIfNewDay();
|
|
2293
2465
|
this._spendingTracker.totalBorrowed += amount;
|
|
2294
|
-
|
|
2295
|
-
/**
|
|
2296
|
-
* Check if a borrow amount is within spending limits.
|
|
2297
|
-
* Considers both fixed daily limits and yield-limited spending.
|
|
2298
|
-
*/
|
|
2299
|
-
async _checkSpendingLimit(amount, morphoClient) {
|
|
2300
|
-
this._resetTrackerIfNewDay();
|
|
2301
|
-
if (this.config.yieldLimitedSpending) {
|
|
2466
|
+
if (this.config.onSpendingUpdate) {
|
|
2302
2467
|
try {
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
try {
|
|
2308
|
-
const estimate = await morphoClient.getYieldEstimate(
|
|
2309
|
-
pos.collateralToken,
|
|
2310
|
-
pos.collateral,
|
|
2311
|
-
1
|
|
2312
|
-
// 1 day
|
|
2313
|
-
);
|
|
2314
|
-
totalDailyYieldUsdc += estimate.estimatedYieldUsd;
|
|
2315
|
-
} catch (e) {
|
|
2316
|
-
console.warn(`[agether] yield calc failed for ${pos.collateralToken}:`, e instanceof Error ? e.message : e);
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
}
|
|
2320
|
-
const yieldLimit = BigInt(Math.round(totalDailyYieldUsdc * 1e6));
|
|
2321
|
-
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2322
|
-
if (yieldLimit > 0n && newTotal > yieldLimit) {
|
|
2323
|
-
return {
|
|
2324
|
-
allowed: false,
|
|
2325
|
-
reason: `Yield-limited spending exceeded. Daily yield cap: $${(Number(yieldLimit) / 1e6).toFixed(2)}, already spent: $${(Number(this._spendingTracker.totalBorrowed) / 1e6).toFixed(2)}, requested: $${(Number(amount) / 1e6).toFixed(2)}`
|
|
2326
|
-
};
|
|
2327
|
-
}
|
|
2468
|
+
this.config.onSpendingUpdate({
|
|
2469
|
+
date: this._spendingTracker.date,
|
|
2470
|
+
totalBorrowed: this._spendingTracker.totalBorrowed.toString()
|
|
2471
|
+
});
|
|
2328
2472
|
} catch (e) {
|
|
2329
|
-
console.warn("[agether]
|
|
2473
|
+
console.warn("[agether] onSpendingUpdate callback failed:", e instanceof Error ? e.message : e);
|
|
2330
2474
|
}
|
|
2331
2475
|
}
|
|
2476
|
+
}
|
|
2477
|
+
/**
|
|
2478
|
+
* Check if a borrow amount is within the fixed daily spending limit.
|
|
2479
|
+
*/
|
|
2480
|
+
async _checkSpendingLimit(amount, _morphoClient) {
|
|
2481
|
+
this._resetTrackerIfNewDay();
|
|
2332
2482
|
if (this._spendingTracker.dailyLimit > 0n) {
|
|
2333
2483
|
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2334
2484
|
if (newTotal > this._spendingTracker.dailyLimit) {
|