@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 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, { ...request2 });
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, args);
437
+ return await original.apply(this, callArgs);
417
438
  } catch (error) {
418
439
  const message = error instanceof Error ? error.message : "";
419
- 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" : message === "Network error: Unable to reach the server" ? "network" : "other";
420
- if (["unauthorized", "forbidden", "rate-limited", "insufficient-balance", "session-key-missing"].includes(request2.failure)) notify("rejected");
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 (depositFunds,
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.depositFunds(
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
- return { ...balance, readMetadata: balance.readMetadata?.map((read2) => ({ ...read2, requestedAt })) };
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 import_viem11 = require("viem");
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/agents/yieldseeker/yieldseeker.wallet-version.ts
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, import_viem9.parseAbi)([
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, import_viem9.parseAbi)([
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, import_viem9.getAddress)(wallet);
3983
- const expectedOwner = (0, import_viem9.getAddress)(owner);
3984
- const expectedAsset = (0, import_viem9.getAddress)(asset);
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, import_viem9.getAddress)(`0x${slot.slice(-40)}`);
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, import_viem9.isAddressEqual)(actualOwner, expectedOwner) && (0, import_viem9.isAddressEqual)(actualAsset, expectedAsset) && (0, import_viem9.isAddressEqual)(actualFactory, YIELDSEEKER_FACTORY) && (0, import_viem9.isAddressEqual)(mapped, agentWallet);
4007
- const version = !verified ? "unknown" : (0, import_viem9.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem9.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
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, import_viem9.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
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 import_viem10 = require("viem");
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, import_viem10.toHex)(BigInt(data.panicCode));
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, import_viem10.toHex)(nonce),
4525
+ nonce: (0, import_viem11.toHex)(nonce),
4322
4526
  initCode: "0x",
4323
4527
  callData: input.callData,
4324
- callGasLimit: (0, import_viem10.toHex)(3e5),
4325
- verificationGasLimit: (0, import_viem10.toHex)(3e5),
4326
- preVerificationGas: (0, import_viem10.toHex)(6e4),
4327
- maxFeePerGas: (0, import_viem10.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
4328
- maxPriorityFeePerGas: (0, import_viem10.toHex)(fees.maxPriorityFeePerGas),
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, import_viem10.toHex)(8453), {}],
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, import_viem10.toHex)(8453), {}],
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, import_viem10.createWalletClient)({
4564
+ const wallet = (0, import_viem11.createWalletClient)({
4361
4565
  account: input.owner,
4362
4566
  chain: import_chains3.base,
4363
- transport: (0, import_viem10.custom)(input.state.provider)
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, import_viem10.toHex)(8453)]?.paymasterService?.supported ?? capabilities?.["8453"]?.paymasterService?.supported ?? capabilities?.["0x0"]?.paymasterService?.supported;
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, import_viem10.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
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, import_viem10.toHex)(8453),
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, import_viem10.getAddress)(input.state.walletAddress);
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, import_viem10.getAddress)(input.state.walletAddress);
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, import_viem11.getAddress)(context.wallet.walletAddress),
4708
- (0, import_viem11.getAddress)(state.walletAddress),
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, import_viem11.encodeEventTopics)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded" })[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, import_viem11.decodeEventLog)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded", data: lastUpgrade.data, topics: lastUpgrade.topics });
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, import_viem11.encodeFunctionData)({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
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, import_viem11.getAddress)(state.walletAddress),
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, import_viem11.getAddress)(state.walletAddress),
5199
+ from: (0, import_viem12.getAddress)(state.walletAddress),
4991
5200
  to: YIELDSEEKER_ASSET_METADATA[asset].address,
4992
- data: (0, import_viem11.encodeFunctionData)({
4993
- abi: import_viem11.erc20Abi,
5201
+ data: (0, import_viem12.encodeFunctionData)({
5202
+ abi: import_viem12.erc20Abi,
4994
5203
  functionName: "transfer",
4995
- args: [(0, import_viem11.getAddress)(context.wallet.walletAddress), BigInt(amount)]
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.advancePortfolioVersion(this.contextKey(state, chainId, asset));
5106
- const portfolioRead = amount === void 0 ? this.loadActivityContext(state, chainId, context, {}).then((value) => ({ positions: value.positions, feeRateBps: 0 })) : this.loadPortfolioContext(state, chainId, context).then((value) => ({ positions: value.positions, feeRateBps: value.snapshot.feeRateBps }));
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, import_viem11.getAddress)(context.wallet.walletAddress);
5109
- const feeTrackerRead = withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5110
- address: agentWallet,
5111
- abi: YIELDSEEKER_AGENT_WALLET_ABI,
5112
- functionName: "feeTracker"
5113
- }), chainId, "eth_call", this.id);
5114
- const readOwnerFunds = async () => {
5115
- const feeTracker = await feeTrackerRead;
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, import_viem11.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
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: import_viem11.erc20Abi,
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, import_viem11.getAddress)(feeTracker),
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 [portfolio, initialFunds] = await Promise.all([portfolioRead, readOwnerFunds()]);
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
- let positions = portfolio.positions;
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
- const seen = /* @__PURE__ */ new Set();
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
- const reads = values.map(async (position2) => {
5172
- const address = (0, import_viem11.getAddress)(position2.yieldOption.address);
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 [underlying, shares, withdrawable] = await Promise.all([
5175
- this.getReceiptClient().readContract({ address, abi: import_viem11.erc4626Abi, functionName: "asset", blockNumber }),
5176
- this.getReceiptClient().readContract({ address, abi: import_viem11.erc20Abi, functionName: "balanceOf", args: [agentWallet], blockNumber }),
5177
- this.getReceiptClient().readContract({ address, abi: import_viem11.erc4626Abi, functionName: "maxWithdraw", args: [agentWallet], blockNumber })
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: import_viem11.erc4626Abi,
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
- validatePositions(positions);
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 availableForOwner = confirmedMovement ? (await readOwnerFunds()).available : initialFunds.available;
5373
- const feeRate = portfolio.feeRateBps;
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, import_viem11.getAddress)(state.walletAddress);
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, import_viem11.encodeFunctionData)({
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, import_viem11.encodeFunctionData)({
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, import_viem11.getAddress)(context.wallet.walletAddress),
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, import_viem11.getAddress)(context.wallet.walletAddress),
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
- return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, { asset, allowMissingPositions: true }));
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: import_viem11.erc20Abi,
6078
+ abi: import_viem12.erc20Abi,
5558
6079
  functionName: "balanceOf",
5559
- args: [(0, import_viem11.getAddress)(context.wallet.walletAddress)]
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, import_viem11.isAddress)(yieldOption.address)
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, import_viem11.isAddress)(walletResponse.agentWallet.walletAddress)) {
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, import_viem11.getAddress)(state.walletAddress);
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, import_viem11.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
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, import_viem11.getAddress)(state.walletAddress);
6244
- const walletClient = (0, import_viem11.createWalletClient)({
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, import_viem11.custom)(state.provider)
6771
+ transport: (0, import_viem12.custom)(state.provider)
6248
6772
  });
6249
- const walletChainClient = (0, import_viem11.createPublicClient)({
6773
+ const walletChainClient = (0, import_viem12.createPublicClient)({
6250
6774
  chain: import_chains4.base,
6251
- transport: (0, import_viem11.custom)(state.provider)
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, import_viem11.getAddress)(transaction.to),
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, import_viem11.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem11.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_viem11.getAddress)(transaction.from) !== (0, import_viem11.getAddress)(state.walletAddress)) {
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, import_viem11.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
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 import_viem12 = require("viem");
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, import_viem12.parseUnits)(tokenBalance.amount, decimals) : 0n;
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, import_viem12.parseUnits)(position2.amount, decimals);
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, import_viem12.parseUnits)(assetTotal.amount, decimals);
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 import_viem16 = require("viem");
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 import_viem14 = require("viem");
7238
+ var import_viem15 = require("viem");
6715
7239
 
6716
7240
  // src/lib/permit2-batch.ts
6717
- var import_viem13 = require("viem");
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, import_viem13.parseAbi)([
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, import_viem13.parseAbi)([
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, import_viem14.keccak256)((0, import_viem14.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
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, import_viem14.isAddressEqual)(body.from, i.owner) || !(0, import_viem14.isAddressEqual)(body.token, i.token) || !body.transfers?.length) return;
6817
- return { txHash: (0, import_viem14.keccak256)(body.serializedTransaction), transfers: body.transfers.map(({ to, amount }) => ({ to, amount })) };
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, import_viem14.keccak256)(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
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 prepared = await postSponsorBatchTransfer({
6881
- apiKey: i.apiKey,
6882
- baseUrl: i.baseUrl,
6883
- body
6884
- });
6885
- if (!prepared.serializedTransaction || (0, import_viem14.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
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, import_viem14.keccak256)(body.serializedTransaction));
7459
+ i.onSubmitted?.((0, import_viem15.keccak256)(body.serializedTransaction));
6897
7460
  } catch {
6898
7461
  }
6899
- const result = await postSponsorBatchTransfer({
6900
- apiKey: i.apiKey,
6901
- baseUrl: i.baseUrl,
6902
- body
6903
- });
6904
- if (result.txHash !== (0, import_viem14.keccak256)(body.serializedTransaction))
6905
- throw new Error(
6906
- "Sponsorship receipt does not match the pending transaction."
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 result.txHash;
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, import_viem14.isAddressEqual)(previous.from, i.owner) || !(0, import_viem14.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
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, import_viem14.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
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 import_viem15 = require("viem");
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, import_viem15.toHex)(chainId)] ?? caps?.[String(chainId)];
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, import_viem15.encodeFunctionData)({
7163
- abi: import_viem15.erc20Abi,
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, import_viem15.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
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, import_viem15.toHex)(chainId),
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, import_viem16.createPublicClient)({
8071
+ getWalletChainClient: (cid) => (0, import_viem17.createPublicClient)({
7495
8072
  chain: VIEM_CHAIN2[cid],
7496
- transport: (0, import_viem16.custom)(provider)
8073
+ transport: (0, import_viem17.custom)(provider)
7497
8074
  }),
7498
- getWalletClient: (cid) => (0, import_viem16.createWalletClient)({
8075
+ getWalletClient: (cid) => (0, import_viem17.createWalletClient)({
7499
8076
  account: owner,
7500
8077
  chain: VIEM_CHAIN2[cid],
7501
- transport: (0, import_viem16.custom)(provider)
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, import_viem16.createPublicClient)({
8126
+ getWalletChainClient: (cid) => (0, import_viem17.createPublicClient)({
7550
8127
  chain: VIEM_CHAIN2[cid],
7551
- transport: (0, import_viem16.custom)(provider)
8128
+ transport: (0, import_viem17.custom)(provider)
7552
8129
  }),
7553
- getWalletClient: (cid) => (0, import_viem16.createWalletClient)({
8130
+ getWalletClient: (cid) => (0, import_viem17.createWalletClient)({
7554
8131
  account: owner,
7555
8132
  chain: VIEM_CHAIN2[cid],
7556
- transport: (0, import_viem16.custom)(provider)
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
- if (options.refreshBalance !== false)
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.getBalances(void 0, { chainId, asset: token });
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
- if (withdrawn < requested) {
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 readBalance(agent);
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 [agentId2] = entries[i];
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.push(reason);
9522
+ agentFailures.set(agentId, reason);
8844
9523
  const retryDelay = rateLimitDelay(reason);
8845
- if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
8846
- agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
8847
- console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
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
- totalBalance: String(totalBalance),
8858
- totalBalanceAsset: "usdc",
8859
- agentBalances: results,
8860
- ...Object.keys(agentErrors).length > 0 ? { agentErrors, agentRetryAt } : {}
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, import_viem16.createPublicClient)({
9916
+ const walletChainClient = (0, import_viem17.createPublicClient)({
9242
9917
  chain: VIEM_CHAIN2[chainId],
9243
- transport: (0, import_viem16.custom)(provider)
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, import_viem16.createWalletClient)({
9922
+ const wallet = (0, import_viem17.createWalletClient)({
9248
9923
  account: state.walletAddress,
9249
9924
  chain: VIEM_CHAIN2[chainId],
9250
- transport: (0, import_viem16.custom)(provider)
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 import_viem17 = require("viem");
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, import_viem17.getAddress)(accountAddress),
10214
+ address: (0, import_viem18.getAddress)(accountAddress),
9540
10215
  chainId: numericChainId(chainId),
9541
10216
  domain,
9542
10217
  uri,