@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/cli.js
CHANGED
|
@@ -274,7 +274,7 @@ var MorphoClient_exports = {};
|
|
|
274
274
|
__export(MorphoClient_exports, {
|
|
275
275
|
MorphoClient: () => MorphoClient
|
|
276
276
|
});
|
|
277
|
-
var import_ethers, import_axios,
|
|
277
|
+
var import_ethers, import_axios, MORPHO_API_URL, MODE_SINGLE, MODE_BATCH, morphoIface, erc20Iface, MorphoClient;
|
|
278
278
|
var init_MorphoClient = __esm({
|
|
279
279
|
"src/clients/MorphoClient.ts"() {
|
|
280
280
|
"use strict";
|
|
@@ -283,11 +283,6 @@ var init_MorphoClient = __esm({
|
|
|
283
283
|
init_types();
|
|
284
284
|
init_abis();
|
|
285
285
|
init_config();
|
|
286
|
-
BASE_COLLATERALS = {
|
|
287
|
-
WETH: { address: "0x4200000000000000000000000000000000000006", symbol: "WETH", decimals: 18 },
|
|
288
|
-
wstETH: { address: "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", symbol: "wstETH", decimals: 18 },
|
|
289
|
-
cbETH: { address: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", symbol: "cbETH", decimals: 18 }
|
|
290
|
-
};
|
|
291
286
|
MORPHO_API_URL = "https://api.morpho.org/graphql";
|
|
292
287
|
MODE_SINGLE = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
293
288
|
MODE_BATCH = "0x0100000000000000000000000000000000000000000000000000000000000000";
|
|
@@ -296,6 +291,8 @@ var init_MorphoClient = __esm({
|
|
|
296
291
|
MorphoClient = class {
|
|
297
292
|
constructor(config) {
|
|
298
293
|
this._marketCache = /* @__PURE__ */ new Map();
|
|
294
|
+
/** Dynamic token registry: symbol (uppercase) → { address, symbol, decimals } */
|
|
295
|
+
this._tokenCache = /* @__PURE__ */ new Map();
|
|
299
296
|
this._discoveredAt = 0;
|
|
300
297
|
const chainId = config.chainId ?? 8453 /* Base */;
|
|
301
298
|
const defaultCfg = getDefaultConfig(chainId);
|
|
@@ -482,15 +479,16 @@ var init_MorphoClient = __esm({
|
|
|
482
479
|
const usdc = new import_ethers.Contract(this.config.contracts.usdc, ERC20_ABI, this.provider);
|
|
483
480
|
const ethBal = await this.provider.getBalance(eoaAddr);
|
|
484
481
|
const usdcBal = await usdc.balanceOf(eoaAddr);
|
|
482
|
+
const discoveredTokens = await this._getDiscoveredTokens();
|
|
485
483
|
const eoaCollateral = {};
|
|
486
|
-
for (const
|
|
484
|
+
for (const info of discoveredTokens) {
|
|
487
485
|
try {
|
|
488
486
|
const token = new import_ethers.Contract(info.address, ERC20_ABI, this.provider);
|
|
489
487
|
const bal = await token.balanceOf(eoaAddr);
|
|
490
|
-
eoaCollateral[symbol] = import_ethers.ethers.formatUnits(bal, info.decimals);
|
|
488
|
+
eoaCollateral[info.symbol] = import_ethers.ethers.formatUnits(bal, info.decimals);
|
|
491
489
|
} catch (e) {
|
|
492
|
-
console.warn(`[agether] EOA collateral fetch failed for ${symbol}:`, e instanceof Error ? e.message : e);
|
|
493
|
-
eoaCollateral[symbol] = "0";
|
|
490
|
+
console.warn(`[agether] EOA collateral fetch failed for ${info.symbol}:`, e instanceof Error ? e.message : e);
|
|
491
|
+
eoaCollateral[info.symbol] = "0";
|
|
494
492
|
}
|
|
495
493
|
}
|
|
496
494
|
const result = {
|
|
@@ -505,14 +503,14 @@ var init_MorphoClient = __esm({
|
|
|
505
503
|
const acctEth = await this.provider.getBalance(acctAddr);
|
|
506
504
|
const acctUsdc = await usdc.balanceOf(acctAddr);
|
|
507
505
|
const acctCollateral = {};
|
|
508
|
-
for (const
|
|
506
|
+
for (const info of discoveredTokens) {
|
|
509
507
|
try {
|
|
510
508
|
const token = new import_ethers.Contract(info.address, ERC20_ABI, this.provider);
|
|
511
509
|
const bal = await token.balanceOf(acctAddr);
|
|
512
|
-
acctCollateral[symbol] = import_ethers.ethers.formatUnits(bal, info.decimals);
|
|
510
|
+
acctCollateral[info.symbol] = import_ethers.ethers.formatUnits(bal, info.decimals);
|
|
513
511
|
} catch (e) {
|
|
514
|
-
console.warn(`[agether] AgentAccount collateral fetch failed for ${symbol}:`, e instanceof Error ? e.message : e);
|
|
515
|
-
acctCollateral[symbol] = "0";
|
|
512
|
+
console.warn(`[agether] AgentAccount collateral fetch failed for ${info.symbol}:`, e instanceof Error ? e.message : e);
|
|
513
|
+
acctCollateral[info.symbol] = "0";
|
|
516
514
|
}
|
|
517
515
|
}
|
|
518
516
|
result.agentAccount = {
|
|
@@ -539,6 +537,50 @@ var init_MorphoClient = __esm({
|
|
|
539
537
|
this._refreshSigner();
|
|
540
538
|
return { tx: receipt.hash, amount: usdcAmount, agentAccount: acctAddr };
|
|
541
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* Withdraw (transfer) a token from AgentAccount to EOA.
|
|
542
|
+
* Executes an ERC-20 transfer via Safe UserOp.
|
|
543
|
+
*
|
|
544
|
+
* @param tokenSymbol - Token to withdraw (e.g. 'USDC', 'WETH', 'wstETH')
|
|
545
|
+
* @param amount - Amount to withdraw (human-readable, e.g. '100' for 100 USDC, or 'all')
|
|
546
|
+
*/
|
|
547
|
+
async withdrawToken(tokenSymbol, amount) {
|
|
548
|
+
const acctAddr = await this.getAccountAddress();
|
|
549
|
+
const eoaAddr = await this.getSignerAddress();
|
|
550
|
+
const tokenInfo = await this._resolveToken(tokenSymbol);
|
|
551
|
+
const tokenContract = new import_ethers.Contract(tokenInfo.address, ERC20_ABI, this.provider);
|
|
552
|
+
let weiAmount;
|
|
553
|
+
if (amount === "all") {
|
|
554
|
+
weiAmount = await tokenContract.balanceOf(acctAddr);
|
|
555
|
+
if (weiAmount === 0n) throw new AgetherError(`No ${tokenSymbol} in AgentAccount`, "INSUFFICIENT_BALANCE");
|
|
556
|
+
} else {
|
|
557
|
+
weiAmount = import_ethers.ethers.parseUnits(amount, tokenInfo.decimals);
|
|
558
|
+
}
|
|
559
|
+
const data = erc20Iface.encodeFunctionData("transfer", [eoaAddr, weiAmount]);
|
|
560
|
+
const receipt = await this.exec(tokenInfo.address, data);
|
|
561
|
+
const actualAmount = amount === "all" ? import_ethers.ethers.formatUnits(weiAmount, tokenInfo.decimals) : amount;
|
|
562
|
+
return { tx: receipt.hash, token: tokenSymbol, amount: actualAmount, destination: eoaAddr };
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Withdraw ETH from AgentAccount to EOA.
|
|
566
|
+
* Executes a native ETH transfer via Safe UserOp.
|
|
567
|
+
*
|
|
568
|
+
* @param amount - ETH amount (e.g. '0.01' or 'all')
|
|
569
|
+
*/
|
|
570
|
+
async withdrawEth(amount) {
|
|
571
|
+
const acctAddr = await this.getAccountAddress();
|
|
572
|
+
const eoaAddr = await this.getSignerAddress();
|
|
573
|
+
let weiAmount;
|
|
574
|
+
if (amount === "all") {
|
|
575
|
+
weiAmount = await this.provider.getBalance(acctAddr);
|
|
576
|
+
if (weiAmount === 0n) throw new AgetherError("No ETH in AgentAccount", "INSUFFICIENT_BALANCE");
|
|
577
|
+
} else {
|
|
578
|
+
weiAmount = import_ethers.ethers.parseEther(amount);
|
|
579
|
+
}
|
|
580
|
+
const receipt = await this.exec(eoaAddr, "0x", weiAmount);
|
|
581
|
+
const actualAmount = amount === "all" ? import_ethers.ethers.formatEther(weiAmount) : amount;
|
|
582
|
+
return { tx: receipt.hash, token: "ETH", amount: actualAmount, destination: eoaAddr };
|
|
583
|
+
}
|
|
542
584
|
// ════════════════════════════════════════════════════════
|
|
543
585
|
// Market Discovery (Morpho GraphQL API)
|
|
544
586
|
// ════════════════════════════════════════════════════════
|
|
@@ -598,6 +640,28 @@ var init_MorphoClient = __esm({
|
|
|
598
640
|
irm: mi.irm,
|
|
599
641
|
lltv: mi.lltv
|
|
600
642
|
});
|
|
643
|
+
this._tokenCache.set(mi.collateralAsset.symbol.toUpperCase(), {
|
|
644
|
+
address: mi.collateralAsset.address,
|
|
645
|
+
symbol: mi.collateralAsset.symbol,
|
|
646
|
+
decimals: mi.collateralAsset.decimals
|
|
647
|
+
});
|
|
648
|
+
this._tokenCache.set(mi.collateralAsset.address.toLowerCase(), {
|
|
649
|
+
address: mi.collateralAsset.address,
|
|
650
|
+
symbol: mi.collateralAsset.symbol,
|
|
651
|
+
decimals: mi.collateralAsset.decimals
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
if (mi.loanAsset.address !== import_ethers.ethers.ZeroAddress) {
|
|
655
|
+
this._tokenCache.set(mi.loanAsset.symbol.toUpperCase(), {
|
|
656
|
+
address: mi.loanAsset.address,
|
|
657
|
+
symbol: mi.loanAsset.symbol,
|
|
658
|
+
decimals: mi.loanAsset.decimals
|
|
659
|
+
});
|
|
660
|
+
this._tokenCache.set(mi.loanAsset.address.toLowerCase(), {
|
|
661
|
+
address: mi.loanAsset.address,
|
|
662
|
+
symbol: mi.loanAsset.symbol,
|
|
663
|
+
decimals: mi.loanAsset.decimals
|
|
664
|
+
});
|
|
601
665
|
}
|
|
602
666
|
}
|
|
603
667
|
return this._discoveredMarkets;
|
|
@@ -611,8 +675,17 @@ var init_MorphoClient = __esm({
|
|
|
611
675
|
* Tries cache → API → onchain idToMarketParams.
|
|
612
676
|
*/
|
|
613
677
|
async findMarketForCollateral(collateralSymbolOrAddress) {
|
|
614
|
-
|
|
615
|
-
|
|
678
|
+
let colAddr;
|
|
679
|
+
if (collateralSymbolOrAddress.startsWith("0x")) {
|
|
680
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
681
|
+
} else {
|
|
682
|
+
try {
|
|
683
|
+
const resolved = await this._resolveToken(collateralSymbolOrAddress);
|
|
684
|
+
colAddr = resolved.address.toLowerCase();
|
|
685
|
+
} catch {
|
|
686
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
687
|
+
}
|
|
688
|
+
}
|
|
616
689
|
const cached = this._marketCache.get(colAddr);
|
|
617
690
|
if (cached) return cached;
|
|
618
691
|
await this.getMarkets();
|
|
@@ -771,8 +844,17 @@ var init_MorphoClient = __esm({
|
|
|
771
844
|
const usdcAddr = this.config.contracts.usdc.toLowerCase();
|
|
772
845
|
let collateralFilter = "";
|
|
773
846
|
if (collateralSymbolOrAddress) {
|
|
774
|
-
|
|
775
|
-
|
|
847
|
+
let colAddr;
|
|
848
|
+
if (collateralSymbolOrAddress.startsWith("0x")) {
|
|
849
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
850
|
+
} else {
|
|
851
|
+
try {
|
|
852
|
+
const resolved = await this._resolveToken(collateralSymbolOrAddress);
|
|
853
|
+
colAddr = resolved.address.toLowerCase();
|
|
854
|
+
} catch {
|
|
855
|
+
colAddr = collateralSymbolOrAddress.toLowerCase();
|
|
856
|
+
}
|
|
857
|
+
}
|
|
776
858
|
collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
|
|
777
859
|
}
|
|
778
860
|
const query = `{
|
|
@@ -831,8 +913,7 @@ var init_MorphoClient = __esm({
|
|
|
831
913
|
* @returns Estimated yield in USD for the period
|
|
832
914
|
*/
|
|
833
915
|
async getYieldEstimate(collateralSymbol, amount, periodDays = 1, ethPriceUsd) {
|
|
834
|
-
const colInfo =
|
|
835
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${collateralSymbol}`, "UNKNOWN_COLLATERAL");
|
|
916
|
+
const colInfo = await this._resolveToken(collateralSymbol);
|
|
836
917
|
const rates = await this.getMarketRates(collateralSymbol);
|
|
837
918
|
if (rates.length === 0) {
|
|
838
919
|
throw new AgetherError(`No market found for ${collateralSymbol}`, "MARKET_NOT_FOUND");
|
|
@@ -1134,8 +1215,7 @@ var init_MorphoClient = __esm({
|
|
|
1134
1215
|
*/
|
|
1135
1216
|
async supplyCollateral(tokenSymbol, amount, marketParams) {
|
|
1136
1217
|
const acctAddr = await this.getAccountAddress();
|
|
1137
|
-
const colInfo =
|
|
1138
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1218
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1139
1219
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1140
1220
|
const weiAmount = import_ethers.ethers.parseUnits(amount, colInfo.decimals);
|
|
1141
1221
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
@@ -1256,8 +1336,7 @@ var init_MorphoClient = __esm({
|
|
|
1256
1336
|
*/
|
|
1257
1337
|
async depositAndBorrow(tokenSymbol, collateralAmount, borrowUsdcAmount, marketParams) {
|
|
1258
1338
|
const acctAddr = await this.getAccountAddress();
|
|
1259
|
-
const colInfo =
|
|
1260
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1339
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1261
1340
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1262
1341
|
const colWei = import_ethers.ethers.parseUnits(collateralAmount, colInfo.decimals);
|
|
1263
1342
|
const borrowWei = import_ethers.ethers.parseUnits(borrowUsdcAmount, 6);
|
|
@@ -1425,8 +1504,7 @@ var init_MorphoClient = __esm({
|
|
|
1425
1504
|
*/
|
|
1426
1505
|
async withdrawCollateral(tokenSymbol, amount, marketParams, receiver) {
|
|
1427
1506
|
const acctAddr = await this.getAccountAddress();
|
|
1428
|
-
const colInfo =
|
|
1429
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1507
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1430
1508
|
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
1431
1509
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1432
1510
|
const usdcAddr = this.config.contracts.usdc;
|
|
@@ -1523,8 +1601,7 @@ var init_MorphoClient = __esm({
|
|
|
1523
1601
|
* (The agent must then supplyCollateral themselves via their own account.)
|
|
1524
1602
|
*/
|
|
1525
1603
|
async sponsor(target, tokenSymbol, amount) {
|
|
1526
|
-
const colInfo =
|
|
1527
|
-
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
1604
|
+
const colInfo = await this._resolveToken(tokenSymbol);
|
|
1528
1605
|
let targetAddr;
|
|
1529
1606
|
if (target.address) {
|
|
1530
1607
|
targetAddr = target.address;
|
|
@@ -1780,6 +1857,43 @@ var init_MorphoClient = __esm({
|
|
|
1780
1857
|
}
|
|
1781
1858
|
throw new AgetherError("No active supply position found", "NO_SUPPLY");
|
|
1782
1859
|
}
|
|
1860
|
+
/**
|
|
1861
|
+
* Resolve a token symbol or address to { address, symbol, decimals }.
|
|
1862
|
+
*
|
|
1863
|
+
* Uses the dynamic `_tokenCache` populated by `getMarkets()` from the
|
|
1864
|
+
* Morpho GraphQL API — no hardcoded token list needed.
|
|
1865
|
+
*
|
|
1866
|
+
* @param symbolOrAddress - e.g. 'WETH', 'wstETH', or '0x4200...'
|
|
1867
|
+
*/
|
|
1868
|
+
async _resolveToken(symbolOrAddress) {
|
|
1869
|
+
const key = symbolOrAddress.startsWith("0x") ? symbolOrAddress.toLowerCase() : symbolOrAddress.toUpperCase();
|
|
1870
|
+
const cached = this._tokenCache.get(key);
|
|
1871
|
+
if (cached) return cached;
|
|
1872
|
+
await this.getMarkets();
|
|
1873
|
+
const fromApi = this._tokenCache.get(key);
|
|
1874
|
+
if (fromApi) return fromApi;
|
|
1875
|
+
throw new AgetherError(
|
|
1876
|
+
`Unknown token: ${symbolOrAddress}. No Morpho market found with this collateral.`,
|
|
1877
|
+
"UNKNOWN_COLLATERAL"
|
|
1878
|
+
);
|
|
1879
|
+
}
|
|
1880
|
+
/**
|
|
1881
|
+
* Get all discovered collateral tokens (for balance iteration, etc.).
|
|
1882
|
+
* Returns unique tokens from the Morpho API market discovery.
|
|
1883
|
+
*/
|
|
1884
|
+
async _getDiscoveredTokens() {
|
|
1885
|
+
await this.getMarkets();
|
|
1886
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1887
|
+
const tokens = [];
|
|
1888
|
+
for (const [key, info] of this._tokenCache) {
|
|
1889
|
+
if (key.startsWith("0x")) continue;
|
|
1890
|
+
if (seen.has(info.address.toLowerCase())) continue;
|
|
1891
|
+
seen.add(info.address.toLowerCase());
|
|
1892
|
+
if (info.symbol === "USDC" || info.symbol === "USDbC") continue;
|
|
1893
|
+
tokens.push(info);
|
|
1894
|
+
}
|
|
1895
|
+
return tokens;
|
|
1896
|
+
}
|
|
1783
1897
|
/**
|
|
1784
1898
|
* Compute net deposited amounts per market using Morpho GraphQL API.
|
|
1785
1899
|
*
|
|
@@ -1928,7 +2042,9 @@ var init_X402Client = __esm({
|
|
|
1928
2042
|
this.paidFetch = (0, import_fetch.wrapFetchWithPayment)(fetch, client);
|
|
1929
2043
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1930
2044
|
const dailyLimit = config.dailySpendLimitUsdc ? BigInt(Math.round(parseFloat(config.dailySpendLimitUsdc) * 1e6)) : 0n;
|
|
1931
|
-
|
|
2045
|
+
const restored = config.initialSpendingState;
|
|
2046
|
+
const restoredBorrowed = restored && restored.date === today ? BigInt(restored.totalBorrowed) : 0n;
|
|
2047
|
+
this._spendingTracker = { date: today, totalBorrowed: restoredBorrowed, dailyLimit };
|
|
1932
2048
|
}
|
|
1933
2049
|
async get(url, opts) {
|
|
1934
2050
|
return this.request(url, { ...opts, method: "GET" });
|
|
@@ -1957,66 +2073,122 @@ var init_X402Client = __esm({
|
|
|
1957
2073
|
return (Number(remaining > 0n ? remaining : 0n) / 1e6).toFixed(2);
|
|
1958
2074
|
}
|
|
1959
2075
|
/**
|
|
1960
|
-
* Pay with auto-
|
|
2076
|
+
* Pay with auto-funding: Make an x402 request with automatic USDC sourcing.
|
|
1961
2077
|
*
|
|
1962
|
-
*
|
|
1963
|
-
*
|
|
1964
|
-
*
|
|
1965
|
-
*
|
|
1966
|
-
*
|
|
1967
|
-
*
|
|
1968
|
-
*
|
|
2078
|
+
* Uses a **plan-then-execute** approach: all reads happen first, and if the
|
|
2079
|
+
* full deficit can't be covered the method fails with NO side-effects
|
|
2080
|
+
* (no yield withdrawn, no USDC borrowed).
|
|
2081
|
+
*
|
|
2082
|
+
* Waterfall (when both autoYield + autoDraw enabled):
|
|
2083
|
+
* 1. Check USDC balance on AgentAccount
|
|
2084
|
+
* 2. Probe the URL to discover payment amount (if 402)
|
|
2085
|
+
* 3. If insufficient USDC — PLANNING PHASE (read-only):
|
|
2086
|
+
* a. Calculate total available yield across supply positions
|
|
2087
|
+
* b. Calculate max borrowable from Morpho markets
|
|
2088
|
+
* c. Verify yield + borrow can cover full deficit — if not, fail immediately
|
|
2089
|
+
* 4. EXECUTION PHASE (only if plan is feasible):
|
|
2090
|
+
* a. Withdraw needed yield from supply positions
|
|
2091
|
+
* b. Borrow remaining deficit from Morpho
|
|
2092
|
+
* 5. Proceed with x402 payment
|
|
1969
2093
|
*/
|
|
1970
2094
|
async payWithAutoDraw(url, opts) {
|
|
1971
2095
|
const { morphoClient, ...fetchOpts } = opts || {};
|
|
1972
|
-
if (!this.config.autoDraw || !morphoClient) {
|
|
2096
|
+
if (!this.config.autoDraw && !this.config.autoYield || !morphoClient) {
|
|
1973
2097
|
return this.request(url, fetchOpts);
|
|
1974
2098
|
}
|
|
1975
2099
|
try {
|
|
1976
2100
|
const usdcBalance = await morphoClient.getUsdcBalance();
|
|
1977
|
-
console.log(` [auto-
|
|
2101
|
+
console.log(` [auto-fund] AgentAccount USDC balance: ${(Number(usdcBalance) / 1e6).toFixed(2)}`);
|
|
1978
2102
|
const paymentAmount = await this._probePaymentAmount(url, fetchOpts);
|
|
1979
2103
|
if (paymentAmount !== null) {
|
|
1980
|
-
console.log(` [auto-
|
|
2104
|
+
console.log(` [auto-fund] Payment required: ${(Number(paymentAmount) / 1e6).toFixed(6)} USDC`);
|
|
1981
2105
|
const bufferStr = this.config.autoDrawBuffer || "0.5";
|
|
1982
2106
|
const buffer = BigInt(Math.round(parseFloat(bufferStr) * 1e6));
|
|
1983
2107
|
const needed = paymentAmount + buffer;
|
|
1984
2108
|
if (usdcBalance < needed) {
|
|
1985
|
-
const
|
|
1986
|
-
console.log(` [auto-
|
|
1987
|
-
const
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2109
|
+
const totalDeficit = needed - usdcBalance;
|
|
2110
|
+
console.log(` [auto-fund] Insufficient balance. Deficit: ${(Number(totalDeficit) / 1e6).toFixed(2)} USDC`);
|
|
2111
|
+
const yieldPlan = [];
|
|
2112
|
+
let totalYieldAvailable = 0n;
|
|
2113
|
+
if (this.config.autoYield) {
|
|
2114
|
+
try {
|
|
2115
|
+
const positions = await morphoClient.getSupplyPositions();
|
|
2116
|
+
const withYield = positions.filter((p) => parseFloat(p.earnedYield) > 1e-6).sort((a, b) => parseFloat(b.earnedYield) - parseFloat(a.earnedYield));
|
|
2117
|
+
for (const pos of withYield) {
|
|
2118
|
+
const yieldRaw = BigInt(Math.floor(parseFloat(pos.earnedYield) * 1e6));
|
|
2119
|
+
if (yieldRaw > 0n) {
|
|
2120
|
+
yieldPlan.push({ collateralToken: pos.collateralToken, amount: yieldRaw });
|
|
2121
|
+
totalYieldAvailable += yieldRaw;
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
console.log(` [auto-fund] Plan: ${(Number(totalYieldAvailable) / 1e6).toFixed(6)} USDC available from yield`);
|
|
2125
|
+
} catch (e) {
|
|
2126
|
+
console.warn(` [auto-fund] Plan: yield check failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
2127
|
+
}
|
|
1993
2128
|
}
|
|
1994
|
-
const
|
|
1995
|
-
|
|
2129
|
+
const yieldToUse = totalYieldAvailable < totalDeficit ? totalYieldAvailable : totalDeficit;
|
|
2130
|
+
const deficitAfterYield = totalDeficit - yieldToUse;
|
|
2131
|
+
let canBorrow = 0n;
|
|
2132
|
+
if (this.config.autoDraw && deficitAfterYield > 0n) {
|
|
2133
|
+
const limitCheck = await this._checkSpendingLimit(deficitAfterYield, morphoClient);
|
|
2134
|
+
if (!limitCheck.allowed) {
|
|
2135
|
+
return { success: false, error: `Auto-fund blocked: ${limitCheck.reason}` };
|
|
2136
|
+
}
|
|
2137
|
+
const maxBorrowable = await morphoClient.getMaxBorrowable();
|
|
2138
|
+
canBorrow = maxBorrowable.total;
|
|
2139
|
+
console.log(` [auto-fund] Plan: ${(Number(canBorrow) / 1e6).toFixed(2)} USDC borrowable from Morpho`);
|
|
2140
|
+
}
|
|
2141
|
+
const totalAvailable = yieldToUse + canBorrow;
|
|
2142
|
+
if (totalAvailable < totalDeficit) {
|
|
2143
|
+
const parts = [];
|
|
2144
|
+
if (totalYieldAvailable > 0n) parts.push(`yield: $${(Number(totalYieldAvailable) / 1e6).toFixed(2)}`);
|
|
2145
|
+
if (canBorrow > 0n) parts.push(`borrowable: $${(Number(canBorrow) / 1e6).toFixed(2)}`);
|
|
2146
|
+
if (!this.config.autoYield) parts.push("autoYield: off");
|
|
2147
|
+
if (!this.config.autoDraw) parts.push("autoDraw: off");
|
|
1996
2148
|
return {
|
|
1997
2149
|
success: false,
|
|
1998
|
-
error: `
|
|
2150
|
+
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)}.`
|
|
1999
2151
|
};
|
|
2000
2152
|
}
|
|
2001
|
-
const
|
|
2002
|
-
|
|
2003
|
-
const borrowResult = await morphoClient.borrow(borrowAmount);
|
|
2004
|
-
console.log(` [auto-draw] Borrow tx: ${borrowResult.tx}`);
|
|
2005
|
-
this._trackSpending(deficit);
|
|
2006
|
-
const result = await this.request(url, fetchOpts);
|
|
2007
|
-
return {
|
|
2008
|
-
...result,
|
|
2009
|
-
autoDrawInfo: {
|
|
2010
|
-
borrowed: borrowAmount,
|
|
2011
|
-
borrowTx: borrowResult.tx,
|
|
2012
|
-
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2013
|
-
}
|
|
2153
|
+
const drawInfo = {
|
|
2154
|
+
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2014
2155
|
};
|
|
2156
|
+
if (yieldToUse > 0n) {
|
|
2157
|
+
let remaining = yieldToUse;
|
|
2158
|
+
let totalWithdrawn = 0n;
|
|
2159
|
+
let lastTx = "";
|
|
2160
|
+
for (const plan of yieldPlan) {
|
|
2161
|
+
if (remaining <= 0n) break;
|
|
2162
|
+
const toWithdraw = plan.amount < remaining ? plan.amount : remaining;
|
|
2163
|
+
const withdrawStr = (Number(toWithdraw) / 1e6).toFixed(6);
|
|
2164
|
+
console.log(` [auto-yield] Withdrawing $${withdrawStr} yield from ${plan.collateralToken} market`);
|
|
2165
|
+
const wr = await morphoClient.withdrawSupply(withdrawStr, plan.collateralToken);
|
|
2166
|
+
lastTx = wr.tx;
|
|
2167
|
+
totalWithdrawn += toWithdraw;
|
|
2168
|
+
remaining -= toWithdraw;
|
|
2169
|
+
}
|
|
2170
|
+
if (totalWithdrawn > 0n) {
|
|
2171
|
+
drawInfo.yieldWithdrawn = (Number(totalWithdrawn) / 1e6).toFixed(6);
|
|
2172
|
+
drawInfo.yieldTx = lastTx;
|
|
2173
|
+
console.log(` [auto-yield] Withdrawn: $${drawInfo.yieldWithdrawn}`);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
if (deficitAfterYield > 0n && this.config.autoDraw) {
|
|
2177
|
+
const borrowAmount = (Number(deficitAfterYield) / 1e6).toFixed(6);
|
|
2178
|
+
console.log(` [auto-draw] Borrowing ${borrowAmount} USDC from Morpho...`);
|
|
2179
|
+
const borrowResult = await morphoClient.borrow(borrowAmount);
|
|
2180
|
+
console.log(` [auto-draw] Borrow tx: ${borrowResult.tx}`);
|
|
2181
|
+
drawInfo.borrowed = borrowAmount;
|
|
2182
|
+
drawInfo.borrowTx = borrowResult.tx;
|
|
2183
|
+
this._trackSpending(deficitAfterYield);
|
|
2184
|
+
}
|
|
2185
|
+
const result = await this.request(url, fetchOpts);
|
|
2186
|
+
return { ...result, autoDrawInfo: drawInfo };
|
|
2015
2187
|
}
|
|
2016
2188
|
}
|
|
2017
2189
|
return this.request(url, fetchOpts);
|
|
2018
2190
|
} catch (error) {
|
|
2019
|
-
console.log(` [auto-
|
|
2191
|
+
console.log(` [auto-fund] Failed: ${error instanceof Error ? error.message : String(error)}. Proceeding with normal request.`);
|
|
2020
2192
|
return this.request(url, fetchOpts);
|
|
2021
2193
|
}
|
|
2022
2194
|
}
|
|
@@ -2117,48 +2289,26 @@ var init_X402Client = __esm({
|
|
|
2117
2289
|
};
|
|
2118
2290
|
}
|
|
2119
2291
|
}
|
|
2120
|
-
/** Track a new spending amount */
|
|
2292
|
+
/** Track a new spending amount and notify the caller for persistence */
|
|
2121
2293
|
_trackSpending(amount) {
|
|
2122
2294
|
this._resetTrackerIfNewDay();
|
|
2123
2295
|
this._spendingTracker.totalBorrowed += amount;
|
|
2124
|
-
|
|
2125
|
-
/**
|
|
2126
|
-
* Check if a borrow amount is within spending limits.
|
|
2127
|
-
* Considers both fixed daily limits and yield-limited spending.
|
|
2128
|
-
*/
|
|
2129
|
-
async _checkSpendingLimit(amount, morphoClient) {
|
|
2130
|
-
this._resetTrackerIfNewDay();
|
|
2131
|
-
if (this.config.yieldLimitedSpending) {
|
|
2296
|
+
if (this.config.onSpendingUpdate) {
|
|
2132
2297
|
try {
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
try {
|
|
2138
|
-
const estimate = await morphoClient.getYieldEstimate(
|
|
2139
|
-
pos.collateralToken,
|
|
2140
|
-
pos.collateral,
|
|
2141
|
-
1
|
|
2142
|
-
// 1 day
|
|
2143
|
-
);
|
|
2144
|
-
totalDailyYieldUsdc += estimate.estimatedYieldUsd;
|
|
2145
|
-
} catch (e) {
|
|
2146
|
-
console.warn(`[agether] yield calc failed for ${pos.collateralToken}:`, e instanceof Error ? e.message : e);
|
|
2147
|
-
}
|
|
2148
|
-
}
|
|
2149
|
-
}
|
|
2150
|
-
const yieldLimit = BigInt(Math.round(totalDailyYieldUsdc * 1e6));
|
|
2151
|
-
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2152
|
-
if (yieldLimit > 0n && newTotal > yieldLimit) {
|
|
2153
|
-
return {
|
|
2154
|
-
allowed: false,
|
|
2155
|
-
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)}`
|
|
2156
|
-
};
|
|
2157
|
-
}
|
|
2298
|
+
this.config.onSpendingUpdate({
|
|
2299
|
+
date: this._spendingTracker.date,
|
|
2300
|
+
totalBorrowed: this._spendingTracker.totalBorrowed.toString()
|
|
2301
|
+
});
|
|
2158
2302
|
} catch (e) {
|
|
2159
|
-
console.warn("[agether]
|
|
2303
|
+
console.warn("[agether] onSpendingUpdate callback failed:", e instanceof Error ? e.message : e);
|
|
2160
2304
|
}
|
|
2161
2305
|
}
|
|
2306
|
+
}
|
|
2307
|
+
/**
|
|
2308
|
+
* Check if a borrow amount is within the fixed daily spending limit.
|
|
2309
|
+
*/
|
|
2310
|
+
async _checkSpendingLimit(amount, _morphoClient) {
|
|
2311
|
+
this._resetTrackerIfNewDay();
|
|
2162
2312
|
if (this._spendingTracker.dailyLimit > 0n) {
|
|
2163
2313
|
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2164
2314
|
if (newTotal > this._spendingTracker.dailyLimit) {
|