@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.js
CHANGED
|
@@ -532,11 +532,6 @@ var AgetherClient = class _AgetherClient {
|
|
|
532
532
|
// src/clients/MorphoClient.ts
|
|
533
533
|
var import_ethers2 = require("ethers");
|
|
534
534
|
var import_axios = __toESM(require("axios"));
|
|
535
|
-
var BASE_COLLATERALS = {
|
|
536
|
-
WETH: { address: "0x4200000000000000000000000000000000000006", symbol: "WETH", decimals: 18 },
|
|
537
|
-
wstETH: { address: "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", symbol: "wstETH", decimals: 18 },
|
|
538
|
-
cbETH: { address: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", symbol: "cbETH", decimals: 18 }
|
|
539
|
-
};
|
|
540
535
|
var MORPHO_API_URL = "https://api.morpho.org/graphql";
|
|
541
536
|
var MODE_SINGLE = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
542
537
|
var MODE_BATCH = "0x0100000000000000000000000000000000000000000000000000000000000000";
|
|
@@ -545,6 +540,8 @@ var erc20Iface = new import_ethers2.ethers.Interface(ERC20_ABI);
|
|
|
545
540
|
var MorphoClient = class {
|
|
546
541
|
constructor(config) {
|
|
547
542
|
this._marketCache = /* @__PURE__ */ new Map();
|
|
543
|
+
/** Dynamic token registry: symbol (uppercase) → { address, symbol, decimals } */
|
|
544
|
+
this._tokenCache = /* @__PURE__ */ new Map();
|
|
548
545
|
this._discoveredAt = 0;
|
|
549
546
|
const chainId = config.chainId ?? 8453 /* Base */;
|
|
550
547
|
const defaultCfg = getDefaultConfig(chainId);
|
|
@@ -731,15 +728,16 @@ var MorphoClient = class {
|
|
|
731
728
|
const usdc = new import_ethers2.Contract(this.config.contracts.usdc, ERC20_ABI, this.provider);
|
|
732
729
|
const ethBal = await this.provider.getBalance(eoaAddr);
|
|
733
730
|
const usdcBal = await usdc.balanceOf(eoaAddr);
|
|
731
|
+
const discoveredTokens = await this._getDiscoveredTokens();
|
|
734
732
|
const eoaCollateral = {};
|
|
735
|
-
for (const
|
|
733
|
+
for (const info of discoveredTokens) {
|
|
736
734
|
try {
|
|
737
735
|
const token = new import_ethers2.Contract(info.address, ERC20_ABI, this.provider);
|
|
738
736
|
const bal = await token.balanceOf(eoaAddr);
|
|
739
|
-
eoaCollateral[symbol] = import_ethers2.ethers.formatUnits(bal, info.decimals);
|
|
737
|
+
eoaCollateral[info.symbol] = import_ethers2.ethers.formatUnits(bal, info.decimals);
|
|
740
738
|
} catch (e) {
|
|
741
|
-
console.warn(`[agether] EOA collateral fetch failed for ${symbol}:`, e instanceof Error ? e.message : e);
|
|
742
|
-
eoaCollateral[symbol] = "0";
|
|
739
|
+
console.warn(`[agether] EOA collateral fetch failed for ${info.symbol}:`, e instanceof Error ? e.message : e);
|
|
740
|
+
eoaCollateral[info.symbol] = "0";
|
|
743
741
|
}
|
|
744
742
|
}
|
|
745
743
|
const result = {
|
|
@@ -754,14 +752,14 @@ var MorphoClient = class {
|
|
|
754
752
|
const acctEth = await this.provider.getBalance(acctAddr);
|
|
755
753
|
const acctUsdc = await usdc.balanceOf(acctAddr);
|
|
756
754
|
const acctCollateral = {};
|
|
757
|
-
for (const
|
|
755
|
+
for (const info of discoveredTokens) {
|
|
758
756
|
try {
|
|
759
757
|
const token = new import_ethers2.Contract(info.address, ERC20_ABI, this.provider);
|
|
760
758
|
const bal = await token.balanceOf(acctAddr);
|
|
761
|
-
acctCollateral[symbol] = import_ethers2.ethers.formatUnits(bal, info.decimals);
|
|
759
|
+
acctCollateral[info.symbol] = import_ethers2.ethers.formatUnits(bal, info.decimals);
|
|
762
760
|
} catch (e) {
|
|
763
|
-
console.warn(`[agether] AgentAccount collateral fetch failed for ${symbol}:`, e instanceof Error ? e.message : e);
|
|
764
|
-
acctCollateral[symbol] = "0";
|
|
761
|
+
console.warn(`[agether] AgentAccount collateral fetch failed for ${info.symbol}:`, e instanceof Error ? e.message : e);
|
|
762
|
+
acctCollateral[info.symbol] = "0";
|
|
765
763
|
}
|
|
766
764
|
}
|
|
767
765
|
result.agentAccount = {
|
|
@@ -788,6 +786,50 @@ var MorphoClient = class {
|
|
|
788
786
|
this._refreshSigner();
|
|
789
787
|
return { tx: receipt.hash, amount: usdcAmount, agentAccount: acctAddr };
|
|
790
788
|
}
|
|
789
|
+
/**
|
|
790
|
+
* Withdraw (transfer) a token from AgentAccount to EOA.
|
|
791
|
+
* Executes an ERC-20 transfer via Safe UserOp.
|
|
792
|
+
*
|
|
793
|
+
* @param tokenSymbol - Token to withdraw (e.g. 'USDC', 'WETH', 'wstETH')
|
|
794
|
+
* @param amount - Amount to withdraw (human-readable, e.g. '100' for 100 USDC, or 'all')
|
|
795
|
+
*/
|
|
796
|
+
async withdrawToken(tokenSymbol, amount) {
|
|
797
|
+
const acctAddr = await this.getAccountAddress();
|
|
798
|
+
const eoaAddr = await this.getSignerAddress();
|
|
799
|
+
const tokenInfo = await this._resolveToken(tokenSymbol);
|
|
800
|
+
const tokenContract = new import_ethers2.Contract(tokenInfo.address, ERC20_ABI, this.provider);
|
|
801
|
+
let weiAmount;
|
|
802
|
+
if (amount === "all") {
|
|
803
|
+
weiAmount = await tokenContract.balanceOf(acctAddr);
|
|
804
|
+
if (weiAmount === 0n) throw new AgetherError(`No ${tokenSymbol} in AgentAccount`, "INSUFFICIENT_BALANCE");
|
|
805
|
+
} else {
|
|
806
|
+
weiAmount = import_ethers2.ethers.parseUnits(amount, tokenInfo.decimals);
|
|
807
|
+
}
|
|
808
|
+
const data = erc20Iface.encodeFunctionData("transfer", [eoaAddr, weiAmount]);
|
|
809
|
+
const receipt = await this.exec(tokenInfo.address, data);
|
|
810
|
+
const actualAmount = amount === "all" ? import_ethers2.ethers.formatUnits(weiAmount, tokenInfo.decimals) : amount;
|
|
811
|
+
return { tx: receipt.hash, token: tokenSymbol, amount: actualAmount, destination: eoaAddr };
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Withdraw ETH from AgentAccount to EOA.
|
|
815
|
+
* Executes a native ETH transfer via Safe UserOp.
|
|
816
|
+
*
|
|
817
|
+
* @param amount - ETH amount (e.g. '0.01' or 'all')
|
|
818
|
+
*/
|
|
819
|
+
async withdrawEth(amount) {
|
|
820
|
+
const acctAddr = await this.getAccountAddress();
|
|
821
|
+
const eoaAddr = await this.getSignerAddress();
|
|
822
|
+
let weiAmount;
|
|
823
|
+
if (amount === "all") {
|
|
824
|
+
weiAmount = await this.provider.getBalance(acctAddr);
|
|
825
|
+
if (weiAmount === 0n) throw new AgetherError("No ETH in AgentAccount", "INSUFFICIENT_BALANCE");
|
|
826
|
+
} else {
|
|
827
|
+
weiAmount = import_ethers2.ethers.parseEther(amount);
|
|
828
|
+
}
|
|
829
|
+
const receipt = await this.exec(eoaAddr, "0x", weiAmount);
|
|
830
|
+
const actualAmount = amount === "all" ? import_ethers2.ethers.formatEther(weiAmount) : amount;
|
|
831
|
+
return { tx: receipt.hash, token: "ETH", amount: actualAmount, destination: eoaAddr };
|
|
832
|
+
}
|
|
791
833
|
// ════════════════════════════════════════════════════════
|
|
792
834
|
// Market Discovery (Morpho GraphQL API)
|
|
793
835
|
// ════════════════════════════════════════════════════════
|
|
@@ -847,6 +889,28 @@ var MorphoClient = class {
|
|
|
847
889
|
irm: mi.irm,
|
|
848
890
|
lltv: mi.lltv
|
|
849
891
|
});
|
|
892
|
+
this._tokenCache.set(mi.collateralAsset.symbol.toUpperCase(), {
|
|
893
|
+
address: mi.collateralAsset.address,
|
|
894
|
+
symbol: mi.collateralAsset.symbol,
|
|
895
|
+
decimals: mi.collateralAsset.decimals
|
|
896
|
+
});
|
|
897
|
+
this._tokenCache.set(mi.collateralAsset.address.toLowerCase(), {
|
|
898
|
+
address: mi.collateralAsset.address,
|
|
899
|
+
symbol: mi.collateralAsset.symbol,
|
|
900
|
+
decimals: mi.collateralAsset.decimals
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
if (mi.loanAsset.address !== import_ethers2.ethers.ZeroAddress) {
|
|
904
|
+
this._tokenCache.set(mi.loanAsset.symbol.toUpperCase(), {
|
|
905
|
+
address: mi.loanAsset.address,
|
|
906
|
+
symbol: mi.loanAsset.symbol,
|
|
907
|
+
decimals: mi.loanAsset.decimals
|
|
908
|
+
});
|
|
909
|
+
this._tokenCache.set(mi.loanAsset.address.toLowerCase(), {
|
|
910
|
+
address: mi.loanAsset.address,
|
|
911
|
+
symbol: mi.loanAsset.symbol,
|
|
912
|
+
decimals: mi.loanAsset.decimals
|
|
913
|
+
});
|
|
850
914
|
}
|
|
851
915
|
}
|
|
852
916
|
return this._discoveredMarkets;
|
|
@@ -860,8 +924,17 @@ var MorphoClient = class {
|
|
|
860
924
|
* Tries cache → API → onchain idToMarketParams.
|
|
861
925
|
*/
|
|
862
926
|
async findMarketForCollateral(collateralSymbolOrAddress) {
|
|
863
|
-
|
|
864
|
-
|
|
927
|
+
let colAddr;
|
|
928
|
+
if (collateralSymbolOrAddress.startsWith("0x")) {
|
|
929
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
930
|
+
} else {
|
|
931
|
+
try {
|
|
932
|
+
const resolved = await this._resolveToken(collateralSymbolOrAddress);
|
|
933
|
+
colAddr = resolved.address.toLowerCase();
|
|
934
|
+
} catch {
|
|
935
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
936
|
+
}
|
|
937
|
+
}
|
|
865
938
|
const cached = this._marketCache.get(colAddr);
|
|
866
939
|
if (cached) return cached;
|
|
867
940
|
await this.getMarkets();
|
|
@@ -1020,8 +1093,17 @@ var MorphoClient = class {
|
|
|
1020
1093
|
const usdcAddr = this.config.contracts.usdc.toLowerCase();
|
|
1021
1094
|
let collateralFilter = "";
|
|
1022
1095
|
if (collateralSymbolOrAddress) {
|
|
1023
|
-
|
|
1024
|
-
|
|
1096
|
+
let colAddr;
|
|
1097
|
+
if (collateralSymbolOrAddress.startsWith("0x")) {
|
|
1098
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
1099
|
+
} else {
|
|
1100
|
+
try {
|
|
1101
|
+
const resolved = await this._resolveToken(collateralSymbolOrAddress);
|
|
1102
|
+
colAddr = resolved.address.toLowerCase();
|
|
1103
|
+
} catch {
|
|
1104
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1025
1107
|
collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
|
|
1026
1108
|
}
|
|
1027
1109
|
const query = `{
|
|
@@ -1080,8 +1162,7 @@ var MorphoClient = class {
|
|
|
1080
1162
|
* @returns Estimated yield in USD for the period
|
|
1081
1163
|
*/
|
|
1082
1164
|
async getYieldEstimate(collateralSymbol, amount, periodDays = 1, ethPriceUsd) {
|
|
1083
|
-
const colInfo =
|
|
1084
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${collateralSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1165
|
+
const colInfo = await this._resolveToken(collateralSymbol);
|
|
1085
1166
|
const rates = await this.getMarketRates(collateralSymbol);
|
|
1086
1167
|
if (rates.length === 0) {
|
|
1087
1168
|
throw new AgetherError(`No market found for ${collateralSymbol}`, "MARKET_NOT_FOUND");
|
|
@@ -1383,8 +1464,7 @@ var MorphoClient = class {
|
|
|
1383
1464
|
*/
|
|
1384
1465
|
async supplyCollateral(tokenSymbol, amount, marketParams) {
|
|
1385
1466
|
const acctAddr = await this.getAccountAddress();
|
|
1386
|
-
const colInfo =
|
|
1387
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1467
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1388
1468
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1389
1469
|
const weiAmount = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
|
|
1390
1470
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
@@ -1505,8 +1585,7 @@ var MorphoClient = class {
|
|
|
1505
1585
|
*/
|
|
1506
1586
|
async depositAndBorrow(tokenSymbol, collateralAmount, borrowUsdcAmount, marketParams) {
|
|
1507
1587
|
const acctAddr = await this.getAccountAddress();
|
|
1508
|
-
const colInfo =
|
|
1509
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1588
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1510
1589
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1511
1590
|
const colWei = import_ethers2.ethers.parseUnits(collateralAmount, colInfo.decimals);
|
|
1512
1591
|
const borrowWei = import_ethers2.ethers.parseUnits(borrowUsdcAmount, 6);
|
|
@@ -1674,8 +1753,7 @@ var MorphoClient = class {
|
|
|
1674
1753
|
*/
|
|
1675
1754
|
async withdrawCollateral(tokenSymbol, amount, marketParams, receiver) {
|
|
1676
1755
|
const acctAddr = await this.getAccountAddress();
|
|
1677
|
-
const colInfo =
|
|
1678
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1756
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1679
1757
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1680
1758
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1681
1759
|
const usdcAddr = this.config.contracts.usdc;
|
|
@@ -1772,8 +1850,7 @@ var MorphoClient = class {
|
|
|
1772
1850
|
* (The agent must then supplyCollateral themselves via their own account.)
|
|
1773
1851
|
*/
|
|
1774
1852
|
async sponsor(target, tokenSymbol, amount) {
|
|
1775
|
-
const colInfo =
|
|
1776
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1853
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1777
1854
|
let targetAddr;
|
|
1778
1855
|
if (target.address) {
|
|
1779
1856
|
targetAddr = target.address;
|
|
@@ -2029,6 +2106,43 @@ var MorphoClient = class {
|
|
|
2029
2106
|
}
|
|
2030
2107
|
throw new AgetherError("No active supply position found", "NO_SUPPLY");
|
|
2031
2108
|
}
|
|
2109
|
+
/**
|
|
2110
|
+
* Resolve a token symbol or address to { address, symbol, decimals }.
|
|
2111
|
+
*
|
|
2112
|
+
* Uses the dynamic `_tokenCache` populated by `getMarkets()` from the
|
|
2113
|
+
* Morpho GraphQL API — no hardcoded token list needed.
|
|
2114
|
+
*
|
|
2115
|
+
* @param symbolOrAddress - e.g. 'WETH', 'wstETH', or '0x4200...'
|
|
2116
|
+
*/
|
|
2117
|
+
async _resolveToken(symbolOrAddress) {
|
|
2118
|
+
const key = symbolOrAddress.startsWith("0x") ? symbolOrAddress.toLowerCase() : symbolOrAddress.toUpperCase();
|
|
2119
|
+
const cached = this._tokenCache.get(key);
|
|
2120
|
+
if (cached) return cached;
|
|
2121
|
+
await this.getMarkets();
|
|
2122
|
+
const fromApi = this._tokenCache.get(key);
|
|
2123
|
+
if (fromApi) return fromApi;
|
|
2124
|
+
throw new AgetherError(
|
|
2125
|
+
`Unknown token: ${symbolOrAddress}. No Morpho market found with this collateral.`,
|
|
2126
|
+
"UNKNOWN_COLLATERAL"
|
|
2127
|
+
);
|
|
2128
|
+
}
|
|
2129
|
+
/**
|
|
2130
|
+
* Get all discovered collateral tokens (for balance iteration, etc.).
|
|
2131
|
+
* Returns unique tokens from the Morpho API market discovery.
|
|
2132
|
+
*/
|
|
2133
|
+
async _getDiscoveredTokens() {
|
|
2134
|
+
await this.getMarkets();
|
|
2135
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2136
|
+
const tokens = [];
|
|
2137
|
+
for (const [key, info] of this._tokenCache) {
|
|
2138
|
+
if (key.startsWith("0x")) continue;
|
|
2139
|
+
if (seen.has(info.address.toLowerCase())) continue;
|
|
2140
|
+
seen.add(info.address.toLowerCase());
|
|
2141
|
+
if (info.symbol === "USDC" || info.symbol === "USDbC") continue;
|
|
2142
|
+
tokens.push(info);
|
|
2143
|
+
}
|
|
2144
|
+
return tokens;
|
|
2145
|
+
}
|
|
2032
2146
|
/**
|
|
2033
2147
|
* Compute net deposited amounts per market using Morpho GraphQL API.
|
|
2034
2148
|
*
|
|
@@ -2170,7 +2284,9 @@ var X402Client = class {
|
|
|
2170
2284
|
this.paidFetch = (0, import_fetch.wrapFetchWithPayment)(fetch, client);
|
|
2171
2285
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2172
2286
|
const dailyLimit = config.dailySpendLimitUsdc ? BigInt(Math.round(parseFloat(config.dailySpendLimitUsdc) * 1e6)) : 0n;
|
|
2173
|
-
|
|
2287
|
+
const restored = config.initialSpendingState;
|
|
2288
|
+
const restoredBorrowed = restored && restored.date === today ? BigInt(restored.totalBorrowed) : 0n;
|
|
2289
|
+
this._spendingTracker = { date: today, totalBorrowed: restoredBorrowed, dailyLimit };
|
|
2174
2290
|
}
|
|
2175
2291
|
async get(url, opts) {
|
|
2176
2292
|
return this.request(url, { ...opts, method: "GET" });
|
|
@@ -2199,66 +2315,122 @@ var X402Client = class {
|
|
|
2199
2315
|
return (Number(remaining > 0n ? remaining : 0n) / 1e6).toFixed(2);
|
|
2200
2316
|
}
|
|
2201
2317
|
/**
|
|
2202
|
-
* Pay with auto-
|
|
2318
|
+
* Pay with auto-funding: Make an x402 request with automatic USDC sourcing.
|
|
2203
2319
|
*
|
|
2204
|
-
*
|
|
2205
|
-
*
|
|
2206
|
-
*
|
|
2207
|
-
*
|
|
2208
|
-
*
|
|
2209
|
-
*
|
|
2210
|
-
*
|
|
2320
|
+
* Uses a **plan-then-execute** approach: all reads happen first, and if the
|
|
2321
|
+
* full deficit can't be covered the method fails with NO side-effects
|
|
2322
|
+
* (no yield withdrawn, no USDC borrowed).
|
|
2323
|
+
*
|
|
2324
|
+
* Waterfall (when both autoYield + autoDraw enabled):
|
|
2325
|
+
* 1. Check USDC balance on AgentAccount
|
|
2326
|
+
* 2. Probe the URL to discover payment amount (if 402)
|
|
2327
|
+
* 3. If insufficient USDC — PLANNING PHASE (read-only):
|
|
2328
|
+
* a. Calculate total available yield across supply positions
|
|
2329
|
+
* b. Calculate max borrowable from Morpho markets
|
|
2330
|
+
* c. Verify yield + borrow can cover full deficit — if not, fail immediately
|
|
2331
|
+
* 4. EXECUTION PHASE (only if plan is feasible):
|
|
2332
|
+
* a. Withdraw needed yield from supply positions
|
|
2333
|
+
* b. Borrow remaining deficit from Morpho
|
|
2334
|
+
* 5. Proceed with x402 payment
|
|
2211
2335
|
*/
|
|
2212
2336
|
async payWithAutoDraw(url, opts) {
|
|
2213
2337
|
const { morphoClient, ...fetchOpts } = opts || {};
|
|
2214
|
-
if (!this.config.autoDraw || !morphoClient) {
|
|
2338
|
+
if (!this.config.autoDraw && !this.config.autoYield || !morphoClient) {
|
|
2215
2339
|
return this.request(url, fetchOpts);
|
|
2216
2340
|
}
|
|
2217
2341
|
try {
|
|
2218
2342
|
const usdcBalance = await morphoClient.getUsdcBalance();
|
|
2219
|
-
console.log(` [auto-
|
|
2343
|
+
console.log(` [auto-fund] AgentAccount USDC balance: ${(Number(usdcBalance) / 1e6).toFixed(2)}`);
|
|
2220
2344
|
const paymentAmount = await this._probePaymentAmount(url, fetchOpts);
|
|
2221
2345
|
if (paymentAmount !== null) {
|
|
2222
|
-
console.log(` [auto-
|
|
2346
|
+
console.log(` [auto-fund] Payment required: ${(Number(paymentAmount) / 1e6).toFixed(6)} USDC`);
|
|
2223
2347
|
const bufferStr = this.config.autoDrawBuffer || "0.5";
|
|
2224
2348
|
const buffer = BigInt(Math.round(parseFloat(bufferStr) * 1e6));
|
|
2225
2349
|
const needed = paymentAmount + buffer;
|
|
2226
2350
|
if (usdcBalance < needed) {
|
|
2227
|
-
const
|
|
2228
|
-
console.log(` [auto-
|
|
2229
|
-
const
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2351
|
+
const totalDeficit = needed - usdcBalance;
|
|
2352
|
+
console.log(` [auto-fund] Insufficient balance. Deficit: ${(Number(totalDeficit) / 1e6).toFixed(2)} USDC`);
|
|
2353
|
+
const yieldPlan = [];
|
|
2354
|
+
let totalYieldAvailable = 0n;
|
|
2355
|
+
if (this.config.autoYield) {
|
|
2356
|
+
try {
|
|
2357
|
+
const positions = await morphoClient.getSupplyPositions();
|
|
2358
|
+
const withYield = positions.filter((p) => parseFloat(p.earnedYield) > 1e-6).sort((a, b) => parseFloat(b.earnedYield) - parseFloat(a.earnedYield));
|
|
2359
|
+
for (const pos of withYield) {
|
|
2360
|
+
const yieldRaw = BigInt(Math.floor(parseFloat(pos.earnedYield) * 1e6));
|
|
2361
|
+
if (yieldRaw > 0n) {
|
|
2362
|
+
yieldPlan.push({ collateralToken: pos.collateralToken, amount: yieldRaw });
|
|
2363
|
+
totalYieldAvailable += yieldRaw;
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
console.log(` [auto-fund] Plan: ${(Number(totalYieldAvailable) / 1e6).toFixed(6)} USDC available from yield`);
|
|
2367
|
+
} catch (e) {
|
|
2368
|
+
console.warn(` [auto-fund] Plan: yield check failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
2369
|
+
}
|
|
2235
2370
|
}
|
|
2236
|
-
const
|
|
2237
|
-
|
|
2371
|
+
const yieldToUse = totalYieldAvailable < totalDeficit ? totalYieldAvailable : totalDeficit;
|
|
2372
|
+
const deficitAfterYield = totalDeficit - yieldToUse;
|
|
2373
|
+
let canBorrow = 0n;
|
|
2374
|
+
if (this.config.autoDraw && deficitAfterYield > 0n) {
|
|
2375
|
+
const limitCheck = await this._checkSpendingLimit(deficitAfterYield, morphoClient);
|
|
2376
|
+
if (!limitCheck.allowed) {
|
|
2377
|
+
return { success: false, error: `Auto-fund blocked: ${limitCheck.reason}` };
|
|
2378
|
+
}
|
|
2379
|
+
const maxBorrowable = await morphoClient.getMaxBorrowable();
|
|
2380
|
+
canBorrow = maxBorrowable.total;
|
|
2381
|
+
console.log(` [auto-fund] Plan: ${(Number(canBorrow) / 1e6).toFixed(2)} USDC borrowable from Morpho`);
|
|
2382
|
+
}
|
|
2383
|
+
const totalAvailable = yieldToUse + canBorrow;
|
|
2384
|
+
if (totalAvailable < totalDeficit) {
|
|
2385
|
+
const parts = [];
|
|
2386
|
+
if (totalYieldAvailable > 0n) parts.push(`yield: $${(Number(totalYieldAvailable) / 1e6).toFixed(2)}`);
|
|
2387
|
+
if (canBorrow > 0n) parts.push(`borrowable: $${(Number(canBorrow) / 1e6).toFixed(2)}`);
|
|
2388
|
+
if (!this.config.autoYield) parts.push("autoYield: off");
|
|
2389
|
+
if (!this.config.autoDraw) parts.push("autoDraw: off");
|
|
2238
2390
|
return {
|
|
2239
2391
|
success: false,
|
|
2240
|
-
error: `
|
|
2392
|
+
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)}.`
|
|
2241
2393
|
};
|
|
2242
2394
|
}
|
|
2243
|
-
const
|
|
2244
|
-
|
|
2245
|
-
const borrowResult = await morphoClient.borrow(borrowAmount);
|
|
2246
|
-
console.log(` [auto-draw] Borrow tx: ${borrowResult.tx}`);
|
|
2247
|
-
this._trackSpending(deficit);
|
|
2248
|
-
const result = await this.request(url, fetchOpts);
|
|
2249
|
-
return {
|
|
2250
|
-
...result,
|
|
2251
|
-
autoDrawInfo: {
|
|
2252
|
-
borrowed: borrowAmount,
|
|
2253
|
-
borrowTx: borrowResult.tx,
|
|
2254
|
-
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2255
|
-
}
|
|
2395
|
+
const drawInfo = {
|
|
2396
|
+
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2256
2397
|
};
|
|
2398
|
+
if (yieldToUse > 0n) {
|
|
2399
|
+
let remaining = yieldToUse;
|
|
2400
|
+
let totalWithdrawn = 0n;
|
|
2401
|
+
let lastTx = "";
|
|
2402
|
+
for (const plan of yieldPlan) {
|
|
2403
|
+
if (remaining <= 0n) break;
|
|
2404
|
+
const toWithdraw = plan.amount < remaining ? plan.amount : remaining;
|
|
2405
|
+
const withdrawStr = (Number(toWithdraw) / 1e6).toFixed(6);
|
|
2406
|
+
console.log(` [auto-yield] Withdrawing $${withdrawStr} yield from ${plan.collateralToken} market`);
|
|
2407
|
+
const wr = await morphoClient.withdrawSupply(withdrawStr, plan.collateralToken);
|
|
2408
|
+
lastTx = wr.tx;
|
|
2409
|
+
totalWithdrawn += toWithdraw;
|
|
2410
|
+
remaining -= toWithdraw;
|
|
2411
|
+
}
|
|
2412
|
+
if (totalWithdrawn > 0n) {
|
|
2413
|
+
drawInfo.yieldWithdrawn = (Number(totalWithdrawn) / 1e6).toFixed(6);
|
|
2414
|
+
drawInfo.yieldTx = lastTx;
|
|
2415
|
+
console.log(` [auto-yield] Withdrawn: $${drawInfo.yieldWithdrawn}`);
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
if (deficitAfterYield > 0n && this.config.autoDraw) {
|
|
2419
|
+
const borrowAmount = (Number(deficitAfterYield) / 1e6).toFixed(6);
|
|
2420
|
+
console.log(` [auto-draw] Borrowing ${borrowAmount} USDC from Morpho...`);
|
|
2421
|
+
const borrowResult = await morphoClient.borrow(borrowAmount);
|
|
2422
|
+
console.log(` [auto-draw] Borrow tx: ${borrowResult.tx}`);
|
|
2423
|
+
drawInfo.borrowed = borrowAmount;
|
|
2424
|
+
drawInfo.borrowTx = borrowResult.tx;
|
|
2425
|
+
this._trackSpending(deficitAfterYield);
|
|
2426
|
+
}
|
|
2427
|
+
const result = await this.request(url, fetchOpts);
|
|
2428
|
+
return { ...result, autoDrawInfo: drawInfo };
|
|
2257
2429
|
}
|
|
2258
2430
|
}
|
|
2259
2431
|
return this.request(url, fetchOpts);
|
|
2260
2432
|
} catch (error) {
|
|
2261
|
-
console.log(` [auto-
|
|
2433
|
+
console.log(` [auto-fund] Failed: ${error instanceof Error ? error.message : String(error)}. Proceeding with normal request.`);
|
|
2262
2434
|
return this.request(url, fetchOpts);
|
|
2263
2435
|
}
|
|
2264
2436
|
}
|
|
@@ -2359,48 +2531,26 @@ var X402Client = class {
|
|
|
2359
2531
|
};
|
|
2360
2532
|
}
|
|
2361
2533
|
}
|
|
2362
|
-
/** Track a new spending amount */
|
|
2534
|
+
/** Track a new spending amount and notify the caller for persistence */
|
|
2363
2535
|
_trackSpending(amount) {
|
|
2364
2536
|
this._resetTrackerIfNewDay();
|
|
2365
2537
|
this._spendingTracker.totalBorrowed += amount;
|
|
2366
|
-
|
|
2367
|
-
/**
|
|
2368
|
-
* Check if a borrow amount is within spending limits.
|
|
2369
|
-
* Considers both fixed daily limits and yield-limited spending.
|
|
2370
|
-
*/
|
|
2371
|
-
async _checkSpendingLimit(amount, morphoClient) {
|
|
2372
|
-
this._resetTrackerIfNewDay();
|
|
2373
|
-
if (this.config.yieldLimitedSpending) {
|
|
2538
|
+
if (this.config.onSpendingUpdate) {
|
|
2374
2539
|
try {
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
try {
|
|
2380
|
-
const estimate = await morphoClient.getYieldEstimate(
|
|
2381
|
-
pos.collateralToken,
|
|
2382
|
-
pos.collateral,
|
|
2383
|
-
1
|
|
2384
|
-
// 1 day
|
|
2385
|
-
);
|
|
2386
|
-
totalDailyYieldUsdc += estimate.estimatedYieldUsd;
|
|
2387
|
-
} catch (e) {
|
|
2388
|
-
console.warn(`[agether] yield calc failed for ${pos.collateralToken}:`, e instanceof Error ? e.message : e);
|
|
2389
|
-
}
|
|
2390
|
-
}
|
|
2391
|
-
}
|
|
2392
|
-
const yieldLimit = BigInt(Math.round(totalDailyYieldUsdc * 1e6));
|
|
2393
|
-
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2394
|
-
if (yieldLimit > 0n && newTotal > yieldLimit) {
|
|
2395
|
-
return {
|
|
2396
|
-
allowed: false,
|
|
2397
|
-
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)}`
|
|
2398
|
-
};
|
|
2399
|
-
}
|
|
2540
|
+
this.config.onSpendingUpdate({
|
|
2541
|
+
date: this._spendingTracker.date,
|
|
2542
|
+
totalBorrowed: this._spendingTracker.totalBorrowed.toString()
|
|
2543
|
+
});
|
|
2400
2544
|
} catch (e) {
|
|
2401
|
-
console.warn("[agether]
|
|
2545
|
+
console.warn("[agether] onSpendingUpdate callback failed:", e instanceof Error ? e.message : e);
|
|
2402
2546
|
}
|
|
2403
2547
|
}
|
|
2548
|
+
}
|
|
2549
|
+
/**
|
|
2550
|
+
* Check if a borrow amount is within the fixed daily spending limit.
|
|
2551
|
+
*/
|
|
2552
|
+
async _checkSpendingLimit(amount, _morphoClient) {
|
|
2553
|
+
this._resetTrackerIfNewDay();
|
|
2404
2554
|
if (this._spendingTracker.dailyLimit > 0n) {
|
|
2405
2555
|
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2406
2556
|
if (newTotal > this._spendingTracker.dailyLimit) {
|