@owney/sdk 0.7.25-beta.14 → 0.7.25-beta.16

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
@@ -102,6 +102,8 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
102
102
  const attributed = /* @__PURE__ */ new Map();
103
103
  const agent = evidence.agentWallet?.toLowerCase();
104
104
  const owner = evidence.walletAddress.toLowerCase();
105
+ const source = direction === "deposit" ? owner : agent;
106
+ const destination = direction === "deposit" ? agent : owner;
105
107
  const logIndexes = [];
106
108
  const transferLogs = [];
107
109
  const zeroTransferLogs = [];
@@ -116,17 +118,13 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
116
118
  if (direction === "withdraw" && value === 0n && from.toLowerCase() === agent && to.toLowerCase() === owner)
117
119
  zeroTransferLogs.push(log.logIndex);
118
120
  let matched = 0n;
119
- if (direction === "deposit") {
120
- if (to.toLowerCase() === agent) matched = value;
121
- } else {
122
- const sender = from.toLowerCase();
123
- const recipient = to.toLowerCase();
124
- const available = sender === agent ? value : attributed.get(sender) ?? 0n;
125
- const carried = available < value ? available : value;
126
- if (sender !== agent) attributed.set(sender, available - carried);
127
- if (recipient === owner) matched = carried;
128
- else if (recipient !== agent) attributed.set(recipient, (attributed.get(recipient) ?? 0n) + carried);
129
- }
121
+ const sender = from.toLowerCase();
122
+ const recipient = to.toLowerCase();
123
+ const available = sender === source ? value : attributed.get(sender) ?? 0n;
124
+ const carried = available < value ? available : value;
125
+ if (sender !== source) attributed.set(sender, available - carried);
126
+ if (recipient === destination) matched = carried;
127
+ else if (recipient !== source) attributed.set(recipient, (attributed.get(recipient) ?? 0n) + carried);
130
128
  if (matched === 0n) continue;
131
129
  amount += matched;
132
130
  transferLogs.push({ logIndex: log.logIndex, amountRaw: matched.toString() });
@@ -406,20 +404,44 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
406
404
  };
407
405
  const notify = (outcome) => {
408
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
+ };
409
413
  for (const observer of matching) {
410
414
  try {
411
- observer.notify(outcome, { ...request2 });
415
+ observer.notify(outcome, evidence);
412
416
  } catch {
413
417
  }
414
418
  }
415
419
  };
416
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
+ }
417
436
  try {
418
- return await original.apply(this, args);
437
+ return await original.apply(this, callArgs);
419
438
  } catch (error) {
420
439
  const message = error instanceof Error ? error.message : "";
421
- request2.failure = message === "Unauthorized: Invalid API key" ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : message === "Network error: Unable to reach the server" ? "network" : "other";
422
- if (["unauthorized", "forbidden", "rate-limited"].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");
423
445
  throw error;
424
446
  } finally {
425
447
  request2.elapsedMs = Math.max(0, Date.now() - startedAt);
@@ -442,9 +464,49 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
442
464
  } };
443
465
  }
444
466
 
467
+ // src/agents/zyfai/zyfai.withdraw-empty.ts
468
+ function hasEmptyWithdrawalSnapshot(raw2, chainId, token) {
469
+ const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
470
+ const response = record(raw2);
471
+ const portfolio = record(response?.portfolio);
472
+ const chains = record(portfolio?.portfolioByChain);
473
+ if (response?.success === false || !chains || !Array.isArray(portfolio?.positions) || !Array.isArray(portfolio.staleBalances)) return false;
474
+ if (portfolio.pendingAsyncWithdrawals !== void 0 && (!Array.isArray(portfolio.pendingAsyncWithdrawals) || portfolio.pendingAsyncWithdrawals.length > 0)) return false;
475
+ const symbol = (value) => typeof value === "string" ? value.toUpperCase() === "ETH" ? "WETH" : value.toUpperCase() : void 0;
476
+ const chain = (value) => {
477
+ if (typeof value === "number") return value;
478
+ if (typeof value !== "string") return void 0;
479
+ return /^\d+$/.test(value) ? Number(value) : { base: 8453, arbitrum: 42161, ethereum: 1, mainnet: 1 }[value.trim().toLowerCase()];
480
+ };
481
+ const zero = (value) => typeof value === "string" && /^(?:0x)?0+$/i.test(value);
482
+ for (const [id, assets] of Object.entries(chains)) {
483
+ const idNumber = chain(id);
484
+ if (idNumber === void 0) return false;
485
+ if (idNumber !== chainId) continue;
486
+ const entries = record(assets);
487
+ if (!entries) return false;
488
+ for (const [asset, value] of Object.entries(entries)) {
489
+ if (symbol(asset) !== token) continue;
490
+ const balance = record(value);
491
+ if (!balance || !zero(balance.balance)) return false;
492
+ }
493
+ }
494
+ for (const value of [...portfolio.positions, ...portfolio.staleBalances]) {
495
+ const entry = record(value);
496
+ if (!entry) return false;
497
+ const id = chain(entry.chainId ?? entry.chain);
498
+ const asset = symbol(entry.tokenSymbol ?? entry.token_symbol);
499
+ if (id === void 0 || asset === void 0) return false;
500
+ if (id !== chainId || asset !== token) continue;
501
+ const amounts = [entry.balance, entry.amount, entry.underlyingAmount].filter((value2) => value2 !== void 0);
502
+ if (!amounts.length || !amounts.every(zero)) return false;
503
+ }
504
+ return true;
505
+ }
506
+
445
507
  // src/agents/zyfai/zyfai.agent.ts
446
508
  var import_sdk = require("@zyfai/sdk");
447
- var import_viem3 = require("viem");
509
+ var import_viem4 = require("viem");
448
510
  var import_chains = require("viem/chains");
449
511
 
450
512
  // src/types/config.ts
@@ -531,14 +593,6 @@ function hexToDecimal(hex, decimals = 6) {
531
593
  const remainder = parsed % divisor;
532
594
  return Number(whole) + Number(remainder) / Number(divisor);
533
595
  }
534
- function normalizeDecimalPlace(value, decimals = 6) {
535
- const parsed = BigInt(value);
536
- if (decimals <= 0) return Number(parsed);
537
- const divisor = BigInt(10 ** decimals);
538
- const whole = parsed / divisor;
539
- const remainder = parsed % divisor;
540
- return Number(whole) + Number(remainder) / Number(divisor);
541
- }
542
596
  var DayFilterMapping = {
543
597
  "30D": 30,
544
598
  "14D": 14,
@@ -546,6 +600,8 @@ var DayFilterMapping = {
546
600
  };
547
601
 
548
602
  // src/agents/zyfai/zyfai.mapper.ts
603
+ var import_viem2 = require("viem");
604
+ var exactHexAmount = (raw2, decimals) => (0, import_viem2.formatUnits)(BigInt(/^0x/i.test(raw2) ? raw2 : `0x${raw2}`), decimals);
549
605
  var DECIMALS_BY_SYMBOL = {
550
606
  usdc: 6,
551
607
  weth: 18,
@@ -600,15 +656,15 @@ function mapPendingAllocations(raw2) {
600
656
  if (typeof e.balance !== "string") continue;
601
657
  let amount;
602
658
  try {
603
- amount = hexToDecimal(e.balance, tokenDecimals(e.tokenSymbol));
659
+ if (BigInt(/^0x/i.test(e.balance) ? e.balance : `0x${e.balance}`) <= 0n) continue;
660
+ amount = exactHexAmount(e.balance, tokenDecimals(e.tokenSymbol));
604
661
  } catch {
605
662
  continue;
606
663
  }
607
- if (!Number.isFinite(amount) || amount <= 0) continue;
608
664
  pending.push({
609
665
  chainId: e.chainId,
610
666
  asset: e.tokenSymbol,
611
- amount: String(amount),
667
+ amount,
612
668
  isPending: e.isPending === true,
613
669
  ...typeof e.updatedAt === "string" && e.updatedAt !== "" ? { since: e.updatedAt } : {}
614
670
  });
@@ -631,13 +687,13 @@ function mapBalances(raw2, _chainId, smartWallet) {
631
687
  { balance, balanceWithFee, decimals }
632
688
  ] of Object.entries(tokensBySymbol ?? {})) {
633
689
  if (balanceWithFee === void 0 && balance === void 0) throw new Error("Zyfai asset balance is unavailable.");
634
- const amount = hexToDecimal(balanceWithFee ?? balance, decimals);
635
- totalBalance += amount;
690
+ const amount = exactHexAmount(balanceWithFee ?? balance, tokenDecimals(symbol, decimals));
691
+ totalBalance += Number(amount);
636
692
  tokens.push({
637
693
  chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
638
694
  chainId: positionChainId,
639
695
  asset: symbol,
640
- amount: String(amount)
696
+ amount
641
697
  });
642
698
  }
643
699
  }
@@ -651,19 +707,17 @@ function mapBalances(raw2, _chainId, smartWallet) {
651
707
  // legacy `amount` fallback is hex. Runtime
652
708
  // getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
653
709
  // when present, otherwise fall back to the per-symbol lookup.
654
- amount: String(
655
- p.underlyingAmountWithFee ?? p.underlyingAmount ? normalizeDecimalPlace(
656
- p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0",
657
- tokenDecimals(
658
- p.token_symbol,
659
- p.decimals
660
- )
661
- ) : hexToDecimal(
662
- p.amount || "0",
663
- tokenDecimals(
664
- p.token_symbol,
665
- p.decimals
666
- )
710
+ amount: p.underlyingAmountWithFee ?? p.underlyingAmount ? (0, import_viem2.formatUnits)(
711
+ BigInt(p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0"),
712
+ tokenDecimals(
713
+ p.token_symbol,
714
+ p.decimals
715
+ )
716
+ ) : exactHexAmount(
717
+ p.amount || "0",
718
+ tokenDecimals(
719
+ p.token_symbol,
720
+ p.decimals
667
721
  )
668
722
  ),
669
723
  apy: netApy(p.pool_apy_withFee, p.pool_apy, "pool_apy_withFee"),
@@ -1318,7 +1372,7 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
1318
1372
  }
1319
1373
 
1320
1374
  // src/lib/paid-rpc.ts
1321
- var import_viem2 = require("viem");
1375
+ var import_viem3 = require("viem");
1322
1376
  var PAID_RPC_RETRY_COUNT = 3;
1323
1377
  var PAID_RPC_RETRY_DELAY_MS = 1e3;
1324
1378
  function configuredRpcProxyBaseUrl(baseUrl) {
@@ -1354,9 +1408,9 @@ function createPaidRpcClient(chain, rpcUrls) {
1354
1408
  rpcUrls,
1355
1409
  chain.id
1356
1410
  );
1357
- return (0, import_viem2.createPublicClient)({
1411
+ return (0, import_viem3.createPublicClient)({
1358
1412
  chain,
1359
- transport: (0, import_viem2.http)(url, {
1413
+ transport: (0, import_viem3.http)(url, {
1360
1414
  retryCount: PAID_RPC_RETRY_COUNT,
1361
1415
  retryDelay: PAID_RPC_RETRY_DELAY_MS
1362
1416
  })
@@ -1453,7 +1507,7 @@ async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
1453
1507
  }
1454
1508
 
1455
1509
  // src/agents/zyfai/zyfai.agent.ts
1456
- var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem3.parseAbi)([
1510
+ var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem4.parseAbi)([
1457
1511
  "function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
1458
1512
  ]);
1459
1513
  var WETH_ADDRESS_BY_CHAIN = {
@@ -1465,6 +1519,7 @@ var VIEM_CHAIN = { 8453: import_chains.base, 42161: import_chains.arbitrum, 1: i
1465
1519
  var SPLITTING_ASSETS = ["USDC", "WETH"];
1466
1520
  var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
1467
1521
  var EARNINGS_REFRESH_COOLDOWN_MS = 60 * 60 * 1e3;
1522
+ var WITHDRAW_BALANCE_TIMEOUT_MS = 2e3;
1468
1523
  var ZyfaiAgent = class _ZyfaiAgent {
1469
1524
  id = "zyfai";
1470
1525
  supportedChainIds = [8453, 42161, 1];
@@ -1604,7 +1659,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1604
1659
  }, asset.address, entry.action === "Withdraw" ? "withdraw" : "deposit");
1605
1660
  if (evidence.transfer === "confirmed" && evidence.confirmedAmount) {
1606
1661
  Object.assign(tx, {
1607
- amount: (0, import_viem3.formatUnits)(BigInt(evidence.confirmedAmount), asset.decimals),
1662
+ amount: (0, import_viem4.formatUnits)(BigInt(evidence.confirmedAmount), asset.decimals),
1608
1663
  amountRaw: evidence.confirmedAmount,
1609
1664
  blockNumber: evidence.blockNumber,
1610
1665
  logIndexes: evidence.logIndexes,
@@ -2331,6 +2386,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
2331
2386
  evidence2 = { ...evidence2, transfer: "unknown" };
2332
2387
  }
2333
2388
  notifyFunding(onProgress, evidence2);
2389
+ if (evidence2.transfer === "failed")
2390
+ throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
2334
2391
  try {
2335
2392
  const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
2336
2393
  await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
@@ -2369,12 +2426,113 @@ var ZyfaiAgent = class _ZyfaiAgent {
2369
2426
  } catch {
2370
2427
  }
2371
2428
  notifyFunding(onProgress, evidence);
2429
+ if (evidence.transfer === "failed")
2430
+ throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash: raw2.txHash }, this.id);
2372
2431
  return { ...mapDeposit(raw2), evidence };
2373
2432
  } catch (error) {
2374
2433
  throw error;
2375
2434
  }
2376
2435
  }
2377
2436
  // --- IAgent: Fund operations ---
2437
+ /** Only after a definitive endpoint rejection, combine a new provider
2438
+ * snapshot with the Safe's exact idle balance. Never parse its rounded error
2439
+ * amount as proof of zero, or use this to skip sending a new MAX request. */
2440
+ async assertNotEmptyRejectedWithdrawal(state, chainId, token, owner) {
2441
+ const internals = this.sdk;
2442
+ const generation = this.earningsGeneration;
2443
+ let expired = false;
2444
+ const unavailable = () => new OwneyError(
2445
+ "WITHDRAW_BALANCE_UNAVAILABLE",
2446
+ "Could not verify Zyfai's balance after its withdrawal rejection.",
2447
+ { chainId, token },
2448
+ this.id
2449
+ );
2450
+ const assertScope = () => {
2451
+ if (expired) throw unavailable();
2452
+ const account = internals.walletClient?.account?.address;
2453
+ 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())
2454
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai withdrawal.", void 0, this.id);
2455
+ };
2456
+ const read2 = async () => {
2457
+ assertScope();
2458
+ const [wallet, registered, portfolio] = await Promise.all([
2459
+ this.sdk.getSmartWalletAddress(owner, chainId),
2460
+ this.sdk.getSmartWalletByEOA(owner),
2461
+ this.sdk.getPortfolio(owner).catch(() => void 0)
2462
+ ]);
2463
+ assertScope();
2464
+ if (!(0, import_viem4.isAddress)(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
2465
+ throw new OwneyError(
2466
+ "AGENT_AUTH_FAILED",
2467
+ "Could not verify Zyfai's withdrawal wallet.",
2468
+ { reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
2469
+ this.id
2470
+ );
2471
+ if (hasEmptyWithdrawalSnapshot(portfolio, chainId, token)) {
2472
+ const asset = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === token);
2473
+ const idle = asset ? await this.getPublicClient(chainId).readContract({
2474
+ address: asset.address,
2475
+ abi: import_viem4.erc20Abi,
2476
+ functionName: "balanceOf",
2477
+ args: [wallet.address]
2478
+ }).catch(() => void 0) : void 0;
2479
+ assertScope();
2480
+ if (idle === 0n) throw new OwneyError(
2481
+ "WITHDRAW_INSUFFICIENT_BALANCE",
2482
+ "No funds remain in Zyfai for this asset.",
2483
+ { available: "0", chainId, token },
2484
+ this.id
2485
+ );
2486
+ }
2487
+ };
2488
+ let timer;
2489
+ try {
2490
+ await Promise.race([read2(), new Promise((_, reject) => {
2491
+ timer = setTimeout(() => {
2492
+ expired = true;
2493
+ reject(unavailable());
2494
+ }, WITHDRAW_BALANCE_TIMEOUT_MS);
2495
+ })]);
2496
+ } finally {
2497
+ expired = true;
2498
+ if (timer) clearTimeout(timer);
2499
+ }
2500
+ }
2501
+ /** Called only after the execution endpoint explicitly rejected a request.
2502
+ * A timeout or an accepted request never authorizes repair and resubmission. */
2503
+ async recoverWithdrawalSession(state, chainId, token, owner) {
2504
+ await this.assertNotEmptyRejectedWithdrawal(state, chainId, token, owner);
2505
+ const internals = this.sdk;
2506
+ const assertScope = () => {
2507
+ const account = internals.walletClient?.account?.address;
2508
+ if (state.walletAddress.toLowerCase() !== owner.toLowerCase() || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
2509
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai withdrawal.", void 0, this.id);
2510
+ };
2511
+ assertScope();
2512
+ const walletState = await this.readWalletState(owner);
2513
+ assertScope();
2514
+ const unavailable = () => new OwneyError(
2515
+ "AGENT_AUTH_FAILED",
2516
+ "Zyfai has no active withdrawal session. Its withdrawal was not submitted.",
2517
+ { reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
2518
+ this.id
2519
+ );
2520
+ if (walletState.predeployed !== false || internals.isPredeployed === true) throw unavailable();
2521
+ internals.hasActiveSessionKey = false;
2522
+ const key2 = `${owner.toLowerCase()}:${chainId}`;
2523
+ this.sessionKeyAttempts.delete(key2);
2524
+ try {
2525
+ const result = await this.sdk.createSessionKey(owner, chainId);
2526
+ assertScope();
2527
+ if (result.success === false || result.alreadyActive === true) throw unavailable();
2528
+ this.sessionKeyAttempts.add(key2);
2529
+ } catch (error) {
2530
+ assertScope();
2531
+ const code = error?.code;
2532
+ if (code === 4001 || code === "ACTION_REJECTED" || error instanceof Error && /user (rejected|denied)|request rejected|action rejected/i.test(error.message)) throw error;
2533
+ throw unavailable();
2534
+ }
2535
+ }
2378
2536
  async withdraw(state, chainId, token, amount, onProgress) {
2379
2537
  const validChainId = isValidChainId(chainId);
2380
2538
  try {
@@ -2392,11 +2550,42 @@ var ZyfaiAgent = class _ZyfaiAgent {
2392
2550
  });
2393
2551
  throw error;
2394
2552
  }
2395
- const observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
2553
+ let observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
2396
2554
  const authToken = this.sdk.httpClient?.authToken;
2555
+ const owner = this.getAddress();
2397
2556
  let raw2;
2398
2557
  try {
2399
- raw2 = await this.sdk.withdrawFunds(this.getAddress(), validChainId, amount, token);
2558
+ for (let attempt = 0; attempt < 2; attempt++) {
2559
+ try {
2560
+ raw2 = await this.sdk.withdrawFunds(owner, validChainId, amount, token);
2561
+ break;
2562
+ } catch (error) {
2563
+ if (!amount && observation.rejected() && observation.request()?.failure === "insufficient-balance") {
2564
+ try {
2565
+ await this.assertNotEmptyRejectedWithdrawal(state, validChainId, token, owner);
2566
+ } catch (proofError) {
2567
+ if (proofError instanceof OwneyError && (proofError.code === "WITHDRAW_INSUFFICIENT_BALANCE" || proofError.code === "NOT_CONNECTED")) throw proofError;
2568
+ }
2569
+ }
2570
+ if (!observation.rejected() || observation.request()?.failure !== "session-key-missing") throw error;
2571
+ if (attempt === 1)
2572
+ throw new OwneyError(
2573
+ "AGENT_AUTH_FAILED",
2574
+ "Zyfai has no active withdrawal session. Its withdrawal was not submitted.",
2575
+ { reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
2576
+ this.id
2577
+ );
2578
+ await this.recoverWithdrawalSession(state, validChainId, token, owner);
2579
+ observation.stop();
2580
+ observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
2581
+ }
2582
+ }
2583
+ if (!raw2) throw new OwneyError(
2584
+ "AGENT_INVALID_RESPONSE",
2585
+ "Zyfai returned no withdrawal result.",
2586
+ { chainId, token, phase: "observe" },
2587
+ this.id
2588
+ );
2400
2589
  } catch (error) {
2401
2590
  this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken);
2402
2591
  if (observation.attempted() !== void 0) {
@@ -2471,6 +2660,67 @@ var ZyfaiAgent = class _ZyfaiAgent {
2471
2660
  async getFundingWallet(state, chainId) {
2472
2661
  return (await this.sdk.getSmartWalletAddress(state.walletAddress, isValidChainId(chainId))).address;
2473
2662
  }
2663
+ /** A positive Safe balance is a withdrawal planning floor when the portfolio
2664
+ * index reports zero. Idle zero cannot prove that Zyfai's protocol positions
2665
+ * are empty: its SDK does not expose a complete contract catalog for them. */
2666
+ async getWithdrawableBalance(state, chainId, token) {
2667
+ const validChainId = isValidChainId(chainId);
2668
+ const asset = SupportedAssets.find((value) => value.chainId === validChainId && value.symbol === token);
2669
+ if (!asset) throw new OwneyError("WITHDRAW_NO_PERMITTED_TOKENS", "Asset selected is not supported", void 0, this.id);
2670
+ const owner = state.walletAddress;
2671
+ const generation = this.earningsGeneration;
2672
+ const internals = this.sdk;
2673
+ let expired = false;
2674
+ const unavailable = () => new OwneyError(
2675
+ "WITHDRAW_BALANCE_UNAVAILABLE",
2676
+ "Could not verify Zyfai's withdrawal balance. Its vault positions may still contain funds.",
2677
+ { reason: "ZYFAI_VAULT_BALANCE_UNVERIFIED", chainId, token },
2678
+ this.id
2679
+ );
2680
+ const assertScope = () => {
2681
+ if (expired) throw unavailable();
2682
+ const account = internals.walletClient?.account?.address;
2683
+ 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())
2684
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while checking Zyfai's withdrawal balance.", void 0, this.id);
2685
+ };
2686
+ let timer;
2687
+ const read2 = async () => {
2688
+ assertScope();
2689
+ const [wallet, registered] = await Promise.all([
2690
+ this.sdk.getSmartWalletAddress(owner, validChainId),
2691
+ this.sdk.getSmartWalletByEOA(owner)
2692
+ ]);
2693
+ assertScope();
2694
+ if (!(0, import_viem4.isAddress)(wallet.address) || wallet.isDeployed !== true || wallet.isOwner !== true || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase()) throw unavailable();
2695
+ const client = this.getPublicClient(validChainId);
2696
+ const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
2697
+ assertScope();
2698
+ const idle = await client.readContract({
2699
+ address: asset.address,
2700
+ abi: import_viem4.erc20Abi,
2701
+ functionName: "balanceOf",
2702
+ args: [wallet.address],
2703
+ blockNumber
2704
+ });
2705
+ assertScope();
2706
+ if (typeof idle !== "bigint" || idle <= 0n) throw unavailable();
2707
+ return idle;
2708
+ };
2709
+ try {
2710
+ return await Promise.race([read2(), new Promise((_, reject) => {
2711
+ timer = setTimeout(() => {
2712
+ expired = true;
2713
+ reject(unavailable());
2714
+ }, WITHDRAW_BALANCE_TIMEOUT_MS);
2715
+ })]);
2716
+ } catch (error) {
2717
+ if (error instanceof OwneyError) throw error;
2718
+ throw unavailable();
2719
+ } finally {
2720
+ expired = true;
2721
+ if (timer) clearTimeout(timer);
2722
+ }
2723
+ }
2474
2724
  async getBalances(state, chainId) {
2475
2725
  const requestedAt = Date.now();
2476
2726
  const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
@@ -2675,7 +2925,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2675
2925
  };
2676
2926
 
2677
2927
  // src/agents/yieldseeker/yieldseeker.agent.ts
2678
- var import_viem10 = require("viem");
2928
+ var import_viem11 = require("viem");
2679
2929
  var import_chains4 = require("viem/chains");
2680
2930
 
2681
2931
  // src/lib/chain-guard.ts
@@ -2714,7 +2964,7 @@ async function ensureWalletOnChain(pub, wallet, expected) {
2714
2964
  }
2715
2965
 
2716
2966
  // src/lib/transfer-auth.ts
2717
- var import_viem4 = require("viem");
2967
+ var import_viem5 = require("viem");
2718
2968
 
2719
2969
  // src/lib/sponsor-client.ts
2720
2970
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
@@ -2830,7 +3080,7 @@ async function postSponsorBatchTransfer(input) {
2830
3080
  }
2831
3081
 
2832
3082
  // src/lib/permit2.ts
2833
- var import_viem5 = require("viem");
3083
+ var import_viem6 = require("viem");
2834
3084
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
2835
3085
  var MAX_UINT256 = 2n ** 256n - 1n;
2836
3086
  function permit2ApprovalAmount(requiredAmount) {
@@ -2871,7 +3121,7 @@ var ERC20_ALLOWANCE_ABI = [
2871
3121
  function randomPermit2Nonce() {
2872
3122
  const bytes = new Uint8Array(32);
2873
3123
  globalThis.crypto.getRandomValues(bytes);
2874
- return BigInt((0, import_viem5.bytesToHex)(bytes));
3124
+ return BigInt((0, import_viem6.bytesToHex)(bytes));
2875
3125
  }
2876
3126
  async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
2877
3127
  return publicClient.readContract({
@@ -2914,7 +3164,7 @@ function makeVerificationAwareDepositCallback(implementation) {
2914
3164
 
2915
3165
  // src/agents/yieldseeker/yieldseeker.auth.ts
2916
3166
  var import_siwe = require("siwe");
2917
- var import_viem6 = require("viem");
3167
+ var import_viem7 = require("viem");
2918
3168
  var import_chains2 = require("viem/chains");
2919
3169
 
2920
3170
  // src/agents/yieldseeker/yieldseeker.auth-cache.ts
@@ -3027,7 +3277,7 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
3027
3277
  return new import_siwe.SiweMessage({
3028
3278
  scheme: url.protocol.slice(0, -1),
3029
3279
  domain: url.host,
3030
- address: (0, import_viem6.getAddress)(address),
3280
+ address: (0, import_viem7.getAddress)(address),
3031
3281
  uri: url.origin,
3032
3282
  version: "1",
3033
3283
  chainId,
@@ -3113,15 +3363,15 @@ var YieldseekerAuth = class {
3113
3363
  clearYieldseekerSession(state.walletAddress, chainId);
3114
3364
  }
3115
3365
  async sign(state, chainId) {
3116
- const account = (0, import_viem6.getAddress)(state.walletAddress);
3117
- const publicClient = (0, import_viem6.createPublicClient)({
3366
+ const account = (0, import_viem7.getAddress)(state.walletAddress);
3367
+ const publicClient = (0, import_viem7.createPublicClient)({
3118
3368
  chain: import_chains2.base,
3119
- transport: (0, import_viem6.custom)(state.provider)
3369
+ transport: (0, import_viem7.custom)(state.provider)
3120
3370
  });
3121
- const walletClient = (0, import_viem6.createWalletClient)({
3371
+ const walletClient = (0, import_viem7.createWalletClient)({
3122
3372
  account,
3123
3373
  chain: import_chains2.base,
3124
- transport: (0, import_viem6.custom)(state.provider)
3374
+ transport: (0, import_viem7.custom)(state.provider)
3125
3375
  });
3126
3376
  await ensureWalletOnChain(
3127
3377
  publicClient,
@@ -3288,7 +3538,7 @@ var YieldseekerApiClient = class {
3288
3538
  };
3289
3539
 
3290
3540
  // src/agents/yieldseeker/yieldseeker.mapper.ts
3291
- var import_viem7 = require("viem");
3541
+ var import_viem8 = require("viem");
3292
3542
 
3293
3543
  // src/lib/helpers/snapshot-apy.ts
3294
3544
  var DAY_MS = 864e5;
@@ -3355,10 +3605,10 @@ function raw(value, endpoint) {
3355
3605
  return BigInt(value);
3356
3606
  }
3357
3607
  function decimal(value, decimals, endpoint) {
3358
- return (0, import_viem7.formatUnits)(raw(value, endpoint), decimals);
3608
+ return (0, import_viem8.formatUnits)(raw(value, endpoint), decimals);
3359
3609
  }
3360
3610
  function usd(rawAmount, decimals, price) {
3361
- return Number((0, import_viem7.formatUnits)(rawAmount, decimals)) * price;
3611
+ return Number((0, import_viem8.formatUnits)(rawAmount, decimals)) * price;
3362
3612
  }
3363
3613
  function percent(value) {
3364
3614
  const result = Number(value);
@@ -3384,7 +3634,7 @@ function assetAddressValue(record, address) {
3384
3634
  }
3385
3635
  function position(value, asset, baseAssetDecimals) {
3386
3636
  const option = value?.yieldOption;
3387
- if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem7.isAddress)(option.address)) {
3637
+ if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem8.isAddress)(option.address)) {
3388
3638
  return invalid("yield positions", "missing vault metadata");
3389
3639
  }
3390
3640
  return {
@@ -3476,7 +3726,7 @@ function mapYieldseekerEarnings(contexts) {
3476
3726
  chain: "BASE",
3477
3727
  chainId: 8453,
3478
3728
  asset: context.asset,
3479
- amount: (0, import_viem7.formatUnits)(amount, context.snapshot.baseAssetDecimals)
3729
+ amount: (0, import_viem8.formatUnits)(amount, context.snapshot.baseAssetDecimals)
3480
3730
  });
3481
3731
  lifetimeEarnings += usd(
3482
3732
  amount,
@@ -3605,7 +3855,7 @@ function allocationEntry(action, context) {
3605
3855
  const leg = deposit;
3606
3856
  const symbol = typeof leg.assetSymbol === "string" ? leg.assetSymbol.toUpperCase() : "";
3607
3857
  if (symbol !== context.asset && !(context.asset === "WETH" && symbol === "ETH")) return void 0;
3608
- if (typeof leg.vaultAddress !== "string" || !(0, import_viem7.isAddress)(leg.vaultAddress)) return void 0;
3858
+ if (typeof leg.vaultAddress !== "string" || !(0, import_viem8.isAddress)(leg.vaultAddress)) return void 0;
3609
3859
  const rawAmount = typeof leg.rawAmount === "string" && /^\d+$/.test(leg.rawAmount) ? BigInt(leg.rawAmount) : typeof leg.rawAmount === "number" && Number.isSafeInteger(leg.rawAmount) ? BigInt(leg.rawAmount) : void 0;
3610
3860
  if (rawAmount === void 0 || rawAmount <= 0n) return void 0;
3611
3861
  const option = context.positions.find((position2) => position2.yieldOption.address.toLowerCase() === leg.vaultAddress.toLowerCase())?.yieldOption;
@@ -3613,7 +3863,7 @@ function allocationEntry(action, context) {
3613
3863
  protocol: typeof leg.vaultProvider === "string" && leg.vaultProvider ? leg.vaultProvider : option?.provider || "Vault",
3614
3864
  pool: typeof leg.vaultName === "string" && leg.vaultName ? leg.vaultName : option?.name,
3615
3865
  tokenSymbol: context.asset,
3616
- amount: (0, import_viem7.formatUnits)(rawAmount, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
3866
+ amount: (0, import_viem8.formatUnits)(rawAmount, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
3617
3867
  });
3618
3868
  amountRaw += rawAmount;
3619
3869
  }
@@ -3632,7 +3882,7 @@ function allocationEntry(action, context) {
3632
3882
  tokenSymbol: context.asset,
3633
3883
  agentWallet: context.wallet.walletAddress,
3634
3884
  amountRaw: String(amountRaw),
3635
- amount: (0, import_viem7.formatUnits)(amountRaw, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
3885
+ amount: (0, import_viem8.formatUnits)(amountRaw, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
3636
3886
  }]
3637
3887
  };
3638
3888
  }
@@ -3756,7 +4006,7 @@ function mapYieldseekerHistory(contexts, options) {
3756
4006
  const known = new Set(target.logIndexes ?? []);
3757
4007
  if (tx.logIndexes?.some((index) => !known.has(index))) {
3758
4008
  target.amountRaw = String(BigInt(target.amountRaw ?? "0") + BigInt(tx.amountRaw));
3759
- target.amount = (0, import_viem7.formatUnits)(
4009
+ target.amount = (0, import_viem8.formatUnits)(
3760
4010
  BigInt(target.amountRaw),
3761
4011
  YIELDSEEKER_ASSET_METADATA[target.tokenSymbol].decimals
3762
4012
  );
@@ -3837,51 +4087,53 @@ function mapYieldseekerAgentApy(options, days) {
3837
4087
  }
3838
4088
 
3839
4089
  // src/agents/yieldseeker/yieldseeker.wallet-version.ts
3840
- var import_viem8 = require("viem");
4090
+ var import_viem9 = require("viem");
3841
4091
  var YIELDSEEKER_FACTORY = "0x9c7410a0faCAC60850C46eE5b58B518daec95130";
3842
4092
  var YIELDSEEKER_V1_IMPLEMENTATION = "0x32403455357589523435dB18b414057D7cE05051";
3843
4093
  var YIELDSEEKER_V2_IMPLEMENTATION = "0x613Db9c924ADB7A7A90c11b02Cce20c913E3Fe50";
3844
4094
  var ERC1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
3845
- var walletAbi = (0, import_viem8.parseAbi)([
4095
+ var walletAbi = (0, import_viem9.parseAbi)([
3846
4096
  "function owner() view returns (address)",
3847
4097
  "function ownerAgentIndex() view returns (uint256)",
3848
4098
  "function baseAsset() view returns (address)",
3849
4099
  "function FACTORY() view returns (address)",
3850
- "function upgradeToLatest()"
4100
+ "function upgradeToLatest()",
4101
+ "event Upgraded(address indexed implementation)"
3851
4102
  ]);
3852
- var factoryAbi = (0, import_viem8.parseAbi)([
4103
+ var factoryAbi = (0, import_viem9.parseAbi)([
3853
4104
  "function userWallets(address owner, uint256 index) view returns (address)",
3854
4105
  "function agentWalletImplementation() view returns (address)"
3855
4106
  ]);
3856
4107
  function yieldseekerOwnerPath(code) {
3857
4108
  return !code || code === "0x" || /^0xef0100[0-9a-f]{40}$/i.test(code) ? "eoa" : "smart";
3858
4109
  }
3859
- async function readYieldseekerWalletStatus(client, wallet, owner, asset) {
3860
- const agentWallet = (0, import_viem8.getAddress)(wallet);
3861
- const expectedOwner = (0, import_viem8.getAddress)(owner);
3862
- const expectedAsset = (0, import_viem8.getAddress)(asset);
4110
+ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNumber) {
4111
+ const agentWallet = (0, import_viem9.getAddress)(wallet);
4112
+ const expectedOwner = (0, import_viem9.getAddress)(owner);
4113
+ const expectedAsset = (0, import_viem9.getAddress)(asset);
3863
4114
  const [code, ownerCode, slot, actualOwner, index, actualAsset, actualFactory, latest] = await Promise.all([
3864
- client.getBytecode({ address: agentWallet }),
3865
- client.getBytecode({ address: expectedOwner }),
3866
- client.getStorageAt({ address: agentWallet, slot: ERC1967_IMPLEMENTATION_SLOT }),
3867
- client.readContract({ address: agentWallet, abi: walletAbi, functionName: "owner" }),
3868
- client.readContract({ address: agentWallet, abi: walletAbi, functionName: "ownerAgentIndex" }),
3869
- client.readContract({ address: agentWallet, abi: walletAbi, functionName: "baseAsset" }),
3870
- client.readContract({ address: agentWallet, abi: walletAbi, functionName: "FACTORY" }),
3871
- client.readContract({ address: YIELDSEEKER_FACTORY, abi: factoryAbi, functionName: "agentWalletImplementation" })
4115
+ client.getBytecode({ address: agentWallet, blockNumber }),
4116
+ client.getBytecode({ address: expectedOwner, blockNumber }),
4117
+ client.getStorageAt({ address: agentWallet, slot: ERC1967_IMPLEMENTATION_SLOT, blockNumber }),
4118
+ client.readContract({ address: agentWallet, abi: walletAbi, functionName: "owner", blockNumber }),
4119
+ client.readContract({ address: agentWallet, abi: walletAbi, functionName: "ownerAgentIndex", blockNumber }),
4120
+ client.readContract({ address: agentWallet, abi: walletAbi, functionName: "baseAsset", blockNumber }),
4121
+ client.readContract({ address: agentWallet, abi: walletAbi, functionName: "FACTORY", blockNumber }),
4122
+ client.readContract({ address: YIELDSEEKER_FACTORY, abi: factoryAbi, functionName: "agentWalletImplementation", blockNumber })
3872
4123
  ]);
3873
4124
  if (!code || code === "0x" || !slot || /^0x0+$/.test(slot)) {
3874
4125
  throw new Error("Yieldseeker wallet is not a deployed ERC1967 proxy");
3875
4126
  }
3876
- const implementation = (0, import_viem8.getAddress)(`0x${slot.slice(-40)}`);
4127
+ const implementation = (0, import_viem9.getAddress)(`0x${slot.slice(-40)}`);
3877
4128
  const mapped = await client.readContract({
3878
4129
  address: YIELDSEEKER_FACTORY,
3879
4130
  abi: factoryAbi,
3880
4131
  functionName: "userWallets",
3881
- args: [expectedOwner, index]
4132
+ args: [expectedOwner, index],
4133
+ blockNumber
3882
4134
  });
3883
- const verified = (0, import_viem8.isAddressEqual)(actualOwner, expectedOwner) && (0, import_viem8.isAddressEqual)(actualAsset, expectedAsset) && (0, import_viem8.isAddressEqual)(actualFactory, YIELDSEEKER_FACTORY) && (0, import_viem8.isAddressEqual)(mapped, agentWallet);
3884
- const version = !verified ? "unknown" : (0, import_viem8.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem8.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
4135
+ 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);
4136
+ const version = !verified ? "unknown" : (0, import_viem9.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem9.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
3885
4137
  return {
3886
4138
  agentWallet,
3887
4139
  owner: actualOwner,
@@ -3890,18 +4142,20 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset) {
3890
4142
  implementation,
3891
4143
  version,
3892
4144
  verified,
3893
- upgradeAvailable: version === "v1" && (0, import_viem8.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
4145
+ upgradeAvailable: version === "v1" && (0, import_viem9.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
3894
4146
  };
3895
4147
  }
3896
4148
  var YIELDSEEKER_UPGRADE_ABI = walletAbi;
3897
4149
 
3898
4150
  // src/agents/yieldseeker/yieldseeker.sponsored-withdraw.ts
3899
- var import_viem9 = require("viem");
4151
+ var import_viem10 = require("viem");
3900
4152
  var import_account_abstraction2 = require("viem/account-abstraction");
3901
4153
  var import_accounts = require("viem/accounts");
3902
4154
  var import_chains3 = require("viem/chains");
3903
4155
  var ENTRY_POINT_V06 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
3904
4156
  var USER_OP_FEE_HEADROOM = 2n;
4157
+ var OBSERVATION_WINDOW_MS = 6e4;
4158
+ var OBSERVATION_POLL_MS = 1500;
3905
4159
  var ENTRY_POINT_NONCE_ABI = [
3906
4160
  { type: "function", name: "getNonce", stateMutability: "view", inputs: [
3907
4161
  { name: "sender", type: "address" },
@@ -3934,10 +4188,119 @@ function sponsorError(message, safeToFallback, details = {}) {
3934
4188
  return new OwneyError(
3935
4189
  "SPONSOR_REQUEST_FAILED",
3936
4190
  message,
3937
- { safeToFallback, ...details },
4191
+ {
4192
+ safeToFallback,
4193
+ phase: safeToFallback ? "prepare" : "observe",
4194
+ failureCode: safeToFallback ? "SPONSOR_PREPARATION_FAILED" : "SPONSOR_OUTCOME_UNRESOLVED",
4195
+ ...safeToFallback ? { rejectedBeforeSubmission: true } : {},
4196
+ ...details
4197
+ },
3938
4198
  "yieldseeker"
3939
4199
  );
3940
4200
  }
4201
+ function providerReason(value) {
4202
+ if (typeof value !== "string") return void 0;
4203
+ return value.replace(/https?:\/\/\S+/gi, "[url]").replace(/0x[0-9a-fA-F]{40,}/g, "[hex]").replace(/[A-Za-z0-9_+=\/-]{48,}/g, "[token]").slice(0, 300);
4204
+ }
4205
+ function rpcDiagnostics(data) {
4206
+ const details = {};
4207
+ if (typeof data?.failureCode === "string" && [
4208
+ "SPONSOR_RATE_LIMIT",
4209
+ "SPONSOR_UPSTREAM_UNAVAILABLE",
4210
+ "SPONSOR_RPC_UNSUPPORTED",
4211
+ "SPONSOR_POLICY_REJECTED",
4212
+ "SPONSOR_BUDGET_REJECTED",
4213
+ "SPONSOR_EXECUTION_REVERTED"
4214
+ ].includes(data.failureCode))
4215
+ details.failureCode = data.failureCode;
4216
+ if (typeof data?.revertSelector === "string" && /^0x[0-9a-fA-F]{8}$/.test(data.revertSelector))
4217
+ details.revertSelector = data.revertSelector;
4218
+ if (typeof data?.revertName === "string" && [
4219
+ "InsufficientBalance",
4220
+ "InvalidAsset",
4221
+ "Unauthorized",
4222
+ "Error",
4223
+ "Panic",
4224
+ "FailedOp",
4225
+ "FailedOpWithRevert"
4226
+ ].includes(data.revertName))
4227
+ details.revertName = data.revertName;
4228
+ if (typeof data?.aaCode === "string" && /^AA[0-9]{2}$/.test(data.aaCode)) details.aaCode = data.aaCode;
4229
+ if (typeof data?.panicCode === "string" && /^0x[0-9a-fA-F]{1,2}$/.test(data.panicCode)) {
4230
+ const panicCode = (0, import_viem10.toHex)(BigInt(data.panicCode));
4231
+ if (["0x0", "0x1", "0x11", "0x12", "0x21", "0x22", "0x31", "0x32", "0x41", "0x51"].includes(panicCode))
4232
+ details.panicCode = panicCode;
4233
+ }
4234
+ return details;
4235
+ }
4236
+ function isHash(value) {
4237
+ return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value);
4238
+ }
4239
+ async function assertOwnerSession(state, owner) {
4240
+ const [chain, accounts] = await Promise.all([
4241
+ state.provider.request({ method: "eth_chainId" }),
4242
+ state.provider.request({ method: "eth_accounts" })
4243
+ ]);
4244
+ if (Number(chain) !== 8453)
4245
+ throw new OwneyError(
4246
+ "CHAIN_MISMATCH",
4247
+ "Switch back to Base before withdrawing.",
4248
+ { rejectedBeforeSubmission: true, phase: "prepare" },
4249
+ "yieldseeker"
4250
+ );
4251
+ if (!Array.isArray(accounts) || typeof accounts[0] !== "string" || accounts[0].toLowerCase() !== owner.toLowerCase())
4252
+ throw new OwneyError(
4253
+ "NOT_CONNECTED",
4254
+ "The connected withdrawal account changed.",
4255
+ { rejectedBeforeSubmission: true, phase: "prepare" },
4256
+ "yieldseeker"
4257
+ );
4258
+ }
4259
+ function savePending(onPending, evidence, beforeSubmission = false) {
4260
+ try {
4261
+ onPending?.(evidence);
4262
+ } catch {
4263
+ if (beforeSubmission)
4264
+ throw sponsorError(
4265
+ "Withdrawal status cannot be saved before submission.",
4266
+ true,
4267
+ { phase: "prepare", rejectedBeforeSubmission: true, failureCode: "RECOVERY_STORAGE_UNAVAILABLE" }
4268
+ );
4269
+ }
4270
+ }
4271
+ async function checkpoint(callback, pending) {
4272
+ try {
4273
+ await callback?.(pending);
4274
+ } catch {
4275
+ throw sponsorError(
4276
+ "Withdrawal status cannot be saved before submission.",
4277
+ true,
4278
+ { phase: "prepare", rejectedBeforeSubmission: true, failureCode: "RECOVERY_STORAGE_UNAVAILABLE" }
4279
+ );
4280
+ }
4281
+ }
4282
+ function finalPaymasterFields(value) {
4283
+ const result = {};
4284
+ for (const key2 of [
4285
+ "paymasterAndData",
4286
+ "callGasLimit",
4287
+ "verificationGasLimit",
4288
+ "preVerificationGas",
4289
+ "maxFeePerGas",
4290
+ "maxPriorityFeePerGas"
4291
+ ]) {
4292
+ const field = value[key2];
4293
+ if (field === void 0) continue;
4294
+ if (typeof field !== "string" || !/^0x[0-9a-fA-F]+$/.test(field))
4295
+ throw sponsorError(
4296
+ "Paymaster returned an invalid final operation.",
4297
+ true,
4298
+ { phase: "prepare", rejectedBeforeSubmission: true, failureCode: "INVALID_PAYMASTER_QUOTE" }
4299
+ );
4300
+ result[key2] = field;
4301
+ }
4302
+ return result;
4303
+ }
3941
4304
  async function requestIntent(input) {
3942
4305
  const baseUrl = (input.routingApiBaseUrl ?? DEFAULT_ROUTING_API).replace(/\/$/, "");
3943
4306
  let response;
@@ -3959,18 +4322,35 @@ async function requestIntent(input) {
3959
4322
  action: input.action,
3960
4323
  yieldseekerUserId: input.userId,
3961
4324
  yieldseekerAgentId: input.agentId
3962
- })
4325
+ }),
4326
+ signal: AbortSignal.timeout(15e3)
3963
4327
  });
3964
4328
  } catch {
3965
- throw sponsorError("Withdrawal sponsorship could not be requested.", true);
4329
+ throw sponsorError(
4330
+ "Withdrawal sponsorship could not be requested.",
4331
+ true,
4332
+ { phase: "prepare", rejectedBeforeSubmission: true, failureCode: "INTENT_NETWORK_ERROR" }
4333
+ );
3966
4334
  }
3967
4335
  const envelope = await response.json().catch(() => null);
3968
4336
  const intent = envelope?.data;
3969
4337
  if (!response.ok || !envelope?.success || !intent?.intent || !["eoa", "smart"].includes(intent.ownerPath)) {
4338
+ const rawCode = typeof envelope?.failureCode === "string" ? envelope.failureCode : envelope?.code;
4339
+ const failureCode = typeof rawCode === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(rawCode) ? rawCode : void 0;
4340
+ if (input.refreshYieldseekerSignature && (failureCode === "YIELDSEEKER_AUTH_REJECTED" || failureCode === "AUTH_REJECTED" || envelope?.sponsorshipStage === "provider-auth" && (envelope.providerStatus === 401 || envelope.providerStatus === 403))) {
4341
+ const signature = await input.refreshYieldseekerSignature(input.yieldseekerSignature);
4342
+ return requestIntent({ ...input, yieldseekerSignature: signature, refreshYieldseekerSignature: void 0 });
4343
+ }
3970
4344
  throw sponsorError(
3971
4345
  "Withdrawal sponsorship is currently unavailable.",
3972
4346
  true,
3973
- { statusCode: response.status }
4347
+ {
4348
+ statusCode: response.status,
4349
+ phase: "prepare",
4350
+ rejectedBeforeSubmission: true,
4351
+ failureCode: failureCode ?? "INTENT_REJECTED",
4352
+ providerReason: providerReason(envelope?.message)
4353
+ }
3974
4354
  );
3975
4355
  }
3976
4356
  const url = new URL(`${baseUrl}/api/v1/sponsor/yieldseeker-withdraw/rpc`);
@@ -3983,19 +4363,55 @@ async function rpc(url, method, params, phase) {
3983
4363
  response = await fetch(url, {
3984
4364
  method: "POST",
3985
4365
  headers: { "content-type": "application/json" },
3986
- body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
4366
+ body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
4367
+ signal: AbortSignal.timeout(15e3)
3987
4368
  });
3988
4369
  } catch {
3989
- throw sponsorError("Withdrawal sponsorship request status is unknown.", phase === "prepare");
4370
+ throw sponsorError(
4371
+ "Withdrawal sponsorship request status is unknown.",
4372
+ phase === "prepare",
4373
+ {
4374
+ phase,
4375
+ rpcMethod: method,
4376
+ ...phase === "prepare" ? { rejectedBeforeSubmission: true } : {},
4377
+ failureCode: "RPC_NETWORK_ERROR"
4378
+ }
4379
+ );
3990
4380
  }
3991
4381
  const payload = await response.json().catch(() => null);
3992
4382
  if (!response.ok || payload?.error || payload?.result === void 0) {
3993
4383
  const rejection = payload?.error;
3994
- const definitiveRejection = phase === "send" && response.status < 500 && !!rejection && (rejection.data?.rejectedBeforeSubmission === true || [-32501, -32502, -32503, -32504, -32505, -32507].includes(rejection.code ?? 0) || /maxFeePerGas.*(?:too low|lower than|less than|must be at least)/i.test(rejection.message ?? ""));
4384
+ const diagnostics = rpcDiagnostics(rejection?.data);
4385
+ const executionReverted = rejection?.code === -32521 || diagnostics.failureCode === "SPONSOR_EXECUTION_REVERTED";
4386
+ const details = {
4387
+ statusCode: response.status,
4388
+ rpcCode: rejection?.code,
4389
+ rpcMethod: method,
4390
+ phase,
4391
+ providerReason: providerReason(rejection?.data?.providerReason) ?? providerReason(rejection?.message),
4392
+ failureCode: "RPC_REJECTED",
4393
+ ...diagnostics
4394
+ };
4395
+ if (phase === "prepare" && executionReverted)
4396
+ throw new OwneyError(
4397
+ "WITHDRAW_FAILED",
4398
+ "Yieldseeker withdrawal simulation reverted before submission.",
4399
+ {
4400
+ ...details,
4401
+ failureCode: "SPONSOR_EXECUTION_REVERTED",
4402
+ safeToFallback: false,
4403
+ rejectedBeforeSubmission: true
4404
+ },
4405
+ "yieldseeker"
4406
+ );
4407
+ const definitiveRejection = phase === "send" && !executionReverted && response.status < 500 && !!rejection && (rejection.data?.rejectedBeforeSubmission === true || [-32501, -32502, -32503, -32504, -32505, -32507].includes(rejection.code ?? 0) || /maxFeePerGas.*(?:too low|lower than|less than|must be at least)/i.test(rejection.message ?? ""));
3995
4408
  throw sponsorError(
3996
4409
  phase !== "prepare" && !definitiveRejection ? "Sponsored withdrawal submission status is unknown." : "Withdrawal sponsorship was declined before submission.",
3997
4410
  phase === "prepare" || definitiveRejection,
3998
- { statusCode: response.status, rpcCode: payload?.error?.code, rpcMethod: method }
4411
+ {
4412
+ ...details,
4413
+ ...phase === "prepare" || definitiveRejection ? { rejectedBeforeSubmission: true } : {}
4414
+ }
3999
4415
  );
4000
4416
  }
4001
4417
  return payload.result;
@@ -4015,13 +4431,15 @@ async function submitEoa(input) {
4015
4431
  let nonce;
4016
4432
  let fees;
4017
4433
  try {
4018
- nonce = await input.client.readContract({
4019
- address: ENTRY_POINT_V06,
4020
- abi: ENTRY_POINT_NONCE_ABI,
4021
- functionName: "getNonce",
4022
- args: [input.agentWallet, 0n]
4023
- });
4024
- fees = await input.client.estimateFeesPerGas();
4434
+ [nonce, fees] = await Promise.all([
4435
+ input.client.readContract({
4436
+ address: ENTRY_POINT_V06,
4437
+ abi: ENTRY_POINT_NONCE_ABI,
4438
+ functionName: "getNonce",
4439
+ args: [input.agentWallet, 0n]
4440
+ }),
4441
+ input.client.estimateFeesPerGas()
4442
+ ]);
4025
4443
  } catch {
4026
4444
  throw sponsorError("Could not prepare the sponsored withdrawal.", true);
4027
4445
  }
@@ -4029,14 +4447,14 @@ async function submitEoa(input) {
4029
4447
  throw sponsorError("Base did not return EIP-1559 gas fees.", true);
4030
4448
  let operation = {
4031
4449
  sender: input.agentWallet,
4032
- nonce: (0, import_viem9.toHex)(nonce),
4450
+ nonce: (0, import_viem10.toHex)(nonce),
4033
4451
  initCode: "0x",
4034
4452
  callData: input.callData,
4035
- callGasLimit: (0, import_viem9.toHex)(3e5),
4036
- verificationGasLimit: (0, import_viem9.toHex)(3e5),
4037
- preVerificationGas: (0, import_viem9.toHex)(6e4),
4038
- maxFeePerGas: (0, import_viem9.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
4039
- maxPriorityFeePerGas: (0, import_viem9.toHex)(fees.maxPriorityFeePerGas),
4453
+ callGasLimit: (0, import_viem10.toHex)(3e5),
4454
+ verificationGasLimit: (0, import_viem10.toHex)(3e5),
4455
+ preVerificationGas: (0, import_viem10.toHex)(6e4),
4456
+ maxFeePerGas: (0, import_viem10.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
4457
+ maxPriorityFeePerGas: (0, import_viem10.toHex)(fees.maxPriorityFeePerGas),
4040
4458
  paymasterAndData: "0x",
4041
4459
  signature: "0x"
4042
4460
  };
@@ -4044,7 +4462,7 @@ async function submitEoa(input) {
4044
4462
  const stub = await rpc(
4045
4463
  input.rpcUrl,
4046
4464
  "pm_getPaymasterStubData",
4047
- [operation, ENTRY_POINT_V06, (0, import_viem9.toHex)(8453), {}],
4465
+ [operation, ENTRY_POINT_V06, (0, import_viem10.toHex)(8453), {}],
4048
4466
  "prepare"
4049
4467
  );
4050
4468
  if (!stub.paymasterAndData?.startsWith("0x"))
@@ -4059,39 +4477,63 @@ async function submitEoa(input) {
4059
4477
  const final = await rpc(
4060
4478
  input.rpcUrl,
4061
4479
  "pm_getPaymasterData",
4062
- [operation, ENTRY_POINT_V06, (0, import_viem9.toHex)(8453), {}],
4480
+ [operation, ENTRY_POINT_V06, (0, import_viem10.toHex)(8453), {}],
4063
4481
  "prepare"
4064
4482
  );
4065
4483
  if (!final.paymasterAndData?.startsWith("0x"))
4066
4484
  throw sponsorError("CDP did not return final paymaster data.", true);
4067
- operation = { ...operation, paymasterAndData: final.paymasterAndData };
4485
+ operation = { ...operation, ...finalPaymasterFields(final) };
4068
4486
  }
4069
4487
  const userOpHash = operationHash(operation);
4070
- const wallet = (0, import_viem9.createWalletClient)({
4488
+ await assertOwnerSession(input.state, input.owner);
4489
+ const wallet = (0, import_viem10.createWalletClient)({
4071
4490
  account: input.owner,
4072
4491
  chain: import_chains3.base,
4073
- transport: (0, import_viem9.custom)(input.state.provider)
4492
+ transport: (0, import_viem10.custom)(input.state.provider)
4074
4493
  });
4075
4494
  const signature = await wallet.signMessage({ message: { raw: userOpHash } });
4076
4495
  operation = { ...operation, signature };
4077
- input.onPending?.({ userOpHash });
4496
+ await assertOwnerSession(input.state, input.owner);
4497
+ const userOperation = {
4498
+ sender: operation.sender,
4499
+ nonce: operation.nonce,
4500
+ initCode: operation.initCode,
4501
+ callData: operation.callData,
4502
+ callGasLimit: operation.callGasLimit,
4503
+ verificationGasLimit: operation.verificationGasLimit,
4504
+ preVerificationGas: operation.preVerificationGas,
4505
+ maxFeePerGas: operation.maxFeePerGas,
4506
+ maxPriorityFeePerGas: operation.maxPriorityFeePerGas,
4507
+ paymasterAndData: operation.paymasterAndData
4508
+ };
4509
+ savePending(input.onPending, { userOpHash, userOperation }, true);
4510
+ await checkpoint(input.onBeforeSubmission, { userOpHash, userOperation });
4078
4511
  const submittedHash = await rpc(
4079
4512
  input.rpcUrl,
4080
4513
  "eth_sendUserOperation",
4081
4514
  [operation, ENTRY_POINT_V06],
4082
4515
  "send"
4083
4516
  );
4084
- if (submittedHash.toLowerCase() !== userOpHash.toLowerCase())
4517
+ if (!isHash(submittedHash) || submittedHash.toLowerCase() !== userOpHash.toLowerCase())
4085
4518
  throw sponsorError(
4086
4519
  "Bundler returned a different UserOperation hash.",
4087
4520
  false,
4088
- { userOpHash, submittedHash }
4521
+ { userOpHash, submittedHash, phase: "send", failureCode: "BUNDLER_HASH_MISMATCH" }
4089
4522
  );
4090
- for (let poll = 0; poll < 40; poll++) {
4091
- const response = await rpc(input.rpcUrl, "eth_getUserOperationReceipt", [userOpHash], "observe");
4523
+ const deadline = Date.now() + OBSERVATION_WINDOW_MS;
4524
+ let lastReadError;
4525
+ for (let poll = 0; poll < 40 && Date.now() < deadline; poll++) {
4526
+ let response;
4527
+ try {
4528
+ response = await rpc(input.rpcUrl, "eth_getUserOperationReceipt", [userOpHash], "observe");
4529
+ } catch (error) {
4530
+ lastReadError = error;
4531
+ await new Promise((resolve) => setTimeout(resolve, Math.min(OBSERVATION_POLL_MS, Math.max(0, deadline - Date.now()))));
4532
+ continue;
4533
+ }
4092
4534
  const txHash = response?.receipt?.transactionHash;
4093
- if (txHash) {
4094
- input.onPending?.({ userOpHash, txHash });
4535
+ if (isHash(txHash)) {
4536
+ savePending(input.onPending, { userOpHash, txHash });
4095
4537
  if (response?.success === false)
4096
4538
  throw new OwneyError(
4097
4539
  "AGENT_TRANSACTION_REVERTED",
@@ -4101,12 +4543,17 @@ async function submitEoa(input) {
4101
4543
  );
4102
4544
  return { txHash, receipt: await waitForTransaction(input.client, txHash) };
4103
4545
  }
4104
- await new Promise((resolve) => setTimeout(resolve, 1500));
4546
+ await new Promise((resolve) => setTimeout(resolve, OBSERVATION_POLL_MS));
4105
4547
  }
4106
4548
  throw sponsorError(
4107
4549
  "Sponsored withdrawal is still pending; check status before retrying.",
4108
4550
  false,
4109
- { userOpHash }
4551
+ {
4552
+ userOpHash,
4553
+ phase: "observe",
4554
+ failureCode: "RECEIPT_UNRESOLVED",
4555
+ ...lastReadError instanceof OwneyError ? { lastObservationError: lastReadError.details } : {}
4556
+ }
4110
4557
  );
4111
4558
  }
4112
4559
  async function submitSmart(input) {
@@ -4119,21 +4566,22 @@ async function submitSmart(input) {
4119
4566
  } catch {
4120
4567
  throw sponsorError("This account cannot check paymaster support on Base.", true);
4121
4568
  }
4122
- if (!capabilities?.[(0, import_viem9.toHex)(8453)]?.paymasterService?.supported && !capabilities?.["8453"]?.paymasterService?.supported)
4569
+ const supportsPaymaster = capabilities?.[(0, import_viem10.toHex)(8453)]?.paymasterService?.supported ?? capabilities?.["8453"]?.paymasterService?.supported ?? capabilities?.["0x0"]?.paymasterService?.supported;
4570
+ if (supportsPaymaster !== true)
4123
4571
  throw sponsorError("This account cannot use a paymaster on Base.", true);
4572
+ await assertOwnerSession(input.state, input.owner);
4124
4573
  let sent;
4125
- try {
4126
- input.onPending?.({});
4127
- } catch {
4128
- throw sponsorError("Upgrade or withdrawal status cannot be saved.", true);
4129
- }
4574
+ const requestedId = (0, import_viem10.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
4575
+ savePending(input.onPending, { callsId: requestedId }, true);
4576
+ await checkpoint(input.onBeforeSubmission, { callsId: requestedId });
4130
4577
  try {
4131
4578
  sent = await input.state.provider.request({
4132
4579
  method: "wallet_sendCalls",
4133
4580
  params: [{
4134
4581
  version: "2.0.0",
4135
4582
  from: input.owner,
4136
- chainId: (0, import_viem9.toHex)(8453),
4583
+ chainId: (0, import_viem10.toHex)(8453),
4584
+ id: requestedId,
4137
4585
  atomicRequired: false,
4138
4586
  calls: [{ to: input.agentWallet, value: "0x0", data: input.callData }],
4139
4587
  capabilities: { paymasterService: { url: input.rpcUrl } }
@@ -4142,17 +4590,27 @@ async function submitSmart(input) {
4142
4590
  } catch (error) {
4143
4591
  if (error?.code === 4001) throw error;
4144
4592
  if ([4200, -32601].includes(Number(error?.code)))
4145
- throw sponsorError("This account does not support sponsored calls.", true);
4146
- throw sponsorError("Wallet submission status is unknown. Check activity before retrying.", false);
4593
+ throw sponsorError(
4594
+ "This account does not support sponsored calls.",
4595
+ true,
4596
+ { phase: "send", failureCode: "WALLET_CALLS_UNSUPPORTED" }
4597
+ );
4598
+ throw sponsorError(
4599
+ "Wallet submission status is unknown. Check activity before retrying.",
4600
+ false,
4601
+ { callsId: requestedId, phase: "send", failureCode: "WALLET_SEND_UNRESOLVED" }
4602
+ );
4147
4603
  }
4148
4604
  const callsId = typeof sent === "string" ? sent : sent?.id;
4149
- if (!callsId) throw sponsorError("Wallet returned no calls ID; submission status is unknown.", false);
4150
- try {
4151
- input.onPending?.({ callsId });
4152
- } catch {
4153
- throw sponsorError("Submitted call status could not be saved.", false, { callsId });
4154
- }
4155
- for (let poll = 0; poll < 40; poll++) {
4605
+ if (typeof callsId !== "string" || callsId.length === 0 || callsId.length > 8194)
4606
+ throw sponsorError(
4607
+ "Wallet returned no calls ID; submission status is unknown.",
4608
+ false,
4609
+ { callsId: requestedId, phase: "send", failureCode: "WALLET_SEND_UNRESOLVED" }
4610
+ );
4611
+ savePending(input.onPending, { callsId });
4612
+ const deadline = Date.now() + OBSERVATION_WINDOW_MS;
4613
+ for (let poll = 0; poll < 40 && Date.now() < deadline; poll++) {
4156
4614
  let status;
4157
4615
  try {
4158
4616
  status = await input.state.provider.request({
@@ -4160,37 +4618,44 @@ async function submitSmart(input) {
4160
4618
  params: [callsId]
4161
4619
  });
4162
4620
  } catch {
4163
- throw sponsorError("Sponsored withdrawal status is unknown.", false, { callsId });
4621
+ await new Promise((resolve) => setTimeout(resolve, Math.min(OBSERVATION_POLL_MS, Math.max(0, deadline - Date.now()))));
4622
+ continue;
4164
4623
  }
4165
- if (status?.status === 400 || status?.status === 500)
4624
+ if (status?.id !== void 0 && status.id !== callsId || status?.chainId !== void 0 && Number(status.chainId) !== 8453)
4625
+ throw sponsorError(
4626
+ "Wallet returned a different sponsored operation status.",
4627
+ false,
4628
+ { callsId, phase: "observe", failureCode: "WALLET_STATUS_MISMATCH" }
4629
+ );
4630
+ if (Number(status?.status) === 400 || Number(status?.status) === 500)
4166
4631
  throw sponsorError(
4167
4632
  "Sponsored withdrawal failed in the wallet.",
4168
4633
  false,
4169
- { callsId, executionFailed: true }
4634
+ { callsId, executionFailed: true, phase: "observe", failureCode: "WALLET_EXECUTION_FAILED" }
4170
4635
  );
4171
- const txHash = status?.receipts?.[0]?.transactionHash;
4636
+ const hashes = [...new Set((status?.receipts ?? []).map((receipt) => receipt.transactionHash).filter(isHash))];
4637
+ const txHash = hashes.length === 1 ? hashes[0] : void 0;
4172
4638
  if (txHash) {
4173
- try {
4174
- input.onPending?.({ callsId, txHash });
4175
- } catch {
4176
- throw sponsorError(
4177
- "Submitted call receipt could not be saved.",
4178
- false,
4179
- { callsId, txHash }
4639
+ savePending(input.onPending, { callsId, txHash });
4640
+ if (status?.receipts?.some((receipt) => receipt.transactionHash === txHash && /^(?:0x0+|0|failed|reverted)$/i.test(String(receipt.status))))
4641
+ throw new OwneyError(
4642
+ "AGENT_TRANSACTION_REVERTED",
4643
+ "Sponsored withdrawal execution reverted.",
4644
+ { callsId, transactionHash: txHash },
4645
+ "yieldseeker"
4180
4646
  );
4181
- }
4182
4647
  return { txHash, receipt: await waitForTransaction(input.client, txHash) };
4183
4648
  }
4184
- await new Promise((resolve) => setTimeout(resolve, 1500));
4649
+ await new Promise((resolve) => setTimeout(resolve, OBSERVATION_POLL_MS));
4185
4650
  }
4186
4651
  throw sponsorError(
4187
4652
  "Sponsored withdrawal is still pending; check status before retrying.",
4188
4653
  false,
4189
- { callsId }
4654
+ { callsId, phase: "observe", failureCode: "RECEIPT_UNRESOLVED" }
4190
4655
  );
4191
4656
  }
4192
4657
  async function submitYieldseekerSponsoredWithdrawal(input) {
4193
- const owner = (0, import_viem9.getAddress)(input.state.walletAddress);
4658
+ const owner = (0, import_viem10.getAddress)(input.state.walletAddress);
4194
4659
  const { intent, rpcUrl } = await requestIntent({
4195
4660
  apiKey: input.apiKey,
4196
4661
  routingApiBaseUrl: input.routingApiBaseUrl,
@@ -4201,13 +4666,14 @@ async function submitYieldseekerSponsoredWithdrawal(input) {
4201
4666
  agentWallet: input.agentWallet,
4202
4667
  asset: input.asset,
4203
4668
  callData: input.callData,
4204
- action: "withdraw"
4669
+ action: "withdraw",
4670
+ refreshYieldseekerSignature: input.refreshYieldseekerSignature
4205
4671
  });
4206
4672
  const args = { ...input, owner, rpcUrl };
4207
4673
  return intent.ownerPath === "eoa" ? submitEoa(args) : submitSmart(args);
4208
4674
  }
4209
4675
  async function submitYieldseekerSponsoredUpgrade(input) {
4210
- const owner = (0, import_viem9.getAddress)(input.state.walletAddress);
4676
+ const owner = (0, import_viem10.getAddress)(input.state.walletAddress);
4211
4677
  const { intent, rpcUrl } = await requestIntent({
4212
4678
  apiKey: input.apiKey,
4213
4679
  routingApiBaseUrl: input.routingApiBaseUrl,
@@ -4218,7 +4684,8 @@ async function submitYieldseekerSponsoredUpgrade(input) {
4218
4684
  agentWallet: input.agentWallet,
4219
4685
  asset: input.asset,
4220
4686
  callData: input.callData,
4221
- action: "upgrade"
4687
+ action: "upgrade",
4688
+ refreshYieldseekerSignature: input.refreshYieldseekerSignature
4222
4689
  });
4223
4690
  if (intent.ownerPath !== "smart")
4224
4691
  throw sponsorError("V1 upgrade sponsorship requires a Base Account owner.", true);
@@ -4240,6 +4707,9 @@ var YIELDSEEKER_READ_FAILURE_COOLDOWN_MS = 6e4;
4240
4707
  var YIELDSEEKER_SETTLEMENT_WINDOW_MS = 5 * 6e4;
4241
4708
  var YIELDSEEKER_ACTIVITY_CACHE_MS = 6e4;
4242
4709
  var YIELDSEEKER_MAX_WITHDRAW_SWEEPS = 3;
4710
+ var YIELDSEEKER_MAX_POSITION_VERIFICATION_MS = 2e3;
4711
+ var YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT = 64;
4712
+ var YIELDSEEKER_VAULTS_PER_RPC_BATCH = 10;
4243
4713
  function generateYieldseekerUsername() {
4244
4714
  const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
4245
4715
  return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
@@ -4250,6 +4720,13 @@ function isUsernameConflict(error) {
4250
4720
  return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
4251
4721
  }
4252
4722
  var YIELDSEEKER_AGENT_WALLET_ABI = [
4723
+ {
4724
+ type: "function",
4725
+ name: "feeTracker",
4726
+ stateMutability: "view",
4727
+ inputs: [],
4728
+ outputs: [{ name: "", type: "address" }]
4729
+ },
4253
4730
  {
4254
4731
  type: "function",
4255
4732
  name: "withdrawAssetToUser",
@@ -4272,6 +4749,13 @@ var YIELDSEEKER_AGENT_WALLET_ABI = [
4272
4749
  outputs: []
4273
4750
  }
4274
4751
  ];
4752
+ var YIELDSEEKER_FEE_TRACKER_ABI = [{
4753
+ type: "function",
4754
+ name: "getFeesOwed",
4755
+ stateMutability: "view",
4756
+ inputs: [{ name: "wallet", type: "address" }],
4757
+ outputs: [{ name: "", type: "uint256" }]
4758
+ }];
4275
4759
  function query(params) {
4276
4760
  const search = new URLSearchParams();
4277
4761
  for (const [key2, value] of Object.entries(params)) {
@@ -4351,21 +4835,26 @@ var YieldseekerAgent = class {
4351
4835
  if (useCachedV2 && cached && Date.now() - cached.at < 15 * 6e4) return { ...cached.status };
4352
4836
  const status = await readYieldseekerWalletStatus(
4353
4837
  this.getReceiptClient(),
4354
- (0, import_viem10.getAddress)(context.wallet.walletAddress),
4355
- (0, import_viem10.getAddress)(state.walletAddress),
4838
+ (0, import_viem11.getAddress)(context.wallet.walletAddress),
4839
+ (0, import_viem11.getAddress)(state.walletAddress),
4356
4840
  YIELDSEEKER_ASSET_METADATA[asset].address
4357
4841
  );
4842
+ this.rememberWalletStatus(key2, status);
4843
+ return status;
4844
+ }
4845
+ rememberWalletStatus(key2, status) {
4358
4846
  if (status.verified && status.version === "v2") {
4359
4847
  this.withdrawalVersionCache.set(key2, { at: Date.now(), status: { ...status } });
4360
4848
  if (this.withdrawalVersionCache.size > 128)
4361
4849
  this.withdrawalVersionCache.delete(this.withdrawalVersionCache.keys().next().value);
4362
4850
  } else this.withdrawalVersionCache.delete(key2);
4363
- return status;
4364
4851
  }
4365
4852
  /** Explicit owner action. This never runs as a side effect of withdrawal. */
4366
4853
  async upgradeWallet(state, chainId, asset, gasMode = "paid") {
4854
+ const generation = this.readGeneration;
4367
4855
  const before = await this.getWalletStatus(state, chainId, asset);
4368
- if (!before?.verified || !before.upgradeAvailable) {
4856
+ this.assertSession(generation, chainId);
4857
+ if (!before?.verified || before.version === "unknown") {
4369
4858
  throw new OwneyError(
4370
4859
  "WITHDRAW_FAILED",
4371
4860
  "This Yieldseeker wallet cannot be upgraded to the reviewed V2 implementation.",
@@ -4373,48 +4862,77 @@ var YieldseekerAgent = class {
4373
4862
  this.id
4374
4863
  );
4375
4864
  }
4376
- const data = (0, import_viem10.encodeFunctionData)({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
4377
- let txHash;
4378
- if (gasMode === "sponsored") {
4379
- if (before.ownerPath !== "smart")
4380
- throw new OwneyError(
4381
- "SPONSOR_REQUEST_FAILED",
4382
- "EOA owners pay Base gas for the V1 upgrade.",
4383
- { safeToFallback: true },
4384
- this.id
4385
- );
4386
- let storage5;
4387
- try {
4388
- storage5 = typeof window === "undefined" ? void 0 : window.localStorage;
4389
- } catch {
4390
- storage5 = void 0;
4391
- }
4392
- if (!storage5)
4865
+ const versionKey = `${chainId}:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}:${asset}`;
4866
+ const pendingKey2 = `owney.yieldseeker.upgrade:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}`;
4867
+ const confirmUpgrade = async (hash, receipt2) => {
4868
+ this.withdrawalVersionCache.delete(versionKey);
4869
+ const upgradeTopic = (0, import_viem11.encodeEventTopics)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded" })[0];
4870
+ const lastUpgrade = receipt2?.logs.filter((log) => log.address.toLowerCase() === before.agentWallet.toLowerCase() && log.topics[0] === upgradeTopic).at(-1);
4871
+ const expectedUpgrade = (() => {
4872
+ if (!lastUpgrade || lastUpgrade.removed) return false;
4873
+ try {
4874
+ const event = (0, import_viem11.decodeEventLog)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded", data: lastUpgrade.data, topics: lastUpgrade.topics });
4875
+ return event.args.implementation.toLowerCase() === YIELDSEEKER_V2_IMPLEMENTATION.toLowerCase();
4876
+ } catch {
4877
+ return false;
4878
+ }
4879
+ })();
4880
+ if (!receipt2 || receipt2.status !== "success" || receipt2.transactionHash.toLowerCase() !== hash.toLowerCase() || typeof receipt2.blockNumber !== "bigint" || !expectedUpgrade)
4393
4881
  throw new OwneyError(
4394
- "SPONSOR_REQUEST_FAILED",
4395
- "Upgrade status cannot be saved in this browser.",
4396
- { safeToFallback: true },
4882
+ "WITHDRAW_FAILED",
4883
+ "Yieldseeker wallet upgrade receipt could not be verified.",
4884
+ { txHash: hash, reason: "UPGRADE_RECEIPT_MISMATCH" },
4397
4885
  this.id
4398
4886
  );
4399
- const pendingKey = `owney.yieldseeker.upgrade:${state.walletAddress.toLowerCase()}:${before.agentWallet.toLowerCase()}`;
4400
- let prior = null;
4401
- let saved;
4887
+ this.assertSession(generation, chainId);
4888
+ const status = {
4889
+ ...before,
4890
+ implementation: YIELDSEEKER_V2_IMPLEMENTATION,
4891
+ version: "v2",
4892
+ upgradeAvailable: false
4893
+ };
4894
+ this.rememberWalletStatus(versionKey, status);
4895
+ return { txHash: hash, status };
4896
+ };
4897
+ let storage5;
4898
+ try {
4899
+ storage5 = typeof window === "undefined" ? void 0 : window.localStorage;
4900
+ } catch {
4901
+ storage5 = void 0;
4902
+ }
4903
+ const clearConfirmedUpgrade = () => {
4402
4904
  try {
4403
- saved = storage5.getItem(pendingKey);
4905
+ storage5?.removeItem(pendingKey2);
4404
4906
  } catch {
4405
- throw new OwneyError(
4406
- "SPONSOR_REQUEST_FAILED",
4407
- "Upgrade status cannot be read in this browser.",
4408
- { safeToFallback: true },
4409
- this.id
4410
- );
4411
4907
  }
4908
+ };
4909
+ if (!storage5 && (gasMode === "sponsored" || before.ownerPath === "smart"))
4910
+ throw new OwneyError(
4911
+ "SPONSOR_REQUEST_FAILED",
4912
+ "Upgrade status cannot be saved in this browser.",
4913
+ { safeToFallback: false },
4914
+ this.id
4915
+ );
4916
+ let prior = null;
4917
+ try {
4918
+ const saved = storage5?.getItem(pendingKey2);
4412
4919
  try {
4413
4920
  prior = JSON.parse(saved ?? "null");
4414
4921
  } catch {
4415
4922
  prior = { callsId: void 0 };
4416
4923
  }
4417
- if (prior) {
4924
+ } catch {
4925
+ if (gasMode === "paid" && before.ownerPath === "eoa") storage5 = void 0;
4926
+ else throw new OwneyError(
4927
+ "SPONSOR_REQUEST_FAILED",
4928
+ "Upgrade status cannot be read in this browser.",
4929
+ { safeToFallback: false },
4930
+ this.id
4931
+ );
4932
+ }
4933
+ if (prior) {
4934
+ let earlierHash = this.isTransactionHash(prior.txHash) ? prior.txHash : void 0;
4935
+ if (!earlierHash) {
4418
4936
  if (!prior.callsId)
4419
4937
  throw new OwneyError(
4420
4938
  "SPONSOR_REQUEST_FAILED",
@@ -4424,10 +4942,7 @@ var YieldseekerAgent = class {
4424
4942
  );
4425
4943
  let callsStatus;
4426
4944
  try {
4427
- callsStatus = await state.provider.request({
4428
- method: "wallet_getCallsStatus",
4429
- params: [prior.callsId]
4430
- });
4945
+ callsStatus = await state.provider.request({ method: "wallet_getCallsStatus", params: [prior.callsId] });
4431
4946
  } catch {
4432
4947
  throw new OwneyError(
4433
4948
  "SPONSOR_REQUEST_FAILED",
@@ -4436,8 +4951,15 @@ var YieldseekerAgent = class {
4436
4951
  this.id
4437
4952
  );
4438
4953
  }
4439
- if (callsStatus?.status === 400 || callsStatus?.status === 500) {
4440
- storage5.removeItem(pendingKey);
4954
+ if (callsStatus?.id !== void 0 && callsStatus.id !== prior.callsId || callsStatus?.chainId !== void 0 && Number(callsStatus.chainId) !== chainId)
4955
+ throw new OwneyError(
4956
+ "SPONSOR_REQUEST_FAILED",
4957
+ "The earlier upgrade returned a different operation status.",
4958
+ { safeToFallback: false, callsId: prior.callsId },
4959
+ this.id
4960
+ );
4961
+ if (Number(callsStatus?.status) === 400 || Number(callsStatus?.status) === 500) {
4962
+ storage5.removeItem(pendingKey2);
4441
4963
  throw new OwneyError(
4442
4964
  "SPONSOR_REQUEST_FAILED",
4443
4965
  "The earlier sponsored upgrade failed.",
@@ -4445,22 +4967,39 @@ var YieldseekerAgent = class {
4445
4967
  this.id
4446
4968
  );
4447
4969
  }
4448
- const earlierHash = callsStatus?.receipts?.[0]?.transactionHash;
4449
- if (earlierHash) {
4450
- await this.waitForReceipt(state, chainId, earlierHash);
4451
- const earlierStatus = await this.getWalletStatus(state, chainId, asset);
4452
- if (earlierStatus?.version === "v2" && earlierStatus.verified) {
4453
- storage5.removeItem(pendingKey);
4454
- return { txHash: earlierHash, status: earlierStatus };
4455
- }
4456
- }
4970
+ const hashes = [...new Set((callsStatus?.receipts ?? []).map((value) => value.transactionHash).filter(this.isTransactionHash))];
4971
+ earlierHash = hashes.length === 1 ? hashes[0] : void 0;
4972
+ }
4973
+ if (earlierHash) {
4974
+ const recovered = await confirmUpgrade(earlierHash, await this.waitForReceipt(state, chainId, earlierHash));
4975
+ clearConfirmedUpgrade();
4976
+ return recovered;
4977
+ }
4978
+ throw new OwneyError(
4979
+ "SPONSOR_REQUEST_FAILED",
4980
+ "An earlier upgrade is still pending.",
4981
+ { safeToFallback: false, callsId: prior.callsId },
4982
+ this.id
4983
+ );
4984
+ }
4985
+ if (!before.upgradeAvailable)
4986
+ throw new OwneyError(
4987
+ "WITHDRAW_FAILED",
4988
+ "This Yieldseeker wallet cannot be upgraded to the reviewed V2 implementation.",
4989
+ { version: before.version },
4990
+ this.id
4991
+ );
4992
+ const data = (0, import_viem11.encodeFunctionData)({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
4993
+ let txHash;
4994
+ let receipt;
4995
+ if (gasMode === "sponsored") {
4996
+ if (before.ownerPath !== "smart")
4457
4997
  throw new OwneyError(
4458
4998
  "SPONSOR_REQUEST_FAILED",
4459
- "An earlier upgrade is still pending.",
4460
- { safeToFallback: false, callsId: prior.callsId },
4999
+ "EOA owners pay Base gas for the V1 upgrade.",
5000
+ { safeToFallback: true },
4461
5001
  this.id
4462
5002
  );
4463
- }
4464
5003
  const context = await this.findAgent(state, chainId, asset);
4465
5004
  if (!context) throw new OwneyError("NOT_CONNECTED", "Yieldseeker agent is unavailable.");
4466
5005
  try {
@@ -4470,14 +5009,15 @@ var YieldseekerAgent = class {
4470
5009
  apiKey: this.owneyApiKey,
4471
5010
  routingApiBaseUrl: this.routingApiBaseUrl,
4472
5011
  yieldseekerSignature: await this.auth.getToken(state, chainId),
5012
+ refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
4473
5013
  userId: context.user.userId,
4474
5014
  agentId: context.agent.agentId,
4475
5015
  agentWallet: before.agentWallet,
4476
5016
  asset: YIELDSEEKER_ASSET_METADATA[asset].address,
4477
5017
  callData: data,
4478
5018
  onPending: (pending) => {
4479
- const current = storage5.getItem(pendingKey);
4480
- storage5.setItem(pendingKey, JSON.stringify({
5019
+ const current = storage5.getItem(pendingKey2);
5020
+ storage5.setItem(pendingKey2, JSON.stringify({
4481
5021
  startedAt: Date.now(),
4482
5022
  ...current ? JSON.parse(current) : {},
4483
5023
  ...pending
@@ -4485,31 +5025,25 @@ var YieldseekerAgent = class {
4485
5025
  }
4486
5026
  });
4487
5027
  txHash = upgraded.txHash;
5028
+ receipt = upgraded.receipt;
4488
5029
  } catch (error) {
4489
- if (error?.code === 4001 || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) storage5.removeItem(pendingKey);
5030
+ if (error?.code === 4001 || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) storage5.removeItem(pendingKey2);
4490
5031
  throw error;
4491
5032
  }
4492
5033
  } else {
4493
5034
  txHash = await this.submitTransaction(state, chainId, {
4494
- from: (0, import_viem10.getAddress)(state.walletAddress),
5035
+ from: (0, import_viem11.getAddress)(state.walletAddress),
4495
5036
  to: before.agentWallet,
4496
5037
  data,
4497
5038
  value: "0",
4498
5039
  chainId
5040
+ }, void 0, (value) => {
5041
+ receipt = value;
4499
5042
  });
4500
5043
  }
4501
- const status = await this.getWalletStatus(state, chainId, asset);
4502
- if (!status?.verified || status.version !== "v2") {
4503
- throw new OwneyError(
4504
- "WITHDRAW_FAILED",
4505
- "Yieldseeker wallet upgrade was not confirmed as V2.",
4506
- { txHash, version: status?.version ?? "missing" },
4507
- this.id
4508
- );
4509
- }
4510
- if (gasMode === "sponsored" && typeof window !== "undefined")
4511
- window.localStorage.removeItem(`owney.yieldseeker.upgrade:${state.walletAddress.toLowerCase()}:${before.agentWallet.toLowerCase()}`);
4512
- return { txHash, status };
5044
+ const confirmed = await confirmUpgrade(txHash, receipt ?? await this.waitForReceipt(state, chainId, txHash));
5045
+ if (gasMode === "sponsored") clearConfirmedUpgrade();
5046
+ return confirmed;
4513
5047
  }
4514
5048
  async disconnect() {
4515
5049
  this.withdrawalVersionCache.clear();
@@ -4582,19 +5116,37 @@ var YieldseekerAgent = class {
4582
5116
  submitted(txHash);
4583
5117
  receipt = await this.waitForReceipt(state, chainId, txHash);
4584
5118
  } else {
4585
- txHash = await this.submitTransaction(state, chainId, {
4586
- from: (0, import_viem10.getAddress)(state.walletAddress),
4587
- to: YIELDSEEKER_ASSET_METADATA[asset].address,
4588
- data: (0, import_viem10.encodeFunctionData)({
4589
- abi: import_viem10.erc20Abi,
4590
- functionName: "transfer",
4591
- args: [(0, import_viem10.getAddress)(context.wallet.walletAddress), BigInt(amount)]
4592
- }),
4593
- value: "0",
4594
- chainId
4595
- }, submitted, (value) => {
4596
- receipt = value;
4597
- });
5119
+ try {
5120
+ txHash = await this.submitTransaction(state, chainId, {
5121
+ from: (0, import_viem11.getAddress)(state.walletAddress),
5122
+ to: YIELDSEEKER_ASSET_METADATA[asset].address,
5123
+ data: (0, import_viem11.encodeFunctionData)({
5124
+ abi: import_viem11.erc20Abi,
5125
+ functionName: "transfer",
5126
+ args: [(0, import_viem11.getAddress)(context.wallet.walletAddress), BigInt(amount)]
5127
+ }),
5128
+ value: "0",
5129
+ chainId
5130
+ }, submitted, (value) => {
5131
+ receipt = value;
5132
+ });
5133
+ } catch (error) {
5134
+ if (error instanceof OwneyError && error.details?.ownerTransferNotExecuted === true && error.details.reason === "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL") {
5135
+ notifyFunding(onProgress, {
5136
+ agentId: this.id,
5137
+ chainId,
5138
+ token: asset,
5139
+ walletAddress: state.walletAddress,
5140
+ agentWallet: context.wallet.walletAddress,
5141
+ intendedAmount: amount,
5142
+ txHash: String(error.details.originalTransactionHash),
5143
+ transfer: "failed",
5144
+ registration: "not-applicable",
5145
+ delivery: "not-applicable"
5146
+ });
5147
+ }
5148
+ throw error;
5149
+ }
4598
5150
  }
4599
5151
  if (this.readGeneration === generation) {
4600
5152
  void this.refreshSnapshotAfterMovement(state, chainId, context, "deposit", movement);
@@ -4604,7 +5156,7 @@ var YieldseekerAgent = class {
4604
5156
  notifyFunding(onProgress, evidence);
4605
5157
  return { txHash, smartWallet: context.wallet.walletAddress, amount, evidence };
4606
5158
  }
4607
- async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto") {
5159
+ async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto", onBeforeSubmission) {
4608
5160
  let ownerTransferAttempted = false;
4609
5161
  let evidence = {
4610
5162
  agentId: this.id,
@@ -4622,10 +5174,10 @@ var YieldseekerAgent = class {
4622
5174
  notifyFunding(onProgress, evidence);
4623
5175
  }, () => {
4624
5176
  ownerTransferAttempted = true;
4625
- }, gasMode);
5177
+ }, gasMode, onBeforeSubmission);
4626
5178
  } catch (error) {
4627
5179
  if (error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED") this.withdrawalVersionCache.clear();
4628
- if (!ownerTransferAttempted || error?.code === 4001 || error instanceof OwneyError && (error.code === "AGENT_TRANSACTION_REVERTED" || error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)))
5180
+ if (!ownerTransferAttempted || error?.code === 4001 || error instanceof OwneyError && (error.code === "AGENT_TRANSACTION_REVERTED" || error.details?.ownerTransferNotExecuted === true || error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)))
4629
5181
  notifyFunding(onProgress, {
4630
5182
  ...evidence,
4631
5183
  ...error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true ? { userOpHash: void 0, callsId: void 0 } : {},
@@ -4635,7 +5187,7 @@ var YieldseekerAgent = class {
4635
5187
  throw error;
4636
5188
  }
4637
5189
  }
4638
- async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted, gasMode = "auto") {
5190
+ async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted, gasMode = "auto", onBeforeSubmission) {
4639
5191
  this.assertChain(chainId);
4640
5192
  this.assertAsset(asset);
4641
5193
  if (amount !== void 0 && BigInt(amount) <= 0n) {
@@ -4680,37 +5232,140 @@ var YieldseekerAgent = class {
4680
5232
  let confirmedMovement = false;
4681
5233
  let settlement = null;
4682
5234
  try {
4683
- if (amount === void 0) this.advancePortfolioVersion(this.contextKey(state, chainId, asset));
4684
- const portfolio = await this.loadPortfolioContext(
4685
- state,
4686
- chainId,
4687
- context
5235
+ const maxSettlementBasis = amount === void 0 ? this.snapshotMovement(state, chainId, context, 0n) : null;
5236
+ const contextKey = this.contextKey(state, chainId, asset);
5237
+ const priorPositions = this.withdrawalVaultHints(state, chainId, context);
5238
+ this.advancePortfolioVersion(contextKey);
5239
+ const boundedVerification = async (read2, timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
5240
+ let timer;
5241
+ try {
5242
+ return await Promise.race([read2, new Promise((resolve) => {
5243
+ timer = setTimeout(() => resolve(void 0), Math.max(0, timeoutMs));
5244
+ })]);
5245
+ } finally {
5246
+ if (timer) clearTimeout(timer);
5247
+ }
5248
+ };
5249
+ const feeRateRead = amount === void 0 ? Promise.resolve(0) : boundedVerification(
5250
+ (() => {
5251
+ try {
5252
+ return this.portfolioSnapshot(state, chainId, context, contextKey).read.then((value) => value.agentSnapshot.feeRateBps);
5253
+ } catch (error) {
5254
+ return Promise.reject(error);
5255
+ }
5256
+ })().catch(() => void 0)
4688
5257
  );
5258
+ const positionsRead = this.readWithdrawalPositions(state, chainId, context);
4689
5259
  const metadata = YIELDSEEKER_ASSET_METADATA[asset];
4690
- const readIdle = async () => {
4691
- const balance = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
4692
- address: metadata.address,
4693
- abi: import_viem10.erc20Abi,
4694
- functionName: "balanceOf",
4695
- args: [(0, import_viem10.getAddress)(context.wallet.walletAddress)]
5260
+ const agentWallet = (0, import_viem11.getAddress)(context.wallet.walletAddress);
5261
+ const readOwnerFunds = async (blockNumber) => {
5262
+ const feeTracker = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5263
+ address: agentWallet,
5264
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
5265
+ functionName: "feeTracker",
5266
+ blockNumber
4696
5267
  }), chainId, "eth_call", this.id);
4697
5268
  this.assertSession(generation, chainId, asset);
4698
- return balance;
5269
+ if (!(0, import_viem11.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
5270
+ throw this.invalidResponse("wallet fee tracker");
5271
+ const [idle2, fees] = await Promise.all([
5272
+ withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5273
+ address: metadata.address,
5274
+ abi: import_viem11.erc20Abi,
5275
+ functionName: "balanceOf",
5276
+ args: [agentWallet],
5277
+ blockNumber
5278
+ }), chainId, "eth_call", this.id),
5279
+ withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5280
+ address: (0, import_viem11.getAddress)(feeTracker),
5281
+ abi: YIELDSEEKER_FEE_TRACKER_ABI,
5282
+ functionName: "getFeesOwed",
5283
+ args: [agentWallet],
5284
+ blockNumber
5285
+ }), chainId, "eth_call", this.id)
5286
+ ]);
5287
+ this.assertSession(generation, chainId, asset);
5288
+ if (typeof idle2 !== "bigint" || idle2 < 0n || typeof fees !== "bigint" || fees < 0n)
5289
+ throw this.invalidResponse("wallet withdrawal balance");
5290
+ return { blockNumber, idle: idle2, fees, available: idle2 > fees ? idle2 - fees : 0n };
4699
5291
  };
4700
- const idle = await readIdle();
4701
- let positions = portfolio.positions;
5292
+ const [funds, feeRateBps] = await Promise.all([
5293
+ positionsRead.then((positions2) => this.resolveWithdrawalLiquidity(
5294
+ state,
5295
+ chainId,
5296
+ context,
5297
+ positions2,
5298
+ priorPositions,
5299
+ amount === void 0 ? void 0 : BigInt(amount)
5300
+ )),
5301
+ feeRateRead
5302
+ ]);
5303
+ const initialFunds = funds;
5304
+ const idle = initialFunds.idle;
5305
+ const maxLiquidity = amount === void 0 ? funds : void 0;
5306
+ let positions = [...funds.positions, ...funds.indexedPositions.filter((position2) => !funds.verifiedVaults.includes(position2.yieldOption.address.toLowerCase()))];
4702
5307
  let positionsStatus = "empty";
4703
- const validatePositions = (values) => {
4704
- const seen = /* @__PURE__ */ new Set();
4705
- for (const value of values) {
4706
- const address = value?.yieldOption?.address;
4707
- if (typeof address !== "string" || !(0, import_viem10.isAddress)(address) || seen.has(address.toLowerCase()) || typeof value.assetsRaw !== "string" || !/^\d+$/.test(value.assetsRaw) || typeof value.withdrawableAssetsRaw !== "string" || !/^\d+$/.test(value.withdrawableAssetsRaw))
4708
- throw this.invalidResponse("yield positions");
4709
- seen.add(address.toLowerCase());
4710
- }
4711
- return values;
5308
+ const validatePositions = (values) => this.validateWithdrawalPositions(values, chainId, asset);
5309
+ validatePositions(positions);
5310
+ const zeroValuePositions = /* @__PURE__ */ new WeakSet();
5311
+ const hasPositions = (values) => values.some((position2) => !zeroValuePositions.has(position2) && (BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n));
5312
+ const knownPositions = /* @__PURE__ */ new Map();
5313
+ const rememberPositions = (values) => {
5314
+ for (const position2 of values) knownPositions.set(position2.yieldOption.address.toLowerCase(), position2);
5315
+ };
5316
+ const verifyKnownPositions = async (blockNumber, values = [...knownPositions.values()], timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
5317
+ const verifiedPositions = [];
5318
+ if (timeoutMs <= 0) return { verified: false, positions: verifiedPositions };
5319
+ let active = true;
5320
+ const checks = this.readVaultLiquidity(values.map((position2) => position2.yieldOption), agentWallet, blockNumber).catch(() => []);
5321
+ const reads = values.map(async (position2, index) => {
5322
+ const address = (0, import_viem11.getAddress)(position2.yieldOption.address);
5323
+ try {
5324
+ const group = (await checks).slice(index * 3, index * 3 + 3);
5325
+ if (!active || group.length !== 3 || group.some((value) => value.status !== "success")) return null;
5326
+ this.assertSession(generation, chainId, asset);
5327
+ const [underlying, shares, withdrawable] = group.map((value) => value.result);
5328
+ 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;
5329
+ const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: withdrawable.toString() };
5330
+ if (shares > 0n && withdrawable === 0n) {
5331
+ let positiveValue = false;
5332
+ const valuations = await Promise.all(["convertToAssets", "previewRedeem"].map(async (functionName) => {
5333
+ try {
5334
+ const value = await this.getReceiptClient().readContract({
5335
+ address,
5336
+ abi: import_viem11.erc4626Abi,
5337
+ functionName,
5338
+ args: [shares],
5339
+ blockNumber
5340
+ });
5341
+ if (typeof value !== "bigint" || value < 0n) return null;
5342
+ if (value > 0n && !positiveValue) {
5343
+ positiveValue = true;
5344
+ verifiedPositions.push(verified);
5345
+ }
5346
+ return value;
5347
+ } catch {
5348
+ return null;
5349
+ }
5350
+ }));
5351
+ if (positiveValue) return verified;
5352
+ if (blockNumber === void 0 || valuations.some((value) => value !== 0n)) return null;
5353
+ zeroValuePositions.add(verified);
5354
+ }
5355
+ verifiedPositions.push(verified);
5356
+ return verified;
5357
+ } catch {
5358
+ return null;
5359
+ }
5360
+ });
5361
+ const result = await boundedVerification(Promise.all(reads), timeoutMs);
5362
+ active = false;
5363
+ this.assertSession(generation, chainId, asset);
5364
+ return {
5365
+ verified: result !== void 0 && result.every((position2) => position2 !== null),
5366
+ positions: [...verifiedPositions]
5367
+ };
4712
5368
  };
4713
- const hasPositions = (values) => values.some((position2) => BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n);
4714
5369
  const refreshPositions = async () => {
4715
5370
  this.assertSession(generation, chainId, asset);
4716
5371
  const response = await this.walletRequest(
@@ -4723,22 +5378,41 @@ var YieldseekerAgent = class {
4723
5378
  return validatePositions(response.yieldPositions);
4724
5379
  };
4725
5380
  if (amount === void 0) {
4726
- validatePositions(positions);
5381
+ rememberPositions(funds.indexedPositions);
5382
+ rememberPositions(positions);
4727
5383
  positionsStatus = hasPositions(positions) ? "remaining" : "empty";
4728
5384
  }
4729
5385
  const deployed = positions.reduce(
4730
5386
  (total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
4731
5387
  0n
4732
5388
  );
4733
- const totalAvailable = idle + deployed;
5389
+ const grossAvailable = idle + deployed;
5390
+ const totalAvailable = grossAvailable > initialFunds.fees ? grossAvailable - initialFunds.fees : 0n;
4734
5391
  const requested = amount === void 0 ? totalAvailable : BigInt(amount);
4735
5392
  if (requested === 0n) {
4736
5393
  this.invalidateFundingRead(state, chainId, asset);
4737
- if (amount === void 0 && positionsStatus === "remaining")
5394
+ const zeroPayoutCandidates = positions.filter((position2) => BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n);
5395
+ if (amount === void 0 && zeroPayoutCandidates.length) {
5396
+ const deadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
5397
+ const blockNumber = initialFunds.blockNumber ?? await boundedVerification(
5398
+ Promise.resolve().then(() => this.getReceiptClient().getBlockNumber({ cacheTime: 0 })).catch(() => void 0)
5399
+ );
5400
+ const proof = typeof blockNumber === "bigint" ? await verifyKnownPositions(blockNumber, zeroPayoutCandidates, deadline - Date.now()) : { verified: false, positions: [] };
5401
+ positions = proof.positions;
5402
+ positionsStatus = hasPositions(positions) ? "remaining" : proof.verified ? "empty" : "unavailable";
5403
+ if (positionsStatus !== "empty")
5404
+ throw new OwneyError(
5405
+ "WITHDRAW_FAILED",
5406
+ "Yieldseeker vault funds are not currently withdrawable.",
5407
+ { asset, positionsStatus, remainingPositions: positions.map((position2) => position2.yieldOption.address) },
5408
+ this.id
5409
+ );
5410
+ }
5411
+ if (grossAvailable > 0n && totalAvailable === 0n)
4738
5412
  throw new OwneyError(
4739
- "WITHDRAW_FAILED",
4740
- "Yieldseeker vault funds are not currently withdrawable.",
4741
- { asset, remainingPositions: positions.map((position2) => position2.yieldOption.address) },
5413
+ "WITHDRAW_INSUFFICIENT_BALANCE",
5414
+ "No owner-withdrawable Yieldseeker funds remain after reserved fees.",
5415
+ { asset, available: "0", reason: "YIELDSEEKER_FEES_ONLY", feesReserved: initialFunds.fees.toString() },
4742
5416
  this.id
4743
5417
  );
4744
5418
  throw new OwneyError(
@@ -4760,28 +5434,50 @@ var YieldseekerAgent = class {
4760
5434
  this.id
4761
5435
  );
4762
5436
  }
4763
- let remaining = requested > idle ? requested - idle : 0n;
5437
+ const requiredIdle = requested + initialFunds.fees;
5438
+ let remaining = requiredIdle > idle ? requiredIdle - idle : 0n;
4764
5439
  let grossUnwound = 0n;
5440
+ let latestUnwindBlock;
4765
5441
  const unwind = async (position2, assetsRaw) => {
4766
5442
  this.assertSession(generation, chainId, asset);
4767
- const response = await this.walletRequest(
4768
- state,
4769
- chainId,
4770
- this.agentPath(context, "withdraw-from-position"),
4771
- {
4772
- method: "POST",
4773
- body: { chainId, vaultAddress: position2.yieldOption.address, assetsRaw: assetsRaw.toString() }
4774
- }
4775
- );
4776
- if (!this.isTransactionHash(response?.transactionHash)) throw this.invalidResponse("position withdrawal");
4777
- await this.waitForReceipt(state, chainId, response.transactionHash);
5443
+ let unwindTransactionHash;
5444
+ try {
5445
+ const response = await this.walletRequest(
5446
+ state,
5447
+ chainId,
5448
+ this.agentPath(context, "withdraw-from-position"),
5449
+ {
5450
+ method: "POST",
5451
+ body: { chainId, vaultAddress: position2.yieldOption.address, assetsRaw: assetsRaw.toString() }
5452
+ }
5453
+ );
5454
+ if (!this.isTransactionHash(response?.transactionHash)) throw this.invalidResponse("position withdrawal");
5455
+ unwindTransactionHash = response.transactionHash;
5456
+ const unwindReceipt = await this.waitForReceipt(state, chainId, unwindTransactionHash);
5457
+ if (unwindReceipt) latestUnwindBlock = unwindReceipt.blockNumber;
5458
+ } catch (error) {
5459
+ this.invalidateFundingRead(state, chainId, asset);
5460
+ throw new OwneyError(
5461
+ error instanceof OwneyError ? error.code : "WITHDRAW_FAILED",
5462
+ "Could not confirm the Yieldseeker vault release. No owner transfer was submitted.",
5463
+ {
5464
+ ...error instanceof OwneyError ? error.details : {},
5465
+ withdrawalPhase: "unwind",
5466
+ reason: "YIELDSEEKER_UNWIND_UNRESOLVED",
5467
+ vaultAddress: position2.yieldOption.address,
5468
+ unwindTransactionHash,
5469
+ ownerTransferSubmitted: false
5470
+ },
5471
+ this.id
5472
+ );
5473
+ }
4778
5474
  confirmedMovement = true;
4779
5475
  this.assertSession(generation, chainId, asset);
4780
5476
  grossUnwound += assetsRaw;
4781
5477
  };
4782
5478
  if (amount === void 0) {
4783
5479
  for (let sweep = 0; sweep < YIELDSEEKER_MAX_WITHDRAW_SWEEPS && hasPositions(positions); sweep++) {
4784
- const before = new Map(positions.map((position2) => [position2.yieldOption.address.toLowerCase(), BigInt(position2.assetsRaw)]));
5480
+ const before = new Map(positions.map((position2) => [position2.yieldOption.address.toLowerCase(), BigInt(position2.withdrawableAssetsRaw)]));
4785
5481
  let unwound = false;
4786
5482
  for (const position2 of positions) {
4787
5483
  const available = BigInt(position2.withdrawableAssetsRaw);
@@ -4790,15 +5486,22 @@ var YieldseekerAgent = class {
4790
5486
  unwound = true;
4791
5487
  }
4792
5488
  if (!unwound) break;
4793
- try {
4794
- positions = await refreshPositions();
4795
- positionsStatus = hasPositions(positions) ? "remaining" : "empty";
4796
- } catch {
4797
- this.assertSession(generation, chainId, asset);
5489
+ const knownBeforeRefresh = new Set(knownPositions.keys());
5490
+ const [catalog, proof] = await Promise.all([
5491
+ boundedVerification(refreshPositions().catch(() => void 0)),
5492
+ verifyKnownPositions(latestUnwindBlock)
5493
+ ]);
5494
+ if (catalog) rememberPositions(catalog);
5495
+ this.assertSession(generation, chainId, asset);
5496
+ if (proof.verified) {
5497
+ positions = [...proof.positions, ...(catalog ?? []).filter((position2) => !knownBeforeRefresh.has(position2.yieldOption.address.toLowerCase()))];
5498
+ } else if (catalog) positions = catalog;
5499
+ else {
4798
5500
  positionsStatus = "unavailable";
4799
5501
  break;
4800
5502
  }
4801
- if (positions.some((position2) => BigInt(position2.assetsRaw) > 0n && before.has(position2.yieldOption.address.toLowerCase()) && BigInt(position2.assetsRaw) >= before.get(position2.yieldOption.address.toLowerCase()))) break;
5503
+ positionsStatus = hasPositions(positions) ? "remaining" : "empty";
5504
+ if (positions.some((position2) => BigInt(position2.withdrawableAssetsRaw) > 0n && before.has(position2.yieldOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) >= before.get(position2.yieldOption.address.toLowerCase()))) break;
4802
5505
  }
4803
5506
  } else {
4804
5507
  for (const position2 of positions) {
@@ -4816,8 +5519,9 @@ var YieldseekerAgent = class {
4816
5519
  remaining: remaining.toString()
4817
5520
  });
4818
5521
  }
4819
- const availableForOwner = confirmedMovement ? await readIdle() : idle;
4820
- const feeRate = portfolio.snapshot.feeRateBps;
5522
+ const ownerFunds = confirmedMovement ? await readOwnerFunds(latestUnwindBlock) : initialFunds;
5523
+ const availableForOwner = ownerFunds.available;
5524
+ const feeRate = feeRateBps ?? 0;
4821
5525
  const maxFee = confirmedMovement && Number.isInteger(feeRate) && feeRate >= 0 && feeRate <= 1e3 ? (grossUnwound * BigInt(feeRate) + 9999n) / 10000n : 0n;
4822
5526
  const shortfall = availableForOwner < requested ? requested - availableForOwner : 0n;
4823
5527
  if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee) {
@@ -4837,20 +5541,23 @@ var YieldseekerAgent = class {
4837
5541
  );
4838
5542
  }
4839
5543
  const payout = amount === void 0 || availableForOwner < requested ? availableForOwner : requested;
4840
- const plannedMovement = this.snapshotMovement(state, chainId, context, -payout);
4841
- const account = (0, import_viem10.getAddress)(state.walletAddress);
5544
+ const plannedMovement = amount === void 0 && maxSettlementBasis ? {
5545
+ ...maxSettlementBasis,
5546
+ delta: maxSettlementBasis.delta - payout,
5547
+ expectedNetDeposits: maxSettlementBasis.expectedNetDeposits - payout
5548
+ } : this.snapshotMovement(state, chainId, context, -payout);
5549
+ const account = (0, import_viem11.getAddress)(state.walletAddress);
4842
5550
  let receipt;
4843
5551
  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" };
4844
- const data = amount === void 0 ? (0, import_viem10.encodeFunctionData)({
5552
+ const data = amount === void 0 ? (0, import_viem11.encodeFunctionData)({
4845
5553
  abi: YIELDSEEKER_AGENT_WALLET_ABI,
4846
5554
  functionName: "withdrawAllAssetToUser",
4847
5555
  args: [account, metadata.address]
4848
- }) : (0, import_viem10.encodeFunctionData)({
5556
+ }) : (0, import_viem11.encodeFunctionData)({
4849
5557
  abi: YIELDSEEKER_AGENT_WALLET_ABI,
4850
5558
  functionName: "withdrawAssetToUser",
4851
5559
  args: [account, metadata.address, payout]
4852
5560
  });
4853
- onOwnerTransferAttempted?.();
4854
5561
  let txHash;
4855
5562
  if (sponsor) {
4856
5563
  const submitted = await submitYieldseekerSponsoredWithdrawal({
@@ -4859,19 +5566,25 @@ var YieldseekerAgent = class {
4859
5566
  apiKey: this.owneyApiKey,
4860
5567
  routingApiBaseUrl: this.routingApiBaseUrl,
4861
5568
  yieldseekerSignature: await this.auth.getToken(state, chainId),
5569
+ refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
4862
5570
  userId: context.user.userId,
4863
5571
  agentId: context.agent.agentId,
4864
- agentWallet: (0, import_viem10.getAddress)(context.wallet.walletAddress),
5572
+ agentWallet: (0, import_viem11.getAddress)(context.wallet.walletAddress),
4865
5573
  asset: metadata.address,
4866
5574
  callData: data,
4867
- onPending: (pending) => notifyFunding(onProgress, { ...base6, ...pending })
5575
+ onPending: (pending) => notifyFunding(onProgress, { ...base6, ...pending }),
5576
+ onBeforeSubmission: async (pending) => {
5577
+ await onBeforeSubmission?.({ ...base6, ...pending });
5578
+ onOwnerTransferAttempted?.();
5579
+ }
4868
5580
  });
4869
5581
  txHash = submitted.txHash;
4870
5582
  receipt = submitted.receipt;
4871
5583
  } else {
5584
+ onOwnerTransferAttempted?.();
4872
5585
  txHash = await this.submitTransaction(state, chainId, {
4873
5586
  from: account,
4874
- to: (0, import_viem10.getAddress)(context.wallet.walletAddress),
5587
+ to: (0, import_viem11.getAddress)(context.wallet.walletAddress),
4875
5588
  data,
4876
5589
  value: "0",
4877
5590
  chainId
@@ -4883,14 +5596,26 @@ var YieldseekerAgent = class {
4883
5596
  settlement = plannedMovement;
4884
5597
  const evidence = receipt ? inspectFundingReceipt(receipt, { ...base6, txHash }, metadata.address, "withdraw") : { ...base6, txHash, confirmedAmount: payout.toString(), transfer: "confirmed", delivery: "confirmed" };
4885
5598
  notifyFunding(onProgress, evidence);
5599
+ let completion = maxLiquidity && !maxLiquidity.complete ? "unverified" : void 0;
4886
5600
  if (amount === void 0) {
4887
- try {
4888
- positions = await refreshPositions();
4889
- positionsStatus = hasPositions(positions) ? "remaining" : "empty";
4890
- } catch {
4891
- positionsStatus = "unavailable";
4892
- }
4893
- if (positionsStatus !== "empty") {
5601
+ const verificationDeadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
5602
+ const knownAtPayout = new Set(knownPositions.keys());
5603
+ const [catalogVerified, knownProof] = await Promise.all([
5604
+ boundedVerification(refreshPositions().then((values) => {
5605
+ rememberPositions(values);
5606
+ return true;
5607
+ }).catch(() => false)),
5608
+ verifyKnownPositions(receipt?.blockNumber)
5609
+ ]);
5610
+ const newPositions = [...knownPositions].filter(([address]) => !knownAtPayout.has(address)).map(([, position2]) => position2);
5611
+ const newProof = newPositions.length ? await verifyKnownPositions(receipt?.blockNumber, newPositions, verificationDeadline - Date.now()) : { verified: true, positions: [] };
5612
+ const proof = {
5613
+ verified: catalogVerified === true && knownProof.verified && newProof.verified,
5614
+ positions: [...knownProof.positions, ...newProof.positions]
5615
+ };
5616
+ positions = proof.positions;
5617
+ positionsStatus = hasPositions(positions) ? "remaining" : proof.verified ? "empty" : "unavailable";
5618
+ if (positionsStatus === "remaining") {
4894
5619
  const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? payout.toString(), evidence };
4895
5620
  throw new OwneyError(
4896
5621
  "WITHDRAW_PARTIAL_FAILURE",
@@ -4899,7 +5624,7 @@ var YieldseekerAgent = class {
4899
5624
  reason: "YIELDSEEKER_MAX_INCOMPLETE",
4900
5625
  asset,
4901
5626
  positionsStatus,
4902
- remainingPositions: positionsStatus === "remaining" ? positions.map((position2) => ({
5627
+ remainingPositions: positionsStatus === "remaining" ? positions.filter((position2) => !zeroValuePositions.has(position2) && (BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n)).map((position2) => ({
4903
5628
  vaultAddress: position2.yieldOption.address,
4904
5629
  sharesRaw: position2.assetsRaw,
4905
5630
  withdrawableAmountRaw: position2.withdrawableAssetsRaw
@@ -4909,6 +5634,7 @@ var YieldseekerAgent = class {
4909
5634
  this.id
4910
5635
  );
4911
5636
  }
5637
+ if (!proof.verified) completion = "unverified";
4912
5638
  }
4913
5639
  if (evidence.transfer === "failed" && evidence.confirmedAmount === "0")
4914
5640
  throw new OwneyError(
@@ -4917,7 +5643,17 @@ var YieldseekerAgent = class {
4917
5643
  { asset, available: "0", txHash },
4918
5644
  this.id
4919
5645
  );
4920
- return { txHash, type: amount === void 0 ? "full" : "partial", amount: evidence.confirmedAmount ?? payout.toString(), evidence };
5646
+ const confirmedAmount = evidence.confirmedAmount && /^\d+$/.test(evidence.confirmedAmount) ? BigInt(evidence.confirmedAmount) : void 0;
5647
+ const feeIncrease = ownerFunds.fees - initialFunds.fees;
5648
+ 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;
5649
+ return {
5650
+ txHash,
5651
+ type: amount === void 0 && !completion ? "full" : "partial",
5652
+ ...completion ? { completion } : {},
5653
+ ...feeAdjustment ? { feeAdjustment } : {},
5654
+ amount: evidence.confirmedAmount ?? payout.toString(),
5655
+ evidence
5656
+ };
4921
5657
  } finally {
4922
5658
  if (confirmedMovement && this.readGeneration === generation) {
4923
5659
  void this.refreshSnapshotAfterMovement(
@@ -4930,6 +5666,236 @@ var YieldseekerAgent = class {
4930
5666
  }
4931
5667
  }
4932
5668
  }
5669
+ /** A withdrawal-only liquidity floor; display snapshots never authorize it. */
5670
+ async getWithdrawableBalance(state, chainId, asset, minimumAmount) {
5671
+ this.assertChain(chainId);
5672
+ this.assertAsset(asset);
5673
+ const generation = this.readGeneration;
5674
+ const context = await this.findAgent(state, chainId, asset);
5675
+ this.assertSession(generation, chainId, asset);
5676
+ if (!context) throw new OwneyError(
5677
+ "WITHDRAW_BALANCE_UNAVAILABLE",
5678
+ "The Yieldseeker withdrawal wallet could not be verified.",
5679
+ { asset },
5680
+ this.id
5681
+ );
5682
+ const hints = this.withdrawalVaultHints(state, chainId, context);
5683
+ const positions = await this.readWithdrawalPositions(state, chainId, context);
5684
+ const proof = await this.resolveWithdrawalLiquidity(state, chainId, context, positions, hints, minimumAmount);
5685
+ this.assertSession(generation, chainId, asset);
5686
+ if (proof.available === 0n && !proof.complete) throw new OwneyError(
5687
+ "WITHDRAW_BALANCE_UNAVAILABLE",
5688
+ "Yieldseeker withdrawal liquidity could not be verified yet.",
5689
+ { asset },
5690
+ this.id
5691
+ );
5692
+ return proof.available;
5693
+ }
5694
+ async readVaultLiquidity(options, wallet, blockNumber) {
5695
+ const owner = (0, import_viem11.getAddress)(wallet);
5696
+ 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));
5697
+ const results = await Promise.all(batches.map((batch) => this.getReceiptClient().multicall({
5698
+ blockNumber,
5699
+ allowFailure: true,
5700
+ contracts: batch.flatMap((option) => {
5701
+ const address = (0, import_viem11.getAddress)(option.address);
5702
+ return [
5703
+ { address, abi: import_viem11.erc4626Abi, functionName: "asset" },
5704
+ { address, abi: import_viem11.erc20Abi, functionName: "balanceOf", args: [owner] },
5705
+ { address, abi: import_viem11.erc4626Abi, functionName: "maxWithdraw", args: [owner] }
5706
+ ];
5707
+ })
5708
+ })));
5709
+ return results.flat();
5710
+ }
5711
+ validateWithdrawalPositions(values, chainId, asset) {
5712
+ const underlying = YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase();
5713
+ const seen = /* @__PURE__ */ new Set();
5714
+ for (const value of values) {
5715
+ const option = value?.yieldOption;
5716
+ if (!option || typeof option.address !== "string" || !(0, import_viem11.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))
5717
+ throw this.invalidResponse("yield positions");
5718
+ seen.add(option.address.toLowerCase());
5719
+ }
5720
+ return values;
5721
+ }
5722
+ withdrawalVaultHints(state, chainId, context) {
5723
+ const key2 = this.contextKey(state, chainId, context.asset);
5724
+ const positions = this.readCache.get(`positions:${key2}`)?.value?.yieldPositions ?? [];
5725
+ const hints = new Map(this.validateWithdrawalPositions(positions, chainId, context.asset).map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
5726
+ const historic = this.readCache.get(`historic:${key2}`)?.value?.position;
5727
+ 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()));
5728
+ for (const option of this.yieldOptions.get(context.asset)?.value ?? []) {
5729
+ if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !(0, import_viem11.isAddress)(option.address) || !receivedAssets.has(option.address.toLowerCase()) || hints.has(option.address.toLowerCase())) continue;
5730
+ hints.set(option.address.toLowerCase(), {
5731
+ yieldOption: option,
5732
+ assetsRaw: "0",
5733
+ assetsBase: "0",
5734
+ assetsUsd: 0,
5735
+ withdrawableAssetsRaw: "0",
5736
+ withdrawableAssetsBase: "0"
5737
+ });
5738
+ }
5739
+ return [...hints.values()];
5740
+ }
5741
+ async readWithdrawalPositions(state, chainId, context) {
5742
+ const generation = this.readGeneration;
5743
+ let timer;
5744
+ try {
5745
+ const positions = await Promise.race([
5746
+ this.loadActivityContext(state, chainId, context, {}).then((value) => value.positions).catch((error) => {
5747
+ if (!(error instanceof OwneyError) || !["AGENT_API_ERROR", "AGENT_TIMEOUT"].includes(error.code)) throw error;
5748
+ return void 0;
5749
+ }),
5750
+ new Promise((resolve) => {
5751
+ timer = setTimeout(() => resolve(void 0), YIELDSEEKER_MAX_POSITION_VERIFICATION_MS);
5752
+ })
5753
+ ]);
5754
+ this.assertSession(generation, chainId, context.asset);
5755
+ return positions === void 0 ? void 0 : this.validateWithdrawalPositions(positions, chainId, context.asset);
5756
+ } finally {
5757
+ if (timer) clearTimeout(timer);
5758
+ }
5759
+ }
5760
+ async resolveWithdrawalLiquidity(state, chainId, context, positions, previousPositions = [], requested) {
5761
+ const generation = this.readGeneration;
5762
+ const contextKey = this.contextKey(state, chainId, context.asset);
5763
+ const version = this.portfolioVersion(contextKey);
5764
+ const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
5765
+ const unavailable = () => new OwneyError(
5766
+ "WITHDRAW_BALANCE_UNAVAILABLE",
5767
+ "Yieldseeker withdrawal liquidity could not be verified yet.",
5768
+ { asset: context.asset, reason: "YIELDSEEKER_LIQUIDITY_UNVERIFIED" },
5769
+ this.id
5770
+ );
5771
+ let active = true;
5772
+ let knownProof;
5773
+ const hasUnverifiedIndexedFunds = (proof) => proof.indexedPositions.some((position2) => !proof.verifiedVaults.includes(position2.yieldOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) > 0n);
5774
+ const assertReadCurrent = () => {
5775
+ this.assertSession(generation, chainId, context.asset);
5776
+ if (!active || this.portfolioVersion(contextKey) !== version) throw unavailable();
5777
+ };
5778
+ const read2 = async () => {
5779
+ const client = this.getReceiptClient();
5780
+ const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
5781
+ const wallet = (0, import_viem11.getAddress)(context.wallet.walletAddress);
5782
+ assertReadCurrent();
5783
+ if (typeof blockNumber !== "bigint") throw unavailable();
5784
+ const indexedPositions = this.validateWithdrawalPositions(positions ?? [], chainId, context.asset);
5785
+ const candidates = /* @__PURE__ */ new Map();
5786
+ for (const position2 of this.validateWithdrawalPositions([...previousPositions], chainId, context.asset))
5787
+ candidates.set(position2.yieldOption.address.toLowerCase(), position2.yieldOption);
5788
+ for (const position2 of indexedPositions) candidates.set(position2.yieldOption.address.toLowerCase(), position2.yieldOption);
5789
+ const options = [...candidates.values()];
5790
+ if (options.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
5791
+ const [checks, idle, feeTracker] = await Promise.all([
5792
+ this.readVaultLiquidity(options, wallet, blockNumber),
5793
+ client.readContract({
5794
+ address: metadata.address,
5795
+ abi: import_viem11.erc20Abi,
5796
+ functionName: "balanceOf",
5797
+ args: [wallet],
5798
+ blockNumber
5799
+ }),
5800
+ client.readContract({
5801
+ address: wallet,
5802
+ abi: YIELDSEEKER_AGENT_WALLET_ABI,
5803
+ functionName: "feeTracker",
5804
+ blockNumber
5805
+ })
5806
+ ]);
5807
+ if (typeof idle !== "bigint" || idle < 0n || !(0, import_viem11.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker)) throw unavailable();
5808
+ assertReadCurrent();
5809
+ const fees = await client.readContract({
5810
+ address: (0, import_viem11.getAddress)(feeTracker),
5811
+ abi: YIELDSEEKER_FEE_TRACKER_ABI,
5812
+ functionName: "getFeesOwed",
5813
+ args: [wallet],
5814
+ blockNumber
5815
+ });
5816
+ if (typeof fees !== "bigint" || fees < 0n) throw unavailable();
5817
+ const verified = [];
5818
+ const verifiedVaults = [];
5819
+ let deployed = 0n;
5820
+ const collect = (options2, results) => {
5821
+ let valid2 = true;
5822
+ options2.forEach((option, index) => {
5823
+ const group = results.slice(index * 3, index * 3 + 3);
5824
+ if (group.length !== 3 || group.some((value) => value.status !== "success")) {
5825
+ valid2 = false;
5826
+ return;
5827
+ }
5828
+ const [underlying, shares, withdrawable] = group.map((value) => value.result);
5829
+ if (typeof underlying !== "string" || !(0, import_viem11.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n || shares === 0n && withdrawable > 0n) {
5830
+ valid2 = false;
5831
+ return;
5832
+ }
5833
+ verifiedVaults.push(option.address.toLowerCase());
5834
+ deployed += withdrawable;
5835
+ verified.push({
5836
+ yieldOption: option,
5837
+ assetsRaw: shares.toString(),
5838
+ assetsBase: "0",
5839
+ assetsUsd: 0,
5840
+ withdrawableAssetsRaw: withdrawable.toString(),
5841
+ withdrawableAssetsBase: withdrawable.toString()
5842
+ });
5843
+ });
5844
+ return valid2;
5845
+ };
5846
+ const allKnownVerified = collect(options, checks);
5847
+ const proof = (complete) => ({
5848
+ blockNumber,
5849
+ idle,
5850
+ fees,
5851
+ available: idle + deployed > fees ? idle + deployed - fees : 0n,
5852
+ positions: [...verified],
5853
+ verifiedVaults: [...verifiedVaults],
5854
+ indexedPositions,
5855
+ complete
5856
+ });
5857
+ knownProof = proof(false);
5858
+ const indexed = new Map(indexedPositions.map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
5859
+ const discrepancy = verified.some((position2) => {
5860
+ const row = indexed.get(position2.yieldOption.address.toLowerCase());
5861
+ return row ? BigInt(position2.withdrawableAssetsRaw) < BigInt(row.withdrawableAssetsRaw) : BigInt(position2.assetsRaw) > 0n;
5862
+ });
5863
+ const typedCoveredByIdle = requested !== void 0 && idle >= requested + fees;
5864
+ const expand = positions === void 0 || indexedPositions.length === 0 && !typedCoveredByIdle || !allKnownVerified || discrepancy || knownProof.available === 0n || requested !== void 0 && requested > knownProof.available;
5865
+ if (!expand) {
5866
+ assertReadCurrent();
5867
+ return proof(true);
5868
+ }
5869
+ const catalog = await this.loadYieldOptions(context.asset).catch(() => void 0);
5870
+ assertReadCurrent();
5871
+ const eligible = /* @__PURE__ */ new Map();
5872
+ for (const option of catalog ?? []) {
5873
+ if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() && (0, import_viem11.isAddress)(option.address))
5874
+ eligible.set(option.address.toLowerCase(), option);
5875
+ }
5876
+ const extra = [...eligible].filter(([address]) => !candidates.has(address)).map(([, option]) => option);
5877
+ if (options.length + extra.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
5878
+ const allExtraVerified = collect(extra, await this.readVaultLiquidity(extra, wallet, blockNumber));
5879
+ assertReadCurrent();
5880
+ const result = proof(eligible.size > 0 && allKnownVerified && allExtraVerified);
5881
+ if (result.available === 0n && !result.complete && !hasUnverifiedIndexedFunds(result)) throw unavailable();
5882
+ return result;
5883
+ };
5884
+ let timer;
5885
+ try {
5886
+ return await Promise.race([read2(), new Promise((_, reject) => {
5887
+ timer = setTimeout(() => reject(unavailable()), YIELDSEEKER_MAX_POSITION_VERIFICATION_MS);
5888
+ })]);
5889
+ } catch (error) {
5890
+ assertReadCurrent();
5891
+ if (knownProof && (knownProof.available > 0n || hasUnverifiedIndexedFunds(knownProof)) && (!(error instanceof OwneyError) || error.code === "WITHDRAW_BALANCE_UNAVAILABLE")) return knownProof;
5892
+ if (error instanceof OwneyError) throw error;
5893
+ throw unavailable();
5894
+ } finally {
5895
+ active = false;
5896
+ if (timer) clearTimeout(timer);
5897
+ }
5898
+ }
4933
5899
  async getBalances(state, chainId, asset) {
4934
5900
  this.assertChain(chainId);
4935
5901
  if (asset !== void 0) this.assertAsset(asset);
@@ -4964,6 +5930,23 @@ var YieldseekerAgent = class {
4964
5930
  if (!context) throw new OwneyError("NOT_CONNECTED", "Reconnect the agent before recovering funding.");
4965
5931
  return context.wallet.walletAddress;
4966
5932
  }
5933
+ async hasExistingDepositFunds(state, chainId, asset) {
5934
+ this.assertChain(chainId);
5935
+ this.assertAsset(asset);
5936
+ const generation = this.readGeneration;
5937
+ const context = await this.findAgent(state, chainId, asset);
5938
+ this.assertSession(generation, chainId, asset);
5939
+ if (!context) return void 0;
5940
+ const idle = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5941
+ address: YIELDSEEKER_ASSET_METADATA[asset].address,
5942
+ abi: import_viem11.erc20Abi,
5943
+ functionName: "balanceOf",
5944
+ args: [(0, import_viem11.getAddress)(context.wallet.walletAddress)]
5945
+ }), chainId, "eth_call", this.id);
5946
+ this.assertSession(generation, chainId, asset);
5947
+ if (typeof idle !== "bigint" || idle < 0n) throw this.invalidResponse("wallet deposit balance");
5948
+ return idle > 0n ? true : void 0;
5949
+ }
4967
5950
  async getEarnings(state, chainId) {
4968
5951
  this.assertChain(chainId);
4969
5952
  return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
@@ -4994,7 +5977,7 @@ var YieldseekerAgent = class {
4994
5977
  );
4995
5978
  const vaultAddresses = new Set(
4996
5979
  catalog.flat().filter(
4997
- (yieldOption) => yieldOption.chainId === chainId && (0, import_viem10.isAddress)(yieldOption.address)
5980
+ (yieldOption) => yieldOption.chainId === chainId && (0, import_viem11.isAddress)(yieldOption.address)
4998
5981
  ).map((yieldOption) => yieldOption.address.toLowerCase())
4999
5982
  );
5000
5983
  return mapYieldseekerHistory(contexts, {
@@ -5121,7 +6104,7 @@ var YieldseekerAgent = class {
5121
6104
  `/users/${user.userId}/agents/${agent.agentId}/wallet`
5122
6105
  ).then((walletResponse) => {
5123
6106
  this.assertSession(generation, chainId, asset);
5124
- if (!walletResponse?.agentWallet || !(0, import_viem10.isAddress)(walletResponse.agentWallet.walletAddress)) {
6107
+ if (!walletResponse?.agentWallet || !(0, import_viem11.isAddress)(walletResponse.agentWallet.walletAddress)) {
5125
6108
  throw this.invalidResponse("agent wallet");
5126
6109
  }
5127
6110
  const context = {
@@ -5151,7 +6134,7 @@ var YieldseekerAgent = class {
5151
6134
  this.users.set(key2, persisted);
5152
6135
  return persisted;
5153
6136
  }
5154
- const walletAddress = (0, import_viem10.getAddress)(state.walletAddress);
6137
+ const walletAddress = (0, import_viem11.getAddress)(state.walletAddress);
5155
6138
  const generation = this.readGeneration;
5156
6139
  let user = null;
5157
6140
  try {
@@ -5534,7 +6517,7 @@ var YieldseekerAgent = class {
5534
6517
  this.agentPath(context, "deploy"),
5535
6518
  { method: "POST", body: {} }
5536
6519
  );
5537
- if (!deployed?.agentWallet || !(0, import_viem10.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
6520
+ if (!deployed?.agentWallet || !(0, import_viem11.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
5538
6521
  throw this.invalidResponse("agent deployment", {
5539
6522
  reason: "Deploy did not return the expected Agent Wallet."
5540
6523
  });
@@ -5642,15 +6625,15 @@ var YieldseekerAgent = class {
5642
6625
  return hash2;
5643
6626
  }
5644
6627
  this.assertTransaction(transaction, state, chainId);
5645
- const account = (0, import_viem10.getAddress)(state.walletAddress);
5646
- const walletClient = (0, import_viem10.createWalletClient)({
6628
+ const account = (0, import_viem11.getAddress)(state.walletAddress);
6629
+ const walletClient = (0, import_viem11.createWalletClient)({
5647
6630
  account,
5648
6631
  chain: import_chains4.base,
5649
- transport: (0, import_viem10.custom)(state.provider)
6632
+ transport: (0, import_viem11.custom)(state.provider)
5650
6633
  });
5651
- const walletChainClient = (0, import_viem10.createPublicClient)({
6634
+ const walletChainClient = (0, import_viem11.createPublicClient)({
5652
6635
  chain: import_chains4.base,
5653
- transport: (0, import_viem10.custom)(state.provider)
6636
+ transport: (0, import_viem11.custom)(state.provider)
5654
6637
  });
5655
6638
  await ensureWalletOnChain(
5656
6639
  walletChainClient,
@@ -5660,30 +6643,84 @@ var YieldseekerAgent = class {
5660
6643
  const hash = await walletClient.sendTransaction({
5661
6644
  account,
5662
6645
  chain: import_chains4.base,
5663
- to: (0, import_viem10.getAddress)(transaction.to),
6646
+ to: (0, import_viem11.getAddress)(transaction.to),
5664
6647
  data: transaction.data,
5665
6648
  value: BigInt(transaction.value)
5666
6649
  });
5667
6650
  onSubmitted?.(hash);
6651
+ let replacement;
5668
6652
  const receipt = await withPaidRpcDiagnostics(
5669
6653
  () => this.getReceiptClient().waitForTransactionReceipt({
5670
6654
  hash,
5671
- confirmations: 1
6655
+ confirmations: 1,
6656
+ onReplaced: (value) => {
6657
+ replacement = value;
6658
+ }
5672
6659
  }),
5673
6660
  8453,
5674
6661
  "eth_getTransactionReceipt",
5675
6662
  this.id
5676
6663
  );
6664
+ const actualHash = receipt.transactionHash;
6665
+ if (!this.isTransactionHash(actualHash))
6666
+ throw new OwneyError(
6667
+ "WITHDRAW_FAILED",
6668
+ "The confirmed transaction hash could not be verified.",
6669
+ { originalTransactionHash: hash, ownerTransferNotExecuted: false },
6670
+ this.id
6671
+ );
6672
+ if (actualHash.toLowerCase() !== hash.toLowerCase()) {
6673
+ if (!replacement || replacement.transaction.hash.toLowerCase() !== actualHash.toLowerCase()) {
6674
+ try {
6675
+ const [original2, actual2] = await Promise.all([
6676
+ this.getReceiptClient().getTransaction({ hash }),
6677
+ this.getReceiptClient().getTransaction({ hash: actualHash })
6678
+ ]);
6679
+ replacement = { replacedTransaction: original2, transaction: actual2 };
6680
+ } catch {
6681
+ replacement = void 0;
6682
+ }
6683
+ }
6684
+ const original = replacement?.replacedTransaction;
6685
+ const actual = replacement?.transaction;
6686
+ const sameNonce = !!original && !!actual && original.hash.toLowerCase() === hash.toLowerCase() && actual.hash.toLowerCase() === actualHash.toLowerCase() && original.from.toLowerCase() === account.toLowerCase() && actual.from.toLowerCase() === account.toLowerCase() && original.nonce === actual.nonce && (actual.chainId === void 0 || actual.chainId === chainId) && receipt.blockNumber !== null && receipt.blockNumber !== void 0;
6687
+ if (!sameNonce)
6688
+ throw new OwneyError(
6689
+ "WITHDRAW_FAILED",
6690
+ "The replacement transaction could not be verified.",
6691
+ {
6692
+ originalTransactionHash: hash,
6693
+ replacementTransactionHash: actualHash,
6694
+ reason: "TRANSACTION_REPLACEMENT_UNRESOLVED",
6695
+ ownerTransferNotExecuted: false
6696
+ },
6697
+ this.id
6698
+ );
6699
+ const sameCall = actual.to?.toLowerCase() === transaction.to.toLowerCase() && actual.input?.toLowerCase() === transaction.data.toLowerCase() && actual.value === BigInt(transaction.value);
6700
+ if (!sameCall)
6701
+ throw new OwneyError(
6702
+ "WITHDRAW_FAILED",
6703
+ "The transaction was replaced without executing this withdrawal.",
6704
+ {
6705
+ originalTransactionHash: hash,
6706
+ replacementTransactionHash: actualHash,
6707
+ reason: "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL",
6708
+ ownerTransferNotExecuted: true
6709
+ },
6710
+ this.id
6711
+ );
6712
+ onSubmitted?.(actualHash);
6713
+ }
5677
6714
  if (receipt.status !== "success") {
5678
6715
  throw new OwneyError(
5679
6716
  "AGENT_TRANSACTION_REVERTED",
5680
- `Yieldseeker transaction reverted (${hash}).`,
5681
- { transactionHash: hash },
6717
+ `Yieldseeker transaction reverted (${actualHash}).`,
6718
+ { transactionHash: actualHash },
5682
6719
  this.id
5683
6720
  );
5684
6721
  }
5685
6722
  onReceipt?.(receipt);
5686
- return hash;
6723
+ return actualHash;
5687
6724
  }
5688
6725
  async waitForReceipt(state, chainId, transactionHash) {
5689
6726
  if (this.unwindReceiptWaiter) {
@@ -5710,12 +6747,12 @@ var YieldseekerAgent = class {
5710
6747
  return receipt;
5711
6748
  }
5712
6749
  assertTransaction(transaction, state, chainId) {
5713
- if (!transaction || typeof transaction.from !== "string" || !(0, import_viem10.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem10.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_viem10.getAddress)(transaction.from) !== (0, import_viem10.getAddress)(state.walletAddress)) {
6750
+ 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)) {
5714
6751
  throw this.invalidResponse("transaction");
5715
6752
  }
5716
6753
  }
5717
6754
  assertAgent(agent) {
5718
- if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem10.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
6755
+ if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem11.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
5719
6756
  throw this.invalidResponse("agent");
5720
6757
  }
5721
6758
  }
@@ -5869,7 +6906,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
5869
6906
  }
5870
6907
 
5871
6908
  // src/lib/helpers/withdraw-helper.ts
5872
- var import_viem11 = require("viem");
6909
+ var import_viem12 = require("viem");
5873
6910
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
5874
6911
  const target = asset.toUpperCase();
5875
6912
  return agents.map((agent) => {
@@ -5877,7 +6914,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
5877
6914
  const tokenBalance = agentBalance?.tokens.find(
5878
6915
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
5879
6916
  );
5880
- let balance = tokenBalance ? (0, import_viem11.parseUnits)(tokenBalance.amount, decimals) : 0n;
6917
+ let balance = tokenBalance ? (0, import_viem12.parseUnits)(tokenBalance.amount, decimals) : 0n;
5881
6918
  if (agent.balanceComposition === "tokens-plus-positions") {
5882
6919
  const chainNameById = {
5883
6920
  1: "ETHEREUM",
@@ -5889,14 +6926,14 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
5889
6926
  const positionChain = position2.chain.trim().toUpperCase();
5890
6927
  const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
5891
6928
  if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
5892
- balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0, import_viem11.parseUnits)(position2.amount, decimals);
6929
+ balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0, import_viem12.parseUnits)(position2.amount, decimals);
5893
6930
  }
5894
6931
  }
5895
6932
  const assetTotal = agentBalance?.assetBalances?.find(
5896
6933
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
5897
6934
  );
5898
6935
  if (assetTotal) {
5899
- const total = (0, import_viem11.parseUnits)(assetTotal.amount, decimals);
6936
+ const total = (0, import_viem12.parseUnits)(assetTotal.amount, decimals);
5900
6937
  if (total < balance) balance = total > 0n ? total : 0n;
5901
6938
  }
5902
6939
  return { agent, balance };
@@ -6055,14 +7092,14 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
6055
7092
  }
6056
7093
 
6057
7094
  // src/client.ts
6058
- var import_viem15 = require("viem");
7095
+ var import_viem16 = require("viem");
6059
7096
  var import_chains5 = require("viem/chains");
6060
7097
 
6061
7098
  // src/lib/sponsored-token-batch.ts
6062
- var import_viem13 = require("viem");
7099
+ var import_viem14 = require("viem");
6063
7100
 
6064
7101
  // src/lib/permit2-batch.ts
6065
- var import_viem12 = require("viem");
7102
+ var import_viem13 = require("viem");
6066
7103
  var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
6067
7104
  var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
6068
7105
  var ERC2612_PERMIT_TYPES = {
@@ -6074,7 +7111,7 @@ var ERC2612_PERMIT_TYPES = {
6074
7111
  { name: "deadline", type: "uint256" }
6075
7112
  ]
6076
7113
  };
6077
- var ERC2612_READ_ABI = (0, import_viem12.parseAbi)([
7114
+ var ERC2612_READ_ABI = (0, import_viem13.parseAbi)([
6078
7115
  "function name() view returns (string)",
6079
7116
  "function version() view returns (string)",
6080
7117
  "function nonces(address owner) view returns (uint256)"
@@ -6112,7 +7149,7 @@ var PERMIT_BATCH_TYPES = {
6112
7149
  { name: "amount", type: "uint256" }
6113
7150
  ]
6114
7151
  };
6115
- var PERMIT2_BATCH_ABI = (0, import_viem12.parseAbi)([
7152
+ var PERMIT2_BATCH_ABI = (0, import_viem13.parseAbi)([
6116
7153
  "struct TokenPermissions { address token; uint256 amount; }",
6117
7154
  "struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
6118
7155
  "struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
@@ -6155,6 +7192,18 @@ var planOf = (transfers) => JSON.stringify(
6155
7192
  amount: BigInt(t.amount).toString()
6156
7193
  }))
6157
7194
  );
7195
+ 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()}`;
7196
+ function pendingTokenBatch(i) {
7197
+ try {
7198
+ const saved = read(pendingKey(i));
7199
+ if (!saved) return;
7200
+ const body = JSON.parse(saved);
7201
+ 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;
7202
+ return { txHash: (0, import_viem14.keccak256)(body.serializedTransaction), transfers: body.transfers.map(({ to, amount }) => ({ to, amount })) };
7203
+ } catch {
7204
+ return;
7205
+ }
7206
+ }
6158
7207
  function read(key2) {
6159
7208
  return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
6160
7209
  }
@@ -6171,11 +7220,11 @@ function clear(key2) {
6171
7220
  else window.localStorage.removeItem(key2);
6172
7221
  }
6173
7222
  function sponsorTokenBatch(i) {
6174
- const key2 = `owney.token-batch.v1:${(0, import_viem13.keccak256)((0, import_viem13.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
7223
+ const key2 = pendingKey(i);
6175
7224
  const plan = planOf(i.transfers);
6176
7225
  const active = inflight.get(key2);
6177
7226
  if (active) {
6178
- if (active.plan !== plan)
7227
+ if (active.plan !== plan || i.resumeTxHash && pendingTokenBatch(i)?.txHash !== i.resumeTxHash)
6179
7228
  return Promise.reject(
6180
7229
  new Error(
6181
7230
  "A token deposit is already in progress. Wait for its result before depositing again."
@@ -6202,7 +7251,7 @@ async function execute(i, key2, plan) {
6202
7251
  baseUrl: i.baseUrl,
6203
7252
  body
6204
7253
  });
6205
- if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0, import_viem13.keccak256)(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
7254
+ if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0, import_viem14.keccak256)(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
6206
7255
  throw new Error(
6207
7256
  "Sponsorship API did not return a valid prepared permit transaction."
6208
7257
  );
@@ -6218,7 +7267,7 @@ async function execute(i, key2, plan) {
6218
7267
  baseUrl: i.baseUrl,
6219
7268
  body
6220
7269
  });
6221
- if (!prepared.serializedTransaction || (0, import_viem13.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
7270
+ if (!prepared.serializedTransaction || (0, import_viem14.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
6222
7271
  throw new Error(
6223
7272
  "Sponsorship API did not return a valid prepared transaction."
6224
7273
  );
@@ -6229,7 +7278,7 @@ async function execute(i, key2, plan) {
6229
7278
  save(key2, body);
6230
7279
  }
6231
7280
  try {
6232
- i.onSubmitted?.((0, import_viem13.keccak256)(body.serializedTransaction));
7281
+ i.onSubmitted?.((0, import_viem14.keccak256)(body.serializedTransaction));
6233
7282
  } catch {
6234
7283
  }
6235
7284
  const result = await postSponsorBatchTransfer({
@@ -6237,7 +7286,7 @@ async function execute(i, key2, plan) {
6237
7286
  baseUrl: i.baseUrl,
6238
7287
  body
6239
7288
  });
6240
- if (result.txHash !== (0, import_viem13.keccak256)(body.serializedTransaction))
7289
+ if (result.txHash !== (0, import_viem14.keccak256)(body.serializedTransaction))
6241
7290
  throw new Error(
6242
7291
  "Sponsorship receipt does not match the pending transaction."
6243
7292
  );
@@ -6250,9 +7299,11 @@ async function execute(i, key2, plan) {
6250
7299
  }
6251
7300
  };
6252
7301
  const saved = read(key2);
7302
+ if (i.resumeTxHash && pendingTokenBatch(i)?.txHash !== i.resumeTxHash)
7303
+ throw new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit changed. Check its status before depositing again.");
6253
7304
  if (saved) {
6254
7305
  const previous = JSON.parse(saved);
6255
- if (previous.chainId !== i.chainId || !(0, import_viem13.isAddressEqual)(previous.from, i.owner) || !(0, import_viem13.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
7306
+ 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)
6256
7307
  throw new Error(
6257
7308
  "Retry the previous token deposit and agent split first to reconcile its status."
6258
7309
  );
@@ -6269,7 +7320,7 @@ async function execute(i, key2, plan) {
6269
7320
  "DEPOSIT_INSUFFICIENT_BALANCE",
6270
7321
  "Insufficient token balance for this deposit."
6271
7322
  );
6272
- if (allowance < total && (i.chainId !== 8453 || !(0, import_viem13.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
7323
+ if (allowance < total && (i.chainId !== 8453 || !(0, import_viem14.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
6273
7324
  throw new OwneyError(
6274
7325
  "PERMIT2_APPROVAL_REQUIRED",
6275
7326
  "token deposits need a one-time Permit2 approval."
@@ -6361,7 +7412,8 @@ function makeSponsoredTokenCallback(deps) {
6361
7412
  pub,
6362
7413
  wallet,
6363
7414
  onApproved: deps.onApproved,
6364
- onSubmitted: (hash) => deps.onSubmitted?.(hash, transfers)
7415
+ onSubmitted: (hash) => deps.onSubmitted?.(hash, transfers),
7416
+ resumeTxHash: deps.resumeTxHash
6365
7417
  });
6366
7418
  };
6367
7419
  const callback = makeVerificationAwareDepositCallback(
@@ -6447,7 +7499,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
6447
7499
  }
6448
7500
 
6449
7501
  // src/lib/sponsored-calls-deposit.ts
6450
- var import_viem14 = require("viem");
7502
+ var import_viem15 = require("viem");
6451
7503
  var DEFAULT_POLL_INTERVAL_MS = 1500;
6452
7504
  var DEFAULT_MAX_POLLS = 30;
6453
7505
  async function paymasterSupported(provider, owner, chainId) {
@@ -6455,7 +7507,7 @@ async function paymasterSupported(provider, owner, chainId) {
6455
7507
  method: "wallet_getCapabilities",
6456
7508
  params: [owner]
6457
7509
  });
6458
- const forChain = caps?.[(0, import_viem14.toHex)(chainId)] ?? caps?.[String(chainId)];
7510
+ const forChain = caps?.[(0, import_viem15.toHex)(chainId)] ?? caps?.[String(chainId)];
6459
7511
  return Boolean(forChain?.paymasterService?.supported);
6460
7512
  }
6461
7513
  function makeSponsoredCallsCallback(deps) {
@@ -6492,8 +7544,8 @@ function makeSponsoredCallsCallback(deps) {
6492
7544
  const calls = transfers.map((transfer) => ({
6493
7545
  to: token,
6494
7546
  value: "0x0",
6495
- data: (0, import_viem14.encodeFunctionData)({
6496
- abi: import_viem14.erc20Abi,
7547
+ data: (0, import_viem15.encodeFunctionData)({
7548
+ abi: import_viem15.erc20Abi,
6497
7549
  functionName: "transfer",
6498
7550
  args: [transfer.to, BigInt(transfer.amount)]
6499
7551
  })
@@ -6525,50 +7577,73 @@ function makeSponsoredCallsCallback(deps) {
6525
7577
  url.searchParams.append("owneyIntent", intent);
6526
7578
  paymasterUrl = url.toString();
6527
7579
  }
6528
- const sendResult = await deps.provider.request({
6529
- method: "wallet_sendCalls",
6530
- params: [
6531
- {
6532
- version: "2.0.0",
6533
- from: deps.ownerAddress,
6534
- chainId: (0, import_viem14.toHex)(chainId),
6535
- atomicRequired: transfers.length > 1,
6536
- calls,
6537
- capabilities: {
6538
- paymasterService: { url: paymasterUrl }
7580
+ const requestedId = (0, import_viem15.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
7581
+ const progress = (id, transfer) => {
7582
+ try {
7583
+ deps.onCallsProgress?.(id, transfers, transfer);
7584
+ } catch {
7585
+ }
7586
+ };
7587
+ progress(requestedId, "pending");
7588
+ let sendResult;
7589
+ try {
7590
+ sendResult = await deps.provider.request({
7591
+ method: "wallet_sendCalls",
7592
+ params: [
7593
+ {
7594
+ version: "2.0.0",
7595
+ id: requestedId,
7596
+ from: deps.ownerAddress,
7597
+ chainId: (0, import_viem15.toHex)(chainId),
7598
+ atomicRequired: transfers.length > 1,
7599
+ calls,
7600
+ capabilities: {
7601
+ paymasterService: { url: paymasterUrl }
7602
+ }
6539
7603
  }
6540
- }
6541
- ]
6542
- });
6543
- deps.onApproved?.();
7604
+ ]
7605
+ });
7606
+ } catch (error) {
7607
+ if (error?.code === 4001 || error?.code === "ACTION_REJECTED")
7608
+ progress(requestedId, "failed");
7609
+ throw error;
7610
+ }
7611
+ try {
7612
+ deps.onApproved?.();
7613
+ } catch {
7614
+ }
6544
7615
  const callsId = typeof sendResult === "string" ? sendResult : sendResult?.id;
6545
7616
  if (!callsId) {
6546
7617
  throw new OwneyError(
6547
7618
  "SPONSORED_CALLS_NO_ID",
6548
7619
  "Wallet accepted wallet_sendCalls but returned no calls id.",
6549
- { chainId }
7620
+ { chainId, callsId: requestedId }
6550
7621
  );
6551
7622
  }
7623
+ progress(callsId, "pending");
6552
7624
  for (let attempt = 0; attempt < maxPolls; attempt += 1) {
6553
7625
  const status = await deps.provider.request({
6554
7626
  method: "wallet_getCallsStatus",
6555
7627
  params: [callsId]
6556
7628
  });
7629
+ if (status?.id && status.id !== callsId || status?.chainId && Number(status.chainId) !== chainId)
7630
+ throw new OwneyError("SPONSORED_CALLS_NO_RECEIPT", "Wallet returned status for a different deposit batch.", { chainId, callsId });
6557
7631
  const txHash = status?.receipts?.[0]?.transactionHash;
6558
- if (txHash && !status?.receipts?.some((receipt) => receipt.transactionHash !== txHash)) {
7632
+ if (txHash && /^0x[\da-f]{64}$/i.test(txHash) && !status?.receipts?.some((receipt) => receipt.transactionHash !== txHash)) {
6559
7633
  try {
6560
7634
  deps.onSubmitted?.(txHash, transfers);
6561
7635
  } catch {
6562
7636
  }
6563
7637
  }
6564
- if (status?.receipts?.some((receipt) => receipt.status === "0x0") || typeof status?.status === "number" && status.status >= 400) {
7638
+ if (status?.status === 400 || status?.status === 500) {
7639
+ progress(callsId, "failed");
6565
7640
  throw new OwneyError(
6566
7641
  "SPONSOR_REQUEST_FAILED",
6567
7642
  "The sponsored deposit did not complete successfully.",
6568
7643
  { chainId, callsId, safeToFallback: false }
6569
7644
  );
6570
7645
  }
6571
- if (txHash && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
7646
+ if (txHash && /^0x[\da-f]{64}$/i.test(txHash) && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
6572
7647
  if (status?.receipts?.some(
6573
7648
  (receipt) => receipt.transactionHash !== txHash
6574
7649
  ))
@@ -6784,8 +7859,8 @@ var OwneySDK = class {
6784
7859
  return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
6785
7860
  }
6786
7861
  /** Builds the default USDC batch callback for the connected wallet. */
6787
- getDefaultSponsoredCallback(onApproved, onSubmitted) {
6788
- if (!onApproved && !onSubmitted && this.cachedSponsoredCallback)
7862
+ getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash) {
7863
+ if (!onApproved && !onSubmitted && !resumeTxHash && this.cachedSponsoredCallback)
6789
7864
  return this.cachedSponsoredCallback;
6790
7865
  const provider = this.requireConnectedProvider();
6791
7866
  const owner = this.state.walletAddress;
@@ -6795,28 +7870,29 @@ var OwneySDK = class {
6795
7870
  ownerAddress: owner,
6796
7871
  onApproved,
6797
7872
  onSubmitted,
7873
+ resumeTxHash,
6798
7874
  tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
6799
7875
  // Casts work around viem's chain-narrowed Client vs the generic
6800
7876
  // PublicClient/WalletClient param types — structurally identical at
6801
7877
  // runtime, but the two share a name TS treats as unrelated.
6802
7878
  getPublicClient: (cid) => this.getPaidRpcClient(cid),
6803
- getWalletChainClient: (cid) => (0, import_viem15.createPublicClient)({
7879
+ getWalletChainClient: (cid) => (0, import_viem16.createPublicClient)({
6804
7880
  chain: VIEM_CHAIN2[cid],
6805
- transport: (0, import_viem15.custom)(provider)
7881
+ transport: (0, import_viem16.custom)(provider)
6806
7882
  }),
6807
- getWalletClient: (cid) => (0, import_viem15.createWalletClient)({
7883
+ getWalletClient: (cid) => (0, import_viem16.createWalletClient)({
6808
7884
  account: owner,
6809
7885
  chain: VIEM_CHAIN2[cid],
6810
- transport: (0, import_viem15.custom)(provider)
7886
+ transport: (0, import_viem16.custom)(provider)
6811
7887
  })
6812
7888
  });
6813
- if (!onApproved && !onSubmitted) this.cachedSponsoredCallback = callback;
7889
+ if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedSponsoredCallback = callback;
6814
7890
  return callback;
6815
7891
  }
6816
7892
  /** Builds the wallet-native sponsored calls callback for compatible paymasters. */
6817
- getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted) {
7893
+ getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress) {
6818
7894
  const cached = this.cachedSponsoredCallsCallbacks.get(asset);
6819
- if (!onApproved && !onSubmitted && cached) return cached;
7895
+ if (!onApproved && !onSubmitted && !onCallsProgress && cached) return cached;
6820
7896
  const provider = this.requireConnectedProvider();
6821
7897
  const callback = makeSponsoredCallsCallback({
6822
7898
  apiKey: this.apiKey,
@@ -6826,9 +7902,10 @@ var OwneySDK = class {
6826
7902
  paymasterServiceUrl: this.paymasterServiceUrl,
6827
7903
  onApproved,
6828
7904
  onSubmitted,
7905
+ onCallsProgress,
6829
7906
  tokenAddressByChain: sponsoredTokensFor(asset)
6830
7907
  });
6831
- if (!onApproved && !onSubmitted) this.cachedSponsoredCallsCallbacks.set(asset, callback);
7908
+ if (!onApproved && !onSubmitted && !onCallsProgress) this.cachedSponsoredCallsCallbacks.set(asset, callback);
6832
7909
  return callback;
6833
7910
  }
6834
7911
  /**
@@ -6837,8 +7914,8 @@ var OwneySDK = class {
6837
7914
  * Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
6838
7915
  * single-use batch authorization instead of an EIP-3009 authorization.
6839
7916
  */
6840
- getDefaultWethSponsoredCallback(onApproved, onSubmitted) {
6841
- if (!onApproved && !onSubmitted && this.cachedWethSponsoredCallback)
7917
+ getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash) {
7918
+ if (!onApproved && !onSubmitted && !resumeTxHash && this.cachedWethSponsoredCallback)
6842
7919
  return this.cachedWethSponsoredCallback;
6843
7920
  const provider = this.requireConnectedProvider();
6844
7921
  const owner = this.state.walletAddress;
@@ -6848,22 +7925,23 @@ var OwneySDK = class {
6848
7925
  ownerAddress: owner,
6849
7926
  onApproved,
6850
7927
  onSubmitted,
7928
+ resumeTxHash,
6851
7929
  tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
6852
7930
  // Casts work around viem's chain-narrowed Client vs the generic
6853
7931
  // PublicClient/WalletClient param types — structurally identical at
6854
7932
  // runtime, but the two share a name TS treats as unrelated.
6855
7933
  getPublicClient: (cid) => this.getPaidRpcClient(cid),
6856
- getWalletChainClient: (cid) => (0, import_viem15.createPublicClient)({
7934
+ getWalletChainClient: (cid) => (0, import_viem16.createPublicClient)({
6857
7935
  chain: VIEM_CHAIN2[cid],
6858
- transport: (0, import_viem15.custom)(provider)
7936
+ transport: (0, import_viem16.custom)(provider)
6859
7937
  }),
6860
- getWalletClient: (cid) => (0, import_viem15.createWalletClient)({
7938
+ getWalletClient: (cid) => (0, import_viem16.createWalletClient)({
6861
7939
  account: owner,
6862
7940
  chain: VIEM_CHAIN2[cid],
6863
- transport: (0, import_viem15.custom)(provider)
7941
+ transport: (0, import_viem16.custom)(provider)
6864
7942
  })
6865
7943
  });
6866
- if (!onApproved && !onSubmitted) this.cachedWethSponsoredCallback = callback;
7944
+ if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedWethSponsoredCallback = callback;
6867
7945
  return callback;
6868
7946
  }
6869
7947
  getAgent(agentId) {
@@ -7172,6 +8250,7 @@ var OwneySDK = class {
7172
8250
  async deposit(options) {
7173
8251
  const { amount, asset, depositCallback, agentId, onApproved } = options;
7174
8252
  const prepared = /* @__PURE__ */ new Map();
8253
+ const paidFallbackAgents = /* @__PURE__ */ new Set();
7175
8254
  const onProgress = options.onProgress ? (evidence) => {
7176
8255
  if (evidence.agentWallet) prepared.set(evidence.agentWallet.toLowerCase(), evidence);
7177
8256
  notifyFunding(options.onProgress, evidence);
@@ -7182,152 +8261,184 @@ var OwneySDK = class {
7182
8261
  if (evidence) notifyFunding(onProgress, { ...evidence, txHash: hash, transfer: "pending" });
7183
8262
  }
7184
8263
  } : void 0;
8264
+ const onCallsProgress = onProgress ? (callsId, destinations, transfer) => {
8265
+ for (const destination of destinations) {
8266
+ const evidence = prepared.get(destination.to.toLowerCase());
8267
+ if (evidence) notifyFunding(onProgress, { ...evidence, callsId, transfer });
8268
+ }
8269
+ } : void 0;
7185
8270
  if (BigInt(amount) === 0n) {
7186
8271
  return { agentResults: {} };
7187
8272
  }
7188
8273
  const state = this.requireState();
7189
8274
  const chainId = this.requireChainId();
7190
- const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved, onSubmitted) : this.getDefaultSponsoredCallback(onApproved, onSubmitted));
7191
- if (agentId) {
7192
- if (this.isAgentDisabled(agentId)) {
7193
- throw new OwneyError(
7194
- "AGENT_DISABLED",
7195
- `Cannot deposit into disabled agent "${agentId}". Existing balances remain visible and withdrawable.`,
7196
- { agentId }
7197
- );
7198
- }
7199
- const agent = this.getAgent(agentId);
7200
- this.validateAssetSupport(agent, chainId, asset);
7201
- await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
7202
- return this.depositWithFallback(
7203
- agent,
7204
- state,
7205
- chainId,
7206
- amount,
7207
- asset,
7208
- effectiveCallback,
7209
- depositCallback,
7210
- onProgress
7211
- );
7212
- }
7213
- const totalAmount = BigInt(amount);
7214
- const eligibleAgents = this.getEligibleAgents(chainId, asset, {
7215
- excludeDisabled: true
7216
- });
7217
- const exemptFlags = await Promise.all(
7218
- eligibleAgents.map(
7219
- (a) => this.hasExistingBalance(a, state, chainId, asset, true)
7220
- )
7221
- );
7222
- const exempt = new Set(
7223
- eligibleAgents.filter((_, i) => exemptFlags[i]).map((a) => a.id)
7224
- );
7225
- const agentAmounts = this.splitDepositAmount(
7226
- totalAmount,
7227
- eligibleAgents,
7228
- chainId,
7229
- asset,
7230
- exempt
7231
- );
7232
- if (agentAmounts.length === 0) {
7233
- const agentsRequiringActivation = eligibleAgents.filter(
7234
- (agent) => !exempt.has(agent.id)
7235
- );
7236
- const perAgentMinimum = agentsRequiringActivation.reduce(
7237
- (highest, agent) => {
7238
- const minimum = this.getMinDepositAmount(agent, chainId, asset);
7239
- return minimum > highest ? minimum : highest;
7240
- },
7241
- 0n
7242
- );
7243
- const minimumRequired = agentsRequiringActivation.length > 0 ? (perAgentMinimum > 0n ? perAgentMinimum : 1n) * BigInt(agentsRequiringActivation.length) : BigInt(eligibleAgents.length);
7244
- throw new OwneyError(
7245
- "DEPOSIT_AMOUNT_BELOW_MINIMUM",
7246
- `Amount "${amount}" cannot activate every eligible agent. The combined minimum is "${minimumRequired.toString()}" for ${asset}.`,
7247
- {
7248
- amount,
7249
- asset,
7250
- chainId,
7251
- minDepositAmount: minimumRequired.toString(),
7252
- perAgentDepositAmount: perAgentMinimum.toString(),
7253
- agentMinimums: agentsRequiringActivation.map((agent) => ({
7254
- agentId: agent.id,
7255
- minDepositAmount: this.getMinDepositAmount(
7256
- agent,
7257
- chainId,
7258
- asset
7259
- ).toString()
7260
- }))
7261
- }
7262
- );
7263
- }
7264
- const batchTransfer = getDepositBatchTransfer(effectiveCallback);
7265
- if (!depositCallback && batchTransfer) {
7266
- return runAgentDepositBatch(
7267
- chainId,
7268
- agentAmounts.map(({ agent, amount: amount2 }) => ({
7269
- id: agent.id,
7270
- amount: amount2,
7271
- run: (callback) => withFailureReporting(
7272
- this.apiKey,
7273
- agent.id,
7274
- () => onProgress ? agent.deposit(state, chainId, amount2, asset, callback, onProgress) : agent.deposit(state, chainId, amount2, asset, callback),
7275
- this.routingApiBaseUrl
7276
- )
7277
- })),
7278
- async (cid, transfers) => {
7279
- try {
7280
- return await batchTransfer(cid, transfers);
7281
- } catch (error) {
7282
- if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
7283
- throw error;
7284
- const requiredAmount = transfers.reduce(
7285
- (sum, transfer) => sum + BigInt(transfer.amount),
7286
- 0n
7287
- );
7288
- await this.approvePermit2(
7289
- asset,
7290
- requiredAmount,
7291
- cid
7292
- );
7293
- return batchTransfer(cid, transfers);
7294
- }
8275
+ if (options.resumeTxHash && (depositCallback || this.paymasterServiceUrl || !/^0x[\da-f]{64}$/i.test(options.resumeTxHash)))
8276
+ throw new OwneyError("DEPOSIT_IN_PROGRESS", "This deposit cannot resume the saved relayer transaction.");
8277
+ const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash) : this.getDefaultSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash));
8278
+ try {
8279
+ if (agentId) {
8280
+ if (this.isAgentDisabled(agentId)) {
8281
+ throw new OwneyError(
8282
+ "AGENT_DISABLED",
8283
+ `Cannot deposit into disabled agent "${agentId}". Existing balances remain visible and withdrawable.`,
8284
+ { agentId }
8285
+ );
7295
8286
  }
7296
- );
7297
- }
7298
- const agentResults = {};
7299
- for (const [
7300
- index,
7301
- { agent, amount: agentAmount }
7302
- ] of agentAmounts.entries()) {
7303
- try {
7304
- agentResults[agent.id] = await this.depositWithFallback(
8287
+ const agent = this.getAgent(agentId);
8288
+ this.validateAssetSupport(agent, chainId, asset);
8289
+ await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
8290
+ return await this.depositWithFallback(
7305
8291
  agent,
7306
8292
  state,
7307
8293
  chainId,
7308
- agentAmount,
8294
+ amount,
7309
8295
  asset,
7310
8296
  effectiveCallback,
7311
8297
  depositCallback,
7312
- onProgress
8298
+ onProgress,
8299
+ () => paidFallbackAgents.add(agent.id),
8300
+ !!options.resumeTxHash
7313
8301
  );
7314
- } catch (error) {
7315
- if (Object.keys(agentResults).length === 0) throw error;
8302
+ }
8303
+ const totalAmount = BigInt(amount);
8304
+ const eligibleAgents = this.getEligibleAgents(chainId, asset, {
8305
+ excludeDisabled: true
8306
+ });
8307
+ const prospectiveShare = totalAmount / BigInt(eligibleAgents.length);
8308
+ const meetsEveryMinimum = eligibleAgents.every((agent) => prospectiveShare >= this.getMinDepositAmount(agent, chainId, asset));
8309
+ const exemptFlags = meetsEveryMinimum ? eligibleAgents.map(() => false) : await Promise.all(eligibleAgents.map((agent) => this.hasExistingBalance(agent, state, chainId, asset, true)));
8310
+ const exempt = new Set(
8311
+ eligibleAgents.filter((_, i) => exemptFlags[i]).map((a) => a.id)
8312
+ );
8313
+ const agentAmounts = this.splitDepositAmount(
8314
+ totalAmount,
8315
+ eligibleAgents,
8316
+ chainId,
8317
+ asset,
8318
+ exempt
8319
+ );
8320
+ if (agentAmounts.length === 0) {
8321
+ const agentsRequiringActivation = eligibleAgents.filter(
8322
+ (agent) => !exempt.has(agent.id)
8323
+ );
8324
+ const perAgentMinimum = agentsRequiringActivation.reduce(
8325
+ (highest, agent) => {
8326
+ const minimum = this.getMinDepositAmount(agent, chainId, asset);
8327
+ return minimum > highest ? minimum : highest;
8328
+ },
8329
+ 0n
8330
+ );
8331
+ const minimumRequired = agentsRequiringActivation.length > 0 ? (perAgentMinimum > 0n ? perAgentMinimum : 1n) * BigInt(agentsRequiringActivation.length) : BigInt(eligibleAgents.length);
7316
8332
  throw new OwneyError(
7317
- "DEPOSIT_PARTIAL_FAILURE",
7318
- "Some deposits completed before another agent failed. Check activity and balances before depositing again; the failed transfer may still settle.",
8333
+ "DEPOSIT_AMOUNT_BELOW_MINIMUM",
8334
+ `Amount "${amount}" cannot activate every eligible agent. The combined minimum is "${minimumRequired.toString()}" for ${asset}.`,
7319
8335
  {
7320
- agentResults,
7321
- failedAgentId: agent.id,
7322
- failedAmount: agentAmount,
7323
- unattemptedAgentIds: agentAmounts.slice(index + 1).map(({ agent: agent2 }) => agent2.id),
7324
- cause: error
7325
- },
7326
- agent.id
8336
+ amount,
8337
+ asset,
8338
+ chainId,
8339
+ minDepositAmount: minimumRequired.toString(),
8340
+ perAgentDepositAmount: perAgentMinimum.toString(),
8341
+ agentMinimums: agentsRequiringActivation.map((agent) => ({
8342
+ agentId: agent.id,
8343
+ minDepositAmount: this.getMinDepositAmount(
8344
+ agent,
8345
+ chainId,
8346
+ asset
8347
+ ).toString()
8348
+ }))
8349
+ }
7327
8350
  );
7328
8351
  }
8352
+ const batchTransfer = getDepositBatchTransfer(effectiveCallback);
8353
+ if (!depositCallback && batchTransfer) {
8354
+ return await runAgentDepositBatch(
8355
+ chainId,
8356
+ agentAmounts.map(({ agent, amount: amount2 }) => ({
8357
+ id: agent.id,
8358
+ amount: amount2,
8359
+ run: (callback) => withFailureReporting(
8360
+ this.apiKey,
8361
+ agent.id,
8362
+ () => onProgress ? agent.deposit(state, chainId, amount2, asset, callback, onProgress) : agent.deposit(state, chainId, amount2, asset, callback),
8363
+ this.routingApiBaseUrl
8364
+ )
8365
+ })),
8366
+ async (cid, transfers) => {
8367
+ try {
8368
+ return await batchTransfer(cid, transfers);
8369
+ } catch (error) {
8370
+ if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
8371
+ throw error;
8372
+ const requiredAmount = transfers.reduce(
8373
+ (sum, transfer) => sum + BigInt(transfer.amount),
8374
+ 0n
8375
+ );
8376
+ await this.approvePermit2(
8377
+ asset,
8378
+ requiredAmount,
8379
+ cid
8380
+ );
8381
+ return batchTransfer(cid, transfers);
8382
+ }
8383
+ }
8384
+ );
8385
+ }
8386
+ const agentResults = {};
8387
+ for (const [
8388
+ index,
8389
+ { agent, amount: agentAmount }
8390
+ ] of agentAmounts.entries()) {
8391
+ try {
8392
+ agentResults[agent.id] = await this.depositWithFallback(
8393
+ agent,
8394
+ state,
8395
+ chainId,
8396
+ agentAmount,
8397
+ asset,
8398
+ effectiveCallback,
8399
+ depositCallback,
8400
+ onProgress,
8401
+ () => paidFallbackAgents.add(agent.id),
8402
+ !!options.resumeTxHash
8403
+ );
8404
+ } catch (error) {
8405
+ if (Object.keys(agentResults).length === 0) throw error;
8406
+ throw new OwneyError(
8407
+ "DEPOSIT_PARTIAL_FAILURE",
8408
+ "Some deposits completed before another agent failed. Check activity and balances before depositing again; the failed transfer may still settle.",
8409
+ {
8410
+ agentResults,
8411
+ failedAgentId: agent.id,
8412
+ failedAmount: agentAmount,
8413
+ unattemptedAgentIds: agentAmounts.slice(index + 1).map(({ agent: agent2 }) => agent2.id),
8414
+ cause: error
8415
+ },
8416
+ agent.id
8417
+ );
8418
+ }
8419
+ }
8420
+ return { agentResults };
8421
+ } catch (error) {
8422
+ if (!depositCallback && onProgress && !options.resumeTxHash) {
8423
+ const ids = agentId ? [agentId] : this.getEligibleAgents(chainId, asset).map((agent) => agent.id);
8424
+ for (const id of ids) {
8425
+ const evidence = [...prepared.values()].find((value) => value.agentId === id);
8426
+ if (paidFallbackAgents.has(id) || evidence?.transfer === "confirmed" || evidence?.transfer === "failed") continue;
8427
+ const rejected = error instanceof OwneyError && error.details?.notSubmitted === true;
8428
+ if (!rejected && (evidence?.txHash || evidence?.userOpHash || evidence?.callsId)) continue;
8429
+ notifyFunding(onProgress, { ...evidence ?? {
8430
+ agentId: id,
8431
+ chainId,
8432
+ token: asset,
8433
+ walletAddress: state.walletAddress,
8434
+ intendedAmount: amount,
8435
+ registration: "unknown",
8436
+ delivery: "not-applicable"
8437
+ }, transfer: "failed" });
8438
+ }
8439
+ }
8440
+ throw error;
7329
8441
  }
7330
- return { agentResults };
7331
8442
  }
7332
8443
  /**
7333
8444
  * Invokes `agent.deposit` with the resolved sponsored callback, composing
@@ -7356,7 +8467,7 @@ var OwneySDK = class {
7356
8467
  * propagates unchanged. Scoped per agent invocation so one agent's failure
7357
8468
  * doesn't force a retry of sibling agents in a multi-agent split.
7358
8469
  */
7359
- async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress) {
8470
+ async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress, onPaidFallback, resumeOnly = false) {
7360
8471
  const attempt = (callback) => withFailureReporting(
7361
8472
  this.apiKey,
7362
8473
  agent.id,
@@ -7368,6 +8479,7 @@ var OwneySDK = class {
7368
8479
  try {
7369
8480
  return await attempt(effectiveCallback);
7370
8481
  } catch (error) {
8482
+ if (resumeOnly) throw error;
7371
8483
  if (!approvalAttempted && appCallback === void 0 && (asset === "WETH" || asset === "USDC") && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
7372
8484
  approvalAttempted = true;
7373
8485
  console.warn(
@@ -7385,6 +8497,7 @@ var OwneySDK = class {
7385
8497
  `[owney-sdk] ${asset} sponsorship unavailable, falling back to user-paid deposit:`,
7386
8498
  error
7387
8499
  );
8500
+ onPaidFallback?.();
7388
8501
  return attempt(void 0);
7389
8502
  }
7390
8503
  }
@@ -7467,7 +8580,12 @@ var OwneySDK = class {
7467
8580
  */
7468
8581
  async hasExistingBalance(agent, state, chainId, asset, requireReliableRead = false) {
7469
8582
  try {
7470
- const balance = await agent.getBalances(state, chainId);
8583
+ if (agent.hasExistingDepositFunds && await agent.hasExistingDepositFunds(
8584
+ state,
8585
+ chainId,
8586
+ asset
8587
+ ).catch(() => void 0) === true) return true;
8588
+ const balance = await agent.getBalances(state, chainId, asset);
7471
8589
  const target = asset.toLowerCase();
7472
8590
  const token = balance.tokens.find(
7473
8591
  (t) => t.chainId === chainId && t.asset.toLowerCase() === target
@@ -7593,7 +8711,8 @@ var OwneySDK = class {
7593
8711
  return verified;
7594
8712
  }
7595
8713
  /** Resolve a saved sponsored-withdrawal ID without opening a wallet prompt. */
7596
- async resolveSponsoredWithdrawalEvidence(evidence) {
8714
+ async resolveSponsoredWithdrawalEvidence(evidence, options = {}) {
8715
+ if (evidence.callsId && !evidence.userOpHash) return this.resolveWalletCallsFundingEvidence(evidence, "withdraw");
7597
8716
  const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
7598
8717
  if (evidence.agentId !== "yieldseeker" || chainId !== 8453 || evidence.txHash || state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase()) return evidence;
7599
8718
  const agent = this.getAgent("yieldseeker");
@@ -7609,18 +8728,26 @@ var OwneySDK = class {
7609
8728
  owner: state.walletAddress,
7610
8729
  agentWallet: wallet,
7611
8730
  asset: SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token)?.address,
7612
- userOpHash: evidence.userOpHash
8731
+ userOpHash: evidence.userOpHash,
8732
+ ...evidence.userOperation ? { userOperation: Object.fromEntries([
8733
+ "sender",
8734
+ "nonce",
8735
+ "initCode",
8736
+ "callData",
8737
+ "callGasLimit",
8738
+ "verificationGasLimit",
8739
+ "preVerificationGas",
8740
+ "maxFeePerGas",
8741
+ "maxPriorityFeePerGas",
8742
+ "paymasterAndData"
8743
+ ].map((key2) => [key2, evidence.userOperation[key2]])) } : {},
8744
+ ...Number.isFinite(options.submittedAt) && options.submittedAt > 0 ? { submittedAt: options.submittedAt } : {}
7613
8745
  })
7614
8746
  });
7615
8747
  if (!response.ok) throw new OwneyError("AGENT_API_ERROR", "Could not resolve sponsored withdrawal status.");
7616
8748
  const payload = await response.json();
7617
8749
  txHash = payload.data?.txHash ?? void 0;
7618
8750
  executionFailed = payload.data?.executionFailed === true;
7619
- } else if (evidence.callsId) {
7620
- const status = await state.provider.request({ method: "wallet_getCallsStatus", params: [evidence.callsId] });
7621
- if (status?.id && status.id !== evidence.callsId || status?.chainId && Number(status.chainId) !== chainId) return evidence;
7622
- executionFailed = status?.status === 400 || status?.status === 500;
7623
- txHash = status?.receipts?.[0]?.transactionHash;
7624
8751
  }
7625
8752
  assertCurrent();
7626
8753
  if (executionFailed) return {
@@ -7631,6 +8758,60 @@ var OwneySDK = class {
7631
8758
  };
7632
8759
  return txHash && /^0x[0-9a-fA-F]{64}$/.test(txHash) ? { ...evidence, txHash } : evidence;
7633
8760
  }
8761
+ /** Recover an EIP-5792 deposit or payout using this account's exact batch ID. */
8762
+ async resolveWalletCallsFundingEvidence(evidence, direction) {
8763
+ const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
8764
+ if (!evidence.callsId || evidence.txHash || state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase()) return evidence;
8765
+ const agent = this.getAgent(evidence.agentId);
8766
+ const wallet = agent.getFundingWallet ? await agent.getFundingWallet(state, chainId, evidence.token) : (await agent.getUserProfile(state, chainId)).smartWallet;
8767
+ if (!wallet || wallet.toLowerCase() !== evidence.agentWallet?.toLowerCase()) return evidence;
8768
+ const status = await state.provider.request({ method: "wallet_getCallsStatus", params: [evidence.callsId] });
8769
+ assertCurrent();
8770
+ if (status?.id !== void 0 && status.id !== evidence.callsId || status?.chainId !== void 0 && Number(status.chainId) !== chainId) return evidence;
8771
+ const statusCode2 = Number(status?.status);
8772
+ if (statusCode2 === 400 || statusCode2 === 500) return {
8773
+ ...evidence,
8774
+ transfer: "failed",
8775
+ delivery: direction === "withdraw" ? "failed" : "not-applicable"
8776
+ };
8777
+ const hashes = [...new Set((status?.receipts ?? []).map((receipt) => receipt.transactionHash?.toLowerCase()).filter((hash) => !!hash && /^0x[\da-f]{64}$/i.test(hash)))];
8778
+ if (hashes.length === 1) return { ...evidence, txHash: hashes[0] };
8779
+ if (hashes.length > 16) return evidence;
8780
+ const matches = [];
8781
+ for (const txHash of hashes) {
8782
+ const inspected = await this.inspectFunding({ ...evidence, txHash }, direction);
8783
+ assertCurrent();
8784
+ if (inspected.transfer === "confirmed") matches.push(inspected);
8785
+ }
8786
+ return matches.length === 1 ? matches[0] : evidence;
8787
+ }
8788
+ /** Whether a foreground deposit can resume the same signed batch, never a
8789
+ * fresh authorization. The batch rail independently rechecks the exact plan. */
8790
+ async canResumePendingDeposit(evidences, amount) {
8791
+ if (this.paymasterServiceUrl || !evidences.length || !/^\d+$/.test(amount)) return false;
8792
+ const first = evidences[0];
8793
+ const { state, chainId, assertCurrent } = this.captureReadScope(first.chainId);
8794
+ const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === first.token);
8795
+ if (!token) return false;
8796
+ const pending = pendingTokenBatch({
8797
+ apiKey: this.apiKey,
8798
+ baseUrl: this.routingApiBaseUrl,
8799
+ owner: state.walletAddress,
8800
+ chainId,
8801
+ token: token.address
8802
+ });
8803
+ if (!pending || pending.transfers.length !== evidences.length || pending.transfers.reduce((sum, transfer) => sum + BigInt(transfer.amount), 0n).toString() !== amount) return false;
8804
+ const destinations = /* @__PURE__ */ new Set();
8805
+ for (const evidence of evidences) {
8806
+ if (evidence.chainId !== chainId || evidence.token !== first.token || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || evidence.callsId || evidence.userOpHash || evidence.txHash?.toLowerCase() !== pending.txHash.toLowerCase()) return false;
8807
+ const agent = this.getAgent(evidence.agentId);
8808
+ const wallet = agent.getFundingWallet ? await agent.getFundingWallet(state, chainId, evidence.token) : (await agent.getUserProfile(state, chainId)).smartWallet;
8809
+ if (!wallet || destinations.has(wallet.toLowerCase()) || !pending.transfers.some((transfer) => transfer.to.toLowerCase() === wallet.toLowerCase() && transfer.amount === evidence.intendedAmount)) return false;
8810
+ destinations.add(wallet.toLowerCase());
8811
+ }
8812
+ assertCurrent();
8813
+ return true;
8814
+ }
7634
8815
  /** A display acknowledgement controls polling only; it never confirms a financial outcome. */
7635
8816
  endFundingBalanceRefresh(evidence, read2) {
7636
8817
  const { state, chainId } = this.captureReadScope(evidence.chainId);
@@ -7731,6 +8912,30 @@ var OwneySDK = class {
7731
8912
  }
7732
8913
  };
7733
8914
  const token = asset;
8915
+ const agentFailures = {};
8916
+ const recordAgentFailure = (id, error) => {
8917
+ if (!(error instanceof OwneyError)) return;
8918
+ const details = error.details ?? {};
8919
+ const codeValue = (value) => typeof value === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(value) ? value : void 0;
8920
+ const revertNames = ["InsufficientBalance", "InvalidAsset", "Unauthorized", "Error", "Panic", "FailedOp", "FailedOpWithRevert"];
8921
+ const panicCodes = [0, 1, 17, 18, 33, 34, 49, 50, 65, 81];
8922
+ agentFailures[id] = {
8923
+ code: error.code,
8924
+ phase: ["prepare", "send", "observe"].includes(String(details.phase)) ? details.phase : void 0,
8925
+ failureCode: codeValue(details.failureCode),
8926
+ reason: codeValue(details.reason),
8927
+ withdrawalPhase: details.withdrawalPhase === "unwind" ? "unwind" : void 0,
8928
+ statusCode: typeof details.statusCode === "number" ? details.statusCode : void 0,
8929
+ rpcCode: typeof details.rpcCode === "number" ? details.rpcCode : void 0,
8930
+ rpcMethod: typeof details.rpcMethod === "string" && /^[A-Za-z_]{1,80}$/.test(details.rpcMethod) ? details.rpcMethod : void 0,
8931
+ revertSelector: typeof details.revertSelector === "string" && /^0x[0-9a-f]{8}$/i.test(details.revertSelector) ? details.revertSelector : void 0,
8932
+ revertName: typeof details.revertName === "string" && revertNames.includes(details.revertName) ? details.revertName : void 0,
8933
+ aaCode: typeof details.aaCode === "string" && /^AA[0-9]{2}$/.test(details.aaCode) ? details.aaCode : void 0,
8934
+ panicCode: typeof details.panicCode === "string" && /^0x[0-9a-f]{1,2}$/i.test(details.panicCode) && panicCodes.includes(Number.parseInt(details.panicCode.slice(2), 16)) ? details.panicCode : void 0,
8935
+ safeToFallback: typeof details.safeToFallback === "boolean" ? details.safeToFallback : void 0,
8936
+ executionFailed: typeof details.executionFailed === "boolean" ? details.executionFailed : void 0
8937
+ };
8938
+ };
7734
8939
  const submissionEvidence = /* @__PURE__ */ new Map();
7735
8940
  const submit = async (agent, requested2) => {
7736
8941
  attempted.add(agent.id);
@@ -7751,9 +8956,19 @@ var OwneySDK = class {
7751
8956
  };
7752
8957
  progress(evidence);
7753
8958
  try {
8959
+ if (options.onBeforeSubmission)
8960
+ return await agent.withdraw(
8961
+ state,
8962
+ chainId,
8963
+ token,
8964
+ requested2,
8965
+ progress,
8966
+ options.gasMode ?? "auto",
8967
+ (next) => options.onBeforeSubmission({ ...evidence, ...next })
8968
+ );
7754
8969
  return await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
7755
8970
  } catch (error) {
7756
- if (!evidence.txHash && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
8971
+ if (!evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
7757
8972
  progress({ ...evidence, transfer: "failed", delivery: "failed" });
7758
8973
  throw error;
7759
8974
  }
@@ -7803,6 +9018,7 @@ var OwneySDK = class {
7803
9018
  }
7804
9019
  console.error(`withdraw failed for agent "${agent.id}":`, err);
7805
9020
  agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
9021
+ recordAgentFailure(agent.id, err);
7806
9022
  const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
7807
9023
  void reportAgentFailure(
7808
9024
  this.apiKey,
@@ -7813,17 +9029,23 @@ var OwneySDK = class {
7813
9029
  }
7814
9030
  }
7815
9031
  if (Object.keys(results2).length === 0) {
9032
+ if (Object.keys(agentErrors2).length === 0)
9033
+ throw new OwneyError(
9034
+ "WITHDRAW_INSUFFICIENT_BALANCE",
9035
+ "No funds are currently available to withdraw for this asset.",
9036
+ { asset, available: "0" }
9037
+ );
7816
9038
  throw new OwneyError(
7817
9039
  "WITHDRAW_ALL_FAILED",
7818
9040
  `Failed to withdraw "${asset}" from all eligible agents.`,
7819
- { asset, agentErrors: agentErrors2 }
9041
+ { asset, agentErrors: agentErrors2, agentFailures }
7820
9042
  );
7821
9043
  }
7822
9044
  if (Object.keys(agentErrors2).length > 0) {
7823
9045
  throw new OwneyError(
7824
9046
  "WITHDRAW_PARTIAL_FAILURE",
7825
9047
  "Some agents could not complete the withdrawal.",
7826
- { asset, partialResults: results2, agentErrors: agentErrors2, ...Object.keys(incompleteWithdrawals).length ? { incompleteWithdrawals } : {} }
9048
+ { asset, partialResults: results2, agentErrors: agentErrors2, agentFailures, ...Object.keys(incompleteWithdrawals).length ? { incompleteWithdrawals } : {} }
7827
9049
  );
7828
9050
  }
7829
9051
  const amountsKnown = Object.values(results2).every(
@@ -7835,11 +9057,10 @@ var OwneySDK = class {
7835
9057
  };
7836
9058
  }
7837
9059
  const requested = BigInt(amount);
7838
- const aggregated = await this.getBalances(void 0, { chainId, asset: token });
9060
+ const { balances: aggregated, failures: balanceFailures } = await this.readAggregatedBalances({ chainId, asset: token });
7839
9061
  const unavailableAgents = eligibleAgents.filter(
7840
9062
  (agent) => !(agent.id in aggregated.agentBalances)
7841
9063
  );
7842
- const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
7843
9064
  const balances = projectAgentBalancesForAsset(
7844
9065
  eligibleAgents,
7845
9066
  aggregated.agentBalances,
@@ -7847,6 +9068,44 @@ var OwneySDK = class {
7847
9068
  asset,
7848
9069
  assetInfo.decimals
7849
9070
  );
9071
+ const indexedAvailable = balances.reduce((sum, entry) => sum + entry.balance, 0n);
9072
+ await Promise.all(balances.map(async (entry) => {
9073
+ if (entry.balance !== 0n && indexedAvailable >= requested || !entry.agent.getWithdrawableBalance) return;
9074
+ const originalFailure = balanceFailures.get(entry.agent.id);
9075
+ if (originalFailure instanceof OwneyError && ![
9076
+ "AGENT_API_ERROR",
9077
+ "AGENT_TIMEOUT",
9078
+ "AGENT_RATE_LIMITED"
9079
+ ].includes(originalFailure.code)) return;
9080
+ if (this.isUserRejectedWithdrawal(originalFailure)) throw originalFailure;
9081
+ if (entry.balance !== 0n && entry.agent.id !== "yieldseeker") {
9082
+ const total = aggregated.agentBalances[entry.agent.id]?.assetBalances?.find((value) => value.chainId === chainId && value.asset.toUpperCase() === token);
9083
+ if (!total || (0, import_viem16.parseUnits)(total.amount, assetInfo.decimals) <= entry.balance) return;
9084
+ }
9085
+ try {
9086
+ const otherAvailable = indexedAvailable - entry.balance;
9087
+ const minimumAmount = requested > otherAvailable ? requested - otherAvailable : void 0;
9088
+ const verified = await entry.agent.getWithdrawableBalance(state, chainId, token, minimumAmount);
9089
+ if (typeof verified !== "bigint" || verified < 0n)
9090
+ throw new OwneyError("WITHDRAW_BALANCE_UNAVAILABLE", "Invalid withdrawal balance proof.", {}, entry.agent.id);
9091
+ if (this.state !== state || this.state.walletAddress !== state.walletAddress || this.state.chainId !== chainId)
9092
+ throw new OwneyError("NOT_CONNECTED", "Account changed while planning withdrawal.");
9093
+ entry.balance = verified;
9094
+ const unavailableIndex = unavailableAgents.indexOf(entry.agent);
9095
+ if (unavailableIndex !== -1) unavailableAgents.splice(unavailableIndex, 1);
9096
+ if (aggregated.agentErrors) delete aggregated.agentErrors[entry.agent.id];
9097
+ } catch (error) {
9098
+ entry.balance = 0n;
9099
+ if (!unavailableAgents.includes(entry.agent)) unavailableAgents.push(entry.agent);
9100
+ aggregated.agentErrors = {
9101
+ ...aggregated.agentErrors,
9102
+ [entry.agent.id]: error instanceof Error ? error.message : String(error)
9103
+ };
9104
+ }
9105
+ }));
9106
+ if (this.state !== state || this.state.chainId !== chainId)
9107
+ throw new OwneyError("NOT_CONNECTED", "Account changed while planning withdrawal.");
9108
+ const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
7850
9109
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
7851
9110
  if (totalAvailable === 0n && unavailableAgents.length > 0) {
7852
9111
  throw new OwneyError(
@@ -7904,6 +9163,7 @@ var OwneySDK = class {
7904
9163
  if (this.isUserRejectedWithdrawal(err)) throw err;
7905
9164
  console.error(`withdraw failed for agent "${p.agent.id}":`, err);
7906
9165
  agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
9166
+ recordAgentFailure(p.agent.id, err);
7907
9167
  const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
7908
9168
  void reportAgentFailure(
7909
9169
  this.apiKey,
@@ -7924,12 +9184,24 @@ var OwneySDK = class {
7924
9184
  {
7925
9185
  asset,
7926
9186
  amount,
7927
- agentErrors
9187
+ agentErrors,
9188
+ agentFailures
7928
9189
  }
7929
9190
  );
7930
9191
  }
7931
9192
  const withdrawn = sumWithdrawnAmount(results);
7932
- if (withdrawn < requested) {
9193
+ const verifiedFeeDeductions = orderedPlans.reduce((total, plan) => {
9194
+ const result = results[plan.agent.id];
9195
+ const adjustment = result?.feeAdjustment;
9196
+ const evidence = result?.evidence;
9197
+ const submitted = submissionEvidence.get(plan.agent.id);
9198
+ 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;
9199
+ const fee = BigInt(adjustment.feeAmount);
9200
+ const net = BigInt(result.amount);
9201
+ const gross = BigInt(adjustment.grossAmount);
9202
+ return fee > 0n && net > 0n && gross === plan.planned && net + fee === gross ? total + fee : total;
9203
+ }, 0n);
9204
+ if (withdrawn + verifiedFeeDeductions < requested) {
7933
9205
  throw new OwneyError(
7934
9206
  "WITHDRAW_PARTIAL_FAILURE",
7935
9207
  `Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
@@ -7939,6 +9211,7 @@ var OwneySDK = class {
7939
9211
  withdrawn: withdrawn.toString(),
7940
9212
  partialResults: results,
7941
9213
  agentErrors,
9214
+ agentFailures,
7942
9215
  ...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
7943
9216
  }
7944
9217
  );
@@ -7952,23 +9225,42 @@ var OwneySDK = class {
7952
9225
  * @returns {AgentBalance} for a single agent, or {OwneyBalances} with totalBalance and per-agent breakdown
7953
9226
  */
7954
9227
  async getBalances(agentId, options) {
7955
- const { state, chainId, assertCurrent } = this.captureReadScope(options?.chainId);
7956
- const asset = options?.asset;
7957
- const readBalance = (agent) => this.readAgent(
7958
- agent,
7959
- "balances",
7960
- () => asset ? agent.getBalances(state, chainId, asset) : agent.getBalances(state, chainId),
7961
- asset ? { asset } : null
7962
- );
7963
9228
  if (agentId) {
9229
+ const { state, chainId, assertCurrent } = this.captureReadScope(options?.chainId);
9230
+ const asset = options?.asset;
7964
9231
  const agent = this.getAgent(agentId);
7965
- const result = await readBalance(agent);
9232
+ const result = await this.readAgent(
9233
+ agent,
9234
+ "balances",
9235
+ () => asset ? agent.getBalances(state, chainId, asset) : agent.getBalances(state, chainId),
9236
+ asset ? { asset } : null
9237
+ );
7966
9238
  assertCurrent();
7967
9239
  return {
7968
9240
  ...result,
7969
9241
  balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
7970
9242
  };
7971
9243
  }
9244
+ const { balances, failures } = await this.readAggregatedBalances(options);
9245
+ if (Object.keys(balances.agentBalances).length === 0) {
9246
+ throw new OwneyError(
9247
+ "BALANCE_ALL_FAILED",
9248
+ "Failed to fetch balances for all active agents.",
9249
+ { agentErrors: balances.agentErrors ?? {}, failures: [...failures.values()] }
9250
+ );
9251
+ }
9252
+ return balances;
9253
+ }
9254
+ /** Preserve failure objects for withdrawal recovery without changing public balance results. */
9255
+ async readAggregatedBalances(options) {
9256
+ const { state, chainId, assertCurrent } = this.captureReadScope(options?.chainId);
9257
+ const asset = options?.asset;
9258
+ const readBalance = (agent) => this.readAgent(
9259
+ agent,
9260
+ "balances",
9261
+ () => asset ? agent.getBalances(state, chainId, asset) : agent.getBalances(state, chainId),
9262
+ asset ? { asset } : null
9263
+ );
7972
9264
  let totalBalance = 0;
7973
9265
  const results = {};
7974
9266
  const entries = [...this.getActiveAgents().entries()];
@@ -7985,39 +9277,33 @@ var OwneySDK = class {
7985
9277
  })
7986
9278
  );
7987
9279
  assertCurrent();
7988
- let successCount = 0;
7989
9280
  const agentErrors = {};
7990
9281
  const agentRetryAt = {};
7991
- const agentFailures = [];
9282
+ const agentFailures = /* @__PURE__ */ new Map();
7992
9283
  for (let i = 0; i < balanceResults.length; i++) {
7993
9284
  const settledResult = balanceResults[i];
7994
- const [agentId2] = entries[i];
9285
+ const [agentId] = entries[i];
7995
9286
  if (settledResult.status === "fulfilled") {
7996
9287
  const [id, b] = settledResult.value;
7997
9288
  results[id] = b;
7998
9289
  totalBalance += Number(b.totalBalance);
7999
- successCount += 1;
8000
9290
  continue;
8001
9291
  }
8002
9292
  const reason = settledResult.reason;
8003
- agentFailures.push(reason);
9293
+ agentFailures.set(agentId, reason);
8004
9294
  const retryDelay = rateLimitDelay(reason);
8005
- if (retryDelay !== void 0) agentRetryAt[agentId2] = Date.now() + retryDelay;
8006
- agentErrors[agentId2] = reason instanceof Error ? reason.message : String(reason);
8007
- console.error(`[owney-sdk] Balance fetch failed for agent "${agentId2}":`, reason);
8008
- }
8009
- if (successCount === 0) {
8010
- throw new OwneyError(
8011
- "BALANCE_ALL_FAILED",
8012
- "Failed to fetch balances for all active agents.",
8013
- { agentErrors, failures: agentFailures }
8014
- );
9295
+ if (retryDelay !== void 0) agentRetryAt[agentId] = Date.now() + retryDelay;
9296
+ agentErrors[agentId] = reason instanceof Error ? reason.message : String(reason);
9297
+ console.error(`[owney-sdk] Balance fetch failed for agent "${agentId}":`, reason);
8015
9298
  }
8016
9299
  return {
8017
- totalBalance: String(totalBalance),
8018
- totalBalanceAsset: "usdc",
8019
- agentBalances: results,
8020
- ...Object.keys(agentErrors).length > 0 ? { agentErrors, agentRetryAt } : {}
9300
+ balances: {
9301
+ totalBalance: String(totalBalance),
9302
+ totalBalanceAsset: "usdc",
9303
+ agentBalances: results,
9304
+ ...Object.keys(agentErrors).length > 0 ? { agentErrors, agentRetryAt } : {}
9305
+ },
9306
+ failures: agentFailures
8021
9307
  };
8022
9308
  }
8023
9309
  /**
@@ -8398,16 +9684,16 @@ var OwneySDK = class {
8398
9684
  );
8399
9685
  }
8400
9686
  const provider = this.requireConnectedProvider();
8401
- const walletChainClient = (0, import_viem15.createPublicClient)({
9687
+ const walletChainClient = (0, import_viem16.createPublicClient)({
8402
9688
  chain: VIEM_CHAIN2[chainId],
8403
- transport: (0, import_viem15.custom)(provider)
9689
+ transport: (0, import_viem16.custom)(provider)
8404
9690
  });
8405
9691
  const publicClient = this.getPaidRpcClient(chainId);
8406
9692
  const approvalAmount = permit2ApprovalAmount(requiredAmount);
8407
- const wallet = (0, import_viem15.createWalletClient)({
9693
+ const wallet = (0, import_viem16.createWalletClient)({
8408
9694
  account: state.walletAddress,
8409
9695
  chain: VIEM_CHAIN2[chainId],
8410
- transport: (0, import_viem15.custom)(provider)
9696
+ transport: (0, import_viem16.custom)(provider)
8411
9697
  });
8412
9698
  await ensureWalletOnChain(walletChainClient, wallet, chainId);
8413
9699
  const hash = await wallet.writeContract({
@@ -8569,7 +9855,7 @@ var OwneySDK = class {
8569
9855
  };
8570
9856
 
8571
9857
  // src/agents/zyfai/zyfai.siwx.ts
8572
- var import_viem16 = require("viem");
9858
+ var import_viem17 = require("viem");
8573
9859
  var import_siwe2 = require("siwe");
8574
9860
  var import_sdk2 = require("@zyfai/sdk");
8575
9861
 
@@ -8696,7 +9982,7 @@ function buildSIWXConfig(deps) {
8696
9982
  issuedAt,
8697
9983
  toString() {
8698
9984
  return new import_siwe2.SiweMessage({
8699
- address: (0, import_viem16.getAddress)(accountAddress),
9985
+ address: (0, import_viem17.getAddress)(accountAddress),
8700
9986
  chainId: numericChainId(chainId),
8701
9987
  domain,
8702
9988
  uri,