@owney/sdk 0.7.25-beta.15 → 0.7.25-beta.17
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/index.cjs +910 -235
- package/dist/index.d.cts +40 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +851 -174
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -404,20 +404,44 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
|
404
404
|
};
|
|
405
405
|
const notify = (outcome) => {
|
|
406
406
|
if (outcome === "rejected" && matching.length !== 1) return;
|
|
407
|
+
const evidence = matching.length === 1 ? { ...request2 } : {
|
|
408
|
+
attempted: request2.attempted,
|
|
409
|
+
method: request2.method,
|
|
410
|
+
path: request2.path,
|
|
411
|
+
...request2.elapsedMs !== void 0 ? { elapsedMs: request2.elapsedMs } : {}
|
|
412
|
+
};
|
|
407
413
|
for (const observer of matching) {
|
|
408
414
|
try {
|
|
409
|
-
observer.notify(outcome,
|
|
415
|
+
observer.notify(outcome, evidence);
|
|
410
416
|
} catch {
|
|
411
417
|
}
|
|
412
418
|
}
|
|
413
419
|
};
|
|
414
420
|
notify("attempted");
|
|
421
|
+
let callArgs = args;
|
|
422
|
+
if (withdrawal && client.client?.defaults) {
|
|
423
|
+
const configIndex = method === "get" ? 1 : 2;
|
|
424
|
+
const config = args[configIndex];
|
|
425
|
+
const transforms = config?.transformResponse !== void 0 ? config.transformResponse : client.client.defaults.transformResponse;
|
|
426
|
+
const captureStatus = (data, _headers, status) => {
|
|
427
|
+
if (typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599) request2.httpStatus = status;
|
|
428
|
+
return data;
|
|
429
|
+
};
|
|
430
|
+
callArgs = [...args];
|
|
431
|
+
callArgs[configIndex] = { ...config, transformResponse: [
|
|
432
|
+
...Array.isArray(transforms) ? transforms : typeof transforms === "function" ? [transforms] : [],
|
|
433
|
+
captureStatus
|
|
434
|
+
] };
|
|
435
|
+
}
|
|
415
436
|
try {
|
|
416
|
-
return await original.apply(this,
|
|
437
|
+
return await original.apply(this, callArgs);
|
|
417
438
|
} catch (error) {
|
|
418
439
|
const message = error instanceof Error ? error.message : "";
|
|
419
|
-
|
|
420
|
-
if (
|
|
440
|
+
const status = error?.response?.status;
|
|
441
|
+
if (request2.httpStatus === void 0 && typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599)
|
|
442
|
+
request2.httpStatus = status;
|
|
443
|
+
request2.failure = message === "Unauthorized: Invalid API key" ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : /^Idle balance \d+(?:\.\d+)? (USDC|WETH|ETH|EURC) is below the minimum withdrawal of \d+(?:\.\d+)? \1\.?$/.test(message) ? "insufficient-balance" : /^User doesn't have any active session key\.?$/.test(message) ? "session-key-missing" : method === "post" && request2.httpStatus === 400 && message === "Partial withdrawal already in progress" ? "withdrawal-in-progress" : message === "Network error: Unable to reach the server" ? "network" : "other";
|
|
444
|
+
if (["unauthorized", "forbidden", "rate-limited", "insufficient-balance", "session-key-missing", "withdrawal-in-progress"].includes(request2.failure) && (request2.httpStatus === void 0 || request2.httpStatus >= 400 && request2.httpStatus < 500)) notify("rejected");
|
|
421
445
|
throw error;
|
|
422
446
|
} finally {
|
|
423
447
|
request2.elapsedMs = Math.max(0, Date.now() - startedAt);
|
|
@@ -1495,6 +1519,7 @@ var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum, 1: i
|
|
|
1495
1519
|
var SPLITTING_ASSETS = ["USDC", "WETH"];
|
|
1496
1520
|
var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
|
|
1497
1521
|
var EARNINGS_REFRESH_COOLDOWN_MS = 60 * 60 * 1e3;
|
|
1522
|
+
var WITHDRAW_BALANCE_TIMEOUT_MS = 2e3;
|
|
1498
1523
|
var ZyfaiAgent = class _ZyfaiAgent {
|
|
1499
1524
|
id = "zyfai";
|
|
1500
1525
|
supportedChainIds = [8453, 42161, 1];
|
|
@@ -1549,6 +1574,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1549
1574
|
earningsRefreshes = /* @__PURE__ */ new Map();
|
|
1550
1575
|
earningsSnapshot = null;
|
|
1551
1576
|
earningsGeneration = 0;
|
|
1577
|
+
balanceSnapshots = /* @__PURE__ */ new WeakMap();
|
|
1552
1578
|
constructor(apiKey, rpcUrls, referralSource) {
|
|
1553
1579
|
this.rpcUrls = resolveRpcUrls(rpcUrls);
|
|
1554
1580
|
this.sdk = new import_sdk.ZyfaiSDK({
|
|
@@ -1752,7 +1778,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1752
1778
|
* chain/account switch fires the provider's `accountsChanged` →
|
|
1753
1779
|
* `disconnectAccount` (nulls walletClient + authenticatedUserId) while our
|
|
1754
1780
|
* own `connectAuth` guard still believes this chain is connected and
|
|
1755
|
-
* early-returns. Without this, authenticated calls (
|
|
1781
|
+
* early-returns. Without this, authenticated calls (sendDeposit,
|
|
1756
1782
|
* withdrawFunds) fail with "No account connected".
|
|
1757
1783
|
*/
|
|
1758
1784
|
async ensureConnected(state, chainId) {
|
|
@@ -2374,7 +2400,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2374
2400
|
return { txHash, smartWallet, amount, evidence: evidence2 };
|
|
2375
2401
|
}
|
|
2376
2402
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
2377
|
-
const raw2 = await this.sdk.
|
|
2403
|
+
const raw2 = await this.sdk.sendDeposit(
|
|
2378
2404
|
this.getAddress(),
|
|
2379
2405
|
validChainId,
|
|
2380
2406
|
amount,
|
|
@@ -2390,7 +2416,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2390
2416
|
intendedAmount: amount,
|
|
2391
2417
|
txHash: raw2.txHash,
|
|
2392
2418
|
transfer: "unknown",
|
|
2393
|
-
registration: "unknown",
|
|
2419
|
+
registration: raw2.registration ? "confirmed" : "unknown",
|
|
2394
2420
|
delivery: "not-applicable"
|
|
2395
2421
|
};
|
|
2396
2422
|
notifyFunding(onProgress, evidence);
|
|
@@ -2409,9 +2435,74 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2409
2435
|
}
|
|
2410
2436
|
}
|
|
2411
2437
|
// --- IAgent: Fund operations ---
|
|
2438
|
+
/** Only after a definitive endpoint rejection, combine a new provider
|
|
2439
|
+
* snapshot with the Safe's exact idle balance. Never parse its rounded error
|
|
2440
|
+
* amount as proof of zero, or use this to skip sending a new MAX request. */
|
|
2441
|
+
async assertNotEmptyRejectedWithdrawal(state, chainId, token, owner) {
|
|
2442
|
+
const internals = this.sdk;
|
|
2443
|
+
const generation = this.earningsGeneration;
|
|
2444
|
+
let expired = false;
|
|
2445
|
+
const unavailable = () => new OwneyError(
|
|
2446
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
2447
|
+
"Could not verify Zyfai's balance after its withdrawal rejection.",
|
|
2448
|
+
{ chainId, token },
|
|
2449
|
+
this.id
|
|
2450
|
+
);
|
|
2451
|
+
const assertScope = () => {
|
|
2452
|
+
if (expired) throw unavailable();
|
|
2453
|
+
const account = internals.walletClient?.account?.address;
|
|
2454
|
+
if (generation !== this.earningsGeneration || state.chainId !== chainId || state.walletAddress.toLowerCase() !== owner.toLowerCase() || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
|
|
2455
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai withdrawal.", void 0, this.id);
|
|
2456
|
+
};
|
|
2457
|
+
const read2 = async () => {
|
|
2458
|
+
assertScope();
|
|
2459
|
+
const [wallet, registered, portfolio] = await Promise.all([
|
|
2460
|
+
this.sdk.getSmartWalletAddress(owner, chainId),
|
|
2461
|
+
this.sdk.getSmartWalletByEOA(owner),
|
|
2462
|
+
this.sdk.getPortfolio(owner).catch(() => void 0)
|
|
2463
|
+
]);
|
|
2464
|
+
assertScope();
|
|
2465
|
+
if (!(0, import_viem4.isAddress)(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
|
|
2466
|
+
throw new OwneyError(
|
|
2467
|
+
"AGENT_AUTH_FAILED",
|
|
2468
|
+
"Could not verify Zyfai's withdrawal wallet.",
|
|
2469
|
+
{ reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
|
|
2470
|
+
this.id
|
|
2471
|
+
);
|
|
2472
|
+
if (hasEmptyWithdrawalSnapshot(portfolio, chainId, token)) {
|
|
2473
|
+
const asset = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === token);
|
|
2474
|
+
const idle = asset ? await this.getPublicClient(chainId).readContract({
|
|
2475
|
+
address: asset.address,
|
|
2476
|
+
abi: import_viem4.erc20Abi,
|
|
2477
|
+
functionName: "balanceOf",
|
|
2478
|
+
args: [wallet.address]
|
|
2479
|
+
}).catch(() => void 0) : void 0;
|
|
2480
|
+
assertScope();
|
|
2481
|
+
if (idle === 0n) throw new OwneyError(
|
|
2482
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
2483
|
+
"No funds remain in Zyfai for this asset.",
|
|
2484
|
+
{ available: "0", chainId, token },
|
|
2485
|
+
this.id
|
|
2486
|
+
);
|
|
2487
|
+
}
|
|
2488
|
+
};
|
|
2489
|
+
let timer;
|
|
2490
|
+
try {
|
|
2491
|
+
await Promise.race([read2(), new Promise((_, reject) => {
|
|
2492
|
+
timer = setTimeout(() => {
|
|
2493
|
+
expired = true;
|
|
2494
|
+
reject(unavailable());
|
|
2495
|
+
}, WITHDRAW_BALANCE_TIMEOUT_MS);
|
|
2496
|
+
})]);
|
|
2497
|
+
} finally {
|
|
2498
|
+
expired = true;
|
|
2499
|
+
if (timer) clearTimeout(timer);
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2412
2502
|
/** Called only after the execution endpoint explicitly rejected a request.
|
|
2413
2503
|
* A timeout or an accepted request never authorizes repair and resubmission. */
|
|
2414
2504
|
async recoverWithdrawalSession(state, chainId, token, owner) {
|
|
2505
|
+
await this.assertNotEmptyRejectedWithdrawal(state, chainId, token, owner);
|
|
2415
2506
|
const internals = this.sdk;
|
|
2416
2507
|
const assertScope = () => {
|
|
2417
2508
|
const account = internals.walletClient?.account?.address;
|
|
@@ -2419,35 +2510,6 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2419
2510
|
throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai withdrawal.", void 0, this.id);
|
|
2420
2511
|
};
|
|
2421
2512
|
assertScope();
|
|
2422
|
-
const [wallet, registered, portfolio] = await Promise.all([
|
|
2423
|
-
this.sdk.getSmartWalletAddress(owner, chainId),
|
|
2424
|
-
this.sdk.getSmartWalletByEOA(owner),
|
|
2425
|
-
this.sdk.getPortfolio(owner).catch(() => void 0)
|
|
2426
|
-
]);
|
|
2427
|
-
assertScope();
|
|
2428
|
-
if (!(0, import_viem4.isAddress)(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
|
|
2429
|
-
throw new OwneyError(
|
|
2430
|
-
"AGENT_AUTH_FAILED",
|
|
2431
|
-
"Could not verify Zyfai's withdrawal wallet.",
|
|
2432
|
-
{ reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
|
|
2433
|
-
this.id
|
|
2434
|
-
);
|
|
2435
|
-
if (hasEmptyWithdrawalSnapshot(portfolio, chainId, token)) {
|
|
2436
|
-
const asset = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === token);
|
|
2437
|
-
const idle = asset ? await this.getPublicClient(chainId).readContract({
|
|
2438
|
-
address: asset.address,
|
|
2439
|
-
abi: import_viem4.erc20Abi,
|
|
2440
|
-
functionName: "balanceOf",
|
|
2441
|
-
args: [wallet.address]
|
|
2442
|
-
}).catch(() => void 0) : void 0;
|
|
2443
|
-
assertScope();
|
|
2444
|
-
if (idle === 0n) throw new OwneyError(
|
|
2445
|
-
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
2446
|
-
"No funds remain in Zyfai for this asset.",
|
|
2447
|
-
{ available: "0", chainId, token },
|
|
2448
|
-
this.id
|
|
2449
|
-
);
|
|
2450
|
-
}
|
|
2451
2513
|
const walletState = await this.readWalletState(owner);
|
|
2452
2514
|
assertScope();
|
|
2453
2515
|
const unavailable = () => new OwneyError(
|
|
@@ -2499,6 +2561,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2499
2561
|
raw2 = await this.sdk.withdrawFunds(owner, validChainId, amount, token);
|
|
2500
2562
|
break;
|
|
2501
2563
|
} catch (error) {
|
|
2564
|
+
if (!amount && observation.rejected() && observation.request()?.failure === "insufficient-balance") {
|
|
2565
|
+
try {
|
|
2566
|
+
await this.assertNotEmptyRejectedWithdrawal(state, validChainId, token, owner);
|
|
2567
|
+
} catch (proofError) {
|
|
2568
|
+
if (proofError instanceof OwneyError && (proofError.code === "WITHDRAW_INSUFFICIENT_BALANCE" || proofError.code === "NOT_CONNECTED")) throw proofError;
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2502
2571
|
if (!observation.rejected() || observation.request()?.failure !== "session-key-missing") throw error;
|
|
2503
2572
|
if (attempt === 1)
|
|
2504
2573
|
throw new OwneyError(
|
|
@@ -2592,12 +2661,100 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2592
2661
|
async getFundingWallet(state, chainId) {
|
|
2593
2662
|
return (await this.sdk.getSmartWalletAddress(state.walletAddress, isValidChainId(chainId))).address;
|
|
2594
2663
|
}
|
|
2664
|
+
/** A positive Safe balance is a withdrawal planning floor when the portfolio
|
|
2665
|
+
* index reports zero. Idle zero cannot prove that Zyfai's protocol positions
|
|
2666
|
+
* are empty: its SDK does not expose a complete contract catalog for them. */
|
|
2667
|
+
async getWithdrawableBalance(state, chainId, token) {
|
|
2668
|
+
const validChainId = isValidChainId(chainId);
|
|
2669
|
+
const asset = SupportedAssets.find((value) => value.chainId === validChainId && value.symbol === token);
|
|
2670
|
+
if (!asset) throw new OwneyError("WITHDRAW_NO_PERMITTED_TOKENS", "Asset selected is not supported", void 0, this.id);
|
|
2671
|
+
const owner = state.walletAddress;
|
|
2672
|
+
const generation = this.earningsGeneration;
|
|
2673
|
+
const internals = this.sdk;
|
|
2674
|
+
let expired = false;
|
|
2675
|
+
const unavailable = () => new OwneyError(
|
|
2676
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
2677
|
+
"Could not verify Zyfai's withdrawal balance. Its vault positions may still contain funds.",
|
|
2678
|
+
{ reason: "ZYFAI_VAULT_BALANCE_UNVERIFIED", chainId, token },
|
|
2679
|
+
this.id
|
|
2680
|
+
);
|
|
2681
|
+
const assertScope = () => {
|
|
2682
|
+
if (expired) throw unavailable();
|
|
2683
|
+
const account = internals.walletClient?.account?.address;
|
|
2684
|
+
if (generation !== this.earningsGeneration || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== validChainId || state.walletAddress.toLowerCase() !== owner.toLowerCase() || state.chainId !== validChainId || account && account.toLowerCase() !== owner.toLowerCase())
|
|
2685
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while checking Zyfai's withdrawal balance.", void 0, this.id);
|
|
2686
|
+
};
|
|
2687
|
+
let timer;
|
|
2688
|
+
const read2 = async () => {
|
|
2689
|
+
assertScope();
|
|
2690
|
+
const [wallet, registered] = await Promise.all([
|
|
2691
|
+
this.sdk.getSmartWalletAddress(owner, validChainId),
|
|
2692
|
+
this.sdk.getSmartWalletByEOA(owner)
|
|
2693
|
+
]);
|
|
2694
|
+
assertScope();
|
|
2695
|
+
if (!(0, import_viem4.isAddress)(wallet.address) || wallet.isDeployed !== true || wallet.isOwner !== true || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase()) throw unavailable();
|
|
2696
|
+
const client = this.getPublicClient(validChainId);
|
|
2697
|
+
const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
|
|
2698
|
+
assertScope();
|
|
2699
|
+
const idle = await client.readContract({
|
|
2700
|
+
address: asset.address,
|
|
2701
|
+
abi: import_viem4.erc20Abi,
|
|
2702
|
+
functionName: "balanceOf",
|
|
2703
|
+
args: [wallet.address],
|
|
2704
|
+
blockNumber
|
|
2705
|
+
});
|
|
2706
|
+
assertScope();
|
|
2707
|
+
if (typeof idle !== "bigint" || idle <= 0n) throw unavailable();
|
|
2708
|
+
return idle;
|
|
2709
|
+
};
|
|
2710
|
+
try {
|
|
2711
|
+
return await Promise.race([read2(), new Promise((_, reject) => {
|
|
2712
|
+
timer = setTimeout(() => {
|
|
2713
|
+
expired = true;
|
|
2714
|
+
reject(unavailable());
|
|
2715
|
+
}, WITHDRAW_BALANCE_TIMEOUT_MS);
|
|
2716
|
+
})]);
|
|
2717
|
+
} catch (error) {
|
|
2718
|
+
if (error instanceof OwneyError) throw error;
|
|
2719
|
+
throw unavailable();
|
|
2720
|
+
} finally {
|
|
2721
|
+
expired = true;
|
|
2722
|
+
if (timer) clearTimeout(timer);
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2595
2725
|
async getBalances(state, chainId) {
|
|
2596
2726
|
const requestedAt = Date.now();
|
|
2727
|
+
const generation = this.earningsGeneration;
|
|
2597
2728
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
2598
2729
|
const raw2 = await this.sdk.getPortfolio(this.getAddress());
|
|
2599
2730
|
const balance = mapBalances(raw2, validChainId, smartWallet);
|
|
2600
|
-
|
|
2731
|
+
const reads = balance.readMetadata?.map((read2) => ({ ...read2, requestedAt })) ?? [];
|
|
2732
|
+
for (const asset of SupportedAssets.filter((item) => item.chainId === chainId)) {
|
|
2733
|
+
if (!reads.some((read2) => read2.chainId === chainId && read2.asset === asset.symbol) && hasEmptyWithdrawalSnapshot(raw2, chainId, asset.symbol)) {
|
|
2734
|
+
reads.push({ chainId, asset: asset.symbol, requestedAt, fetchedAt: Date.now(), positionsStatus: "available" });
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
const result = { ...balance, readMetadata: reads };
|
|
2738
|
+
for (const read2 of reads) this.balanceSnapshots.set(read2, { raw: raw2, generation, owner: state.walletAddress.toLowerCase() });
|
|
2739
|
+
return result;
|
|
2740
|
+
}
|
|
2741
|
+
async verifyWithdrawalBalance(state, evidence, balance) {
|
|
2742
|
+
const read2 = balance.readMetadata?.find((item) => item.chainId === evidence.chainId && item.asset === evidence.token);
|
|
2743
|
+
const snapshot = read2 && this.balanceSnapshots.get(read2);
|
|
2744
|
+
const asset = SupportedAssets.find((item) => item.chainId === evidence.chainId && item.symbol === evidence.token);
|
|
2745
|
+
if (!snapshot || snapshot.generation !== this.earningsGeneration || snapshot.owner !== state.walletAddress.toLowerCase() || !read2 || !asset || !evidence.txHash || !evidence.blockNumber || !balance.smartWallet || balance.smartWallet.toLowerCase() !== evidence.agentWallet?.toLowerCase() || !hasEmptyWithdrawalSnapshot(snapshot.raw, evidence.chainId, evidence.token)) return void 0;
|
|
2746
|
+
const client = this.getPublicClient(evidence.chainId);
|
|
2747
|
+
const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
|
|
2748
|
+
if (blockNumber < BigInt(evidence.blockNumber)) return void 0;
|
|
2749
|
+
const idle = await client.readContract({
|
|
2750
|
+
address: asset.address,
|
|
2751
|
+
abi: import_viem4.erc20Abi,
|
|
2752
|
+
functionName: "balanceOf",
|
|
2753
|
+
args: [balance.smartWallet],
|
|
2754
|
+
blockNumber
|
|
2755
|
+
});
|
|
2756
|
+
if (idle !== 0n || snapshot.generation !== this.earningsGeneration || this.connectedAddress?.toLowerCase() !== snapshot.owner || this.connectedChainId !== evidence.chainId) return void 0;
|
|
2757
|
+
return { ...read2, withdrawalCheck: { txHash: evidence.txHash, blockNumber: blockNumber.toString() } };
|
|
2601
2758
|
}
|
|
2602
2759
|
earningsKey(state, chainId, smartWallet) {
|
|
2603
2760
|
return JSON.stringify([
|
|
@@ -2796,7 +2953,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2796
2953
|
};
|
|
2797
2954
|
|
|
2798
2955
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2799
|
-
var
|
|
2956
|
+
var import_viem12 = require("viem");
|
|
2800
2957
|
var import_chains4 = require("viem/chains");
|
|
2801
2958
|
|
|
2802
2959
|
// src/lib/chain-guard.ts
|
|
@@ -3580,6 +3737,7 @@ function mapYieldseekerBalances(contexts) {
|
|
|
3580
3737
|
chainId: context.agent.chainId,
|
|
3581
3738
|
asset: context.asset,
|
|
3582
3739
|
fetchedAt: context.fetchedAt,
|
|
3740
|
+
requestedAt: context.requestedAt,
|
|
3583
3741
|
readVersion: context.readVersion,
|
|
3584
3742
|
providerUpdatedAt: context.snapshot.updatedDate,
|
|
3585
3743
|
snapshotId: String(context.snapshot.agentWalletSnapshotId),
|
|
@@ -3957,13 +4115,59 @@ function mapYieldseekerAgentApy(options, days) {
|
|
|
3957
4115
|
};
|
|
3958
4116
|
}
|
|
3959
4117
|
|
|
3960
|
-
// src/
|
|
4118
|
+
// src/lib/remaining-vault-balance.ts
|
|
3961
4119
|
var import_viem9 = require("viem");
|
|
4120
|
+
async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber) {
|
|
4121
|
+
const addresses = [...new Set(vaults.map((value) => value.toLowerCase()))];
|
|
4122
|
+
if (addresses.length > 64) return void 0;
|
|
4123
|
+
const batches = [];
|
|
4124
|
+
for (let index = 0; index < addresses.length; index += 10) batches.push(addresses.slice(index, index + 10));
|
|
4125
|
+
const values = await Promise.all(batches.map(async (batch) => {
|
|
4126
|
+
const balances = await client.multicall({
|
|
4127
|
+
blockNumber,
|
|
4128
|
+
allowFailure: true,
|
|
4129
|
+
contracts: batch.map((address) => ({
|
|
4130
|
+
address: (0, import_viem9.getAddress)(address),
|
|
4131
|
+
abi: import_viem9.erc20Abi,
|
|
4132
|
+
functionName: "balanceOf",
|
|
4133
|
+
args: [wallet]
|
|
4134
|
+
}))
|
|
4135
|
+
});
|
|
4136
|
+
if (balances.length !== batch.length) return void 0;
|
|
4137
|
+
const funded = [];
|
|
4138
|
+
for (let index = 0; index < batch.length; index++) {
|
|
4139
|
+
const result = balances[index];
|
|
4140
|
+
if (result?.status !== "success" || typeof result.result !== "bigint" || result.result < 0n) return void 0;
|
|
4141
|
+
if (result.result > 0n) funded.push({ address: (0, import_viem9.getAddress)(batch[index]), shares: result.result });
|
|
4142
|
+
}
|
|
4143
|
+
if (!funded.length) return 0n;
|
|
4144
|
+
const valuations = await client.multicall({
|
|
4145
|
+
blockNumber,
|
|
4146
|
+
allowFailure: true,
|
|
4147
|
+
contracts: funded.flatMap(({ address, shares }) => [
|
|
4148
|
+
{ address, abi: import_viem9.erc4626Abi, functionName: "asset" },
|
|
4149
|
+
{ address, abi: import_viem9.erc4626Abi, functionName: "convertToAssets", args: [shares] }
|
|
4150
|
+
])
|
|
4151
|
+
});
|
|
4152
|
+
if (valuations.length !== funded.length * 2) return void 0;
|
|
4153
|
+
let total = 0n;
|
|
4154
|
+
for (let index = 0; index < funded.length; index++) {
|
|
4155
|
+
const underlying = valuations[index * 2], value = valuations[index * 2 + 1];
|
|
4156
|
+
if (underlying?.status !== "success" || typeof underlying.result !== "string" || underlying.result.toLowerCase() !== asset.toLowerCase() || value?.status !== "success" || typeof value.result !== "bigint" || value.result < 0n) return void 0;
|
|
4157
|
+
total += value.result;
|
|
4158
|
+
}
|
|
4159
|
+
return total;
|
|
4160
|
+
}));
|
|
4161
|
+
return values.some((value) => value === void 0) ? void 0 : values.reduce((sum, value) => sum + value, 0n);
|
|
4162
|
+
}
|
|
4163
|
+
|
|
4164
|
+
// src/agents/yieldseeker/yieldseeker.wallet-version.ts
|
|
4165
|
+
var import_viem10 = require("viem");
|
|
3962
4166
|
var YIELDSEEKER_FACTORY = "0x9c7410a0faCAC60850C46eE5b58B518daec95130";
|
|
3963
4167
|
var YIELDSEEKER_V1_IMPLEMENTATION = "0x32403455357589523435dB18b414057D7cE05051";
|
|
3964
4168
|
var YIELDSEEKER_V2_IMPLEMENTATION = "0x613Db9c924ADB7A7A90c11b02Cce20c913E3Fe50";
|
|
3965
4169
|
var ERC1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
|
3966
|
-
var walletAbi = (0,
|
|
4170
|
+
var walletAbi = (0, import_viem10.parseAbi)([
|
|
3967
4171
|
"function owner() view returns (address)",
|
|
3968
4172
|
"function ownerAgentIndex() view returns (uint256)",
|
|
3969
4173
|
"function baseAsset() view returns (address)",
|
|
@@ -3971,7 +4175,7 @@ var walletAbi = (0, import_viem9.parseAbi)([
|
|
|
3971
4175
|
"function upgradeToLatest()",
|
|
3972
4176
|
"event Upgraded(address indexed implementation)"
|
|
3973
4177
|
]);
|
|
3974
|
-
var factoryAbi = (0,
|
|
4178
|
+
var factoryAbi = (0, import_viem10.parseAbi)([
|
|
3975
4179
|
"function userWallets(address owner, uint256 index) view returns (address)",
|
|
3976
4180
|
"function agentWalletImplementation() view returns (address)"
|
|
3977
4181
|
]);
|
|
@@ -3979,9 +4183,9 @@ function yieldseekerOwnerPath(code) {
|
|
|
3979
4183
|
return !code || code === "0x" || /^0xef0100[0-9a-f]{40}$/i.test(code) ? "eoa" : "smart";
|
|
3980
4184
|
}
|
|
3981
4185
|
async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNumber) {
|
|
3982
|
-
const agentWallet = (0,
|
|
3983
|
-
const expectedOwner = (0,
|
|
3984
|
-
const expectedAsset = (0,
|
|
4186
|
+
const agentWallet = (0, import_viem10.getAddress)(wallet);
|
|
4187
|
+
const expectedOwner = (0, import_viem10.getAddress)(owner);
|
|
4188
|
+
const expectedAsset = (0, import_viem10.getAddress)(asset);
|
|
3985
4189
|
const [code, ownerCode, slot, actualOwner, index, actualAsset, actualFactory, latest] = await Promise.all([
|
|
3986
4190
|
client.getBytecode({ address: agentWallet, blockNumber }),
|
|
3987
4191
|
client.getBytecode({ address: expectedOwner, blockNumber }),
|
|
@@ -3995,7 +4199,7 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
|
|
|
3995
4199
|
if (!code || code === "0x" || !slot || /^0x0+$/.test(slot)) {
|
|
3996
4200
|
throw new Error("Yieldseeker wallet is not a deployed ERC1967 proxy");
|
|
3997
4201
|
}
|
|
3998
|
-
const implementation = (0,
|
|
4202
|
+
const implementation = (0, import_viem10.getAddress)(`0x${slot.slice(-40)}`);
|
|
3999
4203
|
const mapped = await client.readContract({
|
|
4000
4204
|
address: YIELDSEEKER_FACTORY,
|
|
4001
4205
|
abi: factoryAbi,
|
|
@@ -4003,8 +4207,8 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
|
|
|
4003
4207
|
args: [expectedOwner, index],
|
|
4004
4208
|
blockNumber
|
|
4005
4209
|
});
|
|
4006
|
-
const verified = (0,
|
|
4007
|
-
const version = !verified ? "unknown" : (0,
|
|
4210
|
+
const verified = (0, import_viem10.isAddressEqual)(actualOwner, expectedOwner) && (0, import_viem10.isAddressEqual)(actualAsset, expectedAsset) && (0, import_viem10.isAddressEqual)(actualFactory, YIELDSEEKER_FACTORY) && (0, import_viem10.isAddressEqual)(mapped, agentWallet);
|
|
4211
|
+
const version = !verified ? "unknown" : (0, import_viem10.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem10.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
|
|
4008
4212
|
return {
|
|
4009
4213
|
agentWallet,
|
|
4010
4214
|
owner: actualOwner,
|
|
@@ -4013,13 +4217,13 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
|
|
|
4013
4217
|
implementation,
|
|
4014
4218
|
version,
|
|
4015
4219
|
verified,
|
|
4016
|
-
upgradeAvailable: version === "v1" && (0,
|
|
4220
|
+
upgradeAvailable: version === "v1" && (0, import_viem10.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
|
|
4017
4221
|
};
|
|
4018
4222
|
}
|
|
4019
4223
|
var YIELDSEEKER_UPGRADE_ABI = walletAbi;
|
|
4020
4224
|
|
|
4021
4225
|
// src/agents/yieldseeker/yieldseeker.sponsored-withdraw.ts
|
|
4022
|
-
var
|
|
4226
|
+
var import_viem11 = require("viem");
|
|
4023
4227
|
var import_account_abstraction2 = require("viem/account-abstraction");
|
|
4024
4228
|
var import_accounts = require("viem/accounts");
|
|
4025
4229
|
var import_chains3 = require("viem/chains");
|
|
@@ -4098,7 +4302,7 @@ function rpcDiagnostics(data) {
|
|
|
4098
4302
|
details.revertName = data.revertName;
|
|
4099
4303
|
if (typeof data?.aaCode === "string" && /^AA[0-9]{2}$/.test(data.aaCode)) details.aaCode = data.aaCode;
|
|
4100
4304
|
if (typeof data?.panicCode === "string" && /^0x[0-9a-fA-F]{1,2}$/.test(data.panicCode)) {
|
|
4101
|
-
const panicCode = (0,
|
|
4305
|
+
const panicCode = (0, import_viem11.toHex)(BigInt(data.panicCode));
|
|
4102
4306
|
if (["0x0", "0x1", "0x11", "0x12", "0x21", "0x22", "0x31", "0x32", "0x41", "0x51"].includes(panicCode))
|
|
4103
4307
|
details.panicCode = panicCode;
|
|
4104
4308
|
}
|
|
@@ -4318,14 +4522,14 @@ async function submitEoa(input) {
|
|
|
4318
4522
|
throw sponsorError("Base did not return EIP-1559 gas fees.", true);
|
|
4319
4523
|
let operation = {
|
|
4320
4524
|
sender: input.agentWallet,
|
|
4321
|
-
nonce: (0,
|
|
4525
|
+
nonce: (0, import_viem11.toHex)(nonce),
|
|
4322
4526
|
initCode: "0x",
|
|
4323
4527
|
callData: input.callData,
|
|
4324
|
-
callGasLimit: (0,
|
|
4325
|
-
verificationGasLimit: (0,
|
|
4326
|
-
preVerificationGas: (0,
|
|
4327
|
-
maxFeePerGas: (0,
|
|
4328
|
-
maxPriorityFeePerGas: (0,
|
|
4528
|
+
callGasLimit: (0, import_viem11.toHex)(3e5),
|
|
4529
|
+
verificationGasLimit: (0, import_viem11.toHex)(3e5),
|
|
4530
|
+
preVerificationGas: (0, import_viem11.toHex)(6e4),
|
|
4531
|
+
maxFeePerGas: (0, import_viem11.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
|
|
4532
|
+
maxPriorityFeePerGas: (0, import_viem11.toHex)(fees.maxPriorityFeePerGas),
|
|
4329
4533
|
paymasterAndData: "0x",
|
|
4330
4534
|
signature: "0x"
|
|
4331
4535
|
};
|
|
@@ -4333,7 +4537,7 @@ async function submitEoa(input) {
|
|
|
4333
4537
|
const stub = await rpc(
|
|
4334
4538
|
input.rpcUrl,
|
|
4335
4539
|
"pm_getPaymasterStubData",
|
|
4336
|
-
[operation, ENTRY_POINT_V06, (0,
|
|
4540
|
+
[operation, ENTRY_POINT_V06, (0, import_viem11.toHex)(8453), {}],
|
|
4337
4541
|
"prepare"
|
|
4338
4542
|
);
|
|
4339
4543
|
if (!stub.paymasterAndData?.startsWith("0x"))
|
|
@@ -4348,7 +4552,7 @@ async function submitEoa(input) {
|
|
|
4348
4552
|
const final = await rpc(
|
|
4349
4553
|
input.rpcUrl,
|
|
4350
4554
|
"pm_getPaymasterData",
|
|
4351
|
-
[operation, ENTRY_POINT_V06, (0,
|
|
4555
|
+
[operation, ENTRY_POINT_V06, (0, import_viem11.toHex)(8453), {}],
|
|
4352
4556
|
"prepare"
|
|
4353
4557
|
);
|
|
4354
4558
|
if (!final.paymasterAndData?.startsWith("0x"))
|
|
@@ -4357,10 +4561,10 @@ async function submitEoa(input) {
|
|
|
4357
4561
|
}
|
|
4358
4562
|
const userOpHash = operationHash(operation);
|
|
4359
4563
|
await assertOwnerSession(input.state, input.owner);
|
|
4360
|
-
const wallet = (0,
|
|
4564
|
+
const wallet = (0, import_viem11.createWalletClient)({
|
|
4361
4565
|
account: input.owner,
|
|
4362
4566
|
chain: import_chains3.base,
|
|
4363
|
-
transport: (0,
|
|
4567
|
+
transport: (0, import_viem11.custom)(input.state.provider)
|
|
4364
4568
|
});
|
|
4365
4569
|
const signature = await wallet.signMessage({ message: { raw: userOpHash } });
|
|
4366
4570
|
operation = { ...operation, signature };
|
|
@@ -4437,12 +4641,12 @@ async function submitSmart(input) {
|
|
|
4437
4641
|
} catch {
|
|
4438
4642
|
throw sponsorError("This account cannot check paymaster support on Base.", true);
|
|
4439
4643
|
}
|
|
4440
|
-
const supportsPaymaster = capabilities?.[(0,
|
|
4644
|
+
const supportsPaymaster = capabilities?.[(0, import_viem11.toHex)(8453)]?.paymasterService?.supported ?? capabilities?.["8453"]?.paymasterService?.supported ?? capabilities?.["0x0"]?.paymasterService?.supported;
|
|
4441
4645
|
if (supportsPaymaster !== true)
|
|
4442
4646
|
throw sponsorError("This account cannot use a paymaster on Base.", true);
|
|
4443
4647
|
await assertOwnerSession(input.state, input.owner);
|
|
4444
4648
|
let sent;
|
|
4445
|
-
const requestedId = (0,
|
|
4649
|
+
const requestedId = (0, import_viem11.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
|
|
4446
4650
|
savePending(input.onPending, { callsId: requestedId }, true);
|
|
4447
4651
|
await checkpoint(input.onBeforeSubmission, { callsId: requestedId });
|
|
4448
4652
|
try {
|
|
@@ -4451,7 +4655,7 @@ async function submitSmart(input) {
|
|
|
4451
4655
|
params: [{
|
|
4452
4656
|
version: "2.0.0",
|
|
4453
4657
|
from: input.owner,
|
|
4454
|
-
chainId: (0,
|
|
4658
|
+
chainId: (0, import_viem11.toHex)(8453),
|
|
4455
4659
|
id: requestedId,
|
|
4456
4660
|
atomicRequired: false,
|
|
4457
4661
|
calls: [{ to: input.agentWallet, value: "0x0", data: input.callData }],
|
|
@@ -4526,7 +4730,7 @@ async function submitSmart(input) {
|
|
|
4526
4730
|
);
|
|
4527
4731
|
}
|
|
4528
4732
|
async function submitYieldseekerSponsoredWithdrawal(input) {
|
|
4529
|
-
const owner = (0,
|
|
4733
|
+
const owner = (0, import_viem11.getAddress)(input.state.walletAddress);
|
|
4530
4734
|
const { intent, rpcUrl } = await requestIntent({
|
|
4531
4735
|
apiKey: input.apiKey,
|
|
4532
4736
|
routingApiBaseUrl: input.routingApiBaseUrl,
|
|
@@ -4544,7 +4748,7 @@ async function submitYieldseekerSponsoredWithdrawal(input) {
|
|
|
4544
4748
|
return intent.ownerPath === "eoa" ? submitEoa(args) : submitSmart(args);
|
|
4545
4749
|
}
|
|
4546
4750
|
async function submitYieldseekerSponsoredUpgrade(input) {
|
|
4547
|
-
const owner = (0,
|
|
4751
|
+
const owner = (0, import_viem11.getAddress)(input.state.walletAddress);
|
|
4548
4752
|
const { intent, rpcUrl } = await requestIntent({
|
|
4549
4753
|
apiKey: input.apiKey,
|
|
4550
4754
|
routingApiBaseUrl: input.routingApiBaseUrl,
|
|
@@ -4579,6 +4783,8 @@ var YIELDSEEKER_SETTLEMENT_WINDOW_MS = 5 * 6e4;
|
|
|
4579
4783
|
var YIELDSEEKER_ACTIVITY_CACHE_MS = 6e4;
|
|
4580
4784
|
var YIELDSEEKER_MAX_WITHDRAW_SWEEPS = 3;
|
|
4581
4785
|
var YIELDSEEKER_MAX_POSITION_VERIFICATION_MS = 2e3;
|
|
4786
|
+
var YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT = 64;
|
|
4787
|
+
var YIELDSEEKER_VAULTS_PER_RPC_BATCH = 10;
|
|
4582
4788
|
function generateYieldseekerUsername() {
|
|
4583
4789
|
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
4584
4790
|
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
@@ -4659,6 +4865,8 @@ var YieldseekerAgent = class {
|
|
|
4659
4865
|
transactionExecutor;
|
|
4660
4866
|
unwindReceiptWaiter;
|
|
4661
4867
|
agentContexts = /* @__PURE__ */ new Map();
|
|
4868
|
+
balanceContexts = /* @__PURE__ */ new WeakMap();
|
|
4869
|
+
balanceVaults = /* @__PURE__ */ new Map();
|
|
4662
4870
|
users = /* @__PURE__ */ new Map();
|
|
4663
4871
|
pendingAgents = /* @__PURE__ */ new Map();
|
|
4664
4872
|
pendingWalletContexts = /* @__PURE__ */ new Map();
|
|
@@ -4704,8 +4912,8 @@ var YieldseekerAgent = class {
|
|
|
4704
4912
|
if (useCachedV2 && cached && Date.now() - cached.at < 15 * 6e4) return { ...cached.status };
|
|
4705
4913
|
const status = await readYieldseekerWalletStatus(
|
|
4706
4914
|
this.getReceiptClient(),
|
|
4707
|
-
(0,
|
|
4708
|
-
(0,
|
|
4915
|
+
(0, import_viem12.getAddress)(context.wallet.walletAddress),
|
|
4916
|
+
(0, import_viem12.getAddress)(state.walletAddress),
|
|
4709
4917
|
YIELDSEEKER_ASSET_METADATA[asset].address
|
|
4710
4918
|
);
|
|
4711
4919
|
this.rememberWalletStatus(key2, status);
|
|
@@ -4735,12 +4943,12 @@ var YieldseekerAgent = class {
|
|
|
4735
4943
|
const pendingKey2 = `owney.yieldseeker.upgrade:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}`;
|
|
4736
4944
|
const confirmUpgrade = async (hash, receipt2) => {
|
|
4737
4945
|
this.withdrawalVersionCache.delete(versionKey);
|
|
4738
|
-
const upgradeTopic = (0,
|
|
4946
|
+
const upgradeTopic = (0, import_viem12.encodeEventTopics)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded" })[0];
|
|
4739
4947
|
const lastUpgrade = receipt2?.logs.filter((log) => log.address.toLowerCase() === before.agentWallet.toLowerCase() && log.topics[0] === upgradeTopic).at(-1);
|
|
4740
4948
|
const expectedUpgrade = (() => {
|
|
4741
4949
|
if (!lastUpgrade || lastUpgrade.removed) return false;
|
|
4742
4950
|
try {
|
|
4743
|
-
const event = (0,
|
|
4951
|
+
const event = (0, import_viem12.decodeEventLog)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded", data: lastUpgrade.data, topics: lastUpgrade.topics });
|
|
4744
4952
|
return event.args.implementation.toLowerCase() === YIELDSEEKER_V2_IMPLEMENTATION.toLowerCase();
|
|
4745
4953
|
} catch {
|
|
4746
4954
|
return false;
|
|
@@ -4858,7 +5066,7 @@ var YieldseekerAgent = class {
|
|
|
4858
5066
|
{ version: before.version },
|
|
4859
5067
|
this.id
|
|
4860
5068
|
);
|
|
4861
|
-
const data = (0,
|
|
5069
|
+
const data = (0, import_viem12.encodeFunctionData)({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
|
|
4862
5070
|
let txHash;
|
|
4863
5071
|
let receipt;
|
|
4864
5072
|
if (gasMode === "sponsored") {
|
|
@@ -4901,7 +5109,7 @@ var YieldseekerAgent = class {
|
|
|
4901
5109
|
}
|
|
4902
5110
|
} else {
|
|
4903
5111
|
txHash = await this.submitTransaction(state, chainId, {
|
|
4904
|
-
from: (0,
|
|
5112
|
+
from: (0, import_viem12.getAddress)(state.walletAddress),
|
|
4905
5113
|
to: before.agentWallet,
|
|
4906
5114
|
data,
|
|
4907
5115
|
value: "0",
|
|
@@ -4924,6 +5132,7 @@ var YieldseekerAgent = class {
|
|
|
4924
5132
|
}
|
|
4925
5133
|
this.users.clear();
|
|
4926
5134
|
this.agentContexts.clear();
|
|
5135
|
+
this.balanceVaults.clear();
|
|
4927
5136
|
this.pendingAgents.clear();
|
|
4928
5137
|
this.pendingWalletContexts.clear();
|
|
4929
5138
|
this.readCache.clear();
|
|
@@ -4987,12 +5196,12 @@ var YieldseekerAgent = class {
|
|
|
4987
5196
|
} else {
|
|
4988
5197
|
try {
|
|
4989
5198
|
txHash = await this.submitTransaction(state, chainId, {
|
|
4990
|
-
from: (0,
|
|
5199
|
+
from: (0, import_viem12.getAddress)(state.walletAddress),
|
|
4991
5200
|
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
4992
|
-
data: (0,
|
|
4993
|
-
abi:
|
|
5201
|
+
data: (0, import_viem12.encodeFunctionData)({
|
|
5202
|
+
abi: import_viem12.erc20Abi,
|
|
4994
5203
|
functionName: "transfer",
|
|
4995
|
-
args: [(0,
|
|
5204
|
+
args: [(0, import_viem12.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
4996
5205
|
}),
|
|
4997
5206
|
value: "0",
|
|
4998
5207
|
chainId
|
|
@@ -5102,81 +5311,99 @@ var YieldseekerAgent = class {
|
|
|
5102
5311
|
let settlement = null;
|
|
5103
5312
|
try {
|
|
5104
5313
|
const maxSettlementBasis = amount === void 0 ? this.snapshotMovement(state, chainId, context, 0n) : null;
|
|
5105
|
-
this.
|
|
5106
|
-
const
|
|
5314
|
+
const contextKey = this.contextKey(state, chainId, asset);
|
|
5315
|
+
const priorPositions = this.withdrawalVaultHints(state, chainId, context);
|
|
5316
|
+
this.advancePortfolioVersion(contextKey);
|
|
5317
|
+
const boundedVerification = async (read2, timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
|
|
5318
|
+
let timer;
|
|
5319
|
+
try {
|
|
5320
|
+
return await Promise.race([read2, new Promise((resolve) => {
|
|
5321
|
+
timer = setTimeout(() => resolve(void 0), Math.max(0, timeoutMs));
|
|
5322
|
+
})]);
|
|
5323
|
+
} finally {
|
|
5324
|
+
if (timer) clearTimeout(timer);
|
|
5325
|
+
}
|
|
5326
|
+
};
|
|
5327
|
+
const feeRateRead = amount === void 0 ? Promise.resolve(0) : boundedVerification(
|
|
5328
|
+
(() => {
|
|
5329
|
+
try {
|
|
5330
|
+
return this.portfolioSnapshot(state, chainId, context, contextKey).read.then((value) => value.agentSnapshot.feeRateBps);
|
|
5331
|
+
} catch (error) {
|
|
5332
|
+
return Promise.reject(error);
|
|
5333
|
+
}
|
|
5334
|
+
})().catch(() => void 0)
|
|
5335
|
+
);
|
|
5336
|
+
const positionsRead = this.readWithdrawalPositions(state, chainId, context);
|
|
5107
5337
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
5108
|
-
const agentWallet = (0,
|
|
5109
|
-
const
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5338
|
+
const agentWallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
|
|
5339
|
+
const readOwnerFunds = async (blockNumber) => {
|
|
5340
|
+
const feeTracker = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5341
|
+
address: agentWallet,
|
|
5342
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5343
|
+
functionName: "feeTracker",
|
|
5344
|
+
blockNumber
|
|
5345
|
+
}), chainId, "eth_call", this.id);
|
|
5116
5346
|
this.assertSession(generation, chainId, asset);
|
|
5117
|
-
if (!(0,
|
|
5347
|
+
if (!(0, import_viem12.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
|
|
5118
5348
|
throw this.invalidResponse("wallet fee tracker");
|
|
5119
5349
|
const [idle2, fees] = await Promise.all([
|
|
5120
5350
|
withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5121
5351
|
address: metadata.address,
|
|
5122
|
-
abi:
|
|
5352
|
+
abi: import_viem12.erc20Abi,
|
|
5123
5353
|
functionName: "balanceOf",
|
|
5124
|
-
args: [agentWallet]
|
|
5354
|
+
args: [agentWallet],
|
|
5355
|
+
blockNumber
|
|
5125
5356
|
}), chainId, "eth_call", this.id),
|
|
5126
5357
|
withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5127
|
-
address: (0,
|
|
5358
|
+
address: (0, import_viem12.getAddress)(feeTracker),
|
|
5128
5359
|
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
5129
5360
|
functionName: "getFeesOwed",
|
|
5130
|
-
args: [agentWallet]
|
|
5361
|
+
args: [agentWallet],
|
|
5362
|
+
blockNumber
|
|
5131
5363
|
}), chainId, "eth_call", this.id)
|
|
5132
5364
|
]);
|
|
5133
5365
|
this.assertSession(generation, chainId, asset);
|
|
5134
5366
|
if (typeof idle2 !== "bigint" || idle2 < 0n || typeof fees !== "bigint" || fees < 0n)
|
|
5135
5367
|
throw this.invalidResponse("wallet withdrawal balance");
|
|
5136
|
-
return { idle: idle2, fees, available: idle2 > fees ? idle2 - fees : 0n };
|
|
5368
|
+
return { blockNumber, idle: idle2, fees, available: idle2 > fees ? idle2 - fees : 0n };
|
|
5137
5369
|
};
|
|
5138
|
-
const [
|
|
5370
|
+
const [funds, feeRateBps] = await Promise.all([
|
|
5371
|
+
positionsRead.then((positions2) => this.resolveWithdrawalLiquidity(
|
|
5372
|
+
state,
|
|
5373
|
+
chainId,
|
|
5374
|
+
context,
|
|
5375
|
+
positions2,
|
|
5376
|
+
priorPositions,
|
|
5377
|
+
amount === void 0 ? void 0 : BigInt(amount)
|
|
5378
|
+
)),
|
|
5379
|
+
feeRateRead
|
|
5380
|
+
]);
|
|
5381
|
+
const initialFunds = funds;
|
|
5139
5382
|
const idle = initialFunds.idle;
|
|
5140
|
-
|
|
5383
|
+
const maxLiquidity = amount === void 0 ? funds : void 0;
|
|
5384
|
+
let positions = [...funds.positions, ...funds.indexedPositions.filter((position2) => !funds.verifiedVaults.includes(position2.yieldOption.address.toLowerCase()))];
|
|
5141
5385
|
let positionsStatus = "empty";
|
|
5142
|
-
const validatePositions = (values) =>
|
|
5143
|
-
|
|
5144
|
-
for (const value of values) {
|
|
5145
|
-
const address = value?.yieldOption?.address;
|
|
5146
|
-
if (typeof address !== "string" || !(0, import_viem11.isAddress)(address) || seen.has(address.toLowerCase()) || typeof value.assetsRaw !== "string" || !/^\d+$/.test(value.assetsRaw) || typeof value.withdrawableAssetsRaw !== "string" || !/^\d+$/.test(value.withdrawableAssetsRaw))
|
|
5147
|
-
throw this.invalidResponse("yield positions");
|
|
5148
|
-
seen.add(address.toLowerCase());
|
|
5149
|
-
}
|
|
5150
|
-
return values;
|
|
5151
|
-
};
|
|
5386
|
+
const validatePositions = (values) => this.validateWithdrawalPositions(values, chainId, asset);
|
|
5387
|
+
validatePositions(positions);
|
|
5152
5388
|
const zeroValuePositions = /* @__PURE__ */ new WeakSet();
|
|
5153
5389
|
const hasPositions = (values) => values.some((position2) => !zeroValuePositions.has(position2) && (BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n));
|
|
5154
5390
|
const knownPositions = /* @__PURE__ */ new Map();
|
|
5155
5391
|
const rememberPositions = (values) => {
|
|
5156
5392
|
for (const position2 of values) knownPositions.set(position2.yieldOption.address.toLowerCase(), position2);
|
|
5157
5393
|
};
|
|
5158
|
-
const boundedVerification = async (read2, timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
|
|
5159
|
-
let timer;
|
|
5160
|
-
try {
|
|
5161
|
-
return await Promise.race([read2, new Promise((resolve) => {
|
|
5162
|
-
timer = setTimeout(() => resolve(void 0), Math.max(0, timeoutMs));
|
|
5163
|
-
})]);
|
|
5164
|
-
} finally {
|
|
5165
|
-
if (timer) clearTimeout(timer);
|
|
5166
|
-
}
|
|
5167
|
-
};
|
|
5168
5394
|
const verifyKnownPositions = async (blockNumber, values = [...knownPositions.values()], timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
|
|
5169
5395
|
const verifiedPositions = [];
|
|
5170
5396
|
if (timeoutMs <= 0) return { verified: false, positions: verifiedPositions };
|
|
5171
|
-
|
|
5172
|
-
|
|
5397
|
+
let active = true;
|
|
5398
|
+
const checks = this.readVaultLiquidity(values.map((position2) => position2.yieldOption), agentWallet, blockNumber).catch(() => []);
|
|
5399
|
+
const reads = values.map(async (position2, index) => {
|
|
5400
|
+
const address = (0, import_viem12.getAddress)(position2.yieldOption.address);
|
|
5173
5401
|
try {
|
|
5174
|
-
const
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
if (typeof underlying !== "string" || !(0, import_viem11.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
|
|
5402
|
+
const group = (await checks).slice(index * 3, index * 3 + 3);
|
|
5403
|
+
if (!active || group.length !== 3 || group.some((value) => value.status !== "success")) return null;
|
|
5404
|
+
this.assertSession(generation, chainId, asset);
|
|
5405
|
+
const [underlying, shares, withdrawable] = group.map((value) => value.result);
|
|
5406
|
+
if (typeof underlying !== "string" || !(0, import_viem12.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
|
|
5180
5407
|
const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: withdrawable.toString() };
|
|
5181
5408
|
if (shares > 0n && withdrawable === 0n) {
|
|
5182
5409
|
let positiveValue = false;
|
|
@@ -5184,7 +5411,7 @@ var YieldseekerAgent = class {
|
|
|
5184
5411
|
try {
|
|
5185
5412
|
const value = await this.getReceiptClient().readContract({
|
|
5186
5413
|
address,
|
|
5187
|
-
abi:
|
|
5414
|
+
abi: import_viem12.erc4626Abi,
|
|
5188
5415
|
functionName,
|
|
5189
5416
|
args: [shares],
|
|
5190
5417
|
blockNumber
|
|
@@ -5210,6 +5437,7 @@ var YieldseekerAgent = class {
|
|
|
5210
5437
|
}
|
|
5211
5438
|
});
|
|
5212
5439
|
const result = await boundedVerification(Promise.all(reads), timeoutMs);
|
|
5440
|
+
active = false;
|
|
5213
5441
|
this.assertSession(generation, chainId, asset);
|
|
5214
5442
|
return {
|
|
5215
5443
|
verified: result !== void 0 && result.every((position2) => position2 !== null),
|
|
@@ -5228,7 +5456,7 @@ var YieldseekerAgent = class {
|
|
|
5228
5456
|
return validatePositions(response.yieldPositions);
|
|
5229
5457
|
};
|
|
5230
5458
|
if (amount === void 0) {
|
|
5231
|
-
|
|
5459
|
+
rememberPositions(funds.indexedPositions);
|
|
5232
5460
|
rememberPositions(positions);
|
|
5233
5461
|
positionsStatus = hasPositions(positions) ? "remaining" : "empty";
|
|
5234
5462
|
}
|
|
@@ -5244,8 +5472,8 @@ var YieldseekerAgent = class {
|
|
|
5244
5472
|
const zeroPayoutCandidates = positions.filter((position2) => BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n);
|
|
5245
5473
|
if (amount === void 0 && zeroPayoutCandidates.length) {
|
|
5246
5474
|
const deadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
|
|
5247
|
-
const blockNumber = await boundedVerification(
|
|
5248
|
-
Promise.resolve().then(() => this.getReceiptClient().getBlockNumber()).catch(() => void 0)
|
|
5475
|
+
const blockNumber = initialFunds.blockNumber ?? await boundedVerification(
|
|
5476
|
+
Promise.resolve().then(() => this.getReceiptClient().getBlockNumber({ cacheTime: 0 })).catch(() => void 0)
|
|
5249
5477
|
);
|
|
5250
5478
|
const proof = typeof blockNumber === "bigint" ? await verifyKnownPositions(blockNumber, zeroPayoutCandidates, deadline - Date.now()) : { verified: false, positions: [] };
|
|
5251
5479
|
positions = proof.positions;
|
|
@@ -5369,8 +5597,9 @@ var YieldseekerAgent = class {
|
|
|
5369
5597
|
remaining: remaining.toString()
|
|
5370
5598
|
});
|
|
5371
5599
|
}
|
|
5372
|
-
const
|
|
5373
|
-
const
|
|
5600
|
+
const ownerFunds = confirmedMovement ? await readOwnerFunds(latestUnwindBlock) : initialFunds;
|
|
5601
|
+
const availableForOwner = ownerFunds.available;
|
|
5602
|
+
const feeRate = feeRateBps ?? 0;
|
|
5374
5603
|
const maxFee = confirmedMovement && Number.isInteger(feeRate) && feeRate >= 0 && feeRate <= 1e3 ? (grossUnwound * BigInt(feeRate) + 9999n) / 10000n : 0n;
|
|
5375
5604
|
const shortfall = availableForOwner < requested ? requested - availableForOwner : 0n;
|
|
5376
5605
|
if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee) {
|
|
@@ -5395,14 +5624,14 @@ var YieldseekerAgent = class {
|
|
|
5395
5624
|
delta: maxSettlementBasis.delta - payout,
|
|
5396
5625
|
expectedNetDeposits: maxSettlementBasis.expectedNetDeposits - payout
|
|
5397
5626
|
} : this.snapshotMovement(state, chainId, context, -payout);
|
|
5398
|
-
const account = (0,
|
|
5627
|
+
const account = (0, import_viem12.getAddress)(state.walletAddress);
|
|
5399
5628
|
let receipt;
|
|
5400
5629
|
const base6 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount === void 0 ? "MAX" : payout.toString(), transfer: "pending", registration: "not-applicable", delivery: "pending" };
|
|
5401
|
-
const data = amount === void 0 ? (0,
|
|
5630
|
+
const data = amount === void 0 ? (0, import_viem12.encodeFunctionData)({
|
|
5402
5631
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5403
5632
|
functionName: "withdrawAllAssetToUser",
|
|
5404
5633
|
args: [account, metadata.address]
|
|
5405
|
-
}) : (0,
|
|
5634
|
+
}) : (0, import_viem12.encodeFunctionData)({
|
|
5406
5635
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5407
5636
|
functionName: "withdrawAssetToUser",
|
|
5408
5637
|
args: [account, metadata.address, payout]
|
|
@@ -5418,7 +5647,7 @@ var YieldseekerAgent = class {
|
|
|
5418
5647
|
refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
|
|
5419
5648
|
userId: context.user.userId,
|
|
5420
5649
|
agentId: context.agent.agentId,
|
|
5421
|
-
agentWallet: (0,
|
|
5650
|
+
agentWallet: (0, import_viem12.getAddress)(context.wallet.walletAddress),
|
|
5422
5651
|
asset: metadata.address,
|
|
5423
5652
|
callData: data,
|
|
5424
5653
|
onPending: (pending) => notifyFunding(onProgress, { ...base6, ...pending }),
|
|
@@ -5433,7 +5662,7 @@ var YieldseekerAgent = class {
|
|
|
5433
5662
|
onOwnerTransferAttempted?.();
|
|
5434
5663
|
txHash = await this.submitTransaction(state, chainId, {
|
|
5435
5664
|
from: account,
|
|
5436
|
-
to: (0,
|
|
5665
|
+
to: (0, import_viem12.getAddress)(context.wallet.walletAddress),
|
|
5437
5666
|
data,
|
|
5438
5667
|
value: "0",
|
|
5439
5668
|
chainId
|
|
@@ -5445,7 +5674,7 @@ var YieldseekerAgent = class {
|
|
|
5445
5674
|
settlement = plannedMovement;
|
|
5446
5675
|
const evidence = receipt ? inspectFundingReceipt(receipt, { ...base6, txHash }, metadata.address, "withdraw") : { ...base6, txHash, confirmedAmount: payout.toString(), transfer: "confirmed", delivery: "confirmed" };
|
|
5447
5676
|
notifyFunding(onProgress, evidence);
|
|
5448
|
-
let completion;
|
|
5677
|
+
let completion = maxLiquidity && !maxLiquidity.complete ? "unverified" : void 0;
|
|
5449
5678
|
if (amount === void 0) {
|
|
5450
5679
|
const verificationDeadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
|
|
5451
5680
|
const knownAtPayout = new Set(knownPositions.keys());
|
|
@@ -5492,10 +5721,14 @@ var YieldseekerAgent = class {
|
|
|
5492
5721
|
{ asset, available: "0", txHash },
|
|
5493
5722
|
this.id
|
|
5494
5723
|
);
|
|
5724
|
+
const confirmedAmount = evidence.confirmedAmount && /^\d+$/.test(evidence.confirmedAmount) ? BigInt(evidence.confirmedAmount) : void 0;
|
|
5725
|
+
const feeIncrease = ownerFunds.fees - initialFunds.fees;
|
|
5726
|
+
const feeAdjustment = amount !== void 0 && receipt && initialFunds.blockNumber !== void 0 && latestUnwindBlock !== void 0 && initialFunds.blockNumber <= latestUnwindBlock && receipt.blockNumber >= latestUnwindBlock && evidence.transfer === "confirmed" && evidence.delivery === "confirmed" && confirmedAmount === payout && feeIncrease > 0n && feeIncrease <= maxFee && requested - payout === feeIncrease ? { grossAmount: requested.toString(), feeAmount: feeIncrease.toString() } : void 0;
|
|
5495
5727
|
return {
|
|
5496
5728
|
txHash,
|
|
5497
5729
|
type: amount === void 0 && !completion ? "full" : "partial",
|
|
5498
5730
|
...completion ? { completion } : {},
|
|
5731
|
+
...feeAdjustment ? { feeAdjustment } : {},
|
|
5499
5732
|
amount: evidence.confirmedAmount ?? payout.toString(),
|
|
5500
5733
|
evidence
|
|
5501
5734
|
};
|
|
@@ -5511,10 +5744,298 @@ var YieldseekerAgent = class {
|
|
|
5511
5744
|
}
|
|
5512
5745
|
}
|
|
5513
5746
|
}
|
|
5747
|
+
/** A withdrawal-only liquidity floor; display snapshots never authorize it. */
|
|
5748
|
+
async getWithdrawableBalance(state, chainId, asset, minimumAmount) {
|
|
5749
|
+
this.assertChain(chainId);
|
|
5750
|
+
this.assertAsset(asset);
|
|
5751
|
+
const generation = this.readGeneration;
|
|
5752
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
5753
|
+
this.assertSession(generation, chainId, asset);
|
|
5754
|
+
if (!context) throw new OwneyError(
|
|
5755
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
5756
|
+
"The Yieldseeker withdrawal wallet could not be verified.",
|
|
5757
|
+
{ asset },
|
|
5758
|
+
this.id
|
|
5759
|
+
);
|
|
5760
|
+
const hints = this.withdrawalVaultHints(state, chainId, context);
|
|
5761
|
+
const positions = await this.readWithdrawalPositions(state, chainId, context);
|
|
5762
|
+
const proof = await this.resolveWithdrawalLiquidity(state, chainId, context, positions, hints, minimumAmount);
|
|
5763
|
+
this.assertSession(generation, chainId, asset);
|
|
5764
|
+
if (proof.available === 0n && !proof.complete) throw new OwneyError(
|
|
5765
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
5766
|
+
"Yieldseeker withdrawal liquidity could not be verified yet.",
|
|
5767
|
+
{ asset },
|
|
5768
|
+
this.id
|
|
5769
|
+
);
|
|
5770
|
+
return proof.available;
|
|
5771
|
+
}
|
|
5772
|
+
async readVaultLiquidity(options, wallet, blockNumber) {
|
|
5773
|
+
const owner = (0, import_viem12.getAddress)(wallet);
|
|
5774
|
+
const batches = Array.from({ length: Math.ceil(options.length / YIELDSEEKER_VAULTS_PER_RPC_BATCH) }, (_, index) => options.slice(index * YIELDSEEKER_VAULTS_PER_RPC_BATCH, (index + 1) * YIELDSEEKER_VAULTS_PER_RPC_BATCH));
|
|
5775
|
+
const results = await Promise.all(batches.map((batch) => this.getReceiptClient().multicall({
|
|
5776
|
+
blockNumber,
|
|
5777
|
+
allowFailure: true,
|
|
5778
|
+
contracts: batch.flatMap((option) => {
|
|
5779
|
+
const address = (0, import_viem12.getAddress)(option.address);
|
|
5780
|
+
return [
|
|
5781
|
+
{ address, abi: import_viem12.erc4626Abi, functionName: "asset" },
|
|
5782
|
+
{ address, abi: import_viem12.erc20Abi, functionName: "balanceOf", args: [owner] },
|
|
5783
|
+
{ address, abi: import_viem12.erc4626Abi, functionName: "maxWithdraw", args: [owner] }
|
|
5784
|
+
];
|
|
5785
|
+
})
|
|
5786
|
+
})));
|
|
5787
|
+
return results.flat();
|
|
5788
|
+
}
|
|
5789
|
+
validateWithdrawalPositions(values, chainId, asset) {
|
|
5790
|
+
const underlying = YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase();
|
|
5791
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5792
|
+
for (const value of values) {
|
|
5793
|
+
const option = value?.yieldOption;
|
|
5794
|
+
if (!option || typeof option.address !== "string" || !(0, import_viem12.isAddress)(option.address) || seen.has(option.address.toLowerCase()) || option.chainId !== void 0 && option.chainId !== chainId || option.assetAddress !== void 0 && (typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== underlying) || typeof value.assetsRaw !== "string" || !/^\d+$/.test(value.assetsRaw) || typeof value.withdrawableAssetsRaw !== "string" || !/^\d+$/.test(value.withdrawableAssetsRaw))
|
|
5795
|
+
throw this.invalidResponse("yield positions");
|
|
5796
|
+
seen.add(option.address.toLowerCase());
|
|
5797
|
+
}
|
|
5798
|
+
return values;
|
|
5799
|
+
}
|
|
5800
|
+
withdrawalVaultHints(state, chainId, context) {
|
|
5801
|
+
const key2 = this.contextKey(state, chainId, context.asset);
|
|
5802
|
+
const positions = this.readCache.get(`positions:${key2}`)?.value?.yieldPositions ?? [];
|
|
5803
|
+
const hints = new Map(this.validateWithdrawalPositions(positions, chainId, context.asset).map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
|
|
5804
|
+
const historic = this.readCache.get(`historic:${key2}`)?.value?.position;
|
|
5805
|
+
const receivedAssets = new Set((historic?.movements ?? []).filter((movement) => movement.chainId === chainId && movement.agentWalletId === context.wallet.agentWalletId && movement.toAddress?.toLowerCase() === context.wallet.walletAddress.toLowerCase()).filter((movement) => typeof movement.assetAddress === "string").map((movement) => movement.assetAddress.toLowerCase()));
|
|
5806
|
+
for (const option of this.yieldOptions.get(context.asset)?.value ?? []) {
|
|
5807
|
+
if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !(0, import_viem12.isAddress)(option.address) || !receivedAssets.has(option.address.toLowerCase()) || hints.has(option.address.toLowerCase())) continue;
|
|
5808
|
+
hints.set(option.address.toLowerCase(), {
|
|
5809
|
+
yieldOption: option,
|
|
5810
|
+
assetsRaw: "0",
|
|
5811
|
+
assetsBase: "0",
|
|
5812
|
+
assetsUsd: 0,
|
|
5813
|
+
withdrawableAssetsRaw: "0",
|
|
5814
|
+
withdrawableAssetsBase: "0"
|
|
5815
|
+
});
|
|
5816
|
+
}
|
|
5817
|
+
return [...hints.values()];
|
|
5818
|
+
}
|
|
5819
|
+
async readWithdrawalPositions(state, chainId, context) {
|
|
5820
|
+
const generation = this.readGeneration;
|
|
5821
|
+
let timer;
|
|
5822
|
+
try {
|
|
5823
|
+
const positions = await Promise.race([
|
|
5824
|
+
this.loadActivityContext(state, chainId, context, {}).then((value) => value.positions).catch((error) => {
|
|
5825
|
+
if (!(error instanceof OwneyError) || !["AGENT_API_ERROR", "AGENT_TIMEOUT"].includes(error.code)) throw error;
|
|
5826
|
+
return void 0;
|
|
5827
|
+
}),
|
|
5828
|
+
new Promise((resolve) => {
|
|
5829
|
+
timer = setTimeout(() => resolve(void 0), YIELDSEEKER_MAX_POSITION_VERIFICATION_MS);
|
|
5830
|
+
})
|
|
5831
|
+
]);
|
|
5832
|
+
this.assertSession(generation, chainId, context.asset);
|
|
5833
|
+
return positions === void 0 ? void 0 : this.validateWithdrawalPositions(positions, chainId, context.asset);
|
|
5834
|
+
} finally {
|
|
5835
|
+
if (timer) clearTimeout(timer);
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
async resolveWithdrawalLiquidity(state, chainId, context, positions, previousPositions = [], requested) {
|
|
5839
|
+
const generation = this.readGeneration;
|
|
5840
|
+
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
5841
|
+
const version = this.portfolioVersion(contextKey);
|
|
5842
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
5843
|
+
const unavailable = () => new OwneyError(
|
|
5844
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
5845
|
+
"Yieldseeker withdrawal liquidity could not be verified yet.",
|
|
5846
|
+
{ asset: context.asset, reason: "YIELDSEEKER_LIQUIDITY_UNVERIFIED" },
|
|
5847
|
+
this.id
|
|
5848
|
+
);
|
|
5849
|
+
let active = true;
|
|
5850
|
+
let knownProof;
|
|
5851
|
+
const hasUnverifiedIndexedFunds = (proof) => proof.indexedPositions.some((position2) => !proof.verifiedVaults.includes(position2.yieldOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) > 0n);
|
|
5852
|
+
const assertReadCurrent = () => {
|
|
5853
|
+
this.assertSession(generation, chainId, context.asset);
|
|
5854
|
+
if (!active || this.portfolioVersion(contextKey) !== version) throw unavailable();
|
|
5855
|
+
};
|
|
5856
|
+
const read2 = async () => {
|
|
5857
|
+
const client = this.getReceiptClient();
|
|
5858
|
+
const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
|
|
5859
|
+
const wallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
|
|
5860
|
+
assertReadCurrent();
|
|
5861
|
+
if (typeof blockNumber !== "bigint") throw unavailable();
|
|
5862
|
+
const indexedPositions = this.validateWithdrawalPositions(positions ?? [], chainId, context.asset);
|
|
5863
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
5864
|
+
for (const position2 of this.validateWithdrawalPositions([...previousPositions], chainId, context.asset))
|
|
5865
|
+
candidates.set(position2.yieldOption.address.toLowerCase(), position2.yieldOption);
|
|
5866
|
+
for (const position2 of indexedPositions) candidates.set(position2.yieldOption.address.toLowerCase(), position2.yieldOption);
|
|
5867
|
+
const options = [...candidates.values()];
|
|
5868
|
+
if (options.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
|
|
5869
|
+
const [checks, idle, feeTracker] = await Promise.all([
|
|
5870
|
+
this.readVaultLiquidity(options, wallet, blockNumber),
|
|
5871
|
+
client.readContract({
|
|
5872
|
+
address: metadata.address,
|
|
5873
|
+
abi: import_viem12.erc20Abi,
|
|
5874
|
+
functionName: "balanceOf",
|
|
5875
|
+
args: [wallet],
|
|
5876
|
+
blockNumber
|
|
5877
|
+
}),
|
|
5878
|
+
client.readContract({
|
|
5879
|
+
address: wallet,
|
|
5880
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5881
|
+
functionName: "feeTracker",
|
|
5882
|
+
blockNumber
|
|
5883
|
+
})
|
|
5884
|
+
]);
|
|
5885
|
+
if (typeof idle !== "bigint" || idle < 0n || !(0, import_viem12.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker)) throw unavailable();
|
|
5886
|
+
assertReadCurrent();
|
|
5887
|
+
const fees = await client.readContract({
|
|
5888
|
+
address: (0, import_viem12.getAddress)(feeTracker),
|
|
5889
|
+
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
5890
|
+
functionName: "getFeesOwed",
|
|
5891
|
+
args: [wallet],
|
|
5892
|
+
blockNumber
|
|
5893
|
+
});
|
|
5894
|
+
if (typeof fees !== "bigint" || fees < 0n) throw unavailable();
|
|
5895
|
+
const verified = [];
|
|
5896
|
+
const verifiedVaults = [];
|
|
5897
|
+
let deployed = 0n;
|
|
5898
|
+
const collect = (options2, results) => {
|
|
5899
|
+
assertReadCurrent();
|
|
5900
|
+
const known = this.balanceVaults.get(contextKey) ?? /* @__PURE__ */ new Set();
|
|
5901
|
+
this.balanceVaults.set(contextKey, known);
|
|
5902
|
+
let valid2 = true;
|
|
5903
|
+
options2.forEach((option, index) => {
|
|
5904
|
+
const group = results.slice(index * 3, index * 3 + 3);
|
|
5905
|
+
const shareProbe = group[1];
|
|
5906
|
+
if (shareProbe?.status === "success" && typeof shareProbe.result === "bigint" && shareProbe.result > 0n)
|
|
5907
|
+
known.add(option.address.toLowerCase());
|
|
5908
|
+
if (group.length !== 3 || group.some((value) => value.status !== "success")) {
|
|
5909
|
+
valid2 = false;
|
|
5910
|
+
return;
|
|
5911
|
+
}
|
|
5912
|
+
const [underlying, shares, withdrawable] = group.map((value) => value.result);
|
|
5913
|
+
if (typeof underlying !== "string" || !(0, import_viem12.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n || shares === 0n && withdrawable > 0n) {
|
|
5914
|
+
valid2 = false;
|
|
5915
|
+
return;
|
|
5916
|
+
}
|
|
5917
|
+
verifiedVaults.push(option.address.toLowerCase());
|
|
5918
|
+
deployed += withdrawable;
|
|
5919
|
+
verified.push({
|
|
5920
|
+
yieldOption: option,
|
|
5921
|
+
assetsRaw: shares.toString(),
|
|
5922
|
+
assetsBase: "0",
|
|
5923
|
+
assetsUsd: 0,
|
|
5924
|
+
withdrawableAssetsRaw: withdrawable.toString(),
|
|
5925
|
+
withdrawableAssetsBase: withdrawable.toString()
|
|
5926
|
+
});
|
|
5927
|
+
});
|
|
5928
|
+
return valid2;
|
|
5929
|
+
};
|
|
5930
|
+
const allKnownVerified = collect(options, checks);
|
|
5931
|
+
const proof = (complete) => ({
|
|
5932
|
+
blockNumber,
|
|
5933
|
+
idle,
|
|
5934
|
+
fees,
|
|
5935
|
+
available: idle + deployed > fees ? idle + deployed - fees : 0n,
|
|
5936
|
+
positions: [...verified],
|
|
5937
|
+
verifiedVaults: [...verifiedVaults],
|
|
5938
|
+
indexedPositions,
|
|
5939
|
+
complete
|
|
5940
|
+
});
|
|
5941
|
+
knownProof = proof(false);
|
|
5942
|
+
const indexed = new Map(indexedPositions.map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
|
|
5943
|
+
const discrepancy = verified.some((position2) => {
|
|
5944
|
+
const row = indexed.get(position2.yieldOption.address.toLowerCase());
|
|
5945
|
+
return row ? BigInt(position2.withdrawableAssetsRaw) < BigInt(row.withdrawableAssetsRaw) : BigInt(position2.assetsRaw) > 0n;
|
|
5946
|
+
});
|
|
5947
|
+
const typedCoveredByIdle = requested !== void 0 && idle >= requested + fees;
|
|
5948
|
+
const expand = positions === void 0 || indexedPositions.length === 0 && !typedCoveredByIdle || !allKnownVerified || discrepancy || knownProof.available === 0n || requested !== void 0 && requested > knownProof.available;
|
|
5949
|
+
if (!expand) {
|
|
5950
|
+
assertReadCurrent();
|
|
5951
|
+
return proof(true);
|
|
5952
|
+
}
|
|
5953
|
+
const catalog = await this.loadYieldOptions(context.asset).catch(() => void 0);
|
|
5954
|
+
assertReadCurrent();
|
|
5955
|
+
const eligible = /* @__PURE__ */ new Map();
|
|
5956
|
+
for (const option of catalog ?? []) {
|
|
5957
|
+
if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() && (0, import_viem12.isAddress)(option.address))
|
|
5958
|
+
eligible.set(option.address.toLowerCase(), option);
|
|
5959
|
+
}
|
|
5960
|
+
const extra = [...eligible].filter(([address]) => !candidates.has(address)).map(([, option]) => option);
|
|
5961
|
+
if (options.length + extra.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
|
|
5962
|
+
const allExtraVerified = collect(extra, await this.readVaultLiquidity(extra, wallet, blockNumber));
|
|
5963
|
+
assertReadCurrent();
|
|
5964
|
+
const result = proof(eligible.size > 0 && allKnownVerified && allExtraVerified);
|
|
5965
|
+
if (result.available === 0n && !result.complete && !hasUnverifiedIndexedFunds(result)) throw unavailable();
|
|
5966
|
+
return result;
|
|
5967
|
+
};
|
|
5968
|
+
let timer;
|
|
5969
|
+
try {
|
|
5970
|
+
return await Promise.race([read2(), new Promise((_, reject) => {
|
|
5971
|
+
timer = setTimeout(() => reject(unavailable()), YIELDSEEKER_MAX_POSITION_VERIFICATION_MS);
|
|
5972
|
+
})]);
|
|
5973
|
+
} catch (error) {
|
|
5974
|
+
assertReadCurrent();
|
|
5975
|
+
if (knownProof && (knownProof.available > 0n || hasUnverifiedIndexedFunds(knownProof)) && (!(error instanceof OwneyError) || error.code === "WITHDRAW_BALANCE_UNAVAILABLE")) return knownProof;
|
|
5976
|
+
if (error instanceof OwneyError) throw error;
|
|
5977
|
+
throw unavailable();
|
|
5978
|
+
} finally {
|
|
5979
|
+
active = false;
|
|
5980
|
+
if (timer) clearTimeout(timer);
|
|
5981
|
+
}
|
|
5982
|
+
}
|
|
5514
5983
|
async getBalances(state, chainId, asset) {
|
|
5515
5984
|
this.assertChain(chainId);
|
|
5516
5985
|
if (asset !== void 0) this.assertAsset(asset);
|
|
5517
|
-
|
|
5986
|
+
const contexts = await this.loadPortfolio(state, chainId, { asset, allowMissingPositions: true });
|
|
5987
|
+
const balance = mapYieldseekerBalances(contexts);
|
|
5988
|
+
for (const context of contexts) {
|
|
5989
|
+
const read2 = balance.readMetadata.find((item) => item.asset === context.asset);
|
|
5990
|
+
this.balanceContexts.set(read2, { context, generation: this.readGeneration });
|
|
5991
|
+
const key2 = this.contextKey(state, chainId, context.asset);
|
|
5992
|
+
const known = this.balanceVaults.get(key2) ?? /* @__PURE__ */ new Set();
|
|
5993
|
+
for (const position2 of context.positions) known.add(position2.yieldOption.address.toLowerCase());
|
|
5994
|
+
this.balanceVaults.set(key2, known);
|
|
5995
|
+
}
|
|
5996
|
+
return balance;
|
|
5997
|
+
}
|
|
5998
|
+
async verifyWithdrawalBalance(state, evidence, balance) {
|
|
5999
|
+
this.assertAsset(evidence.token);
|
|
6000
|
+
this.assertChain(evidence.chainId);
|
|
6001
|
+
const read2 = balance.readMetadata?.find((item) => item.chainId === evidence.chainId && item.asset === evidence.token);
|
|
6002
|
+
const cached = read2 && this.balanceContexts.get(read2);
|
|
6003
|
+
const context = cached?.generation === this.readGeneration ? cached.context : void 0;
|
|
6004
|
+
if (!context || context.positionsUnavailable || !read2 || !evidence.blockNumber || !evidence.txHash || context.wallet.walletAddress.toLowerCase() !== evidence.agentWallet?.toLowerCase()) return void 0;
|
|
6005
|
+
const key2 = this.contextKey(state, evidence.chainId, evidence.token);
|
|
6006
|
+
const generation = this.readGeneration, version = this.portfolioVersion(key2);
|
|
6007
|
+
if (read2.readVersion !== version) return void 0;
|
|
6008
|
+
const wallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
|
|
6009
|
+
const asset = YIELDSEEKER_ASSET_METADATA[evidence.token];
|
|
6010
|
+
const client = this.getReceiptClient();
|
|
6011
|
+
const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
|
|
6012
|
+
if (blockNumber < BigInt(evidence.blockNumber)) return void 0;
|
|
6013
|
+
const vaults = [...this.balanceVaults.get(key2) ?? []];
|
|
6014
|
+
const [invested, idle, tracker] = await Promise.all([
|
|
6015
|
+
remainingVaultBalance(client, wallet, asset.address, vaults, blockNumber),
|
|
6016
|
+
client.readContract({ address: asset.address, abi: import_viem12.erc20Abi, functionName: "balanceOf", args: [wallet], blockNumber }),
|
|
6017
|
+
client.readContract({ address: wallet, abi: YIELDSEEKER_AGENT_WALLET_ABI, functionName: "feeTracker", blockNumber })
|
|
6018
|
+
]);
|
|
6019
|
+
if (invested === void 0 || typeof idle !== "bigint" || idle < 0n || !(0, import_viem12.isAddress)(tracker) || /^0x0{40}$/i.test(tracker)) return void 0;
|
|
6020
|
+
const fees = await client.readContract({
|
|
6021
|
+
address: tracker,
|
|
6022
|
+
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
6023
|
+
functionName: "getFeesOwed",
|
|
6024
|
+
args: [wallet],
|
|
6025
|
+
blockNumber
|
|
6026
|
+
});
|
|
6027
|
+
this.assertSession(generation, evidence.chainId, evidence.token);
|
|
6028
|
+
if (version !== this.portfolioVersion(key2) || typeof fees !== "bigint" || fees < 0n) return void 0;
|
|
6029
|
+
const total = idle + invested > fees ? idle + invested - fees : 0n;
|
|
6030
|
+
const displayed = balance.assetBalances?.find((item) => item.chainId === evidence.chainId && item.asset === evidence.token);
|
|
6031
|
+
if (!displayed || !evidence.confirmedAmount || !/^\d+$/.test(evidence.confirmedAmount)) return void 0;
|
|
6032
|
+
const amount = (0, import_viem12.parseUnits)(displayed.amount, asset.decimals);
|
|
6033
|
+
if (amount < 0n) return void 0;
|
|
6034
|
+
const displayUnit = evidence.token === "USDC" ? 5000n : 500000000000n;
|
|
6035
|
+
const movementUnit = BigInt(evidence.confirmedAmount) / 1000n;
|
|
6036
|
+
const tolerance = movementUnit < displayUnit ? movementUnit : displayUnit;
|
|
6037
|
+
if ((total > amount ? total - amount : amount - total) > tolerance) return void 0;
|
|
6038
|
+
return { ...read2, withdrawalCheck: { txHash: evidence.txHash, blockNumber: blockNumber.toString() } };
|
|
5518
6039
|
}
|
|
5519
6040
|
invalidateFundingRead(state, chainId, asset, refreshUntil = Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS) {
|
|
5520
6041
|
this.assertAsset(asset);
|
|
@@ -5554,9 +6075,9 @@ var YieldseekerAgent = class {
|
|
|
5554
6075
|
if (!context) return void 0;
|
|
5555
6076
|
const idle = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5556
6077
|
address: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
5557
|
-
abi:
|
|
6078
|
+
abi: import_viem12.erc20Abi,
|
|
5558
6079
|
functionName: "balanceOf",
|
|
5559
|
-
args: [(0,
|
|
6080
|
+
args: [(0, import_viem12.getAddress)(context.wallet.walletAddress)]
|
|
5560
6081
|
}), chainId, "eth_call", this.id);
|
|
5561
6082
|
this.assertSession(generation, chainId, asset);
|
|
5562
6083
|
if (typeof idle !== "bigint" || idle < 0n) throw this.invalidResponse("wallet deposit balance");
|
|
@@ -5592,7 +6113,7 @@ var YieldseekerAgent = class {
|
|
|
5592
6113
|
);
|
|
5593
6114
|
const vaultAddresses = new Set(
|
|
5594
6115
|
catalog.flat().filter(
|
|
5595
|
-
(yieldOption) => yieldOption.chainId === chainId && (0,
|
|
6116
|
+
(yieldOption) => yieldOption.chainId === chainId && (0, import_viem12.isAddress)(yieldOption.address)
|
|
5596
6117
|
).map((yieldOption) => yieldOption.address.toLowerCase())
|
|
5597
6118
|
);
|
|
5598
6119
|
return mapYieldseekerHistory(contexts, {
|
|
@@ -5668,11 +6189,13 @@ var YieldseekerAgent = class {
|
|
|
5668
6189
|
const pending = this.pendingReads.get(key2);
|
|
5669
6190
|
if (pending) return pending;
|
|
5670
6191
|
const generation = this.readGeneration;
|
|
6192
|
+
const requestedAt = Date.now();
|
|
5671
6193
|
const request = Promise.resolve().then(read2).then((value) => {
|
|
5672
6194
|
if (this.readGeneration === generation && this.pendingReads.get(key2) === request) {
|
|
5673
6195
|
this.readCache.set(key2, {
|
|
5674
6196
|
expiresAt: Date.now() + ttlMs,
|
|
5675
6197
|
fetchedAt: Date.now(),
|
|
6198
|
+
requestedAt,
|
|
5676
6199
|
value
|
|
5677
6200
|
});
|
|
5678
6201
|
}
|
|
@@ -5719,7 +6242,7 @@ var YieldseekerAgent = class {
|
|
|
5719
6242
|
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
5720
6243
|
).then((walletResponse) => {
|
|
5721
6244
|
this.assertSession(generation, chainId, asset);
|
|
5722
|
-
if (!walletResponse?.agentWallet || !(0,
|
|
6245
|
+
if (!walletResponse?.agentWallet || !(0, import_viem12.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
5723
6246
|
throw this.invalidResponse("agent wallet");
|
|
5724
6247
|
}
|
|
5725
6248
|
const context = {
|
|
@@ -5749,7 +6272,7 @@ var YieldseekerAgent = class {
|
|
|
5749
6272
|
this.users.set(key2, persisted);
|
|
5750
6273
|
return persisted;
|
|
5751
6274
|
}
|
|
5752
|
-
const walletAddress = (0,
|
|
6275
|
+
const walletAddress = (0, import_viem12.getAddress)(state.walletAddress);
|
|
5753
6276
|
const generation = this.readGeneration;
|
|
5754
6277
|
let user = null;
|
|
5755
6278
|
try {
|
|
@@ -6041,6 +6564,7 @@ var YieldseekerAgent = class {
|
|
|
6041
6564
|
...context,
|
|
6042
6565
|
snapshot: snapshot.agentSnapshot,
|
|
6043
6566
|
fetchedAt: this.readCache.get(`snapshot:${contextKey}`)?.fetchedAt,
|
|
6567
|
+
requestedAt: this.readCache.get(`snapshot:${contextKey}`)?.requestedAt,
|
|
6044
6568
|
readVersion: version,
|
|
6045
6569
|
positionsUnavailable,
|
|
6046
6570
|
...portfolioParts
|
|
@@ -6132,7 +6656,7 @@ var YieldseekerAgent = class {
|
|
|
6132
6656
|
this.agentPath(context, "deploy"),
|
|
6133
6657
|
{ method: "POST", body: {} }
|
|
6134
6658
|
);
|
|
6135
|
-
if (!deployed?.agentWallet || !(0,
|
|
6659
|
+
if (!deployed?.agentWallet || !(0, import_viem12.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
|
|
6136
6660
|
throw this.invalidResponse("agent deployment", {
|
|
6137
6661
|
reason: "Deploy did not return the expected Agent Wallet."
|
|
6138
6662
|
});
|
|
@@ -6240,15 +6764,15 @@ var YieldseekerAgent = class {
|
|
|
6240
6764
|
return hash2;
|
|
6241
6765
|
}
|
|
6242
6766
|
this.assertTransaction(transaction, state, chainId);
|
|
6243
|
-
const account = (0,
|
|
6244
|
-
const walletClient = (0,
|
|
6767
|
+
const account = (0, import_viem12.getAddress)(state.walletAddress);
|
|
6768
|
+
const walletClient = (0, import_viem12.createWalletClient)({
|
|
6245
6769
|
account,
|
|
6246
6770
|
chain: import_chains4.base,
|
|
6247
|
-
transport: (0,
|
|
6771
|
+
transport: (0, import_viem12.custom)(state.provider)
|
|
6248
6772
|
});
|
|
6249
|
-
const walletChainClient = (0,
|
|
6773
|
+
const walletChainClient = (0, import_viem12.createPublicClient)({
|
|
6250
6774
|
chain: import_chains4.base,
|
|
6251
|
-
transport: (0,
|
|
6775
|
+
transport: (0, import_viem12.custom)(state.provider)
|
|
6252
6776
|
});
|
|
6253
6777
|
await ensureWalletOnChain(
|
|
6254
6778
|
walletChainClient,
|
|
@@ -6258,7 +6782,7 @@ var YieldseekerAgent = class {
|
|
|
6258
6782
|
const hash = await walletClient.sendTransaction({
|
|
6259
6783
|
account,
|
|
6260
6784
|
chain: import_chains4.base,
|
|
6261
|
-
to: (0,
|
|
6785
|
+
to: (0, import_viem12.getAddress)(transaction.to),
|
|
6262
6786
|
data: transaction.data,
|
|
6263
6787
|
value: BigInt(transaction.value)
|
|
6264
6788
|
});
|
|
@@ -6362,12 +6886,12 @@ var YieldseekerAgent = class {
|
|
|
6362
6886
|
return receipt;
|
|
6363
6887
|
}
|
|
6364
6888
|
assertTransaction(transaction, state, chainId) {
|
|
6365
|
-
if (!transaction || typeof transaction.from !== "string" || !(0,
|
|
6889
|
+
if (!transaction || typeof transaction.from !== "string" || !(0, import_viem12.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem12.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem12.getAddress)(transaction.from) !== (0, import_viem12.getAddress)(state.walletAddress)) {
|
|
6366
6890
|
throw this.invalidResponse("transaction");
|
|
6367
6891
|
}
|
|
6368
6892
|
}
|
|
6369
6893
|
assertAgent(agent) {
|
|
6370
|
-
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0,
|
|
6894
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem12.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
|
|
6371
6895
|
throw this.invalidResponse("agent");
|
|
6372
6896
|
}
|
|
6373
6897
|
}
|
|
@@ -6521,7 +7045,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
|
6521
7045
|
}
|
|
6522
7046
|
|
|
6523
7047
|
// src/lib/helpers/withdraw-helper.ts
|
|
6524
|
-
var
|
|
7048
|
+
var import_viem13 = require("viem");
|
|
6525
7049
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
6526
7050
|
const target = asset.toUpperCase();
|
|
6527
7051
|
return agents.map((agent) => {
|
|
@@ -6529,7 +7053,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
6529
7053
|
const tokenBalance = agentBalance?.tokens.find(
|
|
6530
7054
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
6531
7055
|
);
|
|
6532
|
-
let balance = tokenBalance ? (0,
|
|
7056
|
+
let balance = tokenBalance ? (0, import_viem13.parseUnits)(tokenBalance.amount, decimals) : 0n;
|
|
6533
7057
|
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
6534
7058
|
const chainNameById = {
|
|
6535
7059
|
1: "ETHEREUM",
|
|
@@ -6541,14 +7065,14 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
6541
7065
|
const positionChain = position2.chain.trim().toUpperCase();
|
|
6542
7066
|
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
6543
7067
|
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
6544
|
-
balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0,
|
|
7068
|
+
balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0, import_viem13.parseUnits)(position2.amount, decimals);
|
|
6545
7069
|
}
|
|
6546
7070
|
}
|
|
6547
7071
|
const assetTotal = agentBalance?.assetBalances?.find(
|
|
6548
7072
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
6549
7073
|
);
|
|
6550
7074
|
if (assetTotal) {
|
|
6551
|
-
const total = (0,
|
|
7075
|
+
const total = (0, import_viem13.parseUnits)(assetTotal.amount, decimals);
|
|
6552
7076
|
if (total < balance) balance = total > 0n ? total : 0n;
|
|
6553
7077
|
}
|
|
6554
7078
|
return { agent, balance };
|
|
@@ -6707,14 +7231,14 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
6707
7231
|
}
|
|
6708
7232
|
|
|
6709
7233
|
// src/client.ts
|
|
6710
|
-
var
|
|
7234
|
+
var import_viem17 = require("viem");
|
|
6711
7235
|
var import_chains5 = require("viem/chains");
|
|
6712
7236
|
|
|
6713
7237
|
// src/lib/sponsored-token-batch.ts
|
|
6714
|
-
var
|
|
7238
|
+
var import_viem15 = require("viem");
|
|
6715
7239
|
|
|
6716
7240
|
// src/lib/permit2-batch.ts
|
|
6717
|
-
var
|
|
7241
|
+
var import_viem14 = require("viem");
|
|
6718
7242
|
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
6719
7243
|
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
6720
7244
|
var ERC2612_PERMIT_TYPES = {
|
|
@@ -6726,7 +7250,7 @@ var ERC2612_PERMIT_TYPES = {
|
|
|
6726
7250
|
{ name: "deadline", type: "uint256" }
|
|
6727
7251
|
]
|
|
6728
7252
|
};
|
|
6729
|
-
var ERC2612_READ_ABI = (0,
|
|
7253
|
+
var ERC2612_READ_ABI = (0, import_viem14.parseAbi)([
|
|
6730
7254
|
"function name() view returns (string)",
|
|
6731
7255
|
"function version() view returns (string)",
|
|
6732
7256
|
"function nonces(address owner) view returns (uint256)"
|
|
@@ -6764,7 +7288,7 @@ var PERMIT_BATCH_TYPES = {
|
|
|
6764
7288
|
{ name: "amount", type: "uint256" }
|
|
6765
7289
|
]
|
|
6766
7290
|
};
|
|
6767
|
-
var PERMIT2_BATCH_ABI = (0,
|
|
7291
|
+
var PERMIT2_BATCH_ABI = (0, import_viem14.parseAbi)([
|
|
6768
7292
|
"struct TokenPermissions { address token; uint256 amount; }",
|
|
6769
7293
|
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
6770
7294
|
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
@@ -6807,14 +7331,14 @@ var planOf = (transfers) => JSON.stringify(
|
|
|
6807
7331
|
amount: BigInt(t.amount).toString()
|
|
6808
7332
|
}))
|
|
6809
7333
|
);
|
|
6810
|
-
var pendingKey = (i) => `owney.token-batch.v1:${(0,
|
|
7334
|
+
var pendingKey = (i) => `owney.token-batch.v1:${(0, import_viem15.keccak256)((0, import_viem15.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
|
|
6811
7335
|
function pendingTokenBatch(i) {
|
|
6812
7336
|
try {
|
|
6813
7337
|
const saved = read(pendingKey(i));
|
|
6814
7338
|
if (!saved) return;
|
|
6815
7339
|
const body = JSON.parse(saved);
|
|
6816
|
-
if (!body.serializedTransaction || body.chainId !== i.chainId || !(0,
|
|
6817
|
-
return { txHash: (0,
|
|
7340
|
+
if (!body.serializedTransaction || body.chainId !== i.chainId || !(0, import_viem15.isAddressEqual)(body.from, i.owner) || !(0, import_viem15.isAddressEqual)(body.token, i.token) || !body.transfers?.length) return;
|
|
7341
|
+
return { txHash: (0, import_viem15.keccak256)(body.serializedTransaction), transfers: body.transfers.map(({ to, amount }) => ({ to, amount })) };
|
|
6818
7342
|
} catch {
|
|
6819
7343
|
return;
|
|
6820
7344
|
}
|
|
@@ -6859,14 +7383,47 @@ async function execute(i, key2, plan) {
|
|
|
6859
7383
|
const send = async (initial) => {
|
|
6860
7384
|
let body = initial;
|
|
6861
7385
|
save(key2, body);
|
|
7386
|
+
const rejectedPreparation = (error) => !body.serializedTransaction && error instanceof OwneyError && typeof error.details?.statusCode === "number" && error.details.statusCode >= 400 && error.details.statusCode < 500 && ![408, 429].includes(error.details.statusCode);
|
|
6862
7387
|
try {
|
|
7388
|
+
const post = () => postSponsorBatchTransfer({ apiKey: i.apiKey, baseUrl: i.baseUrl, body });
|
|
7389
|
+
const pendingError = (phase, txHash) => new OwneyError(
|
|
7390
|
+
"DEPOSIT_IN_PROGRESS",
|
|
7391
|
+
phase === "approval" ? "Deposit approval is awaiting confirmation. Retry the same amount to continue it." : "Deposit is awaiting confirmation. Its transaction is still being checked.",
|
|
7392
|
+
{ sponsorPending: true, phase, txHash, safeToFallback: false }
|
|
7393
|
+
);
|
|
7394
|
+
const assertReceipt = (receipt, phase) => {
|
|
7395
|
+
if (receipt.status !== "success") throw new OwneyError(
|
|
7396
|
+
"SPONSOR_REQUEST_FAILED",
|
|
7397
|
+
`Sponsored ${phase} transaction reverted. Please retry.`,
|
|
7398
|
+
{ notSubmitted: true, safeToFallback: false }
|
|
7399
|
+
);
|
|
7400
|
+
};
|
|
7401
|
+
const waitForConfirmation = async (phase, hash2) => {
|
|
7402
|
+
let receipt;
|
|
7403
|
+
try {
|
|
7404
|
+
receipt = await i.pub.waitForTransactionReceipt({ hash: hash2, confirmations: 1, timeout: 9e4, checkReplacement: false });
|
|
7405
|
+
} catch {
|
|
7406
|
+
receipt = await i.pub.getTransactionReceipt({ hash: hash2 }).catch(() => void 0);
|
|
7407
|
+
}
|
|
7408
|
+
if (!receipt) throw pendingError(phase, hash2);
|
|
7409
|
+
assertReceipt(receipt, phase);
|
|
7410
|
+
};
|
|
7411
|
+
const postSigned = async (phase, hash2) => {
|
|
7412
|
+
try {
|
|
7413
|
+
return await post();
|
|
7414
|
+
} catch (error) {
|
|
7415
|
+
if (rejectedPreparation(error) || error instanceof OwneyError && error.details?.notSubmitted === true) throw error;
|
|
7416
|
+
await waitForConfirmation(phase, hash2);
|
|
7417
|
+
return void 0;
|
|
7418
|
+
}
|
|
7419
|
+
};
|
|
6863
7420
|
if (body.erc2612Permit && !body.serializedPermitTransaction) {
|
|
6864
7421
|
const preparedPermit = await postSponsorBatchTransfer({
|
|
6865
7422
|
apiKey: i.apiKey,
|
|
6866
7423
|
baseUrl: i.baseUrl,
|
|
6867
7424
|
body
|
|
6868
7425
|
});
|
|
6869
|
-
if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0,
|
|
7426
|
+
if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0, import_viem15.keccak256)(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
|
|
6870
7427
|
throw new Error(
|
|
6871
7428
|
"Sponsorship API did not return a valid prepared permit transaction."
|
|
6872
7429
|
);
|
|
@@ -6877,12 +7434,18 @@ async function execute(i, key2, plan) {
|
|
|
6877
7434
|
save(key2, body);
|
|
6878
7435
|
}
|
|
6879
7436
|
if (!body.serializedTransaction) {
|
|
6880
|
-
const
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
7437
|
+
const permitHash = body.serializedPermitTransaction ? (0, import_viem15.keccak256)(body.serializedPermitTransaction) : void 0;
|
|
7438
|
+
let prepared = permitHash ? await postSigned("approval", permitHash) : await post();
|
|
7439
|
+
if (prepared?.status === "pending") {
|
|
7440
|
+
if (!permitHash || prepared.phase !== "approval" || prepared.txHash !== permitHash)
|
|
7441
|
+
throw new Error("Sponsorship approval does not match the pending transaction.");
|
|
7442
|
+
await waitForConfirmation("approval", permitHash);
|
|
7443
|
+
prepared = void 0;
|
|
7444
|
+
}
|
|
7445
|
+
if (!prepared) prepared = await post();
|
|
7446
|
+
if (prepared.status === "pending" && permitHash && prepared.phase === "approval" && prepared.txHash === permitHash)
|
|
7447
|
+
throw pendingError("approval", permitHash);
|
|
7448
|
+
if (!prepared.serializedTransaction || (0, import_viem15.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
|
|
6886
7449
|
throw new Error(
|
|
6887
7450
|
"Sponsorship API did not return a valid prepared transaction."
|
|
6888
7451
|
);
|
|
@@ -6893,22 +7456,33 @@ async function execute(i, key2, plan) {
|
|
|
6893
7456
|
save(key2, body);
|
|
6894
7457
|
}
|
|
6895
7458
|
try {
|
|
6896
|
-
i.onSubmitted?.((0,
|
|
7459
|
+
i.onSubmitted?.((0, import_viem15.keccak256)(body.serializedTransaction));
|
|
6897
7460
|
} catch {
|
|
6898
7461
|
}
|
|
6899
|
-
const
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
7462
|
+
const hash = (0, import_viem15.keccak256)(body.serializedTransaction);
|
|
7463
|
+
const existing = await i.pub.getTransactionReceipt({ hash }).catch(() => void 0);
|
|
7464
|
+
if (existing) {
|
|
7465
|
+
assertReceipt(existing, "deposit");
|
|
7466
|
+
void post().catch(() => void 0);
|
|
7467
|
+
clear(key2);
|
|
7468
|
+
return hash;
|
|
7469
|
+
}
|
|
7470
|
+
let result = await postSigned("deposit", hash);
|
|
7471
|
+
if (result?.status === "pending" && result.phase === "approval" && body.serializedPermitTransaction && result.txHash === (0, import_viem15.keccak256)(body.serializedPermitTransaction)) {
|
|
7472
|
+
await waitForConfirmation("approval", result.txHash);
|
|
7473
|
+
result = await postSigned("deposit", hash);
|
|
7474
|
+
if (result?.status === "pending" && result.phase === "approval" && result.txHash === (0, import_viem15.keccak256)(body.serializedPermitTransaction)) throw pendingError("approval", result.txHash);
|
|
7475
|
+
}
|
|
7476
|
+
if (result) {
|
|
7477
|
+
if (result.txHash !== hash || result.status === "pending" && result.phase !== "deposit")
|
|
7478
|
+
throw new Error("Sponsorship receipt does not match the pending transaction.");
|
|
7479
|
+
if (result.status === "pending") await waitForConfirmation("deposit", hash);
|
|
7480
|
+
}
|
|
7481
|
+
if (!result || result.status === "pending") void post().catch(() => void 0);
|
|
6908
7482
|
clear(key2);
|
|
6909
|
-
return
|
|
7483
|
+
return hash;
|
|
6910
7484
|
} catch (error) {
|
|
6911
|
-
if (!body.serializedTransaction || error instanceof OwneyError && error.details?.notSubmitted === true)
|
|
7485
|
+
if (!body.serializedTransaction && !body.serializedPermitTransaction || rejectedPreparation(error) || error instanceof OwneyError && error.details?.notSubmitted === true)
|
|
6912
7486
|
clear(key2);
|
|
6913
7487
|
throw error;
|
|
6914
7488
|
}
|
|
@@ -6918,7 +7492,7 @@ async function execute(i, key2, plan) {
|
|
|
6918
7492
|
throw new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit changed. Check its status before depositing again.");
|
|
6919
7493
|
if (saved) {
|
|
6920
7494
|
const previous = JSON.parse(saved);
|
|
6921
|
-
if (previous.chainId !== i.chainId || !(0,
|
|
7495
|
+
if (previous.chainId !== i.chainId || !(0, import_viem15.isAddressEqual)(previous.from, i.owner) || !(0, import_viem15.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
6922
7496
|
throw new Error(
|
|
6923
7497
|
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
6924
7498
|
);
|
|
@@ -6935,7 +7509,7 @@ async function execute(i, key2, plan) {
|
|
|
6935
7509
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
6936
7510
|
"Insufficient token balance for this deposit."
|
|
6937
7511
|
);
|
|
6938
|
-
if (allowance < total && (i.chainId !== 8453 || !(0,
|
|
7512
|
+
if (allowance < total && (i.chainId !== 8453 || !(0, import_viem15.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
|
|
6939
7513
|
throw new OwneyError(
|
|
6940
7514
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
6941
7515
|
"token deposits need a one-time Permit2 approval."
|
|
@@ -7114,7 +7688,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
|
7114
7688
|
}
|
|
7115
7689
|
|
|
7116
7690
|
// src/lib/sponsored-calls-deposit.ts
|
|
7117
|
-
var
|
|
7691
|
+
var import_viem16 = require("viem");
|
|
7118
7692
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
7119
7693
|
var DEFAULT_MAX_POLLS = 30;
|
|
7120
7694
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -7122,7 +7696,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
7122
7696
|
method: "wallet_getCapabilities",
|
|
7123
7697
|
params: [owner]
|
|
7124
7698
|
});
|
|
7125
|
-
const forChain = caps?.[(0,
|
|
7699
|
+
const forChain = caps?.[(0, import_viem16.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
7126
7700
|
return Boolean(forChain?.paymasterService?.supported);
|
|
7127
7701
|
}
|
|
7128
7702
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -7159,8 +7733,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
7159
7733
|
const calls = transfers.map((transfer) => ({
|
|
7160
7734
|
to: token,
|
|
7161
7735
|
value: "0x0",
|
|
7162
|
-
data: (0,
|
|
7163
|
-
abi:
|
|
7736
|
+
data: (0, import_viem16.encodeFunctionData)({
|
|
7737
|
+
abi: import_viem16.erc20Abi,
|
|
7164
7738
|
functionName: "transfer",
|
|
7165
7739
|
args: [transfer.to, BigInt(transfer.amount)]
|
|
7166
7740
|
})
|
|
@@ -7192,7 +7766,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
7192
7766
|
url.searchParams.append("owneyIntent", intent);
|
|
7193
7767
|
paymasterUrl = url.toString();
|
|
7194
7768
|
}
|
|
7195
|
-
const requestedId = (0,
|
|
7769
|
+
const requestedId = (0, import_viem16.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
|
|
7196
7770
|
const progress = (id, transfer) => {
|
|
7197
7771
|
try {
|
|
7198
7772
|
deps.onCallsProgress?.(id, transfers, transfer);
|
|
@@ -7209,7 +7783,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
7209
7783
|
version: "2.0.0",
|
|
7210
7784
|
id: requestedId,
|
|
7211
7785
|
from: deps.ownerAddress,
|
|
7212
|
-
chainId: (0,
|
|
7786
|
+
chainId: (0, import_viem16.toHex)(chainId),
|
|
7213
7787
|
atomicRequired: transfers.length > 1,
|
|
7214
7788
|
calls,
|
|
7215
7789
|
capabilities: {
|
|
@@ -7361,6 +7935,7 @@ var OwneySDK = class {
|
|
|
7361
7935
|
cachedSponsoredCallsCallbacks = /* @__PURE__ */ new Map();
|
|
7362
7936
|
agentReads = new AgentReads();
|
|
7363
7937
|
recoveredFundingReads = /* @__PURE__ */ new Set();
|
|
7938
|
+
fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
|
|
7364
7939
|
activationChecks = /* @__PURE__ */ new Map();
|
|
7365
7940
|
readAgent(agent, method, fetch2, params = null) {
|
|
7366
7941
|
const key2 = JSON.stringify([
|
|
@@ -7406,6 +7981,7 @@ var OwneySDK = class {
|
|
|
7406
7981
|
}
|
|
7407
7982
|
this.agentReads.clearInFlight();
|
|
7408
7983
|
this.activationChecks.clear();
|
|
7984
|
+
this.fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
|
|
7409
7985
|
this.state = { provider, walletAddress, chainId: null };
|
|
7410
7986
|
this.cachedSponsoredCallback = null;
|
|
7411
7987
|
this.cachedWethSponsoredCallback = null;
|
|
@@ -7417,6 +7993,7 @@ var OwneySDK = class {
|
|
|
7417
7993
|
*/
|
|
7418
7994
|
async disconnect() {
|
|
7419
7995
|
this.state = null;
|
|
7996
|
+
this.fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
|
|
7420
7997
|
for (const agent of this.agents.values()) {
|
|
7421
7998
|
await agent.disconnect();
|
|
7422
7999
|
}
|
|
@@ -7491,14 +8068,14 @@ var OwneySDK = class {
|
|
|
7491
8068
|
// PublicClient/WalletClient param types — structurally identical at
|
|
7492
8069
|
// runtime, but the two share a name TS treats as unrelated.
|
|
7493
8070
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
7494
|
-
getWalletChainClient: (cid) => (0,
|
|
8071
|
+
getWalletChainClient: (cid) => (0, import_viem17.createPublicClient)({
|
|
7495
8072
|
chain: VIEM_CHAIN2[cid],
|
|
7496
|
-
transport: (0,
|
|
8073
|
+
transport: (0, import_viem17.custom)(provider)
|
|
7497
8074
|
}),
|
|
7498
|
-
getWalletClient: (cid) => (0,
|
|
8075
|
+
getWalletClient: (cid) => (0, import_viem17.createWalletClient)({
|
|
7499
8076
|
account: owner,
|
|
7500
8077
|
chain: VIEM_CHAIN2[cid],
|
|
7501
|
-
transport: (0,
|
|
8078
|
+
transport: (0, import_viem17.custom)(provider)
|
|
7502
8079
|
})
|
|
7503
8080
|
});
|
|
7504
8081
|
if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedSponsoredCallback = callback;
|
|
@@ -7546,14 +8123,14 @@ var OwneySDK = class {
|
|
|
7546
8123
|
// PublicClient/WalletClient param types — structurally identical at
|
|
7547
8124
|
// runtime, but the two share a name TS treats as unrelated.
|
|
7548
8125
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
7549
|
-
getWalletChainClient: (cid) => (0,
|
|
8126
|
+
getWalletChainClient: (cid) => (0, import_viem17.createPublicClient)({
|
|
7550
8127
|
chain: VIEM_CHAIN2[cid],
|
|
7551
|
-
transport: (0,
|
|
8128
|
+
transport: (0, import_viem17.custom)(provider)
|
|
7552
8129
|
}),
|
|
7553
|
-
getWalletClient: (cid) => (0,
|
|
8130
|
+
getWalletClient: (cid) => (0, import_viem17.createWalletClient)({
|
|
7554
8131
|
account: owner,
|
|
7555
8132
|
chain: VIEM_CHAIN2[cid],
|
|
7556
|
-
transport: (0,
|
|
8133
|
+
transport: (0, import_viem17.custom)(provider)
|
|
7557
8134
|
})
|
|
7558
8135
|
});
|
|
7559
8136
|
if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedWethSponsoredCallback = callback;
|
|
@@ -8317,9 +8894,8 @@ var OwneySDK = class {
|
|
|
8317
8894
|
verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet, intendedAmount: "MAX" }, token.address, direction);
|
|
8318
8895
|
}
|
|
8319
8896
|
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${evidence.agentId}:${evidence.token}:${evidence.txHash}`;
|
|
8320
|
-
if ((verified.transfer === "confirmed" || verified.transfer === "failed" && verified.confirmedAmount === "0") && !this.recoveredFundingReads.has(key2)) {
|
|
8321
|
-
|
|
8322
|
-
agent.invalidateFundingRead?.(state, chainId, evidence.token, options.balanceRefreshUntil);
|
|
8897
|
+
if (options.refreshBalance !== false && (verified.transfer === "confirmed" || verified.transfer === "failed" && verified.confirmedAmount === "0") && !this.recoveredFundingReads.has(key2)) {
|
|
8898
|
+
agent.invalidateFundingRead?.(state, chainId, evidence.token, options.balanceRefreshUntil);
|
|
8323
8899
|
this.recoveredFundingReads.add(key2);
|
|
8324
8900
|
if (this.recoveredFundingReads.size > 256) this.recoveredFundingReads.delete(this.recoveredFundingReads.values().next().value);
|
|
8325
8901
|
}
|
|
@@ -8427,6 +9003,44 @@ var OwneySDK = class {
|
|
|
8427
9003
|
assertCurrent();
|
|
8428
9004
|
return true;
|
|
8429
9005
|
}
|
|
9006
|
+
/** Optional post-payout display proof; receipt verification and the provider
|
|
9007
|
+
* adapter must agree before attaching it to this exact balance object. */
|
|
9008
|
+
async verifyWithdrawalBalance(evidence, balance) {
|
|
9009
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
9010
|
+
const agent = this.getAgent(evidence.agentId);
|
|
9011
|
+
if (!agent.verifyWithdrawalBalance || !evidence.txHash || state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase()) return balance;
|
|
9012
|
+
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${evidence.agentId}:${evidence.token}:${evidence.txHash}:${evidence.intendedAmount}`;
|
|
9013
|
+
const checks = this.fundingBalanceChecks.get(balance) ?? /* @__PURE__ */ new Map();
|
|
9014
|
+
this.fundingBalanceChecks.set(balance, checks);
|
|
9015
|
+
const previous = checks.get(key2);
|
|
9016
|
+
if (previous) {
|
|
9017
|
+
const result2 = await previous;
|
|
9018
|
+
assertCurrent();
|
|
9019
|
+
return result2;
|
|
9020
|
+
}
|
|
9021
|
+
let timer;
|
|
9022
|
+
const read2 = async () => {
|
|
9023
|
+
const verified = await this.inspectFunding(evidence, "withdraw", { refreshBalance: false });
|
|
9024
|
+
assertCurrent();
|
|
9025
|
+
if (verified.transfer !== "confirmed" || verified.delivery !== "confirmed" || !verified.blockNumber) return balance;
|
|
9026
|
+
const metadata = await agent.verifyWithdrawalBalance(state, verified, balance);
|
|
9027
|
+
assertCurrent();
|
|
9028
|
+
if (!metadata || metadata.chainId !== chainId || metadata.asset.toUpperCase() !== evidence.token || !metadata.withdrawalCheck || metadata.withdrawalCheck.txHash.toLowerCase() !== verified.txHash?.toLowerCase() || BigInt(metadata.withdrawalCheck.blockNumber) < BigInt(verified.blockNumber)) return balance;
|
|
9029
|
+
return { ...balance, readMetadata: [
|
|
9030
|
+
...(balance.readMetadata ?? []).filter((item) => !(item.chainId === chainId && item.asset.toUpperCase() === evidence.token)),
|
|
9031
|
+
metadata
|
|
9032
|
+
] };
|
|
9033
|
+
};
|
|
9034
|
+
const pending = Promise.race([read2(), new Promise((resolve) => {
|
|
9035
|
+
timer = setTimeout(() => resolve(balance), 2e3);
|
|
9036
|
+
})]).catch(() => balance).finally(() => {
|
|
9037
|
+
if (timer) clearTimeout(timer);
|
|
9038
|
+
});
|
|
9039
|
+
checks.set(key2, pending);
|
|
9040
|
+
const result = await pending;
|
|
9041
|
+
assertCurrent();
|
|
9042
|
+
return result;
|
|
9043
|
+
}
|
|
8430
9044
|
/** A display acknowledgement controls polling only; it never confirms a financial outcome. */
|
|
8431
9045
|
endFundingBalanceRefresh(evidence, read2) {
|
|
8432
9046
|
const { state, chainId } = this.captureReadScope(evidence.chainId);
|
|
@@ -8672,11 +9286,10 @@ var OwneySDK = class {
|
|
|
8672
9286
|
};
|
|
8673
9287
|
}
|
|
8674
9288
|
const requested = BigInt(amount);
|
|
8675
|
-
const aggregated = await this.
|
|
9289
|
+
const { balances: aggregated, failures: balanceFailures } = await this.readAggregatedBalances({ chainId, asset: token });
|
|
8676
9290
|
const unavailableAgents = eligibleAgents.filter(
|
|
8677
9291
|
(agent) => !(agent.id in aggregated.agentBalances)
|
|
8678
9292
|
);
|
|
8679
|
-
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
8680
9293
|
const balances = projectAgentBalancesForAsset(
|
|
8681
9294
|
eligibleAgents,
|
|
8682
9295
|
aggregated.agentBalances,
|
|
@@ -8684,6 +9297,44 @@ var OwneySDK = class {
|
|
|
8684
9297
|
asset,
|
|
8685
9298
|
assetInfo.decimals
|
|
8686
9299
|
);
|
|
9300
|
+
const indexedAvailable = balances.reduce((sum, entry) => sum + entry.balance, 0n);
|
|
9301
|
+
await Promise.all(balances.map(async (entry) => {
|
|
9302
|
+
if (entry.balance !== 0n && indexedAvailable >= requested || !entry.agent.getWithdrawableBalance) return;
|
|
9303
|
+
const originalFailure = balanceFailures.get(entry.agent.id);
|
|
9304
|
+
if (originalFailure instanceof OwneyError && ![
|
|
9305
|
+
"AGENT_API_ERROR",
|
|
9306
|
+
"AGENT_TIMEOUT",
|
|
9307
|
+
"AGENT_RATE_LIMITED"
|
|
9308
|
+
].includes(originalFailure.code)) return;
|
|
9309
|
+
if (this.isUserRejectedWithdrawal(originalFailure)) throw originalFailure;
|
|
9310
|
+
if (entry.balance !== 0n && entry.agent.id !== "yieldseeker") {
|
|
9311
|
+
const total = aggregated.agentBalances[entry.agent.id]?.assetBalances?.find((value) => value.chainId === chainId && value.asset.toUpperCase() === token);
|
|
9312
|
+
if (!total || (0, import_viem17.parseUnits)(total.amount, assetInfo.decimals) <= entry.balance) return;
|
|
9313
|
+
}
|
|
9314
|
+
try {
|
|
9315
|
+
const otherAvailable = indexedAvailable - entry.balance;
|
|
9316
|
+
const minimumAmount = requested > otherAvailable ? requested - otherAvailable : void 0;
|
|
9317
|
+
const verified = await entry.agent.getWithdrawableBalance(state, chainId, token, minimumAmount);
|
|
9318
|
+
if (typeof verified !== "bigint" || verified < 0n)
|
|
9319
|
+
throw new OwneyError("WITHDRAW_BALANCE_UNAVAILABLE", "Invalid withdrawal balance proof.", {}, entry.agent.id);
|
|
9320
|
+
if (this.state !== state || this.state.walletAddress !== state.walletAddress || this.state.chainId !== chainId)
|
|
9321
|
+
throw new OwneyError("NOT_CONNECTED", "Account changed while planning withdrawal.");
|
|
9322
|
+
entry.balance = verified;
|
|
9323
|
+
const unavailableIndex = unavailableAgents.indexOf(entry.agent);
|
|
9324
|
+
if (unavailableIndex !== -1) unavailableAgents.splice(unavailableIndex, 1);
|
|
9325
|
+
if (aggregated.agentErrors) delete aggregated.agentErrors[entry.agent.id];
|
|
9326
|
+
} catch (error) {
|
|
9327
|
+
entry.balance = 0n;
|
|
9328
|
+
if (!unavailableAgents.includes(entry.agent)) unavailableAgents.push(entry.agent);
|
|
9329
|
+
aggregated.agentErrors = {
|
|
9330
|
+
...aggregated.agentErrors,
|
|
9331
|
+
[entry.agent.id]: error instanceof Error ? error.message : String(error)
|
|
9332
|
+
};
|
|
9333
|
+
}
|
|
9334
|
+
}));
|
|
9335
|
+
if (this.state !== state || this.state.chainId !== chainId)
|
|
9336
|
+
throw new OwneyError("NOT_CONNECTED", "Account changed while planning withdrawal.");
|
|
9337
|
+
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
8687
9338
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
8688
9339
|
if (totalAvailable === 0n && unavailableAgents.length > 0) {
|
|
8689
9340
|
throw new OwneyError(
|
|
@@ -8768,7 +9419,18 @@ var OwneySDK = class {
|
|
|
8768
9419
|
);
|
|
8769
9420
|
}
|
|
8770
9421
|
const withdrawn = sumWithdrawnAmount(results);
|
|
8771
|
-
|
|
9422
|
+
const verifiedFeeDeductions = orderedPlans.reduce((total, plan) => {
|
|
9423
|
+
const result = results[plan.agent.id];
|
|
9424
|
+
const adjustment = result?.feeAdjustment;
|
|
9425
|
+
const evidence = result?.evidence;
|
|
9426
|
+
const submitted = submissionEvidence.get(plan.agent.id);
|
|
9427
|
+
if (plan.agent.id !== "yieldseeker" || !adjustment || !evidence || !submitted || typeof result.amount !== "string" || !/^\d+$/.test(result.amount) || typeof adjustment.grossAmount !== "string" || !/^\d+$/.test(adjustment.grossAmount) || typeof adjustment.feeAmount !== "string" || !/^\d+$/.test(adjustment.feeAmount) || evidence.agentId !== plan.agent.id || evidence.chainId !== chainId || evidence.token !== token || typeof evidence.walletAddress !== "string" || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || typeof evidence.agentWallet !== "string" || !/^0x[\da-f]{40}$/i.test(evidence.agentWallet) || typeof result.txHash !== "string" || !/^0x[\da-f]{64}$/i.test(result.txHash) || typeof evidence.txHash !== "string" || typeof submitted.txHash !== "string" || evidence.txHash?.toLowerCase() !== result.txHash.toLowerCase() || submitted.txHash?.toLowerCase() !== result.txHash.toLowerCase() || submitted.agentId !== evidence.agentId || submitted.chainId !== chainId || submitted.token !== token || typeof submitted.walletAddress !== "string" || typeof submitted.agentWallet !== "string" || submitted.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || submitted.agentWallet?.toLowerCase() !== evidence.agentWallet.toLowerCase() || evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || submitted.transfer !== "confirmed" || submitted.delivery !== "confirmed" || evidence.confirmedAmount !== result.amount || submitted.confirmedAmount !== result.amount || evidence.intendedAmount !== result.amount || submitted.intendedAmount !== result.amount) return total;
|
|
9428
|
+
const fee = BigInt(adjustment.feeAmount);
|
|
9429
|
+
const net = BigInt(result.amount);
|
|
9430
|
+
const gross = BigInt(adjustment.grossAmount);
|
|
9431
|
+
return fee > 0n && net > 0n && gross === plan.planned && net + fee === gross ? total + fee : total;
|
|
9432
|
+
}, 0n);
|
|
9433
|
+
if (withdrawn + verifiedFeeDeductions < requested) {
|
|
8772
9434
|
throw new OwneyError(
|
|
8773
9435
|
"WITHDRAW_PARTIAL_FAILURE",
|
|
8774
9436
|
`Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
|
|
@@ -8792,23 +9454,42 @@ var OwneySDK = class {
|
|
|
8792
9454
|
* @returns {AgentBalance} for a single agent, or {OwneyBalances} with totalBalance and per-agent breakdown
|
|
8793
9455
|
*/
|
|
8794
9456
|
async getBalances(agentId, options) {
|
|
8795
|
-
const { state, chainId, assertCurrent } = this.captureReadScope(options?.chainId);
|
|
8796
|
-
const asset = options?.asset;
|
|
8797
|
-
const readBalance = (agent) => this.readAgent(
|
|
8798
|
-
agent,
|
|
8799
|
-
"balances",
|
|
8800
|
-
() => asset ? agent.getBalances(state, chainId, asset) : agent.getBalances(state, chainId),
|
|
8801
|
-
asset ? { asset } : null
|
|
8802
|
-
);
|
|
8803
9457
|
if (agentId) {
|
|
9458
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(options?.chainId);
|
|
9459
|
+
const asset = options?.asset;
|
|
8804
9460
|
const agent = this.getAgent(agentId);
|
|
8805
|
-
const result = await
|
|
9461
|
+
const result = await this.readAgent(
|
|
9462
|
+
agent,
|
|
9463
|
+
"balances",
|
|
9464
|
+
() => asset ? agent.getBalances(state, chainId, asset) : agent.getBalances(state, chainId),
|
|
9465
|
+
asset ? { asset } : null
|
|
9466
|
+
);
|
|
8806
9467
|
assertCurrent();
|
|
8807
9468
|
return {
|
|
8808
9469
|
...result,
|
|
8809
9470
|
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
8810
9471
|
};
|
|
8811
9472
|
}
|
|
9473
|
+
const { balances, failures } = await this.readAggregatedBalances(options);
|
|
9474
|
+
if (Object.keys(balances.agentBalances).length === 0) {
|
|
9475
|
+
throw new OwneyError(
|
|
9476
|
+
"BALANCE_ALL_FAILED",
|
|
9477
|
+
"Failed to fetch balances for all active agents.",
|
|
9478
|
+
{ agentErrors: balances.agentErrors ?? {}, failures: [...failures.values()] }
|
|
9479
|
+
);
|
|
9480
|
+
}
|
|
9481
|
+
return balances;
|
|
9482
|
+
}
|
|
9483
|
+
/** Preserve failure objects for withdrawal recovery without changing public balance results. */
|
|
9484
|
+
async readAggregatedBalances(options) {
|
|
9485
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(options?.chainId);
|
|
9486
|
+
const asset = options?.asset;
|
|
9487
|
+
const readBalance = (agent) => this.readAgent(
|
|
9488
|
+
agent,
|
|
9489
|
+
"balances",
|
|
9490
|
+
() => asset ? agent.getBalances(state, chainId, asset) : agent.getBalances(state, chainId),
|
|
9491
|
+
asset ? { asset } : null
|
|
9492
|
+
);
|
|
8812
9493
|
let totalBalance = 0;
|
|
8813
9494
|
const results = {};
|
|
8814
9495
|
const entries = [...this.getActiveAgents().entries()];
|
|
@@ -8825,39 +9506,33 @@ var OwneySDK = class {
|
|
|
8825
9506
|
})
|
|
8826
9507
|
);
|
|
8827
9508
|
assertCurrent();
|
|
8828
|
-
let successCount = 0;
|
|
8829
9509
|
const agentErrors = {};
|
|
8830
9510
|
const agentRetryAt = {};
|
|
8831
|
-
const agentFailures =
|
|
9511
|
+
const agentFailures = /* @__PURE__ */ new Map();
|
|
8832
9512
|
for (let i = 0; i < balanceResults.length; i++) {
|
|
8833
9513
|
const settledResult = balanceResults[i];
|
|
8834
|
-
const [
|
|
9514
|
+
const [agentId] = entries[i];
|
|
8835
9515
|
if (settledResult.status === "fulfilled") {
|
|
8836
9516
|
const [id, b] = settledResult.value;
|
|
8837
9517
|
results[id] = b;
|
|
8838
9518
|
totalBalance += Number(b.totalBalance);
|
|
8839
|
-
successCount += 1;
|
|
8840
9519
|
continue;
|
|
8841
9520
|
}
|
|
8842
9521
|
const reason = settledResult.reason;
|
|
8843
|
-
agentFailures.
|
|
9522
|
+
agentFailures.set(agentId, reason);
|
|
8844
9523
|
const retryDelay = rateLimitDelay(reason);
|
|
8845
|
-
if (retryDelay !== void 0) agentRetryAt[
|
|
8846
|
-
agentErrors[
|
|
8847
|
-
console.error(`[owney-sdk] Balance fetch failed for agent "${
|
|
8848
|
-
}
|
|
8849
|
-
if (successCount === 0) {
|
|
8850
|
-
throw new OwneyError(
|
|
8851
|
-
"BALANCE_ALL_FAILED",
|
|
8852
|
-
"Failed to fetch balances for all active agents.",
|
|
8853
|
-
{ agentErrors, failures: agentFailures }
|
|
8854
|
-
);
|
|
9524
|
+
if (retryDelay !== void 0) agentRetryAt[agentId] = Date.now() + retryDelay;
|
|
9525
|
+
agentErrors[agentId] = reason instanceof Error ? reason.message : String(reason);
|
|
9526
|
+
console.error(`[owney-sdk] Balance fetch failed for agent "${agentId}":`, reason);
|
|
8855
9527
|
}
|
|
8856
9528
|
return {
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
|
|
9529
|
+
balances: {
|
|
9530
|
+
totalBalance: String(totalBalance),
|
|
9531
|
+
totalBalanceAsset: "usdc",
|
|
9532
|
+
agentBalances: results,
|
|
9533
|
+
...Object.keys(agentErrors).length > 0 ? { agentErrors, agentRetryAt } : {}
|
|
9534
|
+
},
|
|
9535
|
+
failures: agentFailures
|
|
8861
9536
|
};
|
|
8862
9537
|
}
|
|
8863
9538
|
/**
|
|
@@ -9238,16 +9913,16 @@ var OwneySDK = class {
|
|
|
9238
9913
|
);
|
|
9239
9914
|
}
|
|
9240
9915
|
const provider = this.requireConnectedProvider();
|
|
9241
|
-
const walletChainClient = (0,
|
|
9916
|
+
const walletChainClient = (0, import_viem17.createPublicClient)({
|
|
9242
9917
|
chain: VIEM_CHAIN2[chainId],
|
|
9243
|
-
transport: (0,
|
|
9918
|
+
transport: (0, import_viem17.custom)(provider)
|
|
9244
9919
|
});
|
|
9245
9920
|
const publicClient = this.getPaidRpcClient(chainId);
|
|
9246
9921
|
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
9247
|
-
const wallet = (0,
|
|
9922
|
+
const wallet = (0, import_viem17.createWalletClient)({
|
|
9248
9923
|
account: state.walletAddress,
|
|
9249
9924
|
chain: VIEM_CHAIN2[chainId],
|
|
9250
|
-
transport: (0,
|
|
9925
|
+
transport: (0, import_viem17.custom)(provider)
|
|
9251
9926
|
});
|
|
9252
9927
|
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
9253
9928
|
const hash = await wallet.writeContract({
|
|
@@ -9409,7 +10084,7 @@ var OwneySDK = class {
|
|
|
9409
10084
|
};
|
|
9410
10085
|
|
|
9411
10086
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
9412
|
-
var
|
|
10087
|
+
var import_viem18 = require("viem");
|
|
9413
10088
|
var import_siwe2 = require("siwe");
|
|
9414
10089
|
var import_sdk2 = require("@zyfai/sdk");
|
|
9415
10090
|
|
|
@@ -9536,7 +10211,7 @@ function buildSIWXConfig(deps) {
|
|
|
9536
10211
|
issuedAt,
|
|
9537
10212
|
toString() {
|
|
9538
10213
|
return new import_siwe2.SiweMessage({
|
|
9539
|
-
address: (0,
|
|
10214
|
+
address: (0, import_viem18.getAddress)(accountAddress),
|
|
9540
10215
|
chainId: numericChainId(chainId),
|
|
9541
10216
|
domain,
|
|
9542
10217
|
uri,
|