@owney/sdk 0.7.25-beta.19 → 0.7.25-beta.20

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
@@ -50,6 +50,29 @@ var accountExecutionAbi = (0, import_viem.parseAbi)([
50
50
  ]);
51
51
  var entryPoint06 = "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789";
52
52
  var entryPoint07 = "0x0000000071727de22e5e9d8baf0edac6f37da032";
53
+ function yieldseekerExecution(receipt, evidence) {
54
+ const operationEvents = receipt.logs.flatMap((log) => {
55
+ if (![entryPoint06, entryPoint07].includes(log.address.toLowerCase())) return [];
56
+ try {
57
+ const event = (0, import_viem.decodeEventLog)({ abi: import_account_abstraction.entryPoint06Abi, data: log.data, topics: log.topics });
58
+ return event.eventName === "UserOperationEvent" ? [{ ...event.args, logIndex: log.logIndex }] : [];
59
+ } catch {
60
+ return [];
61
+ }
62
+ }).sort((a, b) => a.logIndex - b.logIndex);
63
+ const bundled = !!evidence.userOpHash || !!evidence.callsId || operationEvents.length > 0 || [entryPoint06, entryPoint07].includes(receipt.to?.toLowerCase() ?? "");
64
+ if (!bundled) return { status: "confirmed", logs: receipt.logs };
65
+ const senders = evidence.userOperation?.sender ? [evidence.userOperation.sender.toLowerCase()] : evidence.callsId ? [evidence.walletAddress.toLowerCase()] : [evidence.walletAddress.toLowerCase(), evidence.agentWallet?.toLowerCase()];
66
+ const matches = operationEvents.filter((event) => senders.includes(event.sender.toLowerCase()) && (!evidence.userOpHash || event.userOpHash.toLowerCase() === evidence.userOpHash.toLowerCase()));
67
+ if (matches.length !== 1) return { status: "unknown", logs: [] };
68
+ const operation = matches[0];
69
+ if (!operation.success) return { status: "failed", logs: [] };
70
+ const previous = operationEvents.filter((event) => event.logIndex < operation.logIndex).at(-1)?.logIndex ?? -1;
71
+ return {
72
+ status: "confirmed",
73
+ logs: receipt.logs.filter((log) => log.logIndex > previous && log.logIndex < operation.logIndex)
74
+ };
75
+ }
53
76
  function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress, receipt) {
54
77
  if (evidence.agentId !== "yieldseeker" || transaction.chainId !== evidence.chainId || transaction.hash.toLowerCase() !== evidence.txHash?.toLowerCase() || !evidence.agentWallet) return false;
55
78
  const matchesFullCall = (data) => {
@@ -95,8 +118,27 @@ function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress, receip
95
118
  return false;
96
119
  }
97
120
  }
98
- function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
121
+ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction, options = {}) {
122
+ evidence = {
123
+ ...evidence,
124
+ confirmedAmount: void 0,
125
+ amountCompletion: void 0,
126
+ shortfallAmount: void 0,
127
+ blockNumber: void 0,
128
+ logIndexes: void 0,
129
+ transferLogs: void 0
130
+ };
131
+ if (evidence.txHash && receipt.transactionHash && evidence.txHash.toLowerCase() !== receipt.transactionHash.toLowerCase())
132
+ return { ...evidence, transfer: "unknown", delivery: direction === "withdraw" ? "unknown" : "not-applicable" };
99
133
  if (receipt.status !== "success") return { ...evidence, transfer: "failed", delivery: "failed" };
134
+ const execution = direction === "withdraw" && evidence.agentId === "yieldseeker" ? yieldseekerExecution(receipt, evidence) : { status: "confirmed", logs: receipt.logs };
135
+ if (execution.status !== "confirmed") return {
136
+ ...evidence,
137
+ blockNumber: receipt.blockNumber.toString(),
138
+ ...execution.status === "failed" ? { confirmedAmount: "0" } : {},
139
+ transfer: execution.status,
140
+ delivery: execution.status
141
+ };
100
142
  let amount = 0n;
101
143
  const seen = /* @__PURE__ */ new Set();
102
144
  const attributed = /* @__PURE__ */ new Map();
@@ -107,7 +149,7 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
107
149
  const logIndexes = [];
108
150
  const transferLogs = [];
109
151
  const zeroTransferLogs = [];
110
- for (const log of [...receipt.logs].sort((a, b) => a.logIndex - b.logIndex)) {
152
+ for (const log of [...execution.logs].sort((a, b) => a.logIndex - b.logIndex)) {
111
153
  if (log.address.toLowerCase() !== tokenAddress.toLowerCase()) continue;
112
154
  if (seen.has(log.logIndex)) continue;
113
155
  seen.add(log.logIndex);
@@ -133,7 +175,7 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
133
175
  }
134
176
  }
135
177
  const expected = /^\d+$/.test(evidence.intendedAmount) ? BigInt(evidence.intendedAmount) : void 0;
136
- const emptyPayout = direction === "withdraw" && evidence.agentId === "yieldseeker" && amount === 0n && zeroTransferLogs.length > 0 && receipt.logs.some((log) => {
178
+ const emptyPayout = direction === "withdraw" && evidence.agentId === "yieldseeker" && amount === 0n && zeroTransferLogs.length > 0 && execution.logs.some((log) => {
137
179
  if (log.address.toLowerCase() !== agent) return false;
138
180
  try {
139
181
  const { args } = (0, import_viem.decodeEventLog)({ abi: withdrawalEventAbi, data: log.data, topics: log.topics });
@@ -151,18 +193,30 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
151
193
  transfer: "failed",
152
194
  delivery: "failed"
153
195
  };
154
- const delivered = amount > 0n && (expected === void 0 || amount >= expected);
196
+ const exactReceipt = !!evidence.txHash && /^0x[\da-f]{64}$/i.test(evidence.txHash) && receipt.transactionHash?.toLowerCase() === evidence.txHash.toLowerCase();
197
+ const delivered = amount > 0n && (options.requireAmountMatch ? expected === void 0 || amount === expected : direction === "withdraw" && exactReceipt || expected === void 0 || amount >= expected);
155
198
  return {
156
199
  ...evidence,
157
200
  blockNumber: receipt.blockNumber.toString(),
158
201
  logIndexes,
159
202
  transferLogs,
160
203
  ...amount > 0n ? { confirmedAmount: amount.toString() } : {},
204
+ ...delivered && direction === "withdraw" && expected !== void 0 ? {
205
+ amountCompletion: amount < expected ? "shortfall" : "complete",
206
+ shortfallAmount: (amount < expected ? expected - amount : 0n).toString()
207
+ } : {},
161
208
  transfer: delivered ? "confirmed" : "unknown",
162
209
  delivery: direction === "withdraw" ? delivered ? "confirmed" : "unknown" : "not-applicable"
163
210
  };
164
211
  }
165
212
 
213
+ // src/lib/helpers/funding-amount.ts
214
+ function isFundingAmount(value, allowZero = false) {
215
+ if (typeof value !== "string" || !/^\d{1,78}$/.test(value)) return false;
216
+ const amount = BigInt(value);
217
+ return (allowZero ? amount >= 0n : amount > 0n) && amount <= (1n << 256n) - 1n;
218
+ }
219
+
166
220
  // src/lib/deposit-batch-callback.ts
167
221
  var batchCallbacks = /* @__PURE__ */ new WeakMap();
168
222
  var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
@@ -379,7 +433,7 @@ var AgentReads = class {
379
433
 
380
434
  // src/agents/zyfai/zyfai.withdraw-submission.ts
381
435
  var listenersByClient = /* @__PURE__ */ new WeakMap();
382
- function observeWithdrawalSubmission(sdk, chainId, token) {
436
+ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
383
437
  const client = sdk.httpClient;
384
438
  if (!client || typeof client.get !== "function" || typeof client.post !== "function")
385
439
  return { attempted: () => void 0, rejected: () => false, request: () => void 0, stop: () => {
@@ -396,6 +450,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
396
450
  const withdrawal = method === "get" && url === "/users/withdraw" || method === "post" && url === "/users/partial-withdraw";
397
451
  const payload = method === "get" ? options?.params : options;
398
452
  const matching = withdrawal ? [...observers].filter((observer) => observer.chainId === payload?.chainId && observer.token === payload?.tokenSymbol) : [];
453
+ for (const observer of matching) observer.assertCurrent?.();
399
454
  const startedAt = Date.now();
400
455
  const request2 = {
401
456
  attempted: true,
@@ -440,7 +495,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
440
495
  const status = error?.response?.status;
441
496
  if (request2.httpStatus === void 0 && typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599)
442
497
  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";
498
+ request2.failure = message === "Unauthorized: Invalid API key" ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : request2.httpStatus === 400 && message === "No balance to withdraw" ? "insufficient-balance" : /^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
499
  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");
445
500
  throw error;
446
501
  } finally {
@@ -453,7 +508,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
453
508
  let submitted = false;
454
509
  let rejected = false;
455
510
  let request = { attempted: false };
456
- const listener = { chainId, token, notify: (outcome, evidence) => {
511
+ const listener = { chainId, token, assertCurrent, notify: (outcome, evidence) => {
457
512
  request = evidence;
458
513
  if (outcome === "attempted") submitted = true;
459
514
  else if (outcome === "rejected") rejected = true;
@@ -504,10 +559,8 @@ function hasEmptyWithdrawalSnapshot(raw2, chainId, token) {
504
559
  return true;
505
560
  }
506
561
 
507
- // src/agents/zyfai/zyfai.agent.ts
508
- var import_sdk = require("@zyfai/sdk");
509
- var import_viem4 = require("viem");
510
- var import_chains = require("viem/chains");
562
+ // src/agents/zyfai/zyfai.deposit.ts
563
+ var import_viem2 = require("viem");
511
564
 
512
565
  // src/types/config.ts
513
566
  var P_SUPPORTED_CHAINS = {
@@ -556,6 +609,43 @@ var SupportedAssets = [
556
609
  }
557
610
  ];
558
611
 
612
+ // src/agents/zyfai/zyfai.deposit.ts
613
+ async function submitZyfaiDeposit({ sdk, state, chain, amount, asset, smartWallet, assertCurrent }) {
614
+ const owner = state.walletAddress;
615
+ const provider = state.provider;
616
+ const token = SupportedAssets.find((value) => value.chainId === chain.id && value.symbol === asset);
617
+ if (!token || !(0, import_viem2.isAddress)(owner) || !(0, import_viem2.isAddress)(smartWallet) || !/^\d+$/.test(amount) || BigInt(amount) <= 0n)
618
+ throw new OwneyError("AGENT_INVALID_RESPONSE", "Invalid Zyfai deposit transfer.", { chainId: chain.id, asset }, "zyfai");
619
+ assertCurrent();
620
+ const prepared = await sdk.prepareEnterPosition({ userAddress: owner, chainId: chain.id, amount, asset, strategy: "aggressive" });
621
+ assertCurrent();
622
+ const expectedData = (0, import_viem2.encodeFunctionData)({ abi: import_viem2.erc20Abi, functionName: "transfer", args: [smartWallet, BigInt(amount)] });
623
+ const transfer = prepared?.transfer;
624
+ if (!transfer || transfer.safeAddress?.toLowerCase() !== smartWallet.toLowerCase() || transfer.tokenAddress?.toLowerCase() !== token.address.toLowerCase() || transfer.to?.toLowerCase() !== token.address.toLowerCase() || transfer.data?.toLowerCase() !== expectedData.toLowerCase() || transfer.value !== "0")
625
+ throw new OwneyError("AGENT_INVALID_RESPONSE", "Zyfai prepared a different deposit transfer.", { chainId: chain.id, asset }, "zyfai");
626
+ const wallet = (0, import_viem2.createWalletClient)({ account: owner, chain, transport: (0, import_viem2.custom)({
627
+ request: async (args) => {
628
+ assertCurrent();
629
+ if (args.method === "eth_sendTransaction") {
630
+ const [accounts, chainId] = await Promise.all([
631
+ provider.request({ method: "eth_accounts" }),
632
+ provider.request({ method: "eth_chainId" })
633
+ ]);
634
+ assertCurrent();
635
+ if (!Array.isArray(accounts) || typeof accounts[0] !== "string" || accounts[0].toLowerCase() !== owner.toLowerCase() || Number(chainId) !== chain.id)
636
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai deposit.", void 0, "zyfai");
637
+ }
638
+ return provider.request(args);
639
+ }
640
+ }, { retryCount: 0 }) });
641
+ return wallet.sendTransaction({ account: owner, chain, to: token.address, data: expectedData, value: 0n });
642
+ }
643
+
644
+ // src/agents/zyfai/zyfai.agent.ts
645
+ var import_sdk = require("@zyfai/sdk");
646
+ var import_viem5 = require("viem");
647
+ var import_chains = require("viem/chains");
648
+
559
649
  // src/lib/debug.ts
560
650
  var configuredDebug = false;
561
651
  function setOwneyDebug(enabled) {
@@ -600,8 +690,8 @@ var DayFilterMapping = {
600
690
  };
601
691
 
602
692
  // 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);
693
+ var import_viem3 = require("viem");
694
+ var exactHexAmount = (raw2, decimals) => (0, import_viem3.formatUnits)(BigInt(/^0x/i.test(raw2) ? raw2 : `0x${raw2}`), decimals);
605
695
  var DECIMALS_BY_SYMBOL = {
606
696
  usdc: 6,
607
697
  weth: 18,
@@ -615,13 +705,6 @@ function tokenDecimals(symbol, explicit) {
615
705
  return explicit;
616
706
  return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
617
707
  }
618
- function mapDeposit(raw2) {
619
- return {
620
- txHash: raw2.txHash,
621
- smartWallet: raw2.smartWallet,
622
- amount: raw2.amount
623
- };
624
- }
625
708
  function mapWithdraw(raw2) {
626
709
  return {
627
710
  txHash: raw2.txHash,
@@ -707,7 +790,7 @@ function mapBalances(raw2, _chainId, smartWallet) {
707
790
  // legacy `amount` fallback is hex. Runtime
708
791
  // getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
709
792
  // when present, otherwise fall back to the per-symbol lookup.
710
- amount: p.underlyingAmountWithFee ?? p.underlyingAmount ? (0, import_viem2.formatUnits)(
793
+ amount: p.underlyingAmountWithFee ?? p.underlyingAmount ? (0, import_viem3.formatUnits)(
711
794
  BigInt(p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0"),
712
795
  tokenDecimals(
713
796
  p.token_symbol,
@@ -1372,7 +1455,7 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
1372
1455
  }
1373
1456
 
1374
1457
  // src/lib/paid-rpc.ts
1375
- var import_viem3 = require("viem");
1458
+ var import_viem4 = require("viem");
1376
1459
  var PAID_RPC_RETRY_COUNT = 3;
1377
1460
  var PAID_RPC_RETRY_DELAY_MS = 1e3;
1378
1461
  function configuredRpcProxyBaseUrl(baseUrl) {
@@ -1408,9 +1491,9 @@ function createPaidRpcClient(chain, rpcUrls) {
1408
1491
  rpcUrls,
1409
1492
  chain.id
1410
1493
  );
1411
- return (0, import_viem3.createPublicClient)({
1494
+ return (0, import_viem4.createPublicClient)({
1412
1495
  chain,
1413
- transport: (0, import_viem3.http)(url, {
1496
+ transport: (0, import_viem4.http)(url, {
1414
1497
  retryCount: PAID_RPC_RETRY_COUNT,
1415
1498
  retryDelay: PAID_RPC_RETRY_DELAY_MS
1416
1499
  })
@@ -1507,7 +1590,7 @@ async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
1507
1590
  }
1508
1591
 
1509
1592
  // src/agents/zyfai/zyfai.agent.ts
1510
- var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem4.parseAbi)([
1593
+ var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem5.parseAbi)([
1511
1594
  "function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
1512
1595
  ]);
1513
1596
  var WETH_ADDRESS_BY_CHAIN = {
@@ -1521,6 +1604,14 @@ var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
1521
1604
  var EARNINGS_REFRESH_COOLDOWN_MS = 60 * 60 * 1e3;
1522
1605
  var WITHDRAW_BALANCE_TIMEOUT_MS = 2e3;
1523
1606
  var ZyfaiAgent = class _ZyfaiAgent {
1607
+ constructor(apiKey, rpcUrls, referralSource) {
1608
+ this.apiKey = apiKey;
1609
+ this.referralSource = referralSource;
1610
+ this.rpcUrls = resolveRpcUrls(rpcUrls);
1611
+ this.sdk = this.createSdk();
1612
+ }
1613
+ apiKey;
1614
+ referralSource;
1524
1615
  id = "zyfai";
1525
1616
  supportedChainIds = [8453, 42161, 1];
1526
1617
  supportedAssets = [
@@ -1565,6 +1656,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
1565
1656
  */
1566
1657
  sessionKeyAttempts = /* @__PURE__ */ new Set();
1567
1658
  connectAuthPromise = null;
1659
+ connectAuthScope = null;
1660
+ connectionGeneration = 0;
1661
+ sdkHasConnected = false;
1568
1662
  rpcUrls;
1569
1663
  publicClients = /* @__PURE__ */ new Map();
1570
1664
  // Receipt-derived amounts and log identities, scoped to the owner and asset.
@@ -1575,17 +1669,20 @@ var ZyfaiAgent = class _ZyfaiAgent {
1575
1669
  earningsSnapshot = null;
1576
1670
  earningsGeneration = 0;
1577
1671
  balanceSnapshots = /* @__PURE__ */ new WeakMap();
1578
- constructor(apiKey, rpcUrls, referralSource) {
1579
- this.rpcUrls = resolveRpcUrls(rpcUrls);
1580
- this.sdk = new import_sdk.ZyfaiSDK({
1581
- apiKey,
1672
+ /** A replaced wallet session gets its own vendor client. In-flight calls
1673
+ * keep their original client; authentication must never change owners under
1674
+ * an operation that is waiting for provider reads or an HTTP response. */
1675
+ createSdk() {
1676
+ const sdk = new import_sdk.ZyfaiSDK({
1677
+ apiKey: this.apiKey,
1582
1678
  rpcUrls: this.rpcUrls,
1583
1679
  // Forwarded to /auth/login by the SDK's own SIWE handshake, so
1584
1680
  // attribution survives without the AppKit SIWX config (which carried it
1585
1681
  // via `createOwneySIWX({ referralSource })` when apps used the modal).
1586
- referralSource
1682
+ referralSource: this.referralSource
1587
1683
  });
1588
- this.installAuthCache();
1684
+ this.installAuthCache(sdk);
1685
+ return sdk;
1589
1686
  }
1590
1687
  getPublicClient(chainId) {
1591
1688
  const cached = this.publicClients.get(chainId);
@@ -1660,7 +1757,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1660
1757
  }, asset.address, entry.action === "Withdraw" ? "withdraw" : "deposit");
1661
1758
  if (evidence.transfer === "confirmed" && evidence.confirmedAmount) {
1662
1759
  Object.assign(tx, {
1663
- amount: (0, import_viem4.formatUnits)(BigInt(evidence.confirmedAmount), asset.decimals),
1760
+ amount: (0, import_viem5.formatUnits)(BigInt(evidence.confirmedAmount), asset.decimals),
1664
1761
  amountRaw: evidence.confirmedAmount,
1665
1762
  blockNumber: evidence.blockNumber,
1666
1763
  logIndexes: evidence.logIndexes,
@@ -1684,9 +1781,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
1684
1781
  * No-op on the server (no `window`) — the SDK is constructed during SSR
1685
1782
  * for some apps and the patch only matters in the browser anyway.
1686
1783
  */
1687
- installAuthCache() {
1784
+ installAuthCache(sdk = this.sdk) {
1688
1785
  if (typeof window === "undefined") return;
1689
- const internals = this.sdk;
1786
+ const internals = sdk;
1690
1787
  const original = internals.authenticateUser.bind(internals);
1691
1788
  internals.authenticateUser = async () => {
1692
1789
  if (internals.authenticatedUserId !== null) return;
@@ -1731,25 +1828,41 @@ var ZyfaiAgent = class _ZyfaiAgent {
1731
1828
  /** Light connect: SIWE auth only. Re-auths if chain changed. */
1732
1829
  async connectAuth(state, chainId) {
1733
1830
  const validChainId = isValidChainId(chainId);
1734
- if (this.connectedAddress && this.connectedChainId === validChainId) {
1831
+ const owner = state.walletAddress.toLowerCase();
1832
+ const scope = `${owner}:${validChainId}`;
1833
+ if (this.connectedAddress?.toLowerCase() === owner && this.connectedChainId === validChainId) {
1735
1834
  return;
1736
1835
  }
1737
- if (this.connectAuthPromise) {
1836
+ if (this.connectAuthPromise && this.connectAuthScope === scope) {
1738
1837
  await this.connectAuthPromise;
1739
- return;
1838
+ return this.connectAuth(state, chainId);
1740
1839
  }
1741
- this.connectAuthPromise = (async () => {
1840
+ if (this.sdkHasConnected || this.connectedAddress) this.sdk = this.createSdk();
1841
+ this.sdkHasConnected = true;
1842
+ const executionSdk = this.sdk;
1843
+ const generation = ++this.connectionGeneration;
1844
+ this.connectedAddress = null;
1845
+ this.connectedChainId = null;
1846
+ const pending = (async () => {
1742
1847
  const address = await this.connectAccountWithRetry(
1743
1848
  state.provider,
1744
- validChainId
1849
+ validChainId,
1850
+ executionSdk
1745
1851
  );
1852
+ if (generation !== this.connectionGeneration || address.toLowerCase() !== owner)
1853
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while connecting Zyfai.", void 0, this.id);
1746
1854
  this.connectedAddress = address;
1747
1855
  this.connectedChainId = validChainId;
1748
1856
  })();
1857
+ this.connectAuthPromise = pending;
1858
+ this.connectAuthScope = scope;
1749
1859
  try {
1750
- await this.connectAuthPromise;
1860
+ await pending;
1751
1861
  } finally {
1752
- this.connectAuthPromise = null;
1862
+ if (this.connectAuthPromise === pending) {
1863
+ this.connectAuthPromise = null;
1864
+ this.connectAuthScope = null;
1865
+ }
1753
1866
  }
1754
1867
  }
1755
1868
  /**
@@ -1761,16 +1874,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
1761
1874
  * JWT from a misconfigured API key; we retry once either way, and if the
1762
1875
  * second attempt still fails the original error propagates.
1763
1876
  */
1764
- async connectAccountWithRetry(provider, chainId) {
1877
+ async connectAccountWithRetry(provider, chainId, executionSdk = this.sdk) {
1765
1878
  try {
1766
- return await this.sdk.connectAccount(provider, chainId);
1879
+ return await executionSdk.connectAccount(provider, chainId);
1767
1880
  } catch (error) {
1768
1881
  const message = error instanceof Error ? error.message : String(error);
1769
1882
  if (!message.toLowerCase().includes("unauthorized")) throw error;
1770
- const internals = this.sdk;
1883
+ const internals = executionSdk;
1771
1884
  const address = internals.walletClient?.account?.address;
1772
1885
  if (address) clearSession(address, chainId);
1773
- return await this.sdk.connectAccount(provider, chainId);
1886
+ return await executionSdk.connectAccount(provider, chainId);
1774
1887
  }
1775
1888
  }
1776
1889
  /**
@@ -1782,11 +1895,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
1782
1895
  * withdrawFunds) fail with "No account connected".
1783
1896
  */
1784
1897
  async ensureConnected(state, chainId) {
1898
+ const assertOwner = () => {
1899
+ if (this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== chainId)
1900
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while connecting Zyfai.", void 0, this.id);
1901
+ };
1785
1902
  await this.connectAuth(state, chainId);
1903
+ assertOwner();
1786
1904
  const internals = this.sdk;
1787
1905
  if (!internals.walletClient || internals.authenticatedUserId === null) {
1788
1906
  this.connectedChainId = null;
1789
1907
  await this.connectAuth(state, chainId);
1908
+ assertOwner();
1790
1909
  }
1791
1910
  }
1792
1911
  /** A rejected execution session must not be reused by the next foreground attempt. */
@@ -1847,6 +1966,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1847
1966
  }
1848
1967
  // --- IAgent: Connection lifecycle ---
1849
1968
  async disconnect() {
1969
+ const executionSdk = this.sdk;
1970
+ this.connectionGeneration++;
1850
1971
  this.earningsGeneration++;
1851
1972
  this.earningsReads.clear();
1852
1973
  this.earningsRefreshes.clear();
@@ -1854,10 +1975,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1854
1975
  if (this.connectedAddress && this.connectedChainId !== null) {
1855
1976
  clearSession(this.connectedAddress, this.connectedChainId);
1856
1977
  }
1857
- await this.sdk.disconnectAccount();
1858
1978
  this.connectedAddress = null;
1859
1979
  this.connectedChainId = null;
1980
+ this.connectAuthPromise = null;
1981
+ this.connectAuthScope = null;
1860
1982
  this.smartWalletCache.clear();
1983
+ this.sdk = this.createSdk();
1984
+ this.sdkHasConnected = false;
1985
+ await executionSdk.disconnectAccount();
1861
1986
  }
1862
1987
  // --- IAgent: Account lifecycle ---
1863
1988
  async activateAgent(state, chainId) {
@@ -2346,93 +2471,79 @@ var ZyfaiAgent = class _ZyfaiAgent {
2346
2471
  return redeployed.address;
2347
2472
  }
2348
2473
  async deposit(state, chainId, amount, asset, depositCallback, onProgress) {
2474
+ const validChainId = isValidChainId(chainId);
2475
+ const owner = state.walletAddress;
2476
+ const provider = state.provider;
2477
+ await this.ensureConnected(state, validChainId);
2478
+ const executionSdk = this.sdk;
2479
+ const generation = this.connectionGeneration;
2480
+ const assertCurrent = () => {
2481
+ const account = executionSdk.walletClient?.account?.address;
2482
+ if (generation !== this.connectionGeneration || executionSdk !== this.sdk || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== validChainId || state.walletAddress.toLowerCase() !== owner.toLowerCase() || state.provider !== provider || state.chainId !== null && state.chainId !== validChainId || account && account.toLowerCase() !== owner.toLowerCase())
2483
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai deposit.", void 0, this.id);
2484
+ };
2485
+ assertCurrent();
2486
+ const smartWallet = await this.ensureWalletDeployed(owner, validChainId);
2487
+ assertCurrent();
2488
+ notifyFunding(onProgress, {
2489
+ agentId: this.id,
2490
+ chainId,
2491
+ token: asset,
2492
+ walletAddress: owner,
2493
+ agentWallet: smartWallet,
2494
+ intendedAmount: amount,
2495
+ transfer: "pending",
2496
+ registration: "pending",
2497
+ delivery: "not-applicable"
2498
+ });
2499
+ const txHash = depositCallback ? await depositCallback(smartWallet, validChainId, amount) : await submitZyfaiDeposit({
2500
+ sdk: executionSdk,
2501
+ state: { ...state, walletAddress: owner, provider },
2502
+ chain: VIEM_CHAIN[validChainId],
2503
+ amount,
2504
+ asset,
2505
+ smartWallet,
2506
+ assertCurrent
2507
+ });
2508
+ let evidence = {
2509
+ agentId: this.id,
2510
+ chainId,
2511
+ token: asset,
2512
+ walletAddress: owner,
2513
+ agentWallet: smartWallet,
2514
+ intendedAmount: amount,
2515
+ txHash,
2516
+ transfer: "pending",
2517
+ registration: "pending",
2518
+ delivery: "not-applicable"
2519
+ };
2520
+ notifyFunding(onProgress, evidence);
2349
2521
  try {
2350
- const validChainId = isValidChainId(chainId);
2351
- await this.ensureConnected(state, validChainId);
2352
- if (depositCallback) {
2353
- const smartWallet = await this.ensureWalletDeployed(
2354
- this.getAddress(),
2355
- validChainId
2356
- );
2357
- notifyFunding(onProgress, {
2358
- agentId: this.id,
2359
- chainId,
2360
- token: asset,
2361
- walletAddress: state.walletAddress,
2362
- agentWallet: smartWallet,
2363
- intendedAmount: amount,
2364
- transfer: "pending",
2365
- registration: "pending",
2366
- delivery: "not-applicable"
2367
- });
2368
- const txHash = await depositCallback(smartWallet, validChainId, amount);
2369
- let evidence2 = {
2370
- agentId: this.id,
2371
- chainId,
2372
- token: asset,
2373
- walletAddress: state.walletAddress,
2374
- agentWallet: smartWallet,
2375
- intendedAmount: amount,
2376
- txHash,
2377
- transfer: "pending",
2378
- registration: "pending",
2379
- delivery: "not-applicable"
2380
- };
2381
- notifyFunding(onProgress, evidence2);
2382
- try {
2383
- const receipt = await this.getPublicClient(validChainId).getTransactionReceipt({ hash: txHash });
2384
- const token = SupportedAssets.find((token2) => token2.chainId === chainId && token2.symbol === asset);
2385
- evidence2 = inspectFundingReceipt(receipt, evidence2, token.address, "deposit");
2386
- } catch {
2387
- evidence2 = { ...evidence2, transfer: "unknown" };
2388
- }
2389
- notifyFunding(onProgress, evidence2);
2390
- if (evidence2.transfer === "failed")
2391
- throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
2392
- try {
2393
- const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
2394
- await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
2395
- evidence2 = { ...evidence2, registration: "confirmed" };
2396
- } catch {
2397
- evidence2 = { ...evidence2, registration: "unknown" };
2398
- }
2399
- notifyFunding(onProgress, evidence2);
2400
- return { txHash, smartWallet, amount, evidence: evidence2 };
2401
- }
2402
- await this.ensureWalletDeployed(this.getAddress(), validChainId);
2403
- const raw2 = await this.sdk.sendDeposit(
2404
- this.getAddress(),
2405
- validChainId,
2406
- amount,
2407
- asset,
2408
- "aggressive"
2409
- );
2410
- let evidence = {
2411
- agentId: this.id,
2412
- chainId,
2413
- token: asset,
2414
- walletAddress: state.walletAddress,
2415
- agentWallet: raw2.smartWallet,
2416
- intendedAmount: amount,
2417
- txHash: raw2.txHash,
2418
- transfer: "unknown",
2419
- registration: raw2.registration ? "confirmed" : "unknown",
2420
- delivery: "not-applicable"
2421
- };
2422
- notifyFunding(onProgress, evidence);
2423
- try {
2424
- const receipt = await this.getPublicClient(validChainId).getTransactionReceipt({ hash: raw2.txHash });
2425
- const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
2426
- evidence = inspectFundingReceipt(receipt, evidence, token.address, "deposit");
2427
- } catch {
2428
- }
2429
- notifyFunding(onProgress, evidence);
2430
- if (evidence.transfer === "failed")
2431
- throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash: raw2.txHash }, this.id);
2432
- return { ...mapDeposit(raw2), evidence };
2433
- } catch (error) {
2434
- throw error;
2522
+ const receipt = await this.getPublicClient(validChainId).waitForTransactionReceipt({
2523
+ hash: txHash,
2524
+ confirmations: 1,
2525
+ timeout: 9e4,
2526
+ checkReplacement: false
2527
+ });
2528
+ const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
2529
+ evidence = inspectFundingReceipt(receipt, evidence, token.address, "deposit");
2530
+ } catch {
2531
+ evidence = { ...evidence, transfer: "unknown" };
2532
+ }
2533
+ notifyFunding(onProgress, evidence);
2534
+ if (evidence.transfer === "failed")
2535
+ throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
2536
+ if (evidence.transfer !== "confirmed") return { txHash, smartWallet, amount, evidence };
2537
+ try {
2538
+ assertCurrent();
2539
+ const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
2540
+ await (tokenAddress ? executionSdk.logDeposit(validChainId, txHash, amount, tokenAddress) : executionSdk.logDeposit(validChainId, txHash, amount));
2541
+ evidence = { ...evidence, registration: "confirmed" };
2542
+ } catch {
2543
+ evidence = { ...evidence, registration: "unknown" };
2435
2544
  }
2545
+ notifyFunding(onProgress, evidence);
2546
+ return { txHash, smartWallet, amount, evidence };
2436
2547
  }
2437
2548
  // --- IAgent: Fund operations ---
2438
2549
  /** Only after a definitive endpoint rejection, combine a new provider
@@ -2462,7 +2573,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2462
2573
  this.sdk.getPortfolio(owner).catch(() => void 0)
2463
2574
  ]);
2464
2575
  assertScope();
2465
- if (!(0, import_viem4.isAddress)(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
2576
+ if (!(0, import_viem5.isAddress)(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
2466
2577
  throw new OwneyError(
2467
2578
  "AGENT_AUTH_FAILED",
2468
2579
  "Could not verify Zyfai's withdrawal wallet.",
@@ -2473,7 +2584,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2473
2584
  const asset = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === token);
2474
2585
  const idle = asset ? await this.getPublicClient(chainId).readContract({
2475
2586
  address: asset.address,
2476
- abi: import_viem4.erc20Abi,
2587
+ abi: import_viem5.erc20Abi,
2477
2588
  functionName: "balanceOf",
2478
2589
  args: [wallet.address]
2479
2590
  }).catch(() => void 0) : void 0;
@@ -2502,11 +2613,12 @@ var ZyfaiAgent = class _ZyfaiAgent {
2502
2613
  /** Called only after the execution endpoint explicitly rejected a request.
2503
2614
  * A timeout or an accepted request never authorizes repair and resubmission. */
2504
2615
  async recoverWithdrawalSession(state, chainId, token, owner) {
2616
+ const generation = this.connectionGeneration;
2505
2617
  await this.assertNotEmptyRejectedWithdrawal(state, chainId, token, owner);
2506
2618
  const internals = this.sdk;
2507
2619
  const assertScope = () => {
2508
2620
  const account = internals.walletClient?.account?.address;
2509
- if (state.walletAddress.toLowerCase() !== owner.toLowerCase() || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
2621
+ if (generation !== this.connectionGeneration || state.walletAddress.toLowerCase() !== owner.toLowerCase() || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
2510
2622
  throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai withdrawal.", void 0, this.id);
2511
2623
  };
2512
2624
  assertScope();
@@ -2535,6 +2647,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
2535
2647
  }
2536
2648
  }
2537
2649
  async withdraw(state, chainId, token, amount, onProgress) {
2650
+ if (amount !== void 0 && (typeof amount !== "string" || !/^[0-9]+$/.test(amount) || BigInt(amount) <= 0n))
2651
+ throw new OwneyError("WITHDRAW_INVALID_AMOUNT", "Withdrawal amount must be a positive integer in token base units.", void 0, this.id);
2538
2652
  const validChainId = isValidChainId(chainId);
2539
2653
  try {
2540
2654
  await this.ensureConnected(state, validChainId);
@@ -2551,16 +2665,40 @@ var ZyfaiAgent = class _ZyfaiAgent {
2551
2665
  });
2552
2666
  throw error;
2553
2667
  }
2554
- let observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
2555
- const authToken = this.sdk.httpClient?.authToken;
2556
- const owner = this.getAddress();
2668
+ const executionSdk = this.sdk;
2669
+ const internals = executionSdk;
2670
+ const generation = this.connectionGeneration;
2671
+ const owner = state.walletAddress;
2672
+ let staleSessionError;
2673
+ const assertCurrent = () => {
2674
+ const account = internals.walletClient?.account?.address;
2675
+ if (generation !== this.connectionGeneration || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== validChainId || state.chainId !== null && state.chainId !== validChainId || account && account.toLowerCase() !== owner.toLowerCase()) {
2676
+ staleSessionError = new OwneyError(
2677
+ "NOT_CONNECTED",
2678
+ "Wallet changed before Zyfai withdrawal submission.",
2679
+ { phase: "prepare", rejectedBeforeSubmission: true },
2680
+ this.id
2681
+ );
2682
+ throw staleSessionError;
2683
+ }
2684
+ };
2685
+ let observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertCurrent);
2686
+ const authToken = internals.httpClient?.authToken;
2557
2687
  let raw2;
2558
2688
  try {
2559
2689
  for (let attempt = 0; attempt < 2; attempt++) {
2560
2690
  try {
2561
- raw2 = await this.sdk.withdrawFunds(owner, validChainId, amount, token);
2691
+ assertCurrent();
2692
+ raw2 = await executionSdk.withdrawFunds(owner, validChainId, amount, token);
2562
2693
  break;
2563
2694
  } catch (error) {
2695
+ if (observation.rejected()) {
2696
+ try {
2697
+ assertCurrent();
2698
+ } catch {
2699
+ throw error;
2700
+ }
2701
+ }
2564
2702
  if (!amount && observation.rejected() && observation.request()?.failure === "insufficient-balance") {
2565
2703
  try {
2566
2704
  await this.assertNotEmptyRejectedWithdrawal(state, validChainId, token, owner);
@@ -2578,7 +2716,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2578
2716
  );
2579
2717
  await this.recoverWithdrawalSession(state, validChainId, token, owner);
2580
2718
  observation.stop();
2581
- observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
2719
+ observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertCurrent);
2582
2720
  }
2583
2721
  }
2584
2722
  if (!raw2) throw new OwneyError(
@@ -2588,6 +2726,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2588
2726
  this.id
2589
2727
  );
2590
2728
  } catch (error) {
2729
+ if (staleSessionError && observation.attempted() === false) error = staleSessionError;
2591
2730
  this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken);
2592
2731
  if (observation.attempted() !== void 0) {
2593
2732
  const failed = observation.attempted() === false || observation.rejected();
@@ -2608,7 +2747,6 @@ var ZyfaiAgent = class _ZyfaiAgent {
2608
2747
  observation.stop();
2609
2748
  }
2610
2749
  if (!raw2.success) {
2611
- const unresolved = !!raw2.txHash || /already|pending|queued|in.progress/i.test(raw2.message ?? "");
2612
2750
  notifyFunding(onProgress, {
2613
2751
  agentId: this.id,
2614
2752
  chainId,
@@ -2616,9 +2754,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
2616
2754
  walletAddress: state.walletAddress,
2617
2755
  intendedAmount: amount ?? "MAX",
2618
2756
  txHash: raw2.txHash,
2619
- transfer: unresolved ? "unknown" : "failed",
2757
+ transfer: "unknown",
2620
2758
  registration: "not-applicable",
2621
- delivery: unresolved ? "unknown" : "failed",
2759
+ delivery: "unknown",
2622
2760
  withdrawalRequest: observation.request()
2623
2761
  });
2624
2762
  throw new OwneyError(
@@ -2645,8 +2783,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
2645
2783
  }
2646
2784
  /** Registration recovery never signs, transfers, activates, or refreshes authentication. */
2647
2785
  async retryDepositRegistration(state, evidence) {
2648
- const internals = this.sdk;
2649
- if (!internals.httpClient.authToken || internals.authenticatedUserId === null || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== evidence.chainId) {
2786
+ const executionSdk = this.sdk;
2787
+ const internals = executionSdk;
2788
+ if (evidence.agentId !== this.id || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || evidence.chainId !== state.chainId || !internals.httpClient.authToken || internals.authenticatedUserId === null || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== evidence.chainId) {
2650
2789
  throw new OwneyError("NOT_CONNECTED", "Reconnect the wallet before recovering deposit registration.");
2651
2790
  }
2652
2791
  if (evidence.transfer !== "confirmed" || !evidence.txHash) {
@@ -2654,16 +2793,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
2654
2793
  }
2655
2794
  const cid = isValidChainId(evidence.chainId);
2656
2795
  const tokenAddress = evidence.token === "WETH" ? WETH_ADDRESS_BY_CHAIN[cid] : void 0;
2657
- await this.sdk.logDeposit(cid, evidence.txHash, evidence.intendedAmount, tokenAddress);
2796
+ await executionSdk.logDeposit(cid, evidence.txHash, evidence.intendedAmount, tokenAddress);
2658
2797
  return { ...evidence, registration: "confirmed" };
2659
2798
  }
2660
2799
  // --- IAgent: Portfolio reads ---
2661
2800
  async getFundingWallet(state, chainId) {
2662
2801
  return (await this.sdk.getSmartWalletAddress(state.walletAddress, isValidChainId(chainId))).address;
2663
2802
  }
2664
- /** A positive Safe balance is a withdrawal planning floor when the portfolio
2665
- * index reports zero. Idle zero cannot prove that Zyfai's protocol positions
2666
- * are empty: its SDK does not expose a complete contract catalog for them. */
2803
+ /** A positive deployed Safe balance is a conservative planning floor. Zero
2804
+ * requires both the mapped Safe's exact idle balance and a fresh complete
2805
+ * empty portfolio, including for an undeployed counterfactual Safe. */
2667
2806
  async getWithdrawableBalance(state, chainId, token) {
2668
2807
  const validChainId = isValidChainId(chainId);
2669
2808
  const asset = SupportedAssets.find((value) => value.chainId === validChainId && value.symbol === token);
@@ -2692,20 +2831,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
2692
2831
  this.sdk.getSmartWalletByEOA(owner)
2693
2832
  ]);
2694
2833
  assertScope();
2695
- if (!(0, import_viem4.isAddress)(wallet.address) || wallet.isDeployed !== true || wallet.isOwner !== true || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase()) throw unavailable();
2834
+ if (!(0, import_viem5.isAddress)(wallet.address) || wallet.isDeployed !== false && (wallet.isDeployed !== true || wallet.isOwner !== true) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase()) throw unavailable();
2696
2835
  const client = this.getPublicClient(validChainId);
2697
2836
  const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
2698
2837
  assertScope();
2699
2838
  const idle = await client.readContract({
2700
2839
  address: asset.address,
2701
- abi: import_viem4.erc20Abi,
2840
+ abi: import_viem5.erc20Abi,
2702
2841
  functionName: "balanceOf",
2703
2842
  args: [wallet.address],
2704
2843
  blockNumber
2705
2844
  });
2706
2845
  assertScope();
2707
- if (typeof idle !== "bigint" || idle <= 0n) throw unavailable();
2708
- return idle;
2846
+ if (typeof idle !== "bigint" || idle < 0n) throw unavailable();
2847
+ if (idle > 0n) {
2848
+ if (wallet.isDeployed !== true || wallet.isOwner !== true) throw unavailable();
2849
+ return idle;
2850
+ }
2851
+ const portfolio = await this.sdk.getPortfolio(owner);
2852
+ assertScope();
2853
+ if (!hasEmptyWithdrawalSnapshot(portfolio, validChainId, token)) throw unavailable();
2854
+ return 0n;
2709
2855
  };
2710
2856
  try {
2711
2857
  return await Promise.race([read2(), new Promise((_, reject) => {
@@ -2748,7 +2894,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2748
2894
  if (blockNumber < BigInt(evidence.blockNumber)) return void 0;
2749
2895
  const idle = await client.readContract({
2750
2896
  address: asset.address,
2751
- abi: import_viem4.erc20Abi,
2897
+ abi: import_viem5.erc20Abi,
2752
2898
  functionName: "balanceOf",
2753
2899
  args: [balance.smartWallet],
2754
2900
  blockNumber
@@ -2953,7 +3099,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2953
3099
  };
2954
3100
 
2955
3101
  // src/agents/yieldseeker/yieldseeker.agent.ts
2956
- var import_viem12 = require("viem");
3102
+ var import_viem13 = require("viem");
2957
3103
  var import_chains4 = require("viem/chains");
2958
3104
 
2959
3105
  // src/lib/chain-guard.ts
@@ -2992,7 +3138,7 @@ async function ensureWalletOnChain(pub, wallet, expected) {
2992
3138
  }
2993
3139
 
2994
3140
  // src/lib/transfer-auth.ts
2995
- var import_viem5 = require("viem");
3141
+ var import_viem6 = require("viem");
2996
3142
 
2997
3143
  // src/lib/sponsor-client.ts
2998
3144
  var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
@@ -3108,7 +3254,7 @@ async function postSponsorBatchTransfer(input) {
3108
3254
  }
3109
3255
 
3110
3256
  // src/lib/permit2.ts
3111
- var import_viem6 = require("viem");
3257
+ var import_viem7 = require("viem");
3112
3258
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
3113
3259
  var MAX_UINT256 = 2n ** 256n - 1n;
3114
3260
  function permit2ApprovalAmount(requiredAmount) {
@@ -3149,7 +3295,7 @@ var ERC20_ALLOWANCE_ABI = [
3149
3295
  function randomPermit2Nonce() {
3150
3296
  const bytes = new Uint8Array(32);
3151
3297
  globalThis.crypto.getRandomValues(bytes);
3152
- return BigInt((0, import_viem6.bytesToHex)(bytes));
3298
+ return BigInt((0, import_viem7.bytesToHex)(bytes));
3153
3299
  }
3154
3300
  async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
3155
3301
  return publicClient.readContract({
@@ -3192,7 +3338,7 @@ function makeVerificationAwareDepositCallback(implementation) {
3192
3338
 
3193
3339
  // src/agents/yieldseeker/yieldseeker.auth.ts
3194
3340
  var import_siwe = require("siwe");
3195
- var import_viem7 = require("viem");
3341
+ var import_viem8 = require("viem");
3196
3342
  var import_chains2 = require("viem/chains");
3197
3343
 
3198
3344
  // src/agents/yieldseeker/yieldseeker.auth-cache.ts
@@ -3305,7 +3451,7 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
3305
3451
  return new import_siwe.SiweMessage({
3306
3452
  scheme: url.protocol.slice(0, -1),
3307
3453
  domain: url.host,
3308
- address: (0, import_viem7.getAddress)(address),
3454
+ address: (0, import_viem8.getAddress)(address),
3309
3455
  uri: url.origin,
3310
3456
  version: "1",
3311
3457
  chainId,
@@ -3391,15 +3537,15 @@ var YieldseekerAuth = class {
3391
3537
  clearYieldseekerSession(state.walletAddress, chainId);
3392
3538
  }
3393
3539
  async sign(state, chainId) {
3394
- const account = (0, import_viem7.getAddress)(state.walletAddress);
3395
- const publicClient = (0, import_viem7.createPublicClient)({
3540
+ const account = (0, import_viem8.getAddress)(state.walletAddress);
3541
+ const publicClient = (0, import_viem8.createPublicClient)({
3396
3542
  chain: import_chains2.base,
3397
- transport: (0, import_viem7.custom)(state.provider)
3543
+ transport: (0, import_viem8.custom)(state.provider)
3398
3544
  });
3399
- const walletClient = (0, import_viem7.createWalletClient)({
3545
+ const walletClient = (0, import_viem8.createWalletClient)({
3400
3546
  account,
3401
3547
  chain: import_chains2.base,
3402
- transport: (0, import_viem7.custom)(state.provider)
3548
+ transport: (0, import_viem8.custom)(state.provider)
3403
3549
  });
3404
3550
  await ensureWalletOnChain(
3405
3551
  publicClient,
@@ -3566,7 +3712,7 @@ var YieldseekerApiClient = class {
3566
3712
  };
3567
3713
 
3568
3714
  // src/agents/yieldseeker/yieldseeker.mapper.ts
3569
- var import_viem8 = require("viem");
3715
+ var import_viem9 = require("viem");
3570
3716
 
3571
3717
  // src/lib/helpers/snapshot-apy.ts
3572
3718
  var DAY_MS = 864e5;
@@ -3633,10 +3779,18 @@ function raw(value, endpoint) {
3633
3779
  return BigInt(value);
3634
3780
  }
3635
3781
  function decimal(value, decimals, endpoint) {
3636
- return (0, import_viem8.formatUnits)(raw(value, endpoint), decimals);
3782
+ return (0, import_viem9.formatUnits)(raw(value, endpoint), decimals);
3783
+ }
3784
+ function balanceRaw(value, endpoint) {
3785
+ const amount = raw(value, endpoint);
3786
+ if (amount < 0n) return invalid(endpoint, "expected a non-negative balance");
3787
+ return amount;
3788
+ }
3789
+ function balanceDecimal(value, decimals, endpoint) {
3790
+ return (0, import_viem9.formatUnits)(balanceRaw(value, endpoint), decimals);
3637
3791
  }
3638
3792
  function usd(rawAmount, decimals, price) {
3639
- return Number((0, import_viem8.formatUnits)(rawAmount, decimals)) * price;
3793
+ return Number((0, import_viem9.formatUnits)(rawAmount, decimals)) * price;
3640
3794
  }
3641
3795
  function percent(value) {
3642
3796
  const result = Number(value);
@@ -3662,7 +3816,7 @@ function assetAddressValue(record, address) {
3662
3816
  }
3663
3817
  function position(value, asset, baseAssetDecimals) {
3664
3818
  const option = value?.yieldOption;
3665
- if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem8.isAddress)(option.address)) {
3819
+ if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem9.isAddress)(option.address)) {
3666
3820
  return invalid("yield positions", "missing vault metadata");
3667
3821
  }
3668
3822
  return {
@@ -3676,13 +3830,13 @@ function position(value, asset, baseAssetDecimals) {
3676
3830
  // underlying base-asset units in `assetsBase`; pair that value with the
3677
3831
  // snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
3678
3832
  // share quantity separate from the withdrawable underlying asset amount.
3679
- amount: decimal(
3833
+ amount: balanceDecimal(
3680
3834
  value.assetsBase,
3681
3835
  baseAssetDecimals,
3682
3836
  "yield positions"
3683
3837
  ),
3684
- amountRaw: String(value.assetsRaw),
3685
- withdrawableAmountRaw: String(value.withdrawableAssetsRaw),
3838
+ amountRaw: balanceRaw(value.assetsRaw, "yield positions").toString(),
3839
+ withdrawableAmountRaw: balanceRaw(value.withdrawableAssetsRaw, "yield positions").toString(),
3686
3840
  apy: percent(option.riskAdjustedApy),
3687
3841
  tvl: Number(option.totalDepositsUsd),
3688
3842
  liquidity: Number(option.withdrawableDepositsUsd)
@@ -3699,7 +3853,7 @@ function mapYieldseekerBalances(contexts) {
3699
3853
  chain: "BASE",
3700
3854
  chainId: 8453,
3701
3855
  asset: context.asset,
3702
- amount: decimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
3856
+ amount: balanceDecimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
3703
3857
  });
3704
3858
  const idle = assetAddressValue(
3705
3859
  context.snapshot.tokenBalances,
@@ -3709,7 +3863,7 @@ function mapYieldseekerBalances(contexts) {
3709
3863
  chain: "BASE",
3710
3864
  chainId: 8453,
3711
3865
  asset: context.asset,
3712
- amount: decimal(idle, metadata.decimals, "snapshot")
3866
+ amount: balanceDecimal(idle, metadata.decimals, "snapshot")
3713
3867
  });
3714
3868
  positions.push(
3715
3869
  ...context.positions.map(
@@ -3721,7 +3875,7 @@ function mapYieldseekerBalances(contexts) {
3721
3875
  )
3722
3876
  );
3723
3877
  totalUsd += usd(
3724
- raw(context.snapshot.totalValueBase, "snapshot"),
3878
+ balanceRaw(context.snapshot.totalValueBase, "snapshot"),
3725
3879
  context.snapshot.baseAssetDecimals,
3726
3880
  context.snapshot.baseAssetPriceUsd
3727
3881
  );
@@ -3755,7 +3909,7 @@ function mapYieldseekerEarnings(contexts) {
3755
3909
  chain: "BASE",
3756
3910
  chainId: 8453,
3757
3911
  asset: context.asset,
3758
- amount: (0, import_viem8.formatUnits)(amount, context.snapshot.baseAssetDecimals)
3912
+ amount: (0, import_viem9.formatUnits)(amount, context.snapshot.baseAssetDecimals)
3759
3913
  });
3760
3914
  lifetimeEarnings += usd(
3761
3915
  amount,
@@ -3913,7 +4067,7 @@ function allocationEntry(action, context) {
3913
4067
  const leg = deposit;
3914
4068
  const symbol = typeof leg.assetSymbol === "string" ? leg.assetSymbol.toUpperCase() : "";
3915
4069
  if (symbol !== context.asset && !(context.asset === "WETH" && symbol === "ETH")) return void 0;
3916
- if (typeof leg.vaultAddress !== "string" || !(0, import_viem8.isAddress)(leg.vaultAddress)) return void 0;
4070
+ if (typeof leg.vaultAddress !== "string" || !(0, import_viem9.isAddress)(leg.vaultAddress)) return void 0;
3917
4071
  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;
3918
4072
  if (rawAmount === void 0 || rawAmount <= 0n) return void 0;
3919
4073
  const option = context.positions.find((position2) => position2.yieldOption.address.toLowerCase() === leg.vaultAddress.toLowerCase())?.yieldOption;
@@ -3921,7 +4075,7 @@ function allocationEntry(action, context) {
3921
4075
  protocol: typeof leg.vaultProvider === "string" && leg.vaultProvider ? leg.vaultProvider : option?.provider || "Vault",
3922
4076
  pool: typeof leg.vaultName === "string" && leg.vaultName ? leg.vaultName : option?.name,
3923
4077
  tokenSymbol: context.asset,
3924
- amount: (0, import_viem8.formatUnits)(rawAmount, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
4078
+ amount: (0, import_viem9.formatUnits)(rawAmount, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
3925
4079
  });
3926
4080
  amountRaw += rawAmount;
3927
4081
  }
@@ -3940,7 +4094,7 @@ function allocationEntry(action, context) {
3940
4094
  tokenSymbol: context.asset,
3941
4095
  agentWallet: context.wallet.walletAddress,
3942
4096
  amountRaw: String(amountRaw),
3943
- amount: (0, import_viem8.formatUnits)(amountRaw, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
4097
+ amount: (0, import_viem9.formatUnits)(amountRaw, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
3944
4098
  }]
3945
4099
  };
3946
4100
  }
@@ -4064,7 +4218,7 @@ function mapYieldseekerHistory(contexts, options) {
4064
4218
  const known = new Set(target.logIndexes ?? []);
4065
4219
  if (tx.logIndexes?.some((index) => !known.has(index))) {
4066
4220
  target.amountRaw = String(BigInt(target.amountRaw ?? "0") + BigInt(tx.amountRaw));
4067
- target.amount = (0, import_viem8.formatUnits)(
4221
+ target.amount = (0, import_viem9.formatUnits)(
4068
4222
  BigInt(target.amountRaw),
4069
4223
  YIELDSEEKER_ASSET_METADATA[target.tokenSymbol].decimals
4070
4224
  );
@@ -4145,7 +4299,7 @@ function mapYieldseekerAgentApy(options, days) {
4145
4299
  }
4146
4300
 
4147
4301
  // src/lib/remaining-vault-balance.ts
4148
- var import_viem9 = require("viem");
4302
+ var import_viem10 = require("viem");
4149
4303
  async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber) {
4150
4304
  const addresses = [...new Set(vaults.map((value) => value.toLowerCase()))];
4151
4305
  if (addresses.length > 64) return void 0;
@@ -4156,8 +4310,8 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
4156
4310
  blockNumber,
4157
4311
  allowFailure: true,
4158
4312
  contracts: batch.map((address) => ({
4159
- address: (0, import_viem9.getAddress)(address),
4160
- abi: import_viem9.erc20Abi,
4313
+ address: (0, import_viem10.getAddress)(address),
4314
+ abi: import_viem10.erc20Abi,
4161
4315
  functionName: "balanceOf",
4162
4316
  args: [wallet]
4163
4317
  }))
@@ -4167,15 +4321,15 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
4167
4321
  for (let index = 0; index < batch.length; index++) {
4168
4322
  const result = balances[index];
4169
4323
  if (result?.status !== "success" || typeof result.result !== "bigint" || result.result < 0n) return void 0;
4170
- if (result.result > 0n) funded.push({ address: (0, import_viem9.getAddress)(batch[index]), shares: result.result });
4324
+ if (result.result > 0n) funded.push({ address: (0, import_viem10.getAddress)(batch[index]), shares: result.result });
4171
4325
  }
4172
4326
  if (!funded.length) return 0n;
4173
4327
  const valuations = await client.multicall({
4174
4328
  blockNumber,
4175
4329
  allowFailure: true,
4176
4330
  contracts: funded.flatMap(({ address, shares }) => [
4177
- { address, abi: import_viem9.erc4626Abi, functionName: "asset" },
4178
- { address, abi: import_viem9.erc4626Abi, functionName: "convertToAssets", args: [shares] }
4331
+ { address, abi: import_viem10.erc4626Abi, functionName: "asset" },
4332
+ { address, abi: import_viem10.erc4626Abi, functionName: "convertToAssets", args: [shares] }
4179
4333
  ])
4180
4334
  });
4181
4335
  if (valuations.length !== funded.length * 2) return void 0;
@@ -4191,12 +4345,12 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
4191
4345
  }
4192
4346
 
4193
4347
  // src/agents/yieldseeker/yieldseeker.wallet-version.ts
4194
- var import_viem10 = require("viem");
4348
+ var import_viem11 = require("viem");
4195
4349
  var YIELDSEEKER_FACTORY = "0x9c7410a0faCAC60850C46eE5b58B518daec95130";
4196
4350
  var YIELDSEEKER_V1_IMPLEMENTATION = "0x32403455357589523435dB18b414057D7cE05051";
4197
4351
  var YIELDSEEKER_V2_IMPLEMENTATION = "0x613Db9c924ADB7A7A90c11b02Cce20c913E3Fe50";
4198
4352
  var ERC1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
4199
- var walletAbi = (0, import_viem10.parseAbi)([
4353
+ var walletAbi = (0, import_viem11.parseAbi)([
4200
4354
  "function owner() view returns (address)",
4201
4355
  "function ownerAgentIndex() view returns (uint256)",
4202
4356
  "function baseAsset() view returns (address)",
@@ -4204,7 +4358,7 @@ var walletAbi = (0, import_viem10.parseAbi)([
4204
4358
  "function upgradeToLatest()",
4205
4359
  "event Upgraded(address indexed implementation)"
4206
4360
  ]);
4207
- var factoryAbi = (0, import_viem10.parseAbi)([
4361
+ var factoryAbi = (0, import_viem11.parseAbi)([
4208
4362
  "function userWallets(address owner, uint256 index) view returns (address)",
4209
4363
  "function agentWalletImplementation() view returns (address)"
4210
4364
  ]);
@@ -4212,9 +4366,9 @@ function yieldseekerOwnerPath(code) {
4212
4366
  return !code || code === "0x" || /^0xef0100[0-9a-f]{40}$/i.test(code) ? "eoa" : "smart";
4213
4367
  }
4214
4368
  async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNumber) {
4215
- const agentWallet = (0, import_viem10.getAddress)(wallet);
4216
- const expectedOwner = (0, import_viem10.getAddress)(owner);
4217
- const expectedAsset = (0, import_viem10.getAddress)(asset);
4369
+ const agentWallet = (0, import_viem11.getAddress)(wallet);
4370
+ const expectedOwner = (0, import_viem11.getAddress)(owner);
4371
+ const expectedAsset = (0, import_viem11.getAddress)(asset);
4218
4372
  const [code, ownerCode, slot, actualOwner, index, actualAsset, actualFactory, latest] = await Promise.all([
4219
4373
  client.getBytecode({ address: agentWallet, blockNumber }),
4220
4374
  client.getBytecode({ address: expectedOwner, blockNumber }),
@@ -4228,7 +4382,7 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
4228
4382
  if (!code || code === "0x" || !slot || /^0x0+$/.test(slot)) {
4229
4383
  throw new Error("Yieldseeker wallet is not a deployed ERC1967 proxy");
4230
4384
  }
4231
- const implementation = (0, import_viem10.getAddress)(`0x${slot.slice(-40)}`);
4385
+ const implementation = (0, import_viem11.getAddress)(`0x${slot.slice(-40)}`);
4232
4386
  const mapped = await client.readContract({
4233
4387
  address: YIELDSEEKER_FACTORY,
4234
4388
  abi: factoryAbi,
@@ -4236,8 +4390,8 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
4236
4390
  args: [expectedOwner, index],
4237
4391
  blockNumber
4238
4392
  });
4239
- const verified = (0, import_viem10.isAddressEqual)(actualOwner, expectedOwner) && (0, import_viem10.isAddressEqual)(actualAsset, expectedAsset) && (0, import_viem10.isAddressEqual)(actualFactory, YIELDSEEKER_FACTORY) && (0, import_viem10.isAddressEqual)(mapped, agentWallet);
4240
- const version = !verified ? "unknown" : (0, import_viem10.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem10.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
4393
+ const verified = (0, import_viem11.isAddressEqual)(actualOwner, expectedOwner) && (0, import_viem11.isAddressEqual)(actualAsset, expectedAsset) && (0, import_viem11.isAddressEqual)(actualFactory, YIELDSEEKER_FACTORY) && (0, import_viem11.isAddressEqual)(mapped, agentWallet);
4394
+ const version = !verified ? "unknown" : (0, import_viem11.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem11.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
4241
4395
  return {
4242
4396
  agentWallet,
4243
4397
  owner: actualOwner,
@@ -4246,13 +4400,13 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
4246
4400
  implementation,
4247
4401
  version,
4248
4402
  verified,
4249
- upgradeAvailable: version === "v1" && (0, import_viem10.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
4403
+ upgradeAvailable: version === "v1" && (0, import_viem11.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
4250
4404
  };
4251
4405
  }
4252
4406
  var YIELDSEEKER_UPGRADE_ABI = walletAbi;
4253
4407
 
4254
4408
  // src/agents/yieldseeker/yieldseeker.sponsored-withdraw.ts
4255
- var import_viem11 = require("viem");
4409
+ var import_viem12 = require("viem");
4256
4410
  var import_account_abstraction2 = require("viem/account-abstraction");
4257
4411
  var import_accounts = require("viem/accounts");
4258
4412
  var import_chains3 = require("viem/chains");
@@ -4339,7 +4493,7 @@ function rpcDiagnostics(data) {
4339
4493
  details.revertName = data.revertName;
4340
4494
  if (typeof data?.aaCode === "string" && /^AA[0-9]{2}$/.test(data.aaCode)) details.aaCode = data.aaCode;
4341
4495
  if (typeof data?.panicCode === "string" && /^0x[0-9a-fA-F]{1,2}$/.test(data.panicCode)) {
4342
- const panicCode = (0, import_viem11.toHex)(BigInt(data.panicCode));
4496
+ const panicCode = (0, import_viem12.toHex)(BigInt(data.panicCode));
4343
4497
  if (["0x0", "0x1", "0x11", "0x12", "0x21", "0x22", "0x31", "0x32", "0x41", "0x51"].includes(panicCode))
4344
4498
  details.panicCode = panicCode;
4345
4499
  }
@@ -4559,14 +4713,14 @@ async function submitEoa(input) {
4559
4713
  throw sponsorError("Base did not return EIP-1559 gas fees.", true);
4560
4714
  let operation = {
4561
4715
  sender: input.agentWallet,
4562
- nonce: (0, import_viem11.toHex)(nonce),
4716
+ nonce: (0, import_viem12.toHex)(nonce),
4563
4717
  initCode: "0x",
4564
4718
  callData: input.callData,
4565
- callGasLimit: (0, import_viem11.toHex)(3e5),
4566
- verificationGasLimit: (0, import_viem11.toHex)(3e5),
4567
- preVerificationGas: (0, import_viem11.toHex)(6e4),
4568
- maxFeePerGas: (0, import_viem11.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
4569
- maxPriorityFeePerGas: (0, import_viem11.toHex)(fees.maxPriorityFeePerGas),
4719
+ callGasLimit: (0, import_viem12.toHex)(3e5),
4720
+ verificationGasLimit: (0, import_viem12.toHex)(3e5),
4721
+ preVerificationGas: (0, import_viem12.toHex)(6e4),
4722
+ maxFeePerGas: (0, import_viem12.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
4723
+ maxPriorityFeePerGas: (0, import_viem12.toHex)(fees.maxPriorityFeePerGas),
4570
4724
  paymasterAndData: "0x",
4571
4725
  signature: "0x"
4572
4726
  };
@@ -4574,7 +4728,7 @@ async function submitEoa(input) {
4574
4728
  const stub = await rpc(
4575
4729
  input.rpcUrl,
4576
4730
  "pm_getPaymasterStubData",
4577
- [operation, ENTRY_POINT_V06, (0, import_viem11.toHex)(8453), {}],
4731
+ [operation, ENTRY_POINT_V06, (0, import_viem12.toHex)(8453), {}],
4578
4732
  "prepare"
4579
4733
  );
4580
4734
  if (!stub.paymasterAndData?.startsWith("0x"))
@@ -4589,7 +4743,7 @@ async function submitEoa(input) {
4589
4743
  const final = await rpc(
4590
4744
  input.rpcUrl,
4591
4745
  "pm_getPaymasterData",
4592
- [operation, ENTRY_POINT_V06, (0, import_viem11.toHex)(8453), {}],
4746
+ [operation, ENTRY_POINT_V06, (0, import_viem12.toHex)(8453), {}],
4593
4747
  "prepare"
4594
4748
  );
4595
4749
  if (!final.paymasterAndData?.startsWith("0x"))
@@ -4599,10 +4753,10 @@ async function submitEoa(input) {
4599
4753
  const userOpHash = operationHash(operation);
4600
4754
  await assertOwnerSession(input.state, input.owner);
4601
4755
  assertIntentFresh(input.expiresAt);
4602
- const wallet = (0, import_viem11.createWalletClient)({
4756
+ const wallet = (0, import_viem12.createWalletClient)({
4603
4757
  account: input.owner,
4604
4758
  chain: import_chains3.base,
4605
- transport: (0, import_viem11.custom)(input.state.provider)
4759
+ transport: (0, import_viem12.custom)(input.state.provider)
4606
4760
  });
4607
4761
  const signature = await wallet.signMessage({ message: { raw: userOpHash } });
4608
4762
  operation = { ...operation, signature };
@@ -4680,13 +4834,13 @@ async function submitSmart(input) {
4680
4834
  } catch {
4681
4835
  throw sponsorError("This account cannot check paymaster support on Base.", true);
4682
4836
  }
4683
- const supportsPaymaster = capabilities?.[(0, import_viem11.toHex)(8453)]?.paymasterService?.supported ?? capabilities?.["8453"]?.paymasterService?.supported ?? capabilities?.["0x0"]?.paymasterService?.supported;
4837
+ const supportsPaymaster = capabilities?.[(0, import_viem12.toHex)(8453)]?.paymasterService?.supported ?? capabilities?.["8453"]?.paymasterService?.supported ?? capabilities?.["0x0"]?.paymasterService?.supported;
4684
4838
  if (supportsPaymaster !== true)
4685
4839
  throw sponsorError("This account cannot use a paymaster on Base.", true);
4686
4840
  await assertOwnerSession(input.state, input.owner);
4687
4841
  let sent;
4688
4842
  assertIntentFresh(input.expiresAt);
4689
- const requestedId = (0, import_viem11.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
4843
+ const requestedId = (0, import_viem12.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
4690
4844
  savePending(input.onPending, { callsId: requestedId }, true);
4691
4845
  await checkpoint(input.onBeforeSubmission, { callsId: requestedId });
4692
4846
  try {
@@ -4695,7 +4849,7 @@ async function submitSmart(input) {
4695
4849
  params: [{
4696
4850
  version: "2.0.0",
4697
4851
  from: input.owner,
4698
- chainId: (0, import_viem11.toHex)(8453),
4852
+ chainId: (0, import_viem12.toHex)(8453),
4699
4853
  id: requestedId,
4700
4854
  atomicRequired: false,
4701
4855
  calls: [{ to: input.agentWallet, value: "0x0", data: input.callData }],
@@ -4770,7 +4924,7 @@ async function submitSmart(input) {
4770
4924
  );
4771
4925
  }
4772
4926
  async function submitYieldseekerSponsoredWithdrawal(input) {
4773
- const owner = (0, import_viem11.getAddress)(input.state.walletAddress);
4927
+ const owner = (0, import_viem12.getAddress)(input.state.walletAddress);
4774
4928
  const { intent, rpcUrl } = await requestIntent({
4775
4929
  apiKey: input.apiKey,
4776
4930
  routingApiBaseUrl: input.routingApiBaseUrl,
@@ -4789,7 +4943,7 @@ async function submitYieldseekerSponsoredWithdrawal(input) {
4789
4943
  return intent.ownerPath === "eoa" ? submitEoa(args) : submitSmart(args);
4790
4944
  }
4791
4945
  async function submitYieldseekerSponsoredUpgrade(input) {
4792
- const owner = (0, import_viem11.getAddress)(input.state.walletAddress);
4946
+ const owner = (0, import_viem12.getAddress)(input.state.walletAddress);
4793
4947
  const { intent, rpcUrl } = await requestIntent({
4794
4948
  apiKey: input.apiKey,
4795
4949
  routingApiBaseUrl: input.routingApiBaseUrl,
@@ -4904,6 +5058,7 @@ var YieldseekerAgent = class {
4904
5058
  receiptClient;
4905
5059
  // A client preparation hint only; the routing API still checks live identity.
4906
5060
  withdrawalVersionCache = /* @__PURE__ */ new Map();
5061
+ pendingUpgrades = /* @__PURE__ */ new Map();
4907
5062
  transactionExecutor;
4908
5063
  unwindReceiptWaiter;
4909
5064
  agentContexts = /* @__PURE__ */ new Map();
@@ -4954,8 +5109,8 @@ var YieldseekerAgent = class {
4954
5109
  if (useCachedV2 && cached && Date.now() - cached.at < 15 * 6e4) return { ...cached.status };
4955
5110
  const status = await readYieldseekerWalletStatus(
4956
5111
  this.getReceiptClient(),
4957
- (0, import_viem12.getAddress)(context.wallet.walletAddress),
4958
- (0, import_viem12.getAddress)(state.walletAddress),
5112
+ (0, import_viem13.getAddress)(context.wallet.walletAddress),
5113
+ (0, import_viem13.getAddress)(state.walletAddress),
4959
5114
  YIELDSEEKER_ASSET_METADATA[asset].address
4960
5115
  );
4961
5116
  this.rememberWalletStatus(key2, status);
@@ -4985,12 +5140,12 @@ var YieldseekerAgent = class {
4985
5140
  const pendingKey2 = `owney.yieldseeker.upgrade:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}`;
4986
5141
  const confirmUpgrade = async (hash, receipt2) => {
4987
5142
  this.withdrawalVersionCache.delete(versionKey);
4988
- const upgradeTopic = (0, import_viem12.encodeEventTopics)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded" })[0];
5143
+ const upgradeTopic = (0, import_viem13.encodeEventTopics)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded" })[0];
4989
5144
  const lastUpgrade = receipt2?.logs.filter((log) => log.address.toLowerCase() === before.agentWallet.toLowerCase() && log.topics[0] === upgradeTopic).at(-1);
4990
5145
  const expectedUpgrade = (() => {
4991
5146
  if (!lastUpgrade || lastUpgrade.removed) return false;
4992
5147
  try {
4993
- const event = (0, import_viem12.decodeEventLog)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded", data: lastUpgrade.data, topics: lastUpgrade.topics });
5148
+ const event = (0, import_viem13.decodeEventLog)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded", data: lastUpgrade.data, topics: lastUpgrade.topics });
4994
5149
  return event.args.implementation.toLowerCase() === YIELDSEEKER_V2_IMPLEMENTATION.toLowerCase();
4995
5150
  } catch {
4996
5151
  return false;
@@ -5019,10 +5174,30 @@ var YieldseekerAgent = class {
5019
5174
  } catch {
5020
5175
  storage5 = void 0;
5021
5176
  }
5022
- const clearConfirmedUpgrade = () => {
5177
+ const clearRecordedUpgrade = () => {
5178
+ this.pendingUpgrades.delete(pendingKey2);
5023
5179
  try {
5024
5180
  storage5?.removeItem(pendingKey2);
5025
5181
  } catch {
5182
+ this.pendingUpgrades.set(pendingKey2, null);
5183
+ }
5184
+ };
5185
+ const pendingUpgrade = (hash) => new OwneyError(
5186
+ "WITHDRAW_IN_PROGRESS",
5187
+ "Yieldseeker wallet upgrade is awaiting confirmation. Retry to check its status.",
5188
+ { txHash: hash, reason: "UPGRADE_CONFIRMATION_PENDING", phase: "observe", safeToFallback: false },
5189
+ this.id
5190
+ );
5191
+ const terminalUpgradeFailure = (error) => error instanceof OwneyError && (error.code === "AGENT_TRANSACTION_REVERTED" || error.details?.reason === "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL");
5192
+ const readUpgradeReceipt = async (hash) => {
5193
+ try {
5194
+ return await this.waitForReceipt(state, chainId, hash);
5195
+ } catch (error) {
5196
+ if (terminalUpgradeFailure(error)) {
5197
+ clearRecordedUpgrade();
5198
+ throw error;
5199
+ }
5200
+ throw pendingUpgrade(hash);
5026
5201
  }
5027
5202
  };
5028
5203
  if (!storage5 && (gasMode === "sponsored" || before.ownerPath === "smart"))
@@ -5034,7 +5209,7 @@ var YieldseekerAgent = class {
5034
5209
  );
5035
5210
  let prior = null;
5036
5211
  try {
5037
- const saved = storage5?.getItem(pendingKey2);
5212
+ const saved = this.pendingUpgrades.has(pendingKey2) ? JSON.stringify(this.pendingUpgrades.get(pendingKey2)) : storage5?.getItem(pendingKey2);
5038
5213
  try {
5039
5214
  prior = JSON.parse(saved ?? "null");
5040
5215
  } catch {
@@ -5078,7 +5253,7 @@ var YieldseekerAgent = class {
5078
5253
  this.id
5079
5254
  );
5080
5255
  if (Number(callsStatus?.status) === 400 || Number(callsStatus?.status) === 500) {
5081
- storage5.removeItem(pendingKey2);
5256
+ clearRecordedUpgrade();
5082
5257
  throw new OwneyError(
5083
5258
  "SPONSOR_REQUEST_FAILED",
5084
5259
  "The earlier sponsored upgrade failed.",
@@ -5090,8 +5265,8 @@ var YieldseekerAgent = class {
5090
5265
  earlierHash = hashes.length === 1 ? hashes[0] : void 0;
5091
5266
  }
5092
5267
  if (earlierHash) {
5093
- const recovered = await confirmUpgrade(earlierHash, await this.waitForReceipt(state, chainId, earlierHash));
5094
- clearConfirmedUpgrade();
5268
+ const recovered = await confirmUpgrade(earlierHash, await readUpgradeReceipt(earlierHash));
5269
+ clearRecordedUpgrade();
5095
5270
  return recovered;
5096
5271
  }
5097
5272
  throw new OwneyError(
@@ -5108,7 +5283,7 @@ var YieldseekerAgent = class {
5108
5283
  { version: before.version },
5109
5284
  this.id
5110
5285
  );
5111
- const data = (0, import_viem12.encodeFunctionData)({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
5286
+ const data = (0, import_viem13.encodeFunctionData)({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
5112
5287
  let txHash;
5113
5288
  let receipt;
5114
5289
  if (gasMode === "sponsored") {
@@ -5135,33 +5310,52 @@ var YieldseekerAgent = class {
5135
5310
  asset: YIELDSEEKER_ASSET_METADATA[asset].address,
5136
5311
  callData: data,
5137
5312
  onPending: (pending) => {
5138
- const current = storage5.getItem(pendingKey2);
5139
- storage5.setItem(pendingKey2, JSON.stringify({
5140
- startedAt: Date.now(),
5141
- ...current ? JSON.parse(current) : {},
5142
- ...pending
5143
- }));
5313
+ const current = this.pendingUpgrades.has(pendingKey2) ? this.pendingUpgrades.get(pendingKey2) : JSON.parse(storage5.getItem(pendingKey2) ?? "null");
5314
+ const next = { startedAt: Date.now(), ...current, ...pending };
5315
+ storage5.setItem(pendingKey2, JSON.stringify(next));
5316
+ this.pendingUpgrades.delete(pendingKey2);
5144
5317
  }
5145
5318
  });
5146
5319
  txHash = upgraded.txHash;
5147
5320
  receipt = upgraded.receipt;
5148
5321
  } catch (error) {
5149
- if (error?.code === 4001 || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) storage5.removeItem(pendingKey2);
5322
+ if (error?.code === 4001 || terminalUpgradeFailure(error) || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) clearRecordedUpgrade();
5150
5323
  throw error;
5151
5324
  }
5152
5325
  } else {
5153
- txHash = await this.submitTransaction(state, chainId, {
5154
- from: (0, import_viem12.getAddress)(state.walletAddress),
5155
- to: before.agentWallet,
5156
- data,
5157
- value: "0",
5158
- chainId
5159
- }, void 0, (value) => {
5160
- receipt = value;
5161
- });
5326
+ let submittedHash;
5327
+ try {
5328
+ txHash = await this.submitTransaction(state, chainId, {
5329
+ from: (0, import_viem13.getAddress)(state.walletAddress),
5330
+ to: before.agentWallet,
5331
+ data,
5332
+ value: "0",
5333
+ chainId
5334
+ }, (hash) => {
5335
+ submittedHash = hash;
5336
+ const pending = { txHash: hash };
5337
+ this.pendingUpgrades.set(pendingKey2, pending);
5338
+ try {
5339
+ if (storage5) {
5340
+ storage5.setItem(pendingKey2, JSON.stringify(pending));
5341
+ this.pendingUpgrades.delete(pendingKey2);
5342
+ }
5343
+ } catch {
5344
+ }
5345
+ }, (value) => {
5346
+ receipt = value;
5347
+ });
5348
+ } catch (error) {
5349
+ if (terminalUpgradeFailure(error)) {
5350
+ clearRecordedUpgrade();
5351
+ throw error;
5352
+ }
5353
+ if (submittedHash) throw pendingUpgrade(submittedHash);
5354
+ throw error;
5355
+ }
5162
5356
  }
5163
- const confirmed = await confirmUpgrade(txHash, receipt ?? await this.waitForReceipt(state, chainId, txHash));
5164
- if (gasMode === "sponsored") clearConfirmedUpgrade();
5357
+ const confirmed = await confirmUpgrade(txHash, receipt ?? await readUpgradeReceipt(txHash));
5358
+ clearRecordedUpgrade();
5165
5359
  return confirmed;
5166
5360
  }
5167
5361
  async disconnect() {
@@ -5238,12 +5432,12 @@ var YieldseekerAgent = class {
5238
5432
  } else {
5239
5433
  try {
5240
5434
  txHash = await this.submitTransaction(state, chainId, {
5241
- from: (0, import_viem12.getAddress)(state.walletAddress),
5435
+ from: (0, import_viem13.getAddress)(state.walletAddress),
5242
5436
  to: YIELDSEEKER_ASSET_METADATA[asset].address,
5243
- data: (0, import_viem12.encodeFunctionData)({
5244
- abi: import_viem12.erc20Abi,
5437
+ data: (0, import_viem13.encodeFunctionData)({
5438
+ abi: import_viem13.erc20Abi,
5245
5439
  functionName: "transfer",
5246
- args: [(0, import_viem12.getAddress)(context.wallet.walletAddress), BigInt(amount)]
5440
+ args: [(0, import_viem13.getAddress)(context.wallet.walletAddress), BigInt(amount)]
5247
5441
  }),
5248
5442
  value: "0",
5249
5443
  chainId
@@ -5377,7 +5571,7 @@ var YieldseekerAgent = class {
5377
5571
  );
5378
5572
  const positionsRead = this.readWithdrawalPositions(state, chainId, context);
5379
5573
  const metadata = YIELDSEEKER_ASSET_METADATA[asset];
5380
- const agentWallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
5574
+ const agentWallet = (0, import_viem13.getAddress)(context.wallet.walletAddress);
5381
5575
  const readOwnerFunds = async (blockNumber) => {
5382
5576
  const feeTracker = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5383
5577
  address: agentWallet,
@@ -5386,18 +5580,18 @@ var YieldseekerAgent = class {
5386
5580
  blockNumber
5387
5581
  }), chainId, "eth_call", this.id);
5388
5582
  this.assertSession(generation, chainId, asset);
5389
- if (!(0, import_viem12.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
5583
+ if (!(0, import_viem13.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
5390
5584
  throw this.invalidResponse("wallet fee tracker");
5391
5585
  const [idle2, fees] = await Promise.all([
5392
5586
  withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5393
5587
  address: metadata.address,
5394
- abi: import_viem12.erc20Abi,
5588
+ abi: import_viem13.erc20Abi,
5395
5589
  functionName: "balanceOf",
5396
5590
  args: [agentWallet],
5397
5591
  blockNumber
5398
5592
  }), chainId, "eth_call", this.id),
5399
5593
  withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5400
- address: (0, import_viem12.getAddress)(feeTracker),
5594
+ address: (0, import_viem13.getAddress)(feeTracker),
5401
5595
  abi: YIELDSEEKER_FEE_TRACKER_ABI,
5402
5596
  functionName: "getFeesOwed",
5403
5597
  args: [agentWallet],
@@ -5439,13 +5633,13 @@ var YieldseekerAgent = class {
5439
5633
  let active = true;
5440
5634
  const checks = this.readVaultLiquidity(values.map((position2) => position2.yieldOption), agentWallet, blockNumber).catch(() => []);
5441
5635
  const reads = values.map(async (position2, index) => {
5442
- const address = (0, import_viem12.getAddress)(position2.yieldOption.address);
5636
+ const address = (0, import_viem13.getAddress)(position2.yieldOption.address);
5443
5637
  try {
5444
5638
  const group = (await checks).slice(index * 3, index * 3 + 3);
5445
5639
  if (!active || group.length !== 3 || group.some((value) => value.status !== "success")) return null;
5446
5640
  this.assertSession(generation, chainId, asset);
5447
5641
  const [underlying, shares, withdrawable] = group.map((value) => value.result);
5448
- if (typeof underlying !== "string" || !(0, import_viem12.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
5642
+ if (typeof underlying !== "string" || !(0, import_viem13.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
5449
5643
  const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: withdrawable.toString() };
5450
5644
  if (shares > 0n && withdrawable === 0n) {
5451
5645
  let positiveValue = false;
@@ -5453,7 +5647,7 @@ var YieldseekerAgent = class {
5453
5647
  try {
5454
5648
  const value = await this.getReceiptClient().readContract({
5455
5649
  address,
5456
- abi: import_viem12.erc4626Abi,
5650
+ abi: import_viem13.erc4626Abi,
5457
5651
  functionName,
5458
5652
  args: [shares],
5459
5653
  blockNumber
@@ -5543,6 +5737,18 @@ var YieldseekerAgent = class {
5543
5737
  );
5544
5738
  }
5545
5739
  if (requested > totalAvailable) {
5740
+ if (!funds.complete)
5741
+ throw new OwneyError(
5742
+ "WITHDRAW_BALANCE_UNAVAILABLE",
5743
+ "Yieldseeker withdrawal liquidity could not be verified for the requested amount.",
5744
+ {
5745
+ asset,
5746
+ requested: requested.toString(),
5747
+ verifiedAvailable: totalAvailable.toString(),
5748
+ reason: "YIELDSEEKER_LIQUIDITY_UNVERIFIED"
5749
+ },
5750
+ this.id
5751
+ );
5546
5752
  throw new OwneyError(
5547
5753
  "WITHDRAW_INSUFFICIENT_BALANCE",
5548
5754
  `Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
@@ -5557,6 +5763,7 @@ var YieldseekerAgent = class {
5557
5763
  const requiredIdle = requested + initialFunds.fees;
5558
5764
  let remaining = requiredIdle > idle ? requiredIdle - idle : 0n;
5559
5765
  let grossUnwound = 0n;
5766
+ let confirmedUnwinds = 0n;
5560
5767
  let latestUnwindBlock;
5561
5768
  const unwind = async (position2, assetsRaw) => {
5562
5769
  this.assertSession(generation, chainId, asset);
@@ -5574,7 +5781,10 @@ var YieldseekerAgent = class {
5574
5781
  if (!this.isTransactionHash(response?.transactionHash)) throw this.invalidResponse("position withdrawal");
5575
5782
  unwindTransactionHash = response.transactionHash;
5576
5783
  const unwindReceipt = await this.waitForReceipt(state, chainId, unwindTransactionHash);
5577
- if (unwindReceipt) latestUnwindBlock = unwindReceipt.blockNumber;
5784
+ if (unwindReceipt) {
5785
+ latestUnwindBlock = unwindReceipt.blockNumber;
5786
+ confirmedUnwinds++;
5787
+ }
5578
5788
  } catch (error) {
5579
5789
  this.invalidateFundingRead(state, chainId, asset);
5580
5790
  throw new OwneyError(
@@ -5644,7 +5854,7 @@ var YieldseekerAgent = class {
5644
5854
  const feeRate = feeRateBps ?? 0;
5645
5855
  const maxFee = confirmedMovement && Number.isInteger(feeRate) && feeRate >= 0 && feeRate <= 1e3 ? (grossUnwound * BigInt(feeRate) + 9999n) / 10000n : 0n;
5646
5856
  const shortfall = availableForOwner < requested ? requested - availableForOwner : 0n;
5647
- if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee) {
5857
+ if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee + confirmedUnwinds) {
5648
5858
  this.invalidateFundingRead(state, chainId, asset);
5649
5859
  if (amount === void 0 && positionsStatus !== "empty")
5650
5860
  throw new OwneyError(
@@ -5666,14 +5876,15 @@ var YieldseekerAgent = class {
5666
5876
  delta: maxSettlementBasis.delta - payout,
5667
5877
  expectedNetDeposits: maxSettlementBasis.expectedNetDeposits - payout
5668
5878
  } : this.snapshotMovement(state, chainId, context, -payout);
5669
- const account = (0, import_viem12.getAddress)(state.walletAddress);
5879
+ const account = (0, import_viem13.getAddress)(state.walletAddress);
5670
5880
  let receipt;
5671
- 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" };
5672
- const data = amount === void 0 ? (0, import_viem12.encodeFunctionData)({
5881
+ const base6 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount ?? "MAX", transfer: "pending", registration: "not-applicable", delivery: "pending" };
5882
+ let submissionEvidence = base6;
5883
+ const data = amount === void 0 ? (0, import_viem13.encodeFunctionData)({
5673
5884
  abi: YIELDSEEKER_AGENT_WALLET_ABI,
5674
5885
  functionName: "withdrawAllAssetToUser",
5675
5886
  args: [account, metadata.address]
5676
- }) : (0, import_viem12.encodeFunctionData)({
5887
+ }) : (0, import_viem13.encodeFunctionData)({
5677
5888
  abi: YIELDSEEKER_AGENT_WALLET_ABI,
5678
5889
  functionName: "withdrawAssetToUser",
5679
5890
  args: [account, metadata.address, payout]
@@ -5689,10 +5900,13 @@ var YieldseekerAgent = class {
5689
5900
  refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
5690
5901
  userId: context.user.userId,
5691
5902
  agentId: context.agent.agentId,
5692
- agentWallet: (0, import_viem12.getAddress)(context.wallet.walletAddress),
5903
+ agentWallet: (0, import_viem13.getAddress)(context.wallet.walletAddress),
5693
5904
  asset: metadata.address,
5694
5905
  callData: data,
5695
- onPending: (pending) => notifyFunding(onProgress, { ...base6, ...pending }),
5906
+ onPending: (pending) => {
5907
+ submissionEvidence = { ...submissionEvidence, ...pending };
5908
+ notifyFunding(onProgress, submissionEvidence);
5909
+ },
5696
5910
  onBeforeSubmission: async (pending) => {
5697
5911
  await onBeforeSubmission?.({ ...base6, ...pending });
5698
5912
  onOwnerTransferAttempted?.();
@@ -5704,7 +5918,7 @@ var YieldseekerAgent = class {
5704
5918
  onOwnerTransferAttempted?.();
5705
5919
  txHash = await this.submitTransaction(state, chainId, {
5706
5920
  from: account,
5707
- to: (0, import_viem12.getAddress)(context.wallet.walletAddress),
5921
+ to: (0, import_viem13.getAddress)(context.wallet.walletAddress),
5708
5922
  data,
5709
5923
  value: "0",
5710
5924
  chainId
@@ -5712,10 +5926,20 @@ var YieldseekerAgent = class {
5712
5926
  receipt = value;
5713
5927
  });
5714
5928
  }
5715
- confirmedMovement = true;
5716
- settlement = plannedMovement;
5717
- const evidence = receipt ? inspectFundingReceipt(receipt, { ...base6, txHash }, metadata.address, "withdraw") : { ...base6, txHash, confirmedAmount: payout.toString(), transfer: "confirmed", delivery: "confirmed" };
5929
+ const evidence = receipt ? inspectFundingReceipt(receipt, { ...submissionEvidence, txHash }, metadata.address, "withdraw") : { ...submissionEvidence, txHash, transfer: "pending", delivery: "pending" };
5718
5930
  notifyFunding(onProgress, evidence);
5931
+ const explicitEmptyPayout = evidence.transfer === "failed" && evidence.confirmedAmount === "0" && evidence.transferLogs?.some((log) => log.amountRaw === "0");
5932
+ if (evidence.transfer === "failed" && !explicitEmptyPayout)
5933
+ throw new OwneyError(
5934
+ "AGENT_TRANSACTION_REVERTED",
5935
+ "The Yieldseeker withdrawal completed without transferring funds.",
5936
+ { asset, available: "0", txHash },
5937
+ this.id
5938
+ );
5939
+ if (!explicitEmptyPayout && (evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || !evidence.confirmedAmount))
5940
+ return { txHash, type: "partial", completion: "unverified", amount: "0", evidence };
5941
+ confirmedMovement = true;
5942
+ settlement = evidence.confirmedAmount === payout.toString() ? plannedMovement : null;
5719
5943
  let completion = maxLiquidity && !maxLiquidity.complete ? "unverified" : void 0;
5720
5944
  if (amount === void 0) {
5721
5945
  const verificationDeadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
@@ -5736,7 +5960,7 @@ var YieldseekerAgent = class {
5736
5960
  positions = proof.positions;
5737
5961
  positionsStatus = hasPositions(positions) ? "remaining" : proof.verified ? "empty" : "unavailable";
5738
5962
  if (positionsStatus === "remaining") {
5739
- const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? payout.toString(), evidence };
5963
+ const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? "0", evidence };
5740
5964
  throw new OwneyError(
5741
5965
  "WITHDRAW_PARTIAL_FAILURE",
5742
5966
  "Yieldseeker MAX could not confirm that all vault positions were cleared.",
@@ -5756,7 +5980,7 @@ var YieldseekerAgent = class {
5756
5980
  }
5757
5981
  if (!proof.verified) completion = "unverified";
5758
5982
  }
5759
- if (evidence.transfer === "failed" && evidence.confirmedAmount === "0")
5983
+ if (explicitEmptyPayout)
5760
5984
  throw new OwneyError(
5761
5985
  "WITHDRAW_INSUFFICIENT_BALANCE",
5762
5986
  "The Yieldseeker withdrawal completed without transferring funds.",
@@ -5771,7 +5995,7 @@ var YieldseekerAgent = class {
5771
5995
  type: amount === void 0 && !completion ? "full" : "partial",
5772
5996
  ...completion ? { completion } : {},
5773
5997
  ...feeAdjustment ? { feeAdjustment } : {},
5774
- amount: evidence.confirmedAmount ?? payout.toString(),
5998
+ amount: evidence.confirmedAmount ?? "0",
5775
5999
  evidence
5776
6000
  };
5777
6001
  } finally {
@@ -5812,17 +6036,17 @@ var YieldseekerAgent = class {
5812
6036
  return proof.available;
5813
6037
  }
5814
6038
  async readVaultLiquidity(options, wallet, blockNumber) {
5815
- const owner = (0, import_viem12.getAddress)(wallet);
6039
+ const owner = (0, import_viem13.getAddress)(wallet);
5816
6040
  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));
5817
6041
  const results = await Promise.all(batches.map((batch) => this.getReceiptClient().multicall({
5818
6042
  blockNumber,
5819
6043
  allowFailure: true,
5820
6044
  contracts: batch.flatMap((option) => {
5821
- const address = (0, import_viem12.getAddress)(option.address);
6045
+ const address = (0, import_viem13.getAddress)(option.address);
5822
6046
  return [
5823
- { address, abi: import_viem12.erc4626Abi, functionName: "asset" },
5824
- { address, abi: import_viem12.erc20Abi, functionName: "balanceOf", args: [owner] },
5825
- { address, abi: import_viem12.erc4626Abi, functionName: "maxWithdraw", args: [owner] }
6047
+ { address, abi: import_viem13.erc4626Abi, functionName: "asset" },
6048
+ { address, abi: import_viem13.erc20Abi, functionName: "balanceOf", args: [owner] },
6049
+ { address, abi: import_viem13.erc4626Abi, functionName: "maxWithdraw", args: [owner] }
5826
6050
  ];
5827
6051
  })
5828
6052
  })));
@@ -5833,7 +6057,7 @@ var YieldseekerAgent = class {
5833
6057
  const seen = /* @__PURE__ */ new Set();
5834
6058
  for (const value of values) {
5835
6059
  const option = value?.yieldOption;
5836
- if (!option || typeof option.address !== "string" || !(0, import_viem12.isAddress)(option.address) || seen.has(option.address.toLowerCase()) || option.chainId !== void 0 && option.chainId !== chainId || option.assetAddress !== void 0 && (typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== underlying) || typeof value.assetsRaw !== "string" || !/^\d+$/.test(value.assetsRaw) || typeof value.withdrawableAssetsRaw !== "string" || !/^\d+$/.test(value.withdrawableAssetsRaw))
6060
+ if (!option || typeof option.address !== "string" || !(0, import_viem13.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))
5837
6061
  throw this.invalidResponse("yield positions");
5838
6062
  seen.add(option.address.toLowerCase());
5839
6063
  }
@@ -5846,7 +6070,7 @@ var YieldseekerAgent = class {
5846
6070
  const historic = this.readCache.get(`historic:${key2}`)?.value?.position;
5847
6071
  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()));
5848
6072
  for (const option of this.yieldOptions.get(context.asset)?.value ?? []) {
5849
- if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !(0, import_viem12.isAddress)(option.address) || !receivedAssets.has(option.address.toLowerCase()) || hints.has(option.address.toLowerCase())) continue;
6073
+ if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !(0, import_viem13.isAddress)(option.address) || !receivedAssets.has(option.address.toLowerCase()) || hints.has(option.address.toLowerCase())) continue;
5850
6074
  hints.set(option.address.toLowerCase(), {
5851
6075
  yieldOption: option,
5852
6076
  assetsRaw: "0",
@@ -5898,7 +6122,7 @@ var YieldseekerAgent = class {
5898
6122
  const read2 = async () => {
5899
6123
  const client = this.getReceiptClient();
5900
6124
  const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
5901
- const wallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
6125
+ const wallet = (0, import_viem13.getAddress)(context.wallet.walletAddress);
5902
6126
  assertReadCurrent();
5903
6127
  if (typeof blockNumber !== "bigint") throw unavailable();
5904
6128
  const indexedPositions = this.validateWithdrawalPositions(positions ?? [], chainId, context.asset);
@@ -5908,11 +6132,11 @@ var YieldseekerAgent = class {
5908
6132
  for (const position2 of indexedPositions) candidates.set(position2.yieldOption.address.toLowerCase(), position2.yieldOption);
5909
6133
  const options = [...candidates.values()];
5910
6134
  if (options.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
5911
- const [checks, idle, feeTracker] = await Promise.all([
5912
- this.readVaultLiquidity(options, wallet, blockNumber),
6135
+ const checksRead = this.readVaultLiquidity(options, wallet, blockNumber).then((value) => ({ value }), (error) => ({ error }));
6136
+ const [idle, feeTracker] = await Promise.all([
5913
6137
  client.readContract({
5914
6138
  address: metadata.address,
5915
- abi: import_viem12.erc20Abi,
6139
+ abi: import_viem13.erc20Abi,
5916
6140
  functionName: "balanceOf",
5917
6141
  args: [wallet],
5918
6142
  blockNumber
@@ -5924,10 +6148,10 @@ var YieldseekerAgent = class {
5924
6148
  blockNumber
5925
6149
  })
5926
6150
  ]);
5927
- if (typeof idle !== "bigint" || idle < 0n || !(0, import_viem12.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker)) throw unavailable();
6151
+ if (typeof idle !== "bigint" || idle < 0n || !(0, import_viem13.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker)) throw unavailable();
5928
6152
  assertReadCurrent();
5929
6153
  const fees = await client.readContract({
5930
- address: (0, import_viem12.getAddress)(feeTracker),
6154
+ address: (0, import_viem13.getAddress)(feeTracker),
5931
6155
  abi: YIELDSEEKER_FEE_TRACKER_ABI,
5932
6156
  functionName: "getFeesOwed",
5933
6157
  args: [wallet],
@@ -5937,6 +6161,18 @@ var YieldseekerAgent = class {
5937
6161
  const verified = [];
5938
6162
  const verifiedVaults = [];
5939
6163
  let deployed = 0n;
6164
+ const proof = (complete) => ({
6165
+ blockNumber,
6166
+ idle,
6167
+ fees,
6168
+ available: idle + deployed > fees ? idle + deployed - fees : 0n,
6169
+ positions: [...verified],
6170
+ verifiedVaults: [...verifiedVaults],
6171
+ indexedPositions,
6172
+ complete
6173
+ });
6174
+ assertReadCurrent();
6175
+ knownProof = proof(false);
5940
6176
  const collect = (options2, results) => {
5941
6177
  assertReadCurrent();
5942
6178
  const known = this.balanceVaults.get(contextKey) ?? /* @__PURE__ */ new Set();
@@ -5952,7 +6188,7 @@ var YieldseekerAgent = class {
5952
6188
  return;
5953
6189
  }
5954
6190
  const [underlying, shares, withdrawable] = group.map((value) => value.result);
5955
- if (typeof underlying !== "string" || !(0, import_viem12.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n || shares === 0n && withdrawable > 0n) {
6191
+ if (typeof underlying !== "string" || !(0, import_viem13.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n || shares === 0n && withdrawable > 0n) {
5956
6192
  valid2 = false;
5957
6193
  return;
5958
6194
  }
@@ -5969,17 +6205,10 @@ var YieldseekerAgent = class {
5969
6205
  });
5970
6206
  return valid2;
5971
6207
  };
5972
- const allKnownVerified = collect(options, checks);
5973
- const proof = (complete) => ({
5974
- blockNumber,
5975
- idle,
5976
- fees,
5977
- available: idle + deployed > fees ? idle + deployed - fees : 0n,
5978
- positions: [...verified],
5979
- verifiedVaults: [...verifiedVaults],
5980
- indexedPositions,
5981
- complete
5982
- });
6208
+ const checks = await checksRead;
6209
+ assertReadCurrent();
6210
+ if ("error" in checks) throw checks.error;
6211
+ const allKnownVerified = collect(options, checks.value);
5983
6212
  knownProof = proof(false);
5984
6213
  const indexed = new Map(indexedPositions.map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
5985
6214
  const discrepancy = verified.some((position2) => {
@@ -5996,7 +6225,7 @@ var YieldseekerAgent = class {
5996
6225
  assertReadCurrent();
5997
6226
  const eligible = /* @__PURE__ */ new Map();
5998
6227
  for (const option of catalog ?? []) {
5999
- if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() && (0, import_viem12.isAddress)(option.address))
6228
+ if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() && (0, import_viem13.isAddress)(option.address))
6000
6229
  eligible.set(option.address.toLowerCase(), option);
6001
6230
  }
6002
6231
  const extra = [...eligible].filter(([address]) => !candidates.has(address)).map(([, option]) => option);
@@ -6047,7 +6276,7 @@ var YieldseekerAgent = class {
6047
6276
  const key2 = this.contextKey(state, evidence.chainId, evidence.token);
6048
6277
  const generation = this.readGeneration, version = this.portfolioVersion(key2);
6049
6278
  if (read2.readVersion !== version) return void 0;
6050
- const wallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
6279
+ const wallet = (0, import_viem13.getAddress)(context.wallet.walletAddress);
6051
6280
  const asset = YIELDSEEKER_ASSET_METADATA[evidence.token];
6052
6281
  const client = this.getReceiptClient();
6053
6282
  const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
@@ -6055,10 +6284,10 @@ var YieldseekerAgent = class {
6055
6284
  const vaults = [...this.balanceVaults.get(key2) ?? []];
6056
6285
  const [invested, idle, tracker] = await Promise.all([
6057
6286
  remainingVaultBalance(client, wallet, asset.address, vaults, blockNumber),
6058
- client.readContract({ address: asset.address, abi: import_viem12.erc20Abi, functionName: "balanceOf", args: [wallet], blockNumber }),
6287
+ client.readContract({ address: asset.address, abi: import_viem13.erc20Abi, functionName: "balanceOf", args: [wallet], blockNumber }),
6059
6288
  client.readContract({ address: wallet, abi: YIELDSEEKER_AGENT_WALLET_ABI, functionName: "feeTracker", blockNumber })
6060
6289
  ]);
6061
- if (invested === void 0 || typeof idle !== "bigint" || idle < 0n || !(0, import_viem12.isAddress)(tracker) || /^0x0{40}$/i.test(tracker)) return void 0;
6290
+ if (invested === void 0 || typeof idle !== "bigint" || idle < 0n || !(0, import_viem13.isAddress)(tracker) || /^0x0{40}$/i.test(tracker)) return void 0;
6062
6291
  const fees = await client.readContract({
6063
6292
  address: tracker,
6064
6293
  abi: YIELDSEEKER_FEE_TRACKER_ABI,
@@ -6071,7 +6300,7 @@ var YieldseekerAgent = class {
6071
6300
  const total = idle + invested > fees ? idle + invested - fees : 0n;
6072
6301
  const displayed = balance.assetBalances?.find((item) => item.chainId === evidence.chainId && item.asset === evidence.token);
6073
6302
  if (!displayed || !evidence.confirmedAmount || !/^\d+$/.test(evidence.confirmedAmount)) return void 0;
6074
- const amount = (0, import_viem12.parseUnits)(displayed.amount, asset.decimals);
6303
+ const amount = (0, import_viem13.parseUnits)(displayed.amount, asset.decimals);
6075
6304
  if (amount < 0n) return void 0;
6076
6305
  const displayUnit = evidence.token === "USDC" ? 5000n : 500000000000n;
6077
6306
  const movementUnit = BigInt(evidence.confirmedAmount) / 1000n;
@@ -6117,9 +6346,9 @@ var YieldseekerAgent = class {
6117
6346
  if (!context) return void 0;
6118
6347
  const idle = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
6119
6348
  address: YIELDSEEKER_ASSET_METADATA[asset].address,
6120
- abi: import_viem12.erc20Abi,
6349
+ abi: import_viem13.erc20Abi,
6121
6350
  functionName: "balanceOf",
6122
- args: [(0, import_viem12.getAddress)(context.wallet.walletAddress)]
6351
+ args: [(0, import_viem13.getAddress)(context.wallet.walletAddress)]
6123
6352
  }), chainId, "eth_call", this.id);
6124
6353
  this.assertSession(generation, chainId, asset);
6125
6354
  if (typeof idle !== "bigint" || idle < 0n) throw this.invalidResponse("wallet deposit balance");
@@ -6127,7 +6356,7 @@ var YieldseekerAgent = class {
6127
6356
  }
6128
6357
  async getEarnings(state, chainId) {
6129
6358
  this.assertChain(chainId);
6130
- return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
6359
+ return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, { includePositions: false }));
6131
6360
  }
6132
6361
  async getAccountApy(state, chainId, days, tokenSymbol) {
6133
6362
  this.assertChain(chainId);
@@ -6166,7 +6395,7 @@ var YieldseekerAgent = class {
6166
6395
  );
6167
6396
  const vaultAddresses = new Set(
6168
6397
  catalog.flat().filter(
6169
- (yieldOption) => yieldOption.chainId === chainId && (0, import_viem12.isAddress)(yieldOption.address)
6398
+ (yieldOption) => yieldOption.chainId === chainId && (0, import_viem13.isAddress)(yieldOption.address)
6170
6399
  ).map((yieldOption) => yieldOption.address.toLowerCase())
6171
6400
  );
6172
6401
  return mapYieldseekerHistory(contexts, {
@@ -6295,7 +6524,7 @@ var YieldseekerAgent = class {
6295
6524
  `/users/${user.userId}/agents/${agent.agentId}/wallet`
6296
6525
  ).then((walletResponse) => {
6297
6526
  this.assertSession(generation, chainId, asset);
6298
- if (!walletResponse?.agentWallet || !(0, import_viem12.isAddress)(walletResponse.agentWallet.walletAddress)) {
6527
+ if (!walletResponse?.agentWallet || !(0, import_viem13.isAddress)(walletResponse.agentWallet.walletAddress)) {
6299
6528
  throw this.invalidResponse("agent wallet");
6300
6529
  }
6301
6530
  const context = {
@@ -6325,7 +6554,7 @@ var YieldseekerAgent = class {
6325
6554
  this.users.set(key2, persisted);
6326
6555
  return persisted;
6327
6556
  }
6328
- const walletAddress = (0, import_viem12.getAddress)(state.walletAddress);
6557
+ const walletAddress = (0, import_viem13.getAddress)(state.walletAddress);
6329
6558
  const generation = this.readGeneration;
6330
6559
  let user = null;
6331
6560
  try {
@@ -6709,7 +6938,7 @@ var YieldseekerAgent = class {
6709
6938
  this.agentPath(context, "deploy"),
6710
6939
  { method: "POST", body: {} }
6711
6940
  );
6712
- if (!deployed?.agentWallet || !(0, import_viem12.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
6941
+ if (!deployed?.agentWallet || !(0, import_viem13.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
6713
6942
  throw this.invalidResponse("agent deployment", {
6714
6943
  reason: "Deploy did not return the expected Agent Wallet."
6715
6944
  });
@@ -6812,20 +7041,20 @@ var YieldseekerAgent = class {
6812
7041
  }
6813
7042
  async submitTransaction(state, chainId, transaction, onSubmitted, onReceipt) {
6814
7043
  if (this.transactionExecutor) {
6815
- const hash2 = await this.transactionExecutor(state, chainId, transaction);
7044
+ const hash2 = await this.transactionExecutor(state, chainId, transaction, onReceipt);
6816
7045
  onSubmitted?.(hash2);
6817
7046
  return hash2;
6818
7047
  }
6819
7048
  this.assertTransaction(transaction, state, chainId);
6820
- const account = (0, import_viem12.getAddress)(state.walletAddress);
6821
- const walletClient = (0, import_viem12.createWalletClient)({
7049
+ const account = (0, import_viem13.getAddress)(state.walletAddress);
7050
+ const walletClient = (0, import_viem13.createWalletClient)({
6822
7051
  account,
6823
7052
  chain: import_chains4.base,
6824
- transport: (0, import_viem12.custom)(state.provider)
7053
+ transport: (0, import_viem13.custom)(state.provider)
6825
7054
  });
6826
- const walletChainClient = (0, import_viem12.createPublicClient)({
7055
+ const walletChainClient = (0, import_viem13.createPublicClient)({
6827
7056
  chain: import_chains4.base,
6828
- transport: (0, import_viem12.custom)(state.provider)
7057
+ transport: (0, import_viem13.custom)(state.provider)
6829
7058
  });
6830
7059
  await ensureWalletOnChain(
6831
7060
  walletChainClient,
@@ -6835,7 +7064,7 @@ var YieldseekerAgent = class {
6835
7064
  const hash = await walletClient.sendTransaction({
6836
7065
  account,
6837
7066
  chain: import_chains4.base,
6838
- to: (0, import_viem12.getAddress)(transaction.to),
7067
+ to: (0, import_viem13.getAddress)(transaction.to),
6839
7068
  data: transaction.data,
6840
7069
  value: BigInt(transaction.value)
6841
7070
  });
@@ -6939,12 +7168,12 @@ var YieldseekerAgent = class {
6939
7168
  return receipt;
6940
7169
  }
6941
7170
  assertTransaction(transaction, state, chainId) {
6942
- if (!transaction || typeof transaction.from !== "string" || !(0, import_viem12.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem12.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem12.getAddress)(transaction.from) !== (0, import_viem12.getAddress)(state.walletAddress)) {
7171
+ if (!transaction || typeof transaction.from !== "string" || !(0, import_viem13.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem13.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_viem13.getAddress)(transaction.from) !== (0, import_viem13.getAddress)(state.walletAddress)) {
6943
7172
  throw this.invalidResponse("transaction");
6944
7173
  }
6945
7174
  }
6946
7175
  assertAgent(agent) {
6947
- if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem12.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
7176
+ if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem13.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
6948
7177
  throw this.invalidResponse("agent");
6949
7178
  }
6950
7179
  }
@@ -7098,15 +7327,24 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
7098
7327
  }
7099
7328
 
7100
7329
  // src/lib/helpers/withdraw-helper.ts
7101
- var import_viem13 = require("viem");
7330
+ var import_viem14 = require("viem");
7102
7331
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
7103
7332
  const target = asset.toUpperCase();
7104
7333
  return agents.map((agent) => {
7334
+ let invalidBalance = false;
7335
+ const readAmount = (value, raw2 = false) => {
7336
+ if (typeof value !== "string" || !(raw2 ? /^\d+$/ : /^\d+(?:\.\d+)?$/).test(value)) {
7337
+ invalidBalance = true;
7338
+ return 0n;
7339
+ }
7340
+ const [whole, fraction] = value.split(".");
7341
+ return raw2 ? BigInt(value) : (0, import_viem14.parseUnits)(fraction === void 0 ? whole : `${whole}.${fraction.slice(0, decimals)}`, decimals);
7342
+ };
7105
7343
  const agentBalance = aggregated[agent.id];
7106
7344
  const tokenBalance = agentBalance?.tokens.find(
7107
7345
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
7108
7346
  );
7109
- let balance = tokenBalance ? (0, import_viem13.parseUnits)(tokenBalance.amount, decimals) : 0n;
7347
+ let balance = tokenBalance ? readAmount(tokenBalance.amount) : 0n;
7110
7348
  if (agent.balanceComposition === "tokens-plus-positions") {
7111
7349
  const chainNameById = {
7112
7350
  1: "ETHEREUM",
@@ -7118,20 +7356,22 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
7118
7356
  const positionChain = position2.chain.trim().toUpperCase();
7119
7357
  const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
7120
7358
  if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
7121
- balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0, import_viem13.parseUnits)(position2.amount, decimals);
7359
+ balance += position2.withdrawableAmountRaw !== void 0 ? readAmount(position2.withdrawableAmountRaw, true) : readAmount(position2.amount);
7122
7360
  }
7123
7361
  }
7124
7362
  const assetTotal = agentBalance?.assetBalances?.find(
7125
7363
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
7126
7364
  );
7127
7365
  if (assetTotal) {
7128
- const total = (0, import_viem13.parseUnits)(assetTotal.amount, decimals);
7366
+ const total = readAmount(assetTotal.amount);
7129
7367
  if (total < balance) balance = total > 0n ? total : 0n;
7130
7368
  }
7131
- return { agent, balance };
7369
+ return invalidBalance ? { agent, balance: 0n, invalidBalance: true } : { agent, balance };
7132
7370
  });
7133
7371
  }
7134
7372
  function planProportionalShares(balances, requested, totalAvailable) {
7373
+ if (requested < 0n || totalAvailable < 0n || balances.some((entry) => entry.balance < 0n) || balances.reduce((sum, entry) => sum + entry.balance, 0n) !== totalAvailable || requested > totalAvailable)
7374
+ throw new Error("Invalid withdrawal planning amounts.");
7135
7375
  const plans = balances.map(({ agent, balance }) => ({
7136
7376
  agent,
7137
7377
  balance,
@@ -7154,6 +7394,8 @@ function planProportionalShares(balances, requested, totalAvailable) {
7154
7394
  return plans;
7155
7395
  }
7156
7396
  function planDisabledDrain(disabled, requested) {
7397
+ if (requested < 0n || disabled.some((entry) => entry.balance < 0n))
7398
+ throw new Error("Invalid withdrawal planning amounts.");
7157
7399
  const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
7158
7400
  (a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
7159
7401
  );
@@ -7171,6 +7413,7 @@ function planDisabledDrain(disabled, requested) {
7171
7413
  return { plans, remaining };
7172
7414
  }
7173
7415
  function redistributeShare(plans, fromIndex, amount, candidatePool) {
7416
+ if (amount <= 0n) return;
7174
7417
  const pool = candidatePool ?? plans.slice(fromIndex + 1);
7175
7418
  const candidates = pool.filter((c) => c.balance - c.planned > 0n);
7176
7419
  const totalHeadroom = candidates.reduce(
@@ -7197,7 +7440,13 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
7197
7440
  }
7198
7441
  }
7199
7442
  function sumWithdrawnAmount(results) {
7200
- return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
7443
+ return Object.values(results).reduce((sum, result) => sum + (confirmedWithdrawalAmount(result) ?? 0n), 0n);
7444
+ }
7445
+ function confirmedWithdrawalAmount(result) {
7446
+ const evidence = result.evidence;
7447
+ if (evidence && (evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed")) return void 0;
7448
+ const value = evidence ? evidence.confirmedAmount : result.amount;
7449
+ return typeof value === "string" && /^\d+$/.test(value) ? BigInt(value) : void 0;
7201
7450
  }
7202
7451
 
7203
7452
  // src/lib/helpers/account-apy-helper.ts
@@ -7321,14 +7570,14 @@ function aggregateDailyEarnings(series, tokenSymbol) {
7321
7570
  }
7322
7571
 
7323
7572
  // src/client.ts
7324
- var import_viem17 = require("viem");
7573
+ var import_viem18 = require("viem");
7325
7574
  var import_chains5 = require("viem/chains");
7326
7575
 
7327
7576
  // src/lib/sponsored-token-batch.ts
7328
- var import_viem15 = require("viem");
7577
+ var import_viem16 = require("viem");
7329
7578
 
7330
7579
  // src/lib/permit2-batch.ts
7331
- var import_viem14 = require("viem");
7580
+ var import_viem15 = require("viem");
7332
7581
  var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
7333
7582
  var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
7334
7583
  var ERC2612_PERMIT_TYPES = {
@@ -7340,7 +7589,7 @@ var ERC2612_PERMIT_TYPES = {
7340
7589
  { name: "deadline", type: "uint256" }
7341
7590
  ]
7342
7591
  };
7343
- var ERC2612_READ_ABI = (0, import_viem14.parseAbi)([
7592
+ var ERC2612_READ_ABI = (0, import_viem15.parseAbi)([
7344
7593
  "function name() view returns (string)",
7345
7594
  "function version() view returns (string)",
7346
7595
  "function nonces(address owner) view returns (uint256)"
@@ -7378,7 +7627,7 @@ var PERMIT_BATCH_TYPES = {
7378
7627
  { name: "amount", type: "uint256" }
7379
7628
  ]
7380
7629
  };
7381
- var PERMIT2_BATCH_ABI = (0, import_viem14.parseAbi)([
7630
+ var PERMIT2_BATCH_ABI = (0, import_viem15.parseAbi)([
7382
7631
  "struct TokenPermissions { address token; uint256 amount; }",
7383
7632
  "struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
7384
7633
  "struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
@@ -7411,9 +7660,29 @@ function batchTypedData(b, spender) {
7411
7660
  }
7412
7661
  };
7413
7662
  }
7663
+ var BATCH_WITNESS_TYPE = "Deposit witness)Deposit(address[] recipients)TokenPermissions(address token,uint256 amount)";
7664
+ function batchWitness(b) {
7665
+ return (0, import_viem15.hashStruct)({
7666
+ types: PERMIT_BATCH_TYPES,
7667
+ primaryType: "Deposit",
7668
+ data: { recipients: b.transfers.map((t) => t.to) }
7669
+ });
7670
+ }
7671
+ function batchArgs(b) {
7672
+ return [
7673
+ batchPermit(b),
7674
+ b.transfers.map((t) => ({ to: t.to, requestedAmount: BigInt(t.amount) })),
7675
+ b.from,
7676
+ batchWitness(b),
7677
+ BATCH_WITNESS_TYPE,
7678
+ b.signature
7679
+ ];
7680
+ }
7414
7681
 
7415
7682
  // src/lib/sponsored-token-batch.ts
7416
7683
  var memory = /* @__PURE__ */ new Map();
7684
+ var settledRecords = /* @__PURE__ */ new Map();
7685
+ var expiredRecords = /* @__PURE__ */ new Map();
7417
7686
  var inflight = /* @__PURE__ */ new Map();
7418
7687
  var planOf = (transfers) => JSON.stringify(
7419
7688
  transfers.map((t) => ({
@@ -7421,32 +7690,109 @@ var planOf = (transfers) => JSON.stringify(
7421
7690
  amount: BigInt(t.amount).toString()
7422
7691
  }))
7423
7692
  );
7424
- var pendingKey = (i) => `owney.token-batch.v1:${(0, import_viem15.keccak256)((0, import_viem15.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
7693
+ var pendingKey = (i) => `owney.token-batch.v1:${(0, import_viem16.keccak256)((0, import_viem16.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
7425
7694
  function pendingTokenBatch(i) {
7426
7695
  try {
7427
7696
  const saved = read(pendingKey(i));
7428
7697
  if (!saved) return;
7429
7698
  const body = JSON.parse(saved);
7430
- if (!body.serializedTransaction || body.chainId !== i.chainId || !(0, import_viem15.isAddressEqual)(body.from, i.owner) || !(0, import_viem15.isAddressEqual)(body.token, i.token) || !body.transfers?.length) return;
7431
- return { txHash: (0, import_viem15.keccak256)(body.serializedTransaction), transfers: body.transfers.map(({ to, amount }) => ({ to, amount })) };
7699
+ if (!body.serializedTransaction || body.chainId !== i.chainId || !(0, import_viem16.isAddressEqual)(body.from, i.owner) || !(0, import_viem16.isAddressEqual)(body.token, i.token) || !body.transfers?.length) return;
7700
+ return { txHash: (0, import_viem16.keccak256)(body.serializedTransaction), transfers: body.transfers.map(({ to, amount }) => ({ to, amount })) };
7432
7701
  } catch {
7433
7702
  return;
7434
7703
  }
7435
7704
  }
7705
+ async function releaseExpiredTokenBatch(i) {
7706
+ const key2 = pendingKey(i);
7707
+ let timer;
7708
+ let expired = false;
7709
+ const check = async () => {
7710
+ let active = read(key2);
7711
+ if (active && i.expectedTxHash) {
7712
+ const current = JSON.parse(active);
7713
+ if (!current.serializedTransaction || (0, import_viem16.keccak256)(current.serializedTransaction).toLowerCase() !== i.expectedTxHash.toLowerCase())
7714
+ active = void 0;
7715
+ }
7716
+ const archiveKey = i.expectedTxHash ? `${key2}:expired:${i.expectedTxHash.toLowerCase()}` : void 0;
7717
+ const saved = active ?? (archiveKey && (expiredRecords.get(archiveKey) ?? (typeof window === "undefined" ? void 0 : window.localStorage.getItem(archiveKey))));
7718
+ if (!saved) return;
7719
+ const body = JSON.parse(saved);
7720
+ if (!body.serializedTransaction || body.chainId !== i.chainId || !(0, import_viem16.isAddressEqual)(body.from, i.owner) || !(0, import_viem16.isAddressEqual)(body.token, i.token) || !body.transfers?.length) return;
7721
+ const txHash = (0, import_viem16.keccak256)(body.serializedTransaction);
7722
+ if (i.expectedTxHash && txHash.toLowerCase() !== i.expectedTxHash.toLowerCase()) return;
7723
+ const transaction = (0, import_viem16.parseTransaction)(body.serializedTransaction);
7724
+ if (transaction.chainId !== i.chainId || !transaction.to || !(0, import_viem16.isAddressEqual)(transaction.to, BATCH_PERMIT2_ADDRESS) || (transaction.value ?? 0n) !== 0n || transaction.data !== (0, import_viem16.encodeFunctionData)({
7725
+ abi: PERMIT2_BATCH_ABI,
7726
+ functionName: "permitWitnessTransferFrom",
7727
+ args: batchArgs(body)
7728
+ })) return;
7729
+ const nonce = BigInt(body.nonce);
7730
+ const deadline = BigInt(body.deadline);
7731
+ if (nonce < 0n || nonce > MAX_UINT256 || deadline < 0n) return;
7732
+ const block = await i.pub.getBlock({ blockTag: "finalized" });
7733
+ if (expired || typeof block.number !== "bigint" || typeof block.timestamp !== "bigint" || block.timestamp <= deadline) return;
7734
+ const bitmap = await i.pub.readContract({
7735
+ address: BATCH_PERMIT2_ADDRESS,
7736
+ abi: PERMIT2_BATCH_ABI,
7737
+ functionName: "nonceBitmap",
7738
+ args: [i.owner, nonce >> 8n],
7739
+ blockNumber: block.number
7740
+ });
7741
+ if (expired || typeof bitmap !== "bigint" || bitmap < 0n || bitmap > MAX_UINT256 || (bitmap & 1n << (nonce & 255n)) !== 0n || active && read(key2) !== active) return;
7742
+ if (active) {
7743
+ const terminalKey = `${key2}:expired:${txHash.toLowerCase()}`;
7744
+ expiredRecords.set(terminalKey, saved);
7745
+ let persisted = typeof window === "undefined";
7746
+ try {
7747
+ if (typeof window !== "undefined") {
7748
+ window.localStorage.setItem(terminalKey, saved);
7749
+ persisted = window.localStorage.getItem(terminalKey) === saved;
7750
+ }
7751
+ } catch {
7752
+ }
7753
+ if (!persisted) settledRecords.set(key2, saved);
7754
+ else clear(key2, body);
7755
+ }
7756
+ return { txHash, transfers: body.transfers.map(({ to, amount }) => ({ to, amount })), blockNumber: block.number };
7757
+ };
7758
+ try {
7759
+ return await Promise.race([check(), new Promise((resolve) => {
7760
+ timer = setTimeout(() => {
7761
+ expired = true;
7762
+ resolve(void 0);
7763
+ }, 8e3);
7764
+ })]);
7765
+ } catch {
7766
+ return;
7767
+ } finally {
7768
+ expired = true;
7769
+ if (timer) clearTimeout(timer);
7770
+ }
7771
+ }
7436
7772
  function read(key2) {
7437
- return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
7773
+ const value = typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
7774
+ return value && settledRecords.get(key2) === value ? void 0 : value;
7438
7775
  }
7439
- function save(key2, body) {
7440
- const value = JSON.stringify({
7776
+ function storedBody(body) {
7777
+ return JSON.stringify({
7441
7778
  ...body,
7442
7779
  transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
7443
7780
  });
7781
+ }
7782
+ function save(key2, body) {
7783
+ const value = storedBody(body);
7444
7784
  if (typeof window === "undefined") memory.set(key2, value);
7445
7785
  else window.localStorage.setItem(key2, value);
7786
+ settledRecords.delete(key2);
7446
7787
  }
7447
- function clear(key2) {
7448
- if (typeof window === "undefined") memory.delete(key2);
7449
- else window.localStorage.removeItem(key2);
7788
+ function clear(key2, body) {
7789
+ try {
7790
+ if (typeof window === "undefined") memory.delete(key2);
7791
+ else window.localStorage.removeItem(key2);
7792
+ settledRecords.delete(key2);
7793
+ } catch {
7794
+ settledRecords.set(key2, storedBody(body));
7795
+ }
7450
7796
  }
7451
7797
  function sponsorTokenBatch(i) {
7452
7798
  const key2 = pendingKey(i);
@@ -7513,7 +7859,7 @@ async function execute(i, key2, plan) {
7513
7859
  baseUrl: i.baseUrl,
7514
7860
  body
7515
7861
  });
7516
- if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0, import_viem15.keccak256)(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
7862
+ if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0, import_viem16.keccak256)(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
7517
7863
  throw new Error(
7518
7864
  "Sponsorship API did not return a valid prepared permit transaction."
7519
7865
  );
@@ -7524,7 +7870,7 @@ async function execute(i, key2, plan) {
7524
7870
  save(key2, body);
7525
7871
  }
7526
7872
  if (!body.serializedTransaction) {
7527
- const permitHash = body.serializedPermitTransaction ? (0, import_viem15.keccak256)(body.serializedPermitTransaction) : void 0;
7873
+ const permitHash = body.serializedPermitTransaction ? (0, import_viem16.keccak256)(body.serializedPermitTransaction) : void 0;
7528
7874
  let prepared = permitHash ? await postSigned("approval", permitHash) : await post();
7529
7875
  if (prepared?.status === "pending") {
7530
7876
  if (!permitHash || prepared.phase !== "approval" || prepared.txHash !== permitHash)
@@ -7535,7 +7881,7 @@ async function execute(i, key2, plan) {
7535
7881
  if (!prepared) prepared = await post();
7536
7882
  if (prepared.status === "pending" && permitHash && prepared.phase === "approval" && prepared.txHash === permitHash)
7537
7883
  throw pendingError("approval", permitHash);
7538
- if (!prepared.serializedTransaction || (0, import_viem15.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
7884
+ if (!prepared.serializedTransaction || (0, import_viem16.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
7539
7885
  throw new Error(
7540
7886
  "Sponsorship API did not return a valid prepared transaction."
7541
7887
  );
@@ -7546,22 +7892,22 @@ async function execute(i, key2, plan) {
7546
7892
  save(key2, body);
7547
7893
  }
7548
7894
  try {
7549
- i.onSubmitted?.((0, import_viem15.keccak256)(body.serializedTransaction));
7895
+ i.onSubmitted?.((0, import_viem16.keccak256)(body.serializedTransaction));
7550
7896
  } catch {
7551
7897
  }
7552
- const hash = (0, import_viem15.keccak256)(body.serializedTransaction);
7898
+ const hash = (0, import_viem16.keccak256)(body.serializedTransaction);
7553
7899
  const existing = await i.pub.getTransactionReceipt({ hash }).catch(() => void 0);
7554
7900
  if (existing) {
7555
7901
  assertReceipt(existing, "deposit");
7556
7902
  void post().catch(() => void 0);
7557
- clear(key2);
7903
+ clear(key2, body);
7558
7904
  return hash;
7559
7905
  }
7560
7906
  let result = await postSigned("deposit", hash);
7561
- if (result?.status === "pending" && result.phase === "approval" && body.serializedPermitTransaction && result.txHash === (0, import_viem15.keccak256)(body.serializedPermitTransaction)) {
7907
+ if (result?.status === "pending" && result.phase === "approval" && body.serializedPermitTransaction && result.txHash === (0, import_viem16.keccak256)(body.serializedPermitTransaction)) {
7562
7908
  await waitForConfirmation("approval", result.txHash);
7563
7909
  result = await postSigned("deposit", hash);
7564
- if (result?.status === "pending" && result.phase === "approval" && result.txHash === (0, import_viem15.keccak256)(body.serializedPermitTransaction)) throw pendingError("approval", result.txHash);
7910
+ if (result?.status === "pending" && result.phase === "approval" && result.txHash === (0, import_viem16.keccak256)(body.serializedPermitTransaction)) throw pendingError("approval", result.txHash);
7565
7911
  }
7566
7912
  if (result) {
7567
7913
  if (result.txHash !== hash || result.status === "pending" && result.phase !== "deposit")
@@ -7569,11 +7915,11 @@ async function execute(i, key2, plan) {
7569
7915
  if (result.status === "pending") await waitForConfirmation("deposit", hash);
7570
7916
  }
7571
7917
  if (!result || result.status === "pending") void post().catch(() => void 0);
7572
- clear(key2);
7918
+ clear(key2, body);
7573
7919
  return hash;
7574
7920
  } catch (error) {
7575
7921
  if (!body.serializedTransaction && !body.serializedPermitTransaction || rejectedPreparation(error) || error instanceof OwneyError && error.details?.notSubmitted === true)
7576
- clear(key2);
7922
+ clear(key2, body);
7577
7923
  throw error;
7578
7924
  }
7579
7925
  };
@@ -7582,10 +7928,16 @@ async function execute(i, key2, plan) {
7582
7928
  throw new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit changed. Check its status before depositing again.");
7583
7929
  if (saved) {
7584
7930
  const previous = JSON.parse(saved);
7585
- if (previous.chainId !== i.chainId || !(0, import_viem15.isAddressEqual)(previous.from, i.owner) || !(0, import_viem15.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
7931
+ if (previous.chainId !== i.chainId || !(0, import_viem16.isAddressEqual)(previous.from, i.owner) || !(0, import_viem16.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
7586
7932
  throw new Error(
7587
7933
  "Retry the previous token deposit and agent split first to reconcile its status."
7588
7934
  );
7935
+ if (await releaseExpiredTokenBatch({ ...i, expectedTxHash: previous.serializedTransaction ? (0, import_viem16.keccak256)(previous.serializedTransaction) : void 0 }))
7936
+ throw new OwneyError(
7937
+ "SPONSOR_REQUEST_FAILED",
7938
+ "The deposit authorization expired without transferring funds. Start a new deposit.",
7939
+ { notSubmitted: true, safeToFallback: false }
7940
+ );
7589
7941
  i.onApproved?.();
7590
7942
  return send({ ...previous, transfers: i.transfers });
7591
7943
  }
@@ -7599,7 +7951,7 @@ async function execute(i, key2, plan) {
7599
7951
  "DEPOSIT_INSUFFICIENT_BALANCE",
7600
7952
  "Insufficient token balance for this deposit."
7601
7953
  );
7602
- if (allowance < total && (i.chainId !== 8453 || !(0, import_viem15.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
7954
+ if (allowance < total && (i.chainId !== 8453 || !(0, import_viem16.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
7603
7955
  throw new OwneyError(
7604
7956
  "PERMIT2_APPROVAL_REQUIRED",
7605
7957
  "token deposits need a one-time Permit2 approval."
@@ -7778,7 +8130,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
7778
8130
  }
7779
8131
 
7780
8132
  // src/lib/sponsored-calls-deposit.ts
7781
- var import_viem16 = require("viem");
8133
+ var import_viem17 = require("viem");
7782
8134
  var DEFAULT_POLL_INTERVAL_MS = 1500;
7783
8135
  var DEFAULT_MAX_POLLS = 30;
7784
8136
  async function paymasterSupported(provider, owner, chainId) {
@@ -7786,7 +8138,7 @@ async function paymasterSupported(provider, owner, chainId) {
7786
8138
  method: "wallet_getCapabilities",
7787
8139
  params: [owner]
7788
8140
  });
7789
- const forChain = caps?.[(0, import_viem16.toHex)(chainId)] ?? caps?.[String(chainId)];
8141
+ const forChain = caps?.[(0, import_viem17.toHex)(chainId)] ?? caps?.[String(chainId)];
7790
8142
  return Boolean(forChain?.paymasterService?.supported);
7791
8143
  }
7792
8144
  function makeSponsoredCallsCallback(deps) {
@@ -7823,8 +8175,8 @@ function makeSponsoredCallsCallback(deps) {
7823
8175
  const calls = transfers.map((transfer) => ({
7824
8176
  to: token,
7825
8177
  value: "0x0",
7826
- data: (0, import_viem16.encodeFunctionData)({
7827
- abi: import_viem16.erc20Abi,
8178
+ data: (0, import_viem17.encodeFunctionData)({
8179
+ abi: import_viem17.erc20Abi,
7828
8180
  functionName: "transfer",
7829
8181
  args: [transfer.to, BigInt(transfer.amount)]
7830
8182
  })
@@ -7856,7 +8208,7 @@ function makeSponsoredCallsCallback(deps) {
7856
8208
  url.searchParams.append("owneyIntent", intent);
7857
8209
  paymasterUrl = url.toString();
7858
8210
  }
7859
- const requestedId = (0, import_viem16.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
8211
+ const requestedId = (0, import_viem17.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
7860
8212
  const progress = (id, transfer) => {
7861
8213
  try {
7862
8214
  deps.onCallsProgress?.(id, transfers, transfer);
@@ -7873,7 +8225,7 @@ function makeSponsoredCallsCallback(deps) {
7873
8225
  version: "2.0.0",
7874
8226
  id: requestedId,
7875
8227
  from: deps.ownerAddress,
7876
- chainId: (0, import_viem16.toHex)(chainId),
8228
+ chainId: (0, import_viem17.toHex)(chainId),
7877
8229
  atomicRequired: transfers.length > 1,
7878
8230
  calls,
7879
8231
  capabilities: {
@@ -8006,6 +8358,8 @@ var OwneySDK = class {
8006
8358
  */
8007
8359
  disabledAgents = /* @__PURE__ */ new Set();
8008
8360
  state = null;
8361
+ connectionRevision = 0;
8362
+ disconnectingAgents = null;
8009
8363
  apiKey;
8010
8364
  // The org's agent policy, fetched once per client. Applied to each agent on
8011
8365
  // activation (ROUT-229). `undefined` means "not fetched yet", `null` means
@@ -8027,6 +8381,9 @@ var OwneySDK = class {
8027
8381
  recoveredFundingReads = /* @__PURE__ */ new Set();
8028
8382
  fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
8029
8383
  activationChecks = /* @__PURE__ */ new Map();
8384
+ // Protect the SDK entry point as well as the mini-app hook. This owns only
8385
+ // a running call, never a saved/pending provider outcome or a later retry.
8386
+ withdrawalSubmissions = /* @__PURE__ */ new Set();
8030
8387
  readAgent(agent, method, fetch2, params = null) {
8031
8388
  const key2 = JSON.stringify([
8032
8389
  this.state?.walletAddress.toLowerCase() ?? null,
@@ -8059,9 +8416,10 @@ var OwneySDK = class {
8059
8416
  */
8060
8417
  // TODO: replace with a proper EIP-1193 Provider type
8061
8418
  async connect(provider) {
8062
- const accounts = await provider.request({
8063
- method: "eth_accounts"
8064
- });
8419
+ const revision = ++this.connectionRevision;
8420
+ const accounts = await provider.request({ method: "eth_accounts" });
8421
+ if (revision !== this.connectionRevision)
8422
+ throw new OwneyError("NOT_CONNECTED", "Wallet connection changed while connecting.");
8065
8423
  const walletAddress = accounts[0];
8066
8424
  if (!walletAddress) {
8067
8425
  throw new OwneyError(
@@ -8069,33 +8427,46 @@ var OwneySDK = class {
8069
8427
  "No accounts found. Ensure the wallet is unlocked and connected."
8070
8428
  );
8071
8429
  }
8072
- this.agentReads.clearInFlight();
8073
- this.activationChecks.clear();
8074
- this.fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
8430
+ const replacingOwner = this.state !== null && this.state.walletAddress.toLowerCase() !== walletAddress.toLowerCase();
8431
+ this.clearConnectionState();
8432
+ await (replacingOwner ? this.disconnectAgents() : this.disconnectingAgents);
8433
+ if (revision !== this.connectionRevision)
8434
+ throw new OwneyError("NOT_CONNECTED", "Wallet connection changed while connecting.");
8075
8435
  this.state = { provider, walletAddress, chainId: null };
8076
- this.cachedSponsoredCallback = null;
8077
- this.cachedWethSponsoredCallback = null;
8078
- this.cachedSponsoredCallsCallbacks.clear();
8079
8436
  }
8080
8437
  /**
8081
8438
  * Disconnect from all agents and clear connection state.
8082
8439
  * After calling this, `connect()` must be called again before using agent methods.
8083
8440
  */
8084
8441
  async disconnect() {
8442
+ ++this.connectionRevision;
8443
+ this.clearConnectionState();
8444
+ await this.disconnectAgents();
8445
+ }
8446
+ clearConnectionState() {
8085
8447
  this.state = null;
8086
8448
  this.fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
8087
- for (const agent of this.agents.values()) {
8088
- await agent.disconnect();
8089
- }
8090
8449
  this.activeAgents.clear();
8091
- this.disabledAgents.clear();
8092
8450
  this.agentReads.clearInFlight();
8093
8451
  this.activationChecks.clear();
8094
- this.state = null;
8095
8452
  this.cachedSponsoredCallback = null;
8096
8453
  this.cachedWethSponsoredCallback = null;
8097
8454
  this.cachedSponsoredCallsCallbacks.clear();
8098
8455
  }
8456
+ disconnectAgents() {
8457
+ if (this.disconnectingAgents) return this.disconnectingAgents;
8458
+ const pending = Promise.allSettled([...this.agents.values()].map(async (agent) => agent.disconnect())).then((results) => {
8459
+ const failure = results.find((result) => result.status === "rejected");
8460
+ if (failure?.status === "rejected") throw failure.reason;
8461
+ });
8462
+ this.disconnectingAgents = pending;
8463
+ void pending.then(() => {
8464
+ if (this.disconnectingAgents === pending) this.disconnectingAgents = null;
8465
+ }, () => {
8466
+ if (this.disconnectingAgents === pending) this.disconnectingAgents = null;
8467
+ });
8468
+ return pending;
8469
+ }
8099
8470
  isAgentDisabled(agentId) {
8100
8471
  return this.disabledAgents.has(agentId);
8101
8472
  }
@@ -8158,14 +8529,14 @@ var OwneySDK = class {
8158
8529
  // PublicClient/WalletClient param types — structurally identical at
8159
8530
  // runtime, but the two share a name TS treats as unrelated.
8160
8531
  getPublicClient: (cid) => this.getPaidRpcClient(cid),
8161
- getWalletChainClient: (cid) => (0, import_viem17.createPublicClient)({
8532
+ getWalletChainClient: (cid) => (0, import_viem18.createPublicClient)({
8162
8533
  chain: VIEM_CHAIN2[cid],
8163
- transport: (0, import_viem17.custom)(provider)
8534
+ transport: (0, import_viem18.custom)(provider)
8164
8535
  }),
8165
- getWalletClient: (cid) => (0, import_viem17.createWalletClient)({
8536
+ getWalletClient: (cid) => (0, import_viem18.createWalletClient)({
8166
8537
  account: owner,
8167
8538
  chain: VIEM_CHAIN2[cid],
8168
- transport: (0, import_viem17.custom)(provider)
8539
+ transport: (0, import_viem18.custom)(provider)
8169
8540
  })
8170
8541
  });
8171
8542
  if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedSponsoredCallback = callback;
@@ -8213,14 +8584,14 @@ var OwneySDK = class {
8213
8584
  // PublicClient/WalletClient param types — structurally identical at
8214
8585
  // runtime, but the two share a name TS treats as unrelated.
8215
8586
  getPublicClient: (cid) => this.getPaidRpcClient(cid),
8216
- getWalletChainClient: (cid) => (0, import_viem17.createPublicClient)({
8587
+ getWalletChainClient: (cid) => (0, import_viem18.createPublicClient)({
8217
8588
  chain: VIEM_CHAIN2[cid],
8218
- transport: (0, import_viem17.custom)(provider)
8589
+ transport: (0, import_viem18.custom)(provider)
8219
8590
  }),
8220
- getWalletClient: (cid) => (0, import_viem17.createWalletClient)({
8591
+ getWalletClient: (cid) => (0, import_viem18.createWalletClient)({
8221
8592
  account: owner,
8222
8593
  chain: VIEM_CHAIN2[cid],
8223
- transport: (0, import_viem17.custom)(provider)
8594
+ transport: (0, import_viem18.custom)(provider)
8224
8595
  })
8225
8596
  });
8226
8597
  if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedWethSponsoredCallback = callback;
@@ -8531,6 +8902,12 @@ var OwneySDK = class {
8531
8902
  */
8532
8903
  async deposit(options) {
8533
8904
  const { amount, asset, depositCallback, agentId, onApproved } = options;
8905
+ if (!isFundingAmount(amount, true))
8906
+ throw new OwneyError(
8907
+ "DEPOSIT_INVALID_AMOUNT",
8908
+ "Deposit amount must be an unsigned base-unit integer.",
8909
+ { phase: "prepare", rejectedBeforeSubmission: true }
8910
+ );
8534
8911
  const prepared = /* @__PURE__ */ new Map();
8535
8912
  const paidFallbackAgents = /* @__PURE__ */ new Set();
8536
8913
  const onProgress = options.onProgress ? (evidence) => {
@@ -8558,6 +8935,47 @@ var OwneySDK = class {
8558
8935
  throw new OwneyError("DEPOSIT_IN_PROGRESS", "This deposit cannot resume the saved relayer transaction.");
8559
8936
  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));
8560
8937
  try {
8938
+ if (options.resumeTxHash) {
8939
+ const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
8940
+ const pending = token && pendingTokenBatch({
8941
+ apiKey: this.apiKey,
8942
+ baseUrl: this.routingApiBaseUrl,
8943
+ owner: state.walletAddress,
8944
+ chainId,
8945
+ token: token.address
8946
+ });
8947
+ const invalid2 = () => new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit does not match this request. Check its status before depositing again.");
8948
+ if (!pending || pending.txHash.toLowerCase() !== options.resumeTxHash.toLowerCase() || pending.transfers.some((transfer) => !/^\d+$/.test(transfer.amount) || BigInt(transfer.amount) <= 0n) || pending.transfers.reduce((sum, transfer) => sum + BigInt(transfer.amount), 0n) !== BigInt(amount)) throw invalid2();
8949
+ const candidates = agentId ? [this.getAgent(agentId)] : this.getEligibleAgents(chainId, asset);
8950
+ const wallets = await Promise.all(candidates.map(async (agent) => {
8951
+ this.validateAssetSupport(agent, chainId, asset);
8952
+ const wallet = await (agent.getFundingWallet ? agent.getFundingWallet(state, chainId, asset) : agent.getUserProfile(state, chainId).then((profile) => profile.smartWallet)).catch(() => void 0);
8953
+ return { agent, wallet };
8954
+ }));
8955
+ if (this.state !== state || this.state.chainId !== chainId)
8956
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while recovering the deposit.");
8957
+ const usedAgents = /* @__PURE__ */ new Set();
8958
+ const legs = pending.transfers.map((transfer) => {
8959
+ const matches = wallets.filter((value) => value.wallet?.toLowerCase() === transfer.to.toLowerCase());
8960
+ if (matches.length !== 1 || usedAgents.has(matches[0].agent.id)) throw invalid2();
8961
+ const { agent } = matches[0];
8962
+ usedAgents.add(agent.id);
8963
+ return {
8964
+ id: agent.id,
8965
+ amount: transfer.amount,
8966
+ run: (callback) => withFailureReporting(
8967
+ this.apiKey,
8968
+ agent.id,
8969
+ () => agent.deposit(state, chainId, transfer.amount, asset, callback, onProgress),
8970
+ this.routingApiBaseUrl
8971
+ )
8972
+ };
8973
+ });
8974
+ const batchTransfer2 = getDepositBatchTransfer(effectiveCallback);
8975
+ if (!batchTransfer2) throw invalid2();
8976
+ const result = await runAgentDepositBatch(chainId, legs, batchTransfer2);
8977
+ return agentId ? result.agentResults[agentId] : result;
8978
+ }
8561
8979
  if (agentId) {
8562
8980
  if (this.isAgentDisabled(agentId)) {
8563
8981
  throw new OwneyError(
@@ -8974,9 +9392,36 @@ var OwneySDK = class {
8974
9392
  const token = SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token);
8975
9393
  if (!token) throw new OwneyError("CHAIN_UNSUPPORTED", "Unsupported funding asset.");
8976
9394
  const rpc2 = this.getPaidRpcClient(chainId);
8977
- const receipt = await rpc2.getTransactionReceipt({ hash: evidence.txHash });
9395
+ let receipt;
9396
+ try {
9397
+ receipt = await rpc2.getTransactionReceipt({ hash: evidence.txHash });
9398
+ } catch (error) {
9399
+ if (direction === "deposit") {
9400
+ const expired = await releaseExpiredTokenBatch({
9401
+ apiKey: this.apiKey,
9402
+ baseUrl: this.routingApiBaseUrl,
9403
+ owner: state.walletAddress,
9404
+ chainId,
9405
+ token: token.address,
9406
+ pub: rpc2,
9407
+ expectedTxHash: evidence.txHash
9408
+ });
9409
+ assertCurrent();
9410
+ if (expired?.transfers.some((transfer) => transfer.to.toLowerCase() === agentWallet.toLowerCase() && transfer.amount === evidence.intendedAmount))
9411
+ return {
9412
+ ...evidence,
9413
+ agentWallet,
9414
+ transfer: "failed",
9415
+ delivery: "not-applicable",
9416
+ registration: "not-applicable",
9417
+ confirmedAmount: "0",
9418
+ blockNumber: expired.blockNumber.toString()
9419
+ };
9420
+ }
9421
+ throw error;
9422
+ }
8978
9423
  assertCurrent();
8979
- let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction);
9424
+ let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction, options);
8980
9425
  if (direction === "withdraw" && evidence.agentId === "yieldseeker" && verified.transfer === "unknown" && verified.confirmedAmount && BigInt(verified.confirmedAmount) > 0n) {
8981
9426
  const transaction = await rpc2.getTransaction({ hash: evidence.txHash });
8982
9427
  assertCurrent();
@@ -9150,9 +9595,11 @@ var OwneySDK = class {
9150
9595
  return page;
9151
9596
  }
9152
9597
  async recoverDepositRegistration(evidence) {
9598
+ const { state, assertCurrent } = this.captureReadScope(evidence.chainId);
9153
9599
  const verified = await this.inspectFunding(evidence, "deposit");
9600
+ assertCurrent();
9154
9601
  if (verified.agentId !== "zyfai") return verified;
9155
- return this.getAgent("zyfai").retryDepositRegistration(this.requireState(), verified);
9602
+ return this.getAgent("zyfai").retryDepositRegistration(state, verified);
9156
9603
  }
9157
9604
  // --- Fund operations ---
9158
9605
  /** On-chain version and ownership status for this owner's Yieldseeker agent. */
@@ -9198,10 +9645,24 @@ var OwneySDK = class {
9198
9645
  * @returns {AgentWithdrawResult} for a single agent, or {OwneyWithdrawResult} with per-agent results
9199
9646
  */
9200
9647
  async withdraw(options) {
9648
+ if (options.amount !== void 0 && !isFundingAmount(options.amount))
9649
+ throw new OwneyError(
9650
+ "WITHDRAW_INVALID_AMOUNT",
9651
+ "Withdrawal amount must be a positive base-unit integer.",
9652
+ { phase: "prepare", rejectedBeforeSubmission: true }
9653
+ );
9201
9654
  const state = this.requireState();
9202
9655
  const chainId = this.requireChainId();
9656
+ const submissionKey = `${state.walletAddress.toLowerCase()}:${chainId}:${options.asset.toUpperCase()}`;
9657
+ if (this.withdrawalSubmissions.has(submissionKey))
9658
+ throw new OwneyError(
9659
+ "WITHDRAW_IN_PROGRESS",
9660
+ "A withdrawal request is already running.",
9661
+ { chainId, asset: options.asset, phase: "prepare", rejectedBeforeSubmission: true }
9662
+ );
9203
9663
  const ids = options.agentId ? [options.agentId] : this.getActiveAgentIds().filter((id) => !options.excludeAgentIds?.includes(id));
9204
9664
  const attempted = /* @__PURE__ */ new Set();
9665
+ this.withdrawalSubmissions.add(submissionKey);
9205
9666
  try {
9206
9667
  return await this.withdrawFromAgents(options, state, chainId, attempted);
9207
9668
  } catch (error) {
@@ -9218,6 +9679,8 @@ var OwneySDK = class {
9218
9679
  });
9219
9680
  }
9220
9681
  throw error;
9682
+ } finally {
9683
+ this.withdrawalSubmissions.delete(submissionKey);
9221
9684
  }
9222
9685
  }
9223
9686
  async withdrawFromAgents(options, state, chainId, attempted) {
@@ -9256,7 +9719,19 @@ var OwneySDK = class {
9256
9719
  };
9257
9720
  };
9258
9721
  const submissionEvidence = /* @__PURE__ */ new Map();
9722
+ const pendingSubmissions = () => Object.fromEntries([...submissionEvidence].filter(([id, evidence]) => evidence.agentId === id && evidence.chainId === chainId && evidence.token === token && evidence.walletAddress.toLowerCase() === state.walletAddress.toLowerCase() && evidence.transfer !== "failed" && evidence.delivery !== "failed" && (evidence.transfer === "unknown" || evidence.txHash || evidence.userOpHash || evidence.callsId)));
9723
+ const throwUnconfirmed = (agentErrors2) => {
9724
+ const pending = pendingSubmissions();
9725
+ if (Object.keys(pending).length)
9726
+ throw new OwneyError(
9727
+ "WITHDRAW_UNCONFIRMED",
9728
+ "The withdrawal was submitted, but its outcome could not be confirmed yet.",
9729
+ { asset, amount, agentErrors: agentErrors2, agentFailures, pendingSubmissions: pending }
9730
+ );
9731
+ };
9259
9732
  const submit = async (agent, requested2) => {
9733
+ if (this.state !== state || this.state.chainId !== chainId)
9734
+ throw new OwneyError("NOT_CONNECTED", "Wallet connection changed before withdrawal submission.");
9260
9735
  attempted.add(agent.id);
9261
9736
  let evidence = {
9262
9737
  agentId: agent.id,
@@ -9268,12 +9743,16 @@ var OwneySDK = class {
9268
9743
  registration: "not-applicable",
9269
9744
  delivery: "pending"
9270
9745
  };
9746
+ let adapterReported = false;
9271
9747
  const progress = (next) => {
9748
+ if (next.agentId !== agent.id || next.chainId !== chainId || next.token !== token || next.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase()) return;
9749
+ adapterReported = true;
9272
9750
  evidence = { ...evidence, ...next };
9273
9751
  submissionEvidence.set(agent.id, evidence);
9274
9752
  notifyFunding(options.onProgress, evidence);
9275
9753
  };
9276
9754
  progress(evidence);
9755
+ adapterReported = false;
9277
9756
  try {
9278
9757
  if (options.onBeforeSubmission)
9279
9758
  return await agent.withdraw(
@@ -9287,7 +9766,7 @@ var OwneySDK = class {
9287
9766
  );
9288
9767
  return await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
9289
9768
  } catch (error) {
9290
- if (!evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
9769
+ if (!adapterReported && !evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
9291
9770
  progress({ ...evidence, transfer: "failed", delivery: "failed" });
9292
9771
  throw error;
9293
9772
  }
@@ -9348,6 +9827,7 @@ var OwneySDK = class {
9348
9827
  }
9349
9828
  }
9350
9829
  if (Object.keys(results2).length === 0) {
9830
+ throwUnconfirmed(agentErrors2);
9351
9831
  if (Object.keys(agentErrors2).length === 0)
9352
9832
  throw new OwneyError(
9353
9833
  "WITHDRAW_INSUFFICIENT_BALANCE",
@@ -9368,7 +9848,7 @@ var OwneySDK = class {
9368
9848
  );
9369
9849
  }
9370
9850
  const amountsKnown = Object.values(results2).every(
9371
- (result) => typeof result.amount === "string" && /^\d+$/.test(result.amount)
9851
+ (result) => confirmedWithdrawalAmount(result) !== void 0
9372
9852
  );
9373
9853
  return {
9374
9854
  agentResult: results2,
@@ -9387,6 +9867,13 @@ var OwneySDK = class {
9387
9867
  asset,
9388
9868
  assetInfo.decimals
9389
9869
  );
9870
+ for (const entry of balances) if (entry.invalidBalance) {
9871
+ if (!unavailableAgents.includes(entry.agent)) unavailableAgents.push(entry.agent);
9872
+ aggregated.agentErrors = {
9873
+ ...aggregated.agentErrors,
9874
+ [entry.agent.id]: "Provider returned an invalid withdrawal balance."
9875
+ };
9876
+ }
9390
9877
  const indexedAvailable = balances.reduce((sum, entry) => sum + entry.balance, 0n);
9391
9878
  await Promise.all(balances.map(async (entry) => {
9392
9879
  if (entry.balance !== 0n && indexedAvailable >= requested || !entry.agent.getWithdrawableBalance) return;
@@ -9399,7 +9886,7 @@ var OwneySDK = class {
9399
9886
  if (this.isUserRejectedWithdrawal(originalFailure)) throw originalFailure;
9400
9887
  if (entry.balance !== 0n && entry.agent.id !== "yieldseeker") {
9401
9888
  const total = aggregated.agentBalances[entry.agent.id]?.assetBalances?.find((value) => value.chainId === chainId && value.asset.toUpperCase() === token);
9402
- if (!total || (0, import_viem17.parseUnits)(total.amount, assetInfo.decimals) <= entry.balance) return;
9889
+ if (!total || (0, import_viem18.parseUnits)(total.amount, assetInfo.decimals) <= entry.balance) return;
9403
9890
  }
9404
9891
  try {
9405
9892
  const otherAvailable = indexedAvailable - entry.balance;
@@ -9426,12 +9913,15 @@ var OwneySDK = class {
9426
9913
  throw new OwneyError("NOT_CONNECTED", "Account changed while planning withdrawal.");
9427
9914
  const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
9428
9915
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
9429
- if (totalAvailable === 0n && unavailableAgents.length > 0) {
9916
+ if (totalAvailable < requested && unavailableAgents.length > 0) {
9430
9917
  throw new OwneyError(
9431
9918
  "WITHDRAW_BALANCE_UNAVAILABLE",
9432
- `Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
9919
+ `Cannot cover the requested withdrawal with verified available balances: ${unavailableAgentIds.join(", ")}.`,
9433
9920
  {
9434
9921
  asset,
9922
+ requested: amount,
9923
+ available: totalAvailable.toString(),
9924
+ rejectedBeforeSubmission: true,
9435
9925
  unavailableAgents: unavailableAgentIds,
9436
9926
  agentErrors: aggregated.agentErrors
9437
9927
  }
@@ -9448,7 +9938,7 @@ var OwneySDK = class {
9448
9938
  }
9449
9939
  );
9450
9940
  }
9451
- const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
9941
+ const plannedTarget = requested;
9452
9942
  const disabledBalances = balances.filter(
9453
9943
  (b) => this.isAgentDisabled(b.agent.id)
9454
9944
  );
@@ -9467,13 +9957,23 @@ var OwneySDK = class {
9467
9957
  }));
9468
9958
  const plans = [...disabledPlans, ...enabledPlans];
9469
9959
  const orderedPlans = this.orderAgentsForWithdrawal(plans.map((p) => p.agent)).map((agent) => plans.find((plan) => plan.agent === agent));
9960
+ const assertPlan = () => {
9961
+ if (orderedPlans.some((plan) => plan.planned < 0n || plan.planned > plan.balance) || orderedPlans.reduce((sum, plan) => sum + plan.planned, 0n) > plannedTarget)
9962
+ throw new OwneyError(
9963
+ "WITHDRAW_BALANCE_UNAVAILABLE",
9964
+ "Withdrawal plan failed amount validation.",
9965
+ { asset, rejectedBeforeSubmission: true }
9966
+ );
9967
+ };
9968
+ assertPlan();
9470
9969
  const results = {};
9471
9970
  const agentErrors = {
9472
9971
  ...aggregated.agentErrors ?? {}
9473
9972
  };
9474
9973
  for (let i = 0; i < orderedPlans.length; i++) {
9475
9974
  const p = orderedPlans[i];
9476
- if (p.planned === 0n) continue;
9975
+ assertPlan();
9976
+ if (p.planned <= 0n) continue;
9477
9977
  try {
9478
9978
  results[p.agent.id] = await submit(p.agent, p.planned.toString());
9479
9979
  notifyAgentResult(p.agent.id, results[p.agent.id]);
@@ -9497,6 +9997,7 @@ var OwneySDK = class {
9497
9997
  }
9498
9998
  }
9499
9999
  if (Object.keys(results).length === 0) {
10000
+ throwUnconfirmed(agentErrors);
9500
10001
  throw new OwneyError(
9501
10002
  "WITHDRAW_ALL_FAILED",
9502
10003
  `Failed to withdraw "${amount}" of "${asset}" from all eligible agents.`,
@@ -9514,13 +10015,17 @@ var OwneySDK = class {
9514
10015
  const adjustment = result?.feeAdjustment;
9515
10016
  const evidence = result?.evidence;
9516
10017
  const submitted = submissionEvidence.get(plan.agent.id);
9517
- 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;
10018
+ 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 || ![result.amount, adjustment.grossAmount].includes(evidence.intendedAmount) || ![result.amount, adjustment.grossAmount].includes(submitted.intendedAmount)) return total;
9518
10019
  const fee = BigInt(adjustment.feeAmount);
9519
10020
  const net = BigInt(result.amount);
9520
10021
  const gross = BigInt(adjustment.grossAmount);
9521
10022
  return fee > 0n && net > 0n && gross === plan.planned && net + fee === gross ? total + fee : total;
9522
10023
  }, 0n);
9523
- if (withdrawn + verifiedFeeDeductions < requested) {
10024
+ const pendingAmount = orderedPlans.reduce((sum, plan) => {
10025
+ const evidence = results[plan.agent.id]?.evidence;
10026
+ return evidence && (evidence.transfer === "pending" || evidence.transfer === "unknown") && evidence.delivery !== "failed" ? sum + plan.planned : sum;
10027
+ }, 0n);
10028
+ if (withdrawn + verifiedFeeDeductions + pendingAmount < requested) {
9524
10029
  throw new OwneyError(
9525
10030
  "WITHDRAW_PARTIAL_FAILURE",
9526
10031
  `Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
@@ -9528,6 +10033,7 @@ var OwneySDK = class {
9528
10033
  asset,
9529
10034
  requested: amount,
9530
10035
  withdrawn: withdrawn.toString(),
10036
+ pendingAmount: pendingAmount.toString(),
9531
10037
  partialResults: results,
9532
10038
  agentErrors,
9533
10039
  agentFailures,
@@ -9535,7 +10041,7 @@ var OwneySDK = class {
9535
10041
  }
9536
10042
  );
9537
10043
  }
9538
- return { agentResult: results, totalWithdrawn: withdrawn.toString() };
10044
+ return { agentResult: results, totalWithdrawn: Object.values(results).every((result) => confirmedWithdrawalAmount(result) !== void 0) ? withdrawn.toString() : null };
9539
10045
  }
9540
10046
  // --- Portfolio reads ---
9541
10047
  /**
@@ -9993,16 +10499,16 @@ var OwneySDK = class {
9993
10499
  );
9994
10500
  }
9995
10501
  const provider = this.requireConnectedProvider();
9996
- const walletChainClient = (0, import_viem17.createPublicClient)({
10502
+ const walletChainClient = (0, import_viem18.createPublicClient)({
9997
10503
  chain: VIEM_CHAIN2[chainId],
9998
- transport: (0, import_viem17.custom)(provider)
10504
+ transport: (0, import_viem18.custom)(provider)
9999
10505
  });
10000
10506
  const publicClient = this.getPaidRpcClient(chainId);
10001
10507
  const approvalAmount = permit2ApprovalAmount(requiredAmount);
10002
- const wallet = (0, import_viem17.createWalletClient)({
10508
+ const wallet = (0, import_viem18.createWalletClient)({
10003
10509
  account: state.walletAddress,
10004
10510
  chain: VIEM_CHAIN2[chainId],
10005
- transport: (0, import_viem17.custom)(provider)
10511
+ transport: (0, import_viem18.custom)(provider)
10006
10512
  });
10007
10513
  await ensureWalletOnChain(walletChainClient, wallet, chainId);
10008
10514
  const hash = await wallet.writeContract({
@@ -10164,7 +10670,7 @@ var OwneySDK = class {
10164
10670
  };
10165
10671
 
10166
10672
  // src/agents/zyfai/zyfai.siwx.ts
10167
- var import_viem18 = require("viem");
10673
+ var import_viem19 = require("viem");
10168
10674
  var import_siwe2 = require("siwe");
10169
10675
  var import_sdk2 = require("@zyfai/sdk");
10170
10676
 
@@ -10291,7 +10797,7 @@ function buildSIWXConfig(deps) {
10291
10797
  issuedAt,
10292
10798
  toString() {
10293
10799
  return new import_siwe2.SiweMessage({
10294
- address: (0, import_viem18.getAddress)(accountAddress),
10800
+ address: (0, import_viem19.getAddress)(accountAddress),
10295
10801
  chainId: numericChainId(chainId),
10296
10802
  domain,
10297
10803
  uri,