@agether/sdk 2.5.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 +152 -72
- package/dist/index.d.mts +79 -22
- package/dist/index.d.ts +79 -22
- package/dist/index.js +152 -72
- package/dist/index.mjs +152 -72
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -537,6 +537,50 @@ var init_MorphoClient = __esm({
|
|
|
537
537
|
this._refreshSigner();
|
|
538
538
|
return { tx: receipt.hash, amount: usdcAmount, agentAccount: acctAddr };
|
|
539
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
|
+
}
|
|
540
584
|
// ════════════════════════════════════════════════════════
|
|
541
585
|
// Market Discovery (Morpho GraphQL API)
|
|
542
586
|
// ════════════════════════════════════════════════════════
|
|
@@ -1998,7 +2042,9 @@ var init_X402Client = __esm({
|
|
|
1998
2042
|
this.paidFetch = (0, import_fetch.wrapFetchWithPayment)(fetch, client);
|
|
1999
2043
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2000
2044
|
const dailyLimit = config.dailySpendLimitUsdc ? BigInt(Math.round(parseFloat(config.dailySpendLimitUsdc) * 1e6)) : 0n;
|
|
2001
|
-
|
|
2045
|
+
const restored = config.initialSpendingState;
|
|
2046
|
+
const restoredBorrowed = restored && restored.date === today ? BigInt(restored.totalBorrowed) : 0n;
|
|
2047
|
+
this._spendingTracker = { date: today, totalBorrowed: restoredBorrowed, dailyLimit };
|
|
2002
2048
|
}
|
|
2003
2049
|
async get(url, opts) {
|
|
2004
2050
|
return this.request(url, { ...opts, method: "GET" });
|
|
@@ -2027,66 +2073,122 @@ var init_X402Client = __esm({
|
|
|
2027
2073
|
return (Number(remaining > 0n ? remaining : 0n) / 1e6).toFixed(2);
|
|
2028
2074
|
}
|
|
2029
2075
|
/**
|
|
2030
|
-
* Pay with auto-
|
|
2076
|
+
* Pay with auto-funding: Make an x402 request with automatic USDC sourcing.
|
|
2031
2077
|
*
|
|
2032
|
-
*
|
|
2033
|
-
*
|
|
2034
|
-
*
|
|
2035
|
-
*
|
|
2036
|
-
*
|
|
2037
|
-
*
|
|
2038
|
-
*
|
|
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
|
|
2039
2093
|
*/
|
|
2040
2094
|
async payWithAutoDraw(url, opts) {
|
|
2041
2095
|
const { morphoClient, ...fetchOpts } = opts || {};
|
|
2042
|
-
if (!this.config.autoDraw || !morphoClient) {
|
|
2096
|
+
if (!this.config.autoDraw && !this.config.autoYield || !morphoClient) {
|
|
2043
2097
|
return this.request(url, fetchOpts);
|
|
2044
2098
|
}
|
|
2045
2099
|
try {
|
|
2046
2100
|
const usdcBalance = await morphoClient.getUsdcBalance();
|
|
2047
|
-
console.log(` [auto-
|
|
2101
|
+
console.log(` [auto-fund] AgentAccount USDC balance: ${(Number(usdcBalance) / 1e6).toFixed(2)}`);
|
|
2048
2102
|
const paymentAmount = await this._probePaymentAmount(url, fetchOpts);
|
|
2049
2103
|
if (paymentAmount !== null) {
|
|
2050
|
-
console.log(` [auto-
|
|
2104
|
+
console.log(` [auto-fund] Payment required: ${(Number(paymentAmount) / 1e6).toFixed(6)} USDC`);
|
|
2051
2105
|
const bufferStr = this.config.autoDrawBuffer || "0.5";
|
|
2052
2106
|
const buffer = BigInt(Math.round(parseFloat(bufferStr) * 1e6));
|
|
2053
2107
|
const needed = paymentAmount + buffer;
|
|
2054
2108
|
if (usdcBalance < needed) {
|
|
2055
|
-
const
|
|
2056
|
-
console.log(` [auto-
|
|
2057
|
-
const
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
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
|
+
}
|
|
2128
|
+
}
|
|
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`);
|
|
2063
2140
|
}
|
|
2064
|
-
const
|
|
2065
|
-
if (
|
|
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");
|
|
2066
2148
|
return {
|
|
2067
2149
|
success: false,
|
|
2068
|
-
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)}.`
|
|
2069
2151
|
};
|
|
2070
2152
|
}
|
|
2071
|
-
const
|
|
2072
|
-
|
|
2073
|
-
const borrowResult = await morphoClient.borrow(borrowAmount);
|
|
2074
|
-
console.log(` [auto-draw] Borrow tx: ${borrowResult.tx}`);
|
|
2075
|
-
this._trackSpending(deficit);
|
|
2076
|
-
const result = await this.request(url, fetchOpts);
|
|
2077
|
-
return {
|
|
2078
|
-
...result,
|
|
2079
|
-
autoDrawInfo: {
|
|
2080
|
-
borrowed: borrowAmount,
|
|
2081
|
-
borrowTx: borrowResult.tx,
|
|
2082
|
-
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2083
|
-
}
|
|
2153
|
+
const drawInfo = {
|
|
2154
|
+
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2084
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 };
|
|
2085
2187
|
}
|
|
2086
2188
|
}
|
|
2087
2189
|
return this.request(url, fetchOpts);
|
|
2088
2190
|
} catch (error) {
|
|
2089
|
-
console.log(` [auto-
|
|
2191
|
+
console.log(` [auto-fund] Failed: ${error instanceof Error ? error.message : String(error)}. Proceeding with normal request.`);
|
|
2090
2192
|
return this.request(url, fetchOpts);
|
|
2091
2193
|
}
|
|
2092
2194
|
}
|
|
@@ -2187,48 +2289,26 @@ var init_X402Client = __esm({
|
|
|
2187
2289
|
};
|
|
2188
2290
|
}
|
|
2189
2291
|
}
|
|
2190
|
-
/** Track a new spending amount */
|
|
2292
|
+
/** Track a new spending amount and notify the caller for persistence */
|
|
2191
2293
|
_trackSpending(amount) {
|
|
2192
2294
|
this._resetTrackerIfNewDay();
|
|
2193
2295
|
this._spendingTracker.totalBorrowed += amount;
|
|
2194
|
-
|
|
2195
|
-
/**
|
|
2196
|
-
* Check if a borrow amount is within spending limits.
|
|
2197
|
-
* Considers both fixed daily limits and yield-limited spending.
|
|
2198
|
-
*/
|
|
2199
|
-
async _checkSpendingLimit(amount, morphoClient) {
|
|
2200
|
-
this._resetTrackerIfNewDay();
|
|
2201
|
-
if (this.config.yieldLimitedSpending) {
|
|
2296
|
+
if (this.config.onSpendingUpdate) {
|
|
2202
2297
|
try {
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
try {
|
|
2208
|
-
const estimate = await morphoClient.getYieldEstimate(
|
|
2209
|
-
pos.collateralToken,
|
|
2210
|
-
pos.collateral,
|
|
2211
|
-
1
|
|
2212
|
-
// 1 day
|
|
2213
|
-
);
|
|
2214
|
-
totalDailyYieldUsdc += estimate.estimatedYieldUsd;
|
|
2215
|
-
} catch (e) {
|
|
2216
|
-
console.warn(`[agether] yield calc failed for ${pos.collateralToken}:`, e instanceof Error ? e.message : e);
|
|
2217
|
-
}
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
const yieldLimit = BigInt(Math.round(totalDailyYieldUsdc * 1e6));
|
|
2221
|
-
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2222
|
-
if (yieldLimit > 0n && newTotal > yieldLimit) {
|
|
2223
|
-
return {
|
|
2224
|
-
allowed: false,
|
|
2225
|
-
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)}`
|
|
2226
|
-
};
|
|
2227
|
-
}
|
|
2298
|
+
this.config.onSpendingUpdate({
|
|
2299
|
+
date: this._spendingTracker.date,
|
|
2300
|
+
totalBorrowed: this._spendingTracker.totalBorrowed.toString()
|
|
2301
|
+
});
|
|
2228
2302
|
} catch (e) {
|
|
2229
|
-
console.warn("[agether]
|
|
2303
|
+
console.warn("[agether] onSpendingUpdate callback failed:", e instanceof Error ? e.message : e);
|
|
2230
2304
|
}
|
|
2231
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();
|
|
2232
2312
|
if (this._spendingTracker.dailyLimit > 0n) {
|
|
2233
2313
|
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2234
2314
|
if (newTotal > this._spendingTracker.dailyLimit) {
|
package/dist/index.d.mts
CHANGED
|
@@ -347,6 +347,12 @@ interface FundResult {
|
|
|
347
347
|
amount: string;
|
|
348
348
|
agentAccount: string;
|
|
349
349
|
}
|
|
350
|
+
interface WithdrawFromAccountResult {
|
|
351
|
+
tx: string;
|
|
352
|
+
token: string;
|
|
353
|
+
amount: string;
|
|
354
|
+
destination: string;
|
|
355
|
+
}
|
|
350
356
|
interface SupplyAssetResult {
|
|
351
357
|
tx: string;
|
|
352
358
|
amount: string;
|
|
@@ -432,6 +438,21 @@ declare class MorphoClient {
|
|
|
432
438
|
getBalances(): Promise<BalancesResult>;
|
|
433
439
|
/** Transfer USDC from EOA to AgentAccount. */
|
|
434
440
|
fundAccount(usdcAmount: string): Promise<FundResult>;
|
|
441
|
+
/**
|
|
442
|
+
* Withdraw (transfer) a token from AgentAccount to EOA.
|
|
443
|
+
* Executes an ERC-20 transfer via Safe UserOp.
|
|
444
|
+
*
|
|
445
|
+
* @param tokenSymbol - Token to withdraw (e.g. 'USDC', 'WETH', 'wstETH')
|
|
446
|
+
* @param amount - Amount to withdraw (human-readable, e.g. '100' for 100 USDC, or 'all')
|
|
447
|
+
*/
|
|
448
|
+
withdrawToken(tokenSymbol: string, amount: string): Promise<WithdrawFromAccountResult>;
|
|
449
|
+
/**
|
|
450
|
+
* Withdraw ETH from AgentAccount to EOA.
|
|
451
|
+
* Executes a native ETH transfer via Safe UserOp.
|
|
452
|
+
*
|
|
453
|
+
* @param amount - ETH amount (e.g. '0.01' or 'all')
|
|
454
|
+
*/
|
|
455
|
+
withdrawEth(amount: string): Promise<WithdrawFromAccountResult>;
|
|
435
456
|
/**
|
|
436
457
|
* Fetch USDC borrow markets on Base from Morpho API.
|
|
437
458
|
* Caches results for 5 minutes.
|
|
@@ -708,9 +729,14 @@ declare class MorphoClient {
|
|
|
708
729
|
* Auto-Draw: When autoDraw is enabled and USDC balance is insufficient,
|
|
709
730
|
* the client automatically borrows from Morpho Blue before paying.
|
|
710
731
|
*
|
|
711
|
-
*
|
|
712
|
-
* yield
|
|
713
|
-
*
|
|
732
|
+
* Auto-Yield: When autoYield is enabled, the client first tries to cover
|
|
733
|
+
* the deficit from earned supply yield (principal untouched) before borrowing.
|
|
734
|
+
*
|
|
735
|
+
* Waterfall when both enabled: balance → yield → borrow
|
|
736
|
+
*
|
|
737
|
+
* Spending Limits: Optional daily spending cap (dailySpendLimitUsdc) with
|
|
738
|
+
* persistent state via onSpendingUpdate callback. The caller (e.g. plugin)
|
|
739
|
+
* is responsible for persisting and restoring state via initialSpendingState.
|
|
714
740
|
*
|
|
715
741
|
* Chain support: Base (8453), Base Sepolia (84532), Ethereum (1).
|
|
716
742
|
*/
|
|
@@ -733,21 +759,43 @@ interface X402BaseConfig {
|
|
|
733
759
|
* Default: false
|
|
734
760
|
*/
|
|
735
761
|
autoDraw?: boolean;
|
|
762
|
+
/**
|
|
763
|
+
* Auto-yield: when USDC is insufficient, try to cover the deficit from
|
|
764
|
+
* earned supply yield BEFORE borrowing. Withdraws only the yield portion
|
|
765
|
+
* (principal stays intact). Works independently or combined with autoDraw.
|
|
766
|
+
*
|
|
767
|
+
* Waterfall when both enabled: balance → yield → borrow
|
|
768
|
+
* Default: false
|
|
769
|
+
*/
|
|
770
|
+
autoYield?: boolean;
|
|
736
771
|
/**
|
|
737
772
|
* Daily spending limit in USDC (e.g. '100' for $100/day).
|
|
738
773
|
* Tracks cumulative daily borrows and rejects auto-draw if exceeded.
|
|
739
774
|
*/
|
|
740
775
|
dailySpendLimitUsdc?: string;
|
|
741
|
-
/**
|
|
742
|
-
* When true, auto-calculates the daily spending limit based on
|
|
743
|
-
* theoretical yield of deposited collateral. Overrides dailySpendLimitUsdc.
|
|
744
|
-
*/
|
|
745
|
-
yieldLimitedSpending?: boolean;
|
|
746
776
|
/**
|
|
747
777
|
* Safety margin: borrow this much extra beyond what's needed (in USDC, e.g. '1').
|
|
748
778
|
* Helps avoid rounding issues. Default: '0.5'
|
|
749
779
|
*/
|
|
750
780
|
autoDrawBuffer?: string;
|
|
781
|
+
/**
|
|
782
|
+
* Pre-loaded spending state from a previous session.
|
|
783
|
+
* Pass this to resume the daily spending tracker after a restart.
|
|
784
|
+
* If the date doesn't match today, it's ignored (fresh day).
|
|
785
|
+
*/
|
|
786
|
+
initialSpendingState?: {
|
|
787
|
+
date: string;
|
|
788
|
+
totalBorrowed: string;
|
|
789
|
+
};
|
|
790
|
+
/**
|
|
791
|
+
* Called after every auto-draw borrow so the caller can persist the
|
|
792
|
+
* updated spending state (e.g. to a cache file). Receives the full
|
|
793
|
+
* SpendingTracker with the current date and cumulative amount.
|
|
794
|
+
*/
|
|
795
|
+
onSpendingUpdate?: (state: {
|
|
796
|
+
date: string;
|
|
797
|
+
totalBorrowed: string;
|
|
798
|
+
}) => void;
|
|
751
799
|
/**
|
|
752
800
|
* ERC-7579 validator module address (e.g. Agether8004ValidationModule).
|
|
753
801
|
* Required for Safe7579 smart wallets so that `isValidSignature` calls
|
|
@@ -790,8 +838,10 @@ interface X402Response<T = unknown> {
|
|
|
790
838
|
txHash?: string;
|
|
791
839
|
};
|
|
792
840
|
autoDrawInfo?: {
|
|
793
|
-
borrowed
|
|
794
|
-
borrowTx
|
|
841
|
+
borrowed?: string;
|
|
842
|
+
borrowTx?: string;
|
|
843
|
+
yieldWithdrawn?: string;
|
|
844
|
+
yieldTx?: string;
|
|
795
845
|
reason: string;
|
|
796
846
|
};
|
|
797
847
|
}
|
|
@@ -828,15 +878,23 @@ declare class X402Client {
|
|
|
828
878
|
/** Get remaining daily spending allowance in USDC (human-readable) */
|
|
829
879
|
getRemainingDailyAllowance(): string;
|
|
830
880
|
/**
|
|
831
|
-
* Pay with auto-
|
|
881
|
+
* Pay with auto-funding: Make an x402 request with automatic USDC sourcing.
|
|
832
882
|
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
836
|
-
*
|
|
837
|
-
*
|
|
838
|
-
*
|
|
839
|
-
*
|
|
883
|
+
* Uses a **plan-then-execute** approach: all reads happen first, and if the
|
|
884
|
+
* full deficit can't be covered the method fails with NO side-effects
|
|
885
|
+
* (no yield withdrawn, no USDC borrowed).
|
|
886
|
+
*
|
|
887
|
+
* Waterfall (when both autoYield + autoDraw enabled):
|
|
888
|
+
* 1. Check USDC balance on AgentAccount
|
|
889
|
+
* 2. Probe the URL to discover payment amount (if 402)
|
|
890
|
+
* 3. If insufficient USDC — PLANNING PHASE (read-only):
|
|
891
|
+
* a. Calculate total available yield across supply positions
|
|
892
|
+
* b. Calculate max borrowable from Morpho markets
|
|
893
|
+
* c. Verify yield + borrow can cover full deficit — if not, fail immediately
|
|
894
|
+
* 4. EXECUTION PHASE (only if plan is feasible):
|
|
895
|
+
* a. Withdraw needed yield from supply positions
|
|
896
|
+
* b. Borrow remaining deficit from Morpho
|
|
897
|
+
* 5. Proceed with x402 payment
|
|
840
898
|
*/
|
|
841
899
|
payWithAutoDraw<T = unknown>(url: string, opts?: RequestInit & {
|
|
842
900
|
morphoClient?: any;
|
|
@@ -850,11 +908,10 @@ declare class X402Client {
|
|
|
850
908
|
private _probePaymentAmount;
|
|
851
909
|
/** Reset spending tracker if it's a new day */
|
|
852
910
|
private _resetTrackerIfNewDay;
|
|
853
|
-
/** Track a new spending amount */
|
|
911
|
+
/** Track a new spending amount and notify the caller for persistence */
|
|
854
912
|
private _trackSpending;
|
|
855
913
|
/**
|
|
856
|
-
* Check if a borrow amount is within spending
|
|
857
|
-
* Considers both fixed daily limits and yield-limited spending.
|
|
914
|
+
* Check if a borrow amount is within the fixed daily spending limit.
|
|
858
915
|
*/
|
|
859
916
|
private _checkSpendingLimit;
|
|
860
917
|
}
|
|
@@ -1227,4 +1284,4 @@ declare function getDefaultConfig(chainId: ChainId): AgetherConfig;
|
|
|
1227
1284
|
*/
|
|
1228
1285
|
declare function createConfig(chainId: ChainId, options?: Partial<AgetherConfig>): AgetherConfig;
|
|
1229
1286
|
|
|
1230
|
-
export { ACCOUNT_FACTORY_ABI, AGENT_REPUTATION_ABI, AGETHER_4337_FACTORY_ABI, AGETHER_8004_SCORER_ABI, AGETHER_8004_VALIDATION_MODULE_ABI, AGETHER_HOOK_MULTIPLEXER_ABI, AgentIdentityClient, type AgentIdentityClientOptions, AgentNotApprovedError, AgetherClient, type AgetherClientOptions, type AgetherConfig, AgetherError, type AgetherSigner, type AgetherViemWallet, type BalancesResult, type BorrowResult, ChainId, type ContractAddresses, type DepositAndBorrowResult, type DepositResult, ENTRYPOINT_V07_ABI, ERC20_ABI, ERC8004_VALIDATION_MODULE_ABI, type FundResult, HOOK_MULTIPLEXER_ABI, IDENTITY_REGISTRY_ABI, InsufficientBalanceError, MORPHO_BLUE_ABI, MorphoClient, type MorphoClientConfig, type MorphoMarketInfo, type MorphoMarketParams, type MorphoPosition, type PayFromYieldResult, type PaymentRequirements, type PositionResult, type RegisterResult, type RepayResult, SAFE7579_ACCOUNT_ABI, SAFE_AGENT_FACTORY_ABI, type ScoreAttestation, type ScoreResult, ScoringClient, type ScoringClientConfig, ScoringRejectedError, type SpendingTracker, type StatusResult, type SupplyAssetResult, type SupplyPositionResult, type TransactionResult, VALIDATION_REGISTRY_ABI, type WithdrawResult, type WithdrawSupplyResult, X402Client, type X402Config, type X402PaymentRequest, type X402PaymentResult, type X402Response, bpsToRate, createConfig, formatAPR, formatAddress, formatHealthFactor, formatPercent, formatTimestamp, formatUSD, formatUnits, getDefaultConfig, parseUnits, rateToBps };
|
|
1287
|
+
export { ACCOUNT_FACTORY_ABI, AGENT_REPUTATION_ABI, AGETHER_4337_FACTORY_ABI, AGETHER_8004_SCORER_ABI, AGETHER_8004_VALIDATION_MODULE_ABI, AGETHER_HOOK_MULTIPLEXER_ABI, AgentIdentityClient, type AgentIdentityClientOptions, AgentNotApprovedError, AgetherClient, type AgetherClientOptions, type AgetherConfig, AgetherError, type AgetherSigner, type AgetherViemWallet, type BalancesResult, type BorrowResult, ChainId, type ContractAddresses, type DepositAndBorrowResult, type DepositResult, ENTRYPOINT_V07_ABI, ERC20_ABI, ERC8004_VALIDATION_MODULE_ABI, type FundResult, HOOK_MULTIPLEXER_ABI, IDENTITY_REGISTRY_ABI, InsufficientBalanceError, MORPHO_BLUE_ABI, MorphoClient, type MorphoClientConfig, type MorphoMarketInfo, type MorphoMarketParams, type MorphoPosition, type PayFromYieldResult, type PaymentRequirements, type PositionResult, type RegisterResult, type RepayResult, SAFE7579_ACCOUNT_ABI, SAFE_AGENT_FACTORY_ABI, type ScoreAttestation, type ScoreResult, ScoringClient, type ScoringClientConfig, ScoringRejectedError, type SpendingTracker, type StatusResult, type SupplyAssetResult, type SupplyPositionResult, type TransactionResult, VALIDATION_REGISTRY_ABI, type WithdrawFromAccountResult, type WithdrawResult, type WithdrawSupplyResult, X402Client, type X402Config, type X402PaymentRequest, type X402PaymentResult, type X402Response, bpsToRate, createConfig, formatAPR, formatAddress, formatHealthFactor, formatPercent, formatTimestamp, formatUSD, formatUnits, getDefaultConfig, parseUnits, rateToBps };
|
package/dist/index.d.ts
CHANGED
|
@@ -347,6 +347,12 @@ interface FundResult {
|
|
|
347
347
|
amount: string;
|
|
348
348
|
agentAccount: string;
|
|
349
349
|
}
|
|
350
|
+
interface WithdrawFromAccountResult {
|
|
351
|
+
tx: string;
|
|
352
|
+
token: string;
|
|
353
|
+
amount: string;
|
|
354
|
+
destination: string;
|
|
355
|
+
}
|
|
350
356
|
interface SupplyAssetResult {
|
|
351
357
|
tx: string;
|
|
352
358
|
amount: string;
|
|
@@ -432,6 +438,21 @@ declare class MorphoClient {
|
|
|
432
438
|
getBalances(): Promise<BalancesResult>;
|
|
433
439
|
/** Transfer USDC from EOA to AgentAccount. */
|
|
434
440
|
fundAccount(usdcAmount: string): Promise<FundResult>;
|
|
441
|
+
/**
|
|
442
|
+
* Withdraw (transfer) a token from AgentAccount to EOA.
|
|
443
|
+
* Executes an ERC-20 transfer via Safe UserOp.
|
|
444
|
+
*
|
|
445
|
+
* @param tokenSymbol - Token to withdraw (e.g. 'USDC', 'WETH', 'wstETH')
|
|
446
|
+
* @param amount - Amount to withdraw (human-readable, e.g. '100' for 100 USDC, or 'all')
|
|
447
|
+
*/
|
|
448
|
+
withdrawToken(tokenSymbol: string, amount: string): Promise<WithdrawFromAccountResult>;
|
|
449
|
+
/**
|
|
450
|
+
* Withdraw ETH from AgentAccount to EOA.
|
|
451
|
+
* Executes a native ETH transfer via Safe UserOp.
|
|
452
|
+
*
|
|
453
|
+
* @param amount - ETH amount (e.g. '0.01' or 'all')
|
|
454
|
+
*/
|
|
455
|
+
withdrawEth(amount: string): Promise<WithdrawFromAccountResult>;
|
|
435
456
|
/**
|
|
436
457
|
* Fetch USDC borrow markets on Base from Morpho API.
|
|
437
458
|
* Caches results for 5 minutes.
|
|
@@ -708,9 +729,14 @@ declare class MorphoClient {
|
|
|
708
729
|
* Auto-Draw: When autoDraw is enabled and USDC balance is insufficient,
|
|
709
730
|
* the client automatically borrows from Morpho Blue before paying.
|
|
710
731
|
*
|
|
711
|
-
*
|
|
712
|
-
* yield
|
|
713
|
-
*
|
|
732
|
+
* Auto-Yield: When autoYield is enabled, the client first tries to cover
|
|
733
|
+
* the deficit from earned supply yield (principal untouched) before borrowing.
|
|
734
|
+
*
|
|
735
|
+
* Waterfall when both enabled: balance → yield → borrow
|
|
736
|
+
*
|
|
737
|
+
* Spending Limits: Optional daily spending cap (dailySpendLimitUsdc) with
|
|
738
|
+
* persistent state via onSpendingUpdate callback. The caller (e.g. plugin)
|
|
739
|
+
* is responsible for persisting and restoring state via initialSpendingState.
|
|
714
740
|
*
|
|
715
741
|
* Chain support: Base (8453), Base Sepolia (84532), Ethereum (1).
|
|
716
742
|
*/
|
|
@@ -733,21 +759,43 @@ interface X402BaseConfig {
|
|
|
733
759
|
* Default: false
|
|
734
760
|
*/
|
|
735
761
|
autoDraw?: boolean;
|
|
762
|
+
/**
|
|
763
|
+
* Auto-yield: when USDC is insufficient, try to cover the deficit from
|
|
764
|
+
* earned supply yield BEFORE borrowing. Withdraws only the yield portion
|
|
765
|
+
* (principal stays intact). Works independently or combined with autoDraw.
|
|
766
|
+
*
|
|
767
|
+
* Waterfall when both enabled: balance → yield → borrow
|
|
768
|
+
* Default: false
|
|
769
|
+
*/
|
|
770
|
+
autoYield?: boolean;
|
|
736
771
|
/**
|
|
737
772
|
* Daily spending limit in USDC (e.g. '100' for $100/day).
|
|
738
773
|
* Tracks cumulative daily borrows and rejects auto-draw if exceeded.
|
|
739
774
|
*/
|
|
740
775
|
dailySpendLimitUsdc?: string;
|
|
741
|
-
/**
|
|
742
|
-
* When true, auto-calculates the daily spending limit based on
|
|
743
|
-
* theoretical yield of deposited collateral. Overrides dailySpendLimitUsdc.
|
|
744
|
-
*/
|
|
745
|
-
yieldLimitedSpending?: boolean;
|
|
746
776
|
/**
|
|
747
777
|
* Safety margin: borrow this much extra beyond what's needed (in USDC, e.g. '1').
|
|
748
778
|
* Helps avoid rounding issues. Default: '0.5'
|
|
749
779
|
*/
|
|
750
780
|
autoDrawBuffer?: string;
|
|
781
|
+
/**
|
|
782
|
+
* Pre-loaded spending state from a previous session.
|
|
783
|
+
* Pass this to resume the daily spending tracker after a restart.
|
|
784
|
+
* If the date doesn't match today, it's ignored (fresh day).
|
|
785
|
+
*/
|
|
786
|
+
initialSpendingState?: {
|
|
787
|
+
date: string;
|
|
788
|
+
totalBorrowed: string;
|
|
789
|
+
};
|
|
790
|
+
/**
|
|
791
|
+
* Called after every auto-draw borrow so the caller can persist the
|
|
792
|
+
* updated spending state (e.g. to a cache file). Receives the full
|
|
793
|
+
* SpendingTracker with the current date and cumulative amount.
|
|
794
|
+
*/
|
|
795
|
+
onSpendingUpdate?: (state: {
|
|
796
|
+
date: string;
|
|
797
|
+
totalBorrowed: string;
|
|
798
|
+
}) => void;
|
|
751
799
|
/**
|
|
752
800
|
* ERC-7579 validator module address (e.g. Agether8004ValidationModule).
|
|
753
801
|
* Required for Safe7579 smart wallets so that `isValidSignature` calls
|
|
@@ -790,8 +838,10 @@ interface X402Response<T = unknown> {
|
|
|
790
838
|
txHash?: string;
|
|
791
839
|
};
|
|
792
840
|
autoDrawInfo?: {
|
|
793
|
-
borrowed
|
|
794
|
-
borrowTx
|
|
841
|
+
borrowed?: string;
|
|
842
|
+
borrowTx?: string;
|
|
843
|
+
yieldWithdrawn?: string;
|
|
844
|
+
yieldTx?: string;
|
|
795
845
|
reason: string;
|
|
796
846
|
};
|
|
797
847
|
}
|
|
@@ -828,15 +878,23 @@ declare class X402Client {
|
|
|
828
878
|
/** Get remaining daily spending allowance in USDC (human-readable) */
|
|
829
879
|
getRemainingDailyAllowance(): string;
|
|
830
880
|
/**
|
|
831
|
-
* Pay with auto-
|
|
881
|
+
* Pay with auto-funding: Make an x402 request with automatic USDC sourcing.
|
|
832
882
|
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
836
|
-
*
|
|
837
|
-
*
|
|
838
|
-
*
|
|
839
|
-
*
|
|
883
|
+
* Uses a **plan-then-execute** approach: all reads happen first, and if the
|
|
884
|
+
* full deficit can't be covered the method fails with NO side-effects
|
|
885
|
+
* (no yield withdrawn, no USDC borrowed).
|
|
886
|
+
*
|
|
887
|
+
* Waterfall (when both autoYield + autoDraw enabled):
|
|
888
|
+
* 1. Check USDC balance on AgentAccount
|
|
889
|
+
* 2. Probe the URL to discover payment amount (if 402)
|
|
890
|
+
* 3. If insufficient USDC — PLANNING PHASE (read-only):
|
|
891
|
+
* a. Calculate total available yield across supply positions
|
|
892
|
+
* b. Calculate max borrowable from Morpho markets
|
|
893
|
+
* c. Verify yield + borrow can cover full deficit — if not, fail immediately
|
|
894
|
+
* 4. EXECUTION PHASE (only if plan is feasible):
|
|
895
|
+
* a. Withdraw needed yield from supply positions
|
|
896
|
+
* b. Borrow remaining deficit from Morpho
|
|
897
|
+
* 5. Proceed with x402 payment
|
|
840
898
|
*/
|
|
841
899
|
payWithAutoDraw<T = unknown>(url: string, opts?: RequestInit & {
|
|
842
900
|
morphoClient?: any;
|
|
@@ -850,11 +908,10 @@ declare class X402Client {
|
|
|
850
908
|
private _probePaymentAmount;
|
|
851
909
|
/** Reset spending tracker if it's a new day */
|
|
852
910
|
private _resetTrackerIfNewDay;
|
|
853
|
-
/** Track a new spending amount */
|
|
911
|
+
/** Track a new spending amount and notify the caller for persistence */
|
|
854
912
|
private _trackSpending;
|
|
855
913
|
/**
|
|
856
|
-
* Check if a borrow amount is within spending
|
|
857
|
-
* Considers both fixed daily limits and yield-limited spending.
|
|
914
|
+
* Check if a borrow amount is within the fixed daily spending limit.
|
|
858
915
|
*/
|
|
859
916
|
private _checkSpendingLimit;
|
|
860
917
|
}
|
|
@@ -1227,4 +1284,4 @@ declare function getDefaultConfig(chainId: ChainId): AgetherConfig;
|
|
|
1227
1284
|
*/
|
|
1228
1285
|
declare function createConfig(chainId: ChainId, options?: Partial<AgetherConfig>): AgetherConfig;
|
|
1229
1286
|
|
|
1230
|
-
export { ACCOUNT_FACTORY_ABI, AGENT_REPUTATION_ABI, AGETHER_4337_FACTORY_ABI, AGETHER_8004_SCORER_ABI, AGETHER_8004_VALIDATION_MODULE_ABI, AGETHER_HOOK_MULTIPLEXER_ABI, AgentIdentityClient, type AgentIdentityClientOptions, AgentNotApprovedError, AgetherClient, type AgetherClientOptions, type AgetherConfig, AgetherError, type AgetherSigner, type AgetherViemWallet, type BalancesResult, type BorrowResult, ChainId, type ContractAddresses, type DepositAndBorrowResult, type DepositResult, ENTRYPOINT_V07_ABI, ERC20_ABI, ERC8004_VALIDATION_MODULE_ABI, type FundResult, HOOK_MULTIPLEXER_ABI, IDENTITY_REGISTRY_ABI, InsufficientBalanceError, MORPHO_BLUE_ABI, MorphoClient, type MorphoClientConfig, type MorphoMarketInfo, type MorphoMarketParams, type MorphoPosition, type PayFromYieldResult, type PaymentRequirements, type PositionResult, type RegisterResult, type RepayResult, SAFE7579_ACCOUNT_ABI, SAFE_AGENT_FACTORY_ABI, type ScoreAttestation, type ScoreResult, ScoringClient, type ScoringClientConfig, ScoringRejectedError, type SpendingTracker, type StatusResult, type SupplyAssetResult, type SupplyPositionResult, type TransactionResult, VALIDATION_REGISTRY_ABI, type WithdrawResult, type WithdrawSupplyResult, X402Client, type X402Config, type X402PaymentRequest, type X402PaymentResult, type X402Response, bpsToRate, createConfig, formatAPR, formatAddress, formatHealthFactor, formatPercent, formatTimestamp, formatUSD, formatUnits, getDefaultConfig, parseUnits, rateToBps };
|
|
1287
|
+
export { ACCOUNT_FACTORY_ABI, AGENT_REPUTATION_ABI, AGETHER_4337_FACTORY_ABI, AGETHER_8004_SCORER_ABI, AGETHER_8004_VALIDATION_MODULE_ABI, AGETHER_HOOK_MULTIPLEXER_ABI, AgentIdentityClient, type AgentIdentityClientOptions, AgentNotApprovedError, AgetherClient, type AgetherClientOptions, type AgetherConfig, AgetherError, type AgetherSigner, type AgetherViemWallet, type BalancesResult, type BorrowResult, ChainId, type ContractAddresses, type DepositAndBorrowResult, type DepositResult, ENTRYPOINT_V07_ABI, ERC20_ABI, ERC8004_VALIDATION_MODULE_ABI, type FundResult, HOOK_MULTIPLEXER_ABI, IDENTITY_REGISTRY_ABI, InsufficientBalanceError, MORPHO_BLUE_ABI, MorphoClient, type MorphoClientConfig, type MorphoMarketInfo, type MorphoMarketParams, type MorphoPosition, type PayFromYieldResult, type PaymentRequirements, type PositionResult, type RegisterResult, type RepayResult, SAFE7579_ACCOUNT_ABI, SAFE_AGENT_FACTORY_ABI, type ScoreAttestation, type ScoreResult, ScoringClient, type ScoringClientConfig, ScoringRejectedError, type SpendingTracker, type StatusResult, type SupplyAssetResult, type SupplyPositionResult, type TransactionResult, VALIDATION_REGISTRY_ABI, type WithdrawFromAccountResult, type WithdrawResult, type WithdrawSupplyResult, X402Client, type X402Config, type X402PaymentRequest, type X402PaymentResult, type X402Response, bpsToRate, createConfig, formatAPR, formatAddress, formatHealthFactor, formatPercent, formatTimestamp, formatUSD, formatUnits, getDefaultConfig, parseUnits, rateToBps };
|
package/dist/index.js
CHANGED
|
@@ -786,6 +786,50 @@ var MorphoClient = class {
|
|
|
786
786
|
this._refreshSigner();
|
|
787
787
|
return { tx: receipt.hash, amount: usdcAmount, agentAccount: acctAddr };
|
|
788
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
|
+
}
|
|
789
833
|
// ════════════════════════════════════════════════════════
|
|
790
834
|
// Market Discovery (Morpho GraphQL API)
|
|
791
835
|
// ════════════════════════════════════════════════════════
|
|
@@ -2240,7 +2284,9 @@ var X402Client = class {
|
|
|
2240
2284
|
this.paidFetch = (0, import_fetch.wrapFetchWithPayment)(fetch, client);
|
|
2241
2285
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2242
2286
|
const dailyLimit = config.dailySpendLimitUsdc ? BigInt(Math.round(parseFloat(config.dailySpendLimitUsdc) * 1e6)) : 0n;
|
|
2243
|
-
|
|
2287
|
+
const restored = config.initialSpendingState;
|
|
2288
|
+
const restoredBorrowed = restored && restored.date === today ? BigInt(restored.totalBorrowed) : 0n;
|
|
2289
|
+
this._spendingTracker = { date: today, totalBorrowed: restoredBorrowed, dailyLimit };
|
|
2244
2290
|
}
|
|
2245
2291
|
async get(url, opts) {
|
|
2246
2292
|
return this.request(url, { ...opts, method: "GET" });
|
|
@@ -2269,66 +2315,122 @@ var X402Client = class {
|
|
|
2269
2315
|
return (Number(remaining > 0n ? remaining : 0n) / 1e6).toFixed(2);
|
|
2270
2316
|
}
|
|
2271
2317
|
/**
|
|
2272
|
-
* Pay with auto-
|
|
2318
|
+
* Pay with auto-funding: Make an x402 request with automatic USDC sourcing.
|
|
2273
2319
|
*
|
|
2274
|
-
*
|
|
2275
|
-
*
|
|
2276
|
-
*
|
|
2277
|
-
*
|
|
2278
|
-
*
|
|
2279
|
-
*
|
|
2280
|
-
*
|
|
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
|
|
2281
2335
|
*/
|
|
2282
2336
|
async payWithAutoDraw(url, opts) {
|
|
2283
2337
|
const { morphoClient, ...fetchOpts } = opts || {};
|
|
2284
|
-
if (!this.config.autoDraw || !morphoClient) {
|
|
2338
|
+
if (!this.config.autoDraw && !this.config.autoYield || !morphoClient) {
|
|
2285
2339
|
return this.request(url, fetchOpts);
|
|
2286
2340
|
}
|
|
2287
2341
|
try {
|
|
2288
2342
|
const usdcBalance = await morphoClient.getUsdcBalance();
|
|
2289
|
-
console.log(` [auto-
|
|
2343
|
+
console.log(` [auto-fund] AgentAccount USDC balance: ${(Number(usdcBalance) / 1e6).toFixed(2)}`);
|
|
2290
2344
|
const paymentAmount = await this._probePaymentAmount(url, fetchOpts);
|
|
2291
2345
|
if (paymentAmount !== null) {
|
|
2292
|
-
console.log(` [auto-
|
|
2346
|
+
console.log(` [auto-fund] Payment required: ${(Number(paymentAmount) / 1e6).toFixed(6)} USDC`);
|
|
2293
2347
|
const bufferStr = this.config.autoDrawBuffer || "0.5";
|
|
2294
2348
|
const buffer = BigInt(Math.round(parseFloat(bufferStr) * 1e6));
|
|
2295
2349
|
const needed = paymentAmount + buffer;
|
|
2296
2350
|
if (usdcBalance < needed) {
|
|
2297
|
-
const
|
|
2298
|
-
console.log(` [auto-
|
|
2299
|
-
const
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
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
|
+
}
|
|
2370
|
+
}
|
|
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`);
|
|
2305
2382
|
}
|
|
2306
|
-
const
|
|
2307
|
-
if (
|
|
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");
|
|
2308
2390
|
return {
|
|
2309
2391
|
success: false,
|
|
2310
|
-
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)}.`
|
|
2311
2393
|
};
|
|
2312
2394
|
}
|
|
2313
|
-
const
|
|
2314
|
-
|
|
2315
|
-
const borrowResult = await morphoClient.borrow(borrowAmount);
|
|
2316
|
-
console.log(` [auto-draw] Borrow tx: ${borrowResult.tx}`);
|
|
2317
|
-
this._trackSpending(deficit);
|
|
2318
|
-
const result = await this.request(url, fetchOpts);
|
|
2319
|
-
return {
|
|
2320
|
-
...result,
|
|
2321
|
-
autoDrawInfo: {
|
|
2322
|
-
borrowed: borrowAmount,
|
|
2323
|
-
borrowTx: borrowResult.tx,
|
|
2324
|
-
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2325
|
-
}
|
|
2395
|
+
const drawInfo = {
|
|
2396
|
+
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2326
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 };
|
|
2327
2429
|
}
|
|
2328
2430
|
}
|
|
2329
2431
|
return this.request(url, fetchOpts);
|
|
2330
2432
|
} catch (error) {
|
|
2331
|
-
console.log(` [auto-
|
|
2433
|
+
console.log(` [auto-fund] Failed: ${error instanceof Error ? error.message : String(error)}. Proceeding with normal request.`);
|
|
2332
2434
|
return this.request(url, fetchOpts);
|
|
2333
2435
|
}
|
|
2334
2436
|
}
|
|
@@ -2429,48 +2531,26 @@ var X402Client = class {
|
|
|
2429
2531
|
};
|
|
2430
2532
|
}
|
|
2431
2533
|
}
|
|
2432
|
-
/** Track a new spending amount */
|
|
2534
|
+
/** Track a new spending amount and notify the caller for persistence */
|
|
2433
2535
|
_trackSpending(amount) {
|
|
2434
2536
|
this._resetTrackerIfNewDay();
|
|
2435
2537
|
this._spendingTracker.totalBorrowed += amount;
|
|
2436
|
-
|
|
2437
|
-
/**
|
|
2438
|
-
* Check if a borrow amount is within spending limits.
|
|
2439
|
-
* Considers both fixed daily limits and yield-limited spending.
|
|
2440
|
-
*/
|
|
2441
|
-
async _checkSpendingLimit(amount, morphoClient) {
|
|
2442
|
-
this._resetTrackerIfNewDay();
|
|
2443
|
-
if (this.config.yieldLimitedSpending) {
|
|
2538
|
+
if (this.config.onSpendingUpdate) {
|
|
2444
2539
|
try {
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
try {
|
|
2450
|
-
const estimate = await morphoClient.getYieldEstimate(
|
|
2451
|
-
pos.collateralToken,
|
|
2452
|
-
pos.collateral,
|
|
2453
|
-
1
|
|
2454
|
-
// 1 day
|
|
2455
|
-
);
|
|
2456
|
-
totalDailyYieldUsdc += estimate.estimatedYieldUsd;
|
|
2457
|
-
} catch (e) {
|
|
2458
|
-
console.warn(`[agether] yield calc failed for ${pos.collateralToken}:`, e instanceof Error ? e.message : e);
|
|
2459
|
-
}
|
|
2460
|
-
}
|
|
2461
|
-
}
|
|
2462
|
-
const yieldLimit = BigInt(Math.round(totalDailyYieldUsdc * 1e6));
|
|
2463
|
-
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2464
|
-
if (yieldLimit > 0n && newTotal > yieldLimit) {
|
|
2465
|
-
return {
|
|
2466
|
-
allowed: false,
|
|
2467
|
-
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)}`
|
|
2468
|
-
};
|
|
2469
|
-
}
|
|
2540
|
+
this.config.onSpendingUpdate({
|
|
2541
|
+
date: this._spendingTracker.date,
|
|
2542
|
+
totalBorrowed: this._spendingTracker.totalBorrowed.toString()
|
|
2543
|
+
});
|
|
2470
2544
|
} catch (e) {
|
|
2471
|
-
console.warn("[agether]
|
|
2545
|
+
console.warn("[agether] onSpendingUpdate callback failed:", e instanceof Error ? e.message : e);
|
|
2472
2546
|
}
|
|
2473
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();
|
|
2474
2554
|
if (this._spendingTracker.dailyLimit > 0n) {
|
|
2475
2555
|
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2476
2556
|
if (newTotal > this._spendingTracker.dailyLimit) {
|
package/dist/index.mjs
CHANGED
|
@@ -714,6 +714,50 @@ var MorphoClient = class {
|
|
|
714
714
|
this._refreshSigner();
|
|
715
715
|
return { tx: receipt.hash, amount: usdcAmount, agentAccount: acctAddr };
|
|
716
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
|
+
}
|
|
717
761
|
// ════════════════════════════════════════════════════════
|
|
718
762
|
// Market Discovery (Morpho GraphQL API)
|
|
719
763
|
// ════════════════════════════════════════════════════════
|
|
@@ -2168,7 +2212,9 @@ var X402Client = class {
|
|
|
2168
2212
|
this.paidFetch = wrapFetchWithPayment(fetch, client);
|
|
2169
2213
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2170
2214
|
const dailyLimit = config.dailySpendLimitUsdc ? BigInt(Math.round(parseFloat(config.dailySpendLimitUsdc) * 1e6)) : 0n;
|
|
2171
|
-
|
|
2215
|
+
const restored = config.initialSpendingState;
|
|
2216
|
+
const restoredBorrowed = restored && restored.date === today ? BigInt(restored.totalBorrowed) : 0n;
|
|
2217
|
+
this._spendingTracker = { date: today, totalBorrowed: restoredBorrowed, dailyLimit };
|
|
2172
2218
|
}
|
|
2173
2219
|
async get(url, opts) {
|
|
2174
2220
|
return this.request(url, { ...opts, method: "GET" });
|
|
@@ -2197,66 +2243,122 @@ var X402Client = class {
|
|
|
2197
2243
|
return (Number(remaining > 0n ? remaining : 0n) / 1e6).toFixed(2);
|
|
2198
2244
|
}
|
|
2199
2245
|
/**
|
|
2200
|
-
* Pay with auto-
|
|
2246
|
+
* Pay with auto-funding: Make an x402 request with automatic USDC sourcing.
|
|
2201
2247
|
*
|
|
2202
|
-
*
|
|
2203
|
-
*
|
|
2204
|
-
*
|
|
2205
|
-
*
|
|
2206
|
-
*
|
|
2207
|
-
*
|
|
2208
|
-
*
|
|
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
|
|
2209
2263
|
*/
|
|
2210
2264
|
async payWithAutoDraw(url, opts) {
|
|
2211
2265
|
const { morphoClient, ...fetchOpts } = opts || {};
|
|
2212
|
-
if (!this.config.autoDraw || !morphoClient) {
|
|
2266
|
+
if (!this.config.autoDraw && !this.config.autoYield || !morphoClient) {
|
|
2213
2267
|
return this.request(url, fetchOpts);
|
|
2214
2268
|
}
|
|
2215
2269
|
try {
|
|
2216
2270
|
const usdcBalance = await morphoClient.getUsdcBalance();
|
|
2217
|
-
console.log(` [auto-
|
|
2271
|
+
console.log(` [auto-fund] AgentAccount USDC balance: ${(Number(usdcBalance) / 1e6).toFixed(2)}`);
|
|
2218
2272
|
const paymentAmount = await this._probePaymentAmount(url, fetchOpts);
|
|
2219
2273
|
if (paymentAmount !== null) {
|
|
2220
|
-
console.log(` [auto-
|
|
2274
|
+
console.log(` [auto-fund] Payment required: ${(Number(paymentAmount) / 1e6).toFixed(6)} USDC`);
|
|
2221
2275
|
const bufferStr = this.config.autoDrawBuffer || "0.5";
|
|
2222
2276
|
const buffer = BigInt(Math.round(parseFloat(bufferStr) * 1e6));
|
|
2223
2277
|
const needed = paymentAmount + buffer;
|
|
2224
2278
|
if (usdcBalance < needed) {
|
|
2225
|
-
const
|
|
2226
|
-
console.log(` [auto-
|
|
2227
|
-
const
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
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
|
+
}
|
|
2298
|
+
}
|
|
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`);
|
|
2233
2310
|
}
|
|
2234
|
-
const
|
|
2235
|
-
if (
|
|
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");
|
|
2236
2318
|
return {
|
|
2237
2319
|
success: false,
|
|
2238
|
-
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)}.`
|
|
2239
2321
|
};
|
|
2240
2322
|
}
|
|
2241
|
-
const
|
|
2242
|
-
|
|
2243
|
-
const borrowResult = await morphoClient.borrow(borrowAmount);
|
|
2244
|
-
console.log(` [auto-draw] Borrow tx: ${borrowResult.tx}`);
|
|
2245
|
-
this._trackSpending(deficit);
|
|
2246
|
-
const result = await this.request(url, fetchOpts);
|
|
2247
|
-
return {
|
|
2248
|
-
...result,
|
|
2249
|
-
autoDrawInfo: {
|
|
2250
|
-
borrowed: borrowAmount,
|
|
2251
|
-
borrowTx: borrowResult.tx,
|
|
2252
|
-
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2253
|
-
}
|
|
2323
|
+
const drawInfo = {
|
|
2324
|
+
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
2254
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 };
|
|
2255
2357
|
}
|
|
2256
2358
|
}
|
|
2257
2359
|
return this.request(url, fetchOpts);
|
|
2258
2360
|
} catch (error) {
|
|
2259
|
-
console.log(` [auto-
|
|
2361
|
+
console.log(` [auto-fund] Failed: ${error instanceof Error ? error.message : String(error)}. Proceeding with normal request.`);
|
|
2260
2362
|
return this.request(url, fetchOpts);
|
|
2261
2363
|
}
|
|
2262
2364
|
}
|
|
@@ -2357,48 +2459,26 @@ var X402Client = class {
|
|
|
2357
2459
|
};
|
|
2358
2460
|
}
|
|
2359
2461
|
}
|
|
2360
|
-
/** Track a new spending amount */
|
|
2462
|
+
/** Track a new spending amount and notify the caller for persistence */
|
|
2361
2463
|
_trackSpending(amount) {
|
|
2362
2464
|
this._resetTrackerIfNewDay();
|
|
2363
2465
|
this._spendingTracker.totalBorrowed += amount;
|
|
2364
|
-
|
|
2365
|
-
/**
|
|
2366
|
-
* Check if a borrow amount is within spending limits.
|
|
2367
|
-
* Considers both fixed daily limits and yield-limited spending.
|
|
2368
|
-
*/
|
|
2369
|
-
async _checkSpendingLimit(amount, morphoClient) {
|
|
2370
|
-
this._resetTrackerIfNewDay();
|
|
2371
|
-
if (this.config.yieldLimitedSpending) {
|
|
2466
|
+
if (this.config.onSpendingUpdate) {
|
|
2372
2467
|
try {
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
try {
|
|
2378
|
-
const estimate = await morphoClient.getYieldEstimate(
|
|
2379
|
-
pos.collateralToken,
|
|
2380
|
-
pos.collateral,
|
|
2381
|
-
1
|
|
2382
|
-
// 1 day
|
|
2383
|
-
);
|
|
2384
|
-
totalDailyYieldUsdc += estimate.estimatedYieldUsd;
|
|
2385
|
-
} catch (e) {
|
|
2386
|
-
console.warn(`[agether] yield calc failed for ${pos.collateralToken}:`, e instanceof Error ? e.message : e);
|
|
2387
|
-
}
|
|
2388
|
-
}
|
|
2389
|
-
}
|
|
2390
|
-
const yieldLimit = BigInt(Math.round(totalDailyYieldUsdc * 1e6));
|
|
2391
|
-
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2392
|
-
if (yieldLimit > 0n && newTotal > yieldLimit) {
|
|
2393
|
-
return {
|
|
2394
|
-
allowed: false,
|
|
2395
|
-
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)}`
|
|
2396
|
-
};
|
|
2397
|
-
}
|
|
2468
|
+
this.config.onSpendingUpdate({
|
|
2469
|
+
date: this._spendingTracker.date,
|
|
2470
|
+
totalBorrowed: this._spendingTracker.totalBorrowed.toString()
|
|
2471
|
+
});
|
|
2398
2472
|
} catch (e) {
|
|
2399
|
-
console.warn("[agether]
|
|
2473
|
+
console.warn("[agether] onSpendingUpdate callback failed:", e instanceof Error ? e.message : e);
|
|
2400
2474
|
}
|
|
2401
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();
|
|
2402
2482
|
if (this._spendingTracker.dailyLimit > 0n) {
|
|
2403
2483
|
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
2404
2484
|
if (newTotal > this._spendingTracker.dailyLimit) {
|