@owney/sdk 0.7.25-beta.18 → 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" };
2435
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" };
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,
@@ -3769,6 +3923,35 @@ function mapYieldseekerEarnings(contexts) {
3769
3923
  tokens
3770
3924
  };
3771
3925
  }
3926
+ function mapYieldseekerDailyEarnings(walletAddress, contexts, days, now = Date.now()) {
3927
+ const today = new Date(now).toISOString().slice(0, 10);
3928
+ const dayCount = { "7D": 7, "14D": 14, "30D": 30 }[days];
3929
+ const cutoff = new Date(Date.parse(today) - dayCount * 864e5).toISOString().slice(0, 10);
3930
+ const assets = contexts.flatMap((context) => {
3931
+ const snapshots = context.historic?.dailyYieldSnapshots;
3932
+ if (!Array.isArray(snapshots)) invalid("historic position", "missing daily yield snapshots");
3933
+ const byDate = /* @__PURE__ */ new Map();
3934
+ for (const snapshot of snapshots) {
3935
+ const time = Date.parse(snapshot.date);
3936
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(snapshot.date) || !Number.isFinite(time) || new Date(time).toISOString().slice(0, 10) !== snapshot.date) {
3937
+ invalid("historic position", "expected a UTC snapshot date");
3938
+ }
3939
+ if (snapshot.date > today) continue;
3940
+ if (snapshot.baseAssetDecimals !== YIELDSEEKER_ASSET_METADATA[context.asset].decimals) {
3941
+ invalid("historic position", "base asset decimals do not match the selected asset");
3942
+ }
3943
+ const amount = Number(decimal(snapshot.cumulativeYieldBase, snapshot.baseAssetDecimals, "historic position"));
3944
+ if (!Number.isFinite(amount)) invalid("historic position", "expected finite cumulative yield");
3945
+ byDate.set(snapshot.date, { date: snapshot.date, amount });
3946
+ }
3947
+ const sorted = [...byDate.values()].sort((a, b) => a.date.localeCompare(b.date));
3948
+ const window2 = sorted.filter((point) => point.date >= cutoff);
3949
+ if (!window2.length) return [];
3950
+ const opening = sorted.filter((point) => point.date < cutoff).at(-1);
3951
+ return [{ asset: context.asset, points: opening ? [opening, ...window2] : window2 }];
3952
+ }).sort((a, b) => a.asset.localeCompare(b.asset));
3953
+ return { walletAddress, chainId: 8453, assets };
3954
+ }
3772
3955
  function apyForDays(context, days, now) {
3773
3956
  if (days === "7D") return percent(context.snapshot.apy7d);
3774
3957
  if (days === "30D") return percent(context.snapshot.apy30d);
@@ -3884,7 +4067,7 @@ function allocationEntry(action, context) {
3884
4067
  const leg = deposit;
3885
4068
  const symbol = typeof leg.assetSymbol === "string" ? leg.assetSymbol.toUpperCase() : "";
3886
4069
  if (symbol !== context.asset && !(context.asset === "WETH" && symbol === "ETH")) return void 0;
3887
- 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;
3888
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;
3889
4072
  if (rawAmount === void 0 || rawAmount <= 0n) return void 0;
3890
4073
  const option = context.positions.find((position2) => position2.yieldOption.address.toLowerCase() === leg.vaultAddress.toLowerCase())?.yieldOption;
@@ -3892,7 +4075,7 @@ function allocationEntry(action, context) {
3892
4075
  protocol: typeof leg.vaultProvider === "string" && leg.vaultProvider ? leg.vaultProvider : option?.provider || "Vault",
3893
4076
  pool: typeof leg.vaultName === "string" && leg.vaultName ? leg.vaultName : option?.name,
3894
4077
  tokenSymbol: context.asset,
3895
- 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)
3896
4079
  });
3897
4080
  amountRaw += rawAmount;
3898
4081
  }
@@ -3911,7 +4094,7 @@ function allocationEntry(action, context) {
3911
4094
  tokenSymbol: context.asset,
3912
4095
  agentWallet: context.wallet.walletAddress,
3913
4096
  amountRaw: String(amountRaw),
3914
- 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)
3915
4098
  }]
3916
4099
  };
3917
4100
  }
@@ -4035,7 +4218,7 @@ function mapYieldseekerHistory(contexts, options) {
4035
4218
  const known = new Set(target.logIndexes ?? []);
4036
4219
  if (tx.logIndexes?.some((index) => !known.has(index))) {
4037
4220
  target.amountRaw = String(BigInt(target.amountRaw ?? "0") + BigInt(tx.amountRaw));
4038
- target.amount = (0, import_viem8.formatUnits)(
4221
+ target.amount = (0, import_viem9.formatUnits)(
4039
4222
  BigInt(target.amountRaw),
4040
4223
  YIELDSEEKER_ASSET_METADATA[target.tokenSymbol].decimals
4041
4224
  );
@@ -4116,7 +4299,7 @@ function mapYieldseekerAgentApy(options, days) {
4116
4299
  }
4117
4300
 
4118
4301
  // src/lib/remaining-vault-balance.ts
4119
- var import_viem9 = require("viem");
4302
+ var import_viem10 = require("viem");
4120
4303
  async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber) {
4121
4304
  const addresses = [...new Set(vaults.map((value) => value.toLowerCase()))];
4122
4305
  if (addresses.length > 64) return void 0;
@@ -4127,8 +4310,8 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
4127
4310
  blockNumber,
4128
4311
  allowFailure: true,
4129
4312
  contracts: batch.map((address) => ({
4130
- address: (0, import_viem9.getAddress)(address),
4131
- abi: import_viem9.erc20Abi,
4313
+ address: (0, import_viem10.getAddress)(address),
4314
+ abi: import_viem10.erc20Abi,
4132
4315
  functionName: "balanceOf",
4133
4316
  args: [wallet]
4134
4317
  }))
@@ -4138,15 +4321,15 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
4138
4321
  for (let index = 0; index < batch.length; index++) {
4139
4322
  const result = balances[index];
4140
4323
  if (result?.status !== "success" || typeof result.result !== "bigint" || result.result < 0n) return void 0;
4141
- if (result.result > 0n) funded.push({ address: (0, import_viem9.getAddress)(batch[index]), shares: result.result });
4324
+ if (result.result > 0n) funded.push({ address: (0, import_viem10.getAddress)(batch[index]), shares: result.result });
4142
4325
  }
4143
4326
  if (!funded.length) return 0n;
4144
4327
  const valuations = await client.multicall({
4145
4328
  blockNumber,
4146
4329
  allowFailure: true,
4147
4330
  contracts: funded.flatMap(({ address, shares }) => [
4148
- { address, abi: import_viem9.erc4626Abi, functionName: "asset" },
4149
- { 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] }
4150
4333
  ])
4151
4334
  });
4152
4335
  if (valuations.length !== funded.length * 2) return void 0;
@@ -4162,12 +4345,12 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
4162
4345
  }
4163
4346
 
4164
4347
  // src/agents/yieldseeker/yieldseeker.wallet-version.ts
4165
- var import_viem10 = require("viem");
4348
+ var import_viem11 = require("viem");
4166
4349
  var YIELDSEEKER_FACTORY = "0x9c7410a0faCAC60850C46eE5b58B518daec95130";
4167
4350
  var YIELDSEEKER_V1_IMPLEMENTATION = "0x32403455357589523435dB18b414057D7cE05051";
4168
4351
  var YIELDSEEKER_V2_IMPLEMENTATION = "0x613Db9c924ADB7A7A90c11b02Cce20c913E3Fe50";
4169
4352
  var ERC1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
4170
- var walletAbi = (0, import_viem10.parseAbi)([
4353
+ var walletAbi = (0, import_viem11.parseAbi)([
4171
4354
  "function owner() view returns (address)",
4172
4355
  "function ownerAgentIndex() view returns (uint256)",
4173
4356
  "function baseAsset() view returns (address)",
@@ -4175,7 +4358,7 @@ var walletAbi = (0, import_viem10.parseAbi)([
4175
4358
  "function upgradeToLatest()",
4176
4359
  "event Upgraded(address indexed implementation)"
4177
4360
  ]);
4178
- var factoryAbi = (0, import_viem10.parseAbi)([
4361
+ var factoryAbi = (0, import_viem11.parseAbi)([
4179
4362
  "function userWallets(address owner, uint256 index) view returns (address)",
4180
4363
  "function agentWalletImplementation() view returns (address)"
4181
4364
  ]);
@@ -4183,9 +4366,9 @@ function yieldseekerOwnerPath(code) {
4183
4366
  return !code || code === "0x" || /^0xef0100[0-9a-f]{40}$/i.test(code) ? "eoa" : "smart";
4184
4367
  }
4185
4368
  async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNumber) {
4186
- const agentWallet = (0, import_viem10.getAddress)(wallet);
4187
- const expectedOwner = (0, import_viem10.getAddress)(owner);
4188
- 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);
4189
4372
  const [code, ownerCode, slot, actualOwner, index, actualAsset, actualFactory, latest] = await Promise.all([
4190
4373
  client.getBytecode({ address: agentWallet, blockNumber }),
4191
4374
  client.getBytecode({ address: expectedOwner, blockNumber }),
@@ -4199,7 +4382,7 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
4199
4382
  if (!code || code === "0x" || !slot || /^0x0+$/.test(slot)) {
4200
4383
  throw new Error("Yieldseeker wallet is not a deployed ERC1967 proxy");
4201
4384
  }
4202
- const implementation = (0, import_viem10.getAddress)(`0x${slot.slice(-40)}`);
4385
+ const implementation = (0, import_viem11.getAddress)(`0x${slot.slice(-40)}`);
4203
4386
  const mapped = await client.readContract({
4204
4387
  address: YIELDSEEKER_FACTORY,
4205
4388
  abi: factoryAbi,
@@ -4207,8 +4390,8 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
4207
4390
  args: [expectedOwner, index],
4208
4391
  blockNumber
4209
4392
  });
4210
- const verified = (0, import_viem10.isAddressEqual)(actualOwner, expectedOwner) && (0, import_viem10.isAddressEqual)(actualAsset, expectedAsset) && (0, import_viem10.isAddressEqual)(actualFactory, YIELDSEEKER_FACTORY) && (0, import_viem10.isAddressEqual)(mapped, agentWallet);
4211
- const version = !verified ? "unknown" : (0, import_viem10.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem10.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
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";
4212
4395
  return {
4213
4396
  agentWallet,
4214
4397
  owner: actualOwner,
@@ -4217,13 +4400,13 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
4217
4400
  implementation,
4218
4401
  version,
4219
4402
  verified,
4220
- upgradeAvailable: version === "v1" && (0, import_viem10.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
4403
+ upgradeAvailable: version === "v1" && (0, import_viem11.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
4221
4404
  };
4222
4405
  }
4223
4406
  var YIELDSEEKER_UPGRADE_ABI = walletAbi;
4224
4407
 
4225
4408
  // src/agents/yieldseeker/yieldseeker.sponsored-withdraw.ts
4226
- var import_viem11 = require("viem");
4409
+ var import_viem12 = require("viem");
4227
4410
  var import_account_abstraction2 = require("viem/account-abstraction");
4228
4411
  var import_accounts = require("viem/accounts");
4229
4412
  var import_chains3 = require("viem/chains");
@@ -4310,7 +4493,7 @@ function rpcDiagnostics(data) {
4310
4493
  details.revertName = data.revertName;
4311
4494
  if (typeof data?.aaCode === "string" && /^AA[0-9]{2}$/.test(data.aaCode)) details.aaCode = data.aaCode;
4312
4495
  if (typeof data?.panicCode === "string" && /^0x[0-9a-fA-F]{1,2}$/.test(data.panicCode)) {
4313
- const panicCode = (0, import_viem11.toHex)(BigInt(data.panicCode));
4496
+ const panicCode = (0, import_viem12.toHex)(BigInt(data.panicCode));
4314
4497
  if (["0x0", "0x1", "0x11", "0x12", "0x21", "0x22", "0x31", "0x32", "0x41", "0x51"].includes(panicCode))
4315
4498
  details.panicCode = panicCode;
4316
4499
  }
@@ -4530,14 +4713,14 @@ async function submitEoa(input) {
4530
4713
  throw sponsorError("Base did not return EIP-1559 gas fees.", true);
4531
4714
  let operation = {
4532
4715
  sender: input.agentWallet,
4533
- nonce: (0, import_viem11.toHex)(nonce),
4716
+ nonce: (0, import_viem12.toHex)(nonce),
4534
4717
  initCode: "0x",
4535
4718
  callData: input.callData,
4536
- callGasLimit: (0, import_viem11.toHex)(3e5),
4537
- verificationGasLimit: (0, import_viem11.toHex)(3e5),
4538
- preVerificationGas: (0, import_viem11.toHex)(6e4),
4539
- maxFeePerGas: (0, import_viem11.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
4540
- 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),
4541
4724
  paymasterAndData: "0x",
4542
4725
  signature: "0x"
4543
4726
  };
@@ -4545,7 +4728,7 @@ async function submitEoa(input) {
4545
4728
  const stub = await rpc(
4546
4729
  input.rpcUrl,
4547
4730
  "pm_getPaymasterStubData",
4548
- [operation, ENTRY_POINT_V06, (0, import_viem11.toHex)(8453), {}],
4731
+ [operation, ENTRY_POINT_V06, (0, import_viem12.toHex)(8453), {}],
4549
4732
  "prepare"
4550
4733
  );
4551
4734
  if (!stub.paymasterAndData?.startsWith("0x"))
@@ -4560,7 +4743,7 @@ async function submitEoa(input) {
4560
4743
  const final = await rpc(
4561
4744
  input.rpcUrl,
4562
4745
  "pm_getPaymasterData",
4563
- [operation, ENTRY_POINT_V06, (0, import_viem11.toHex)(8453), {}],
4746
+ [operation, ENTRY_POINT_V06, (0, import_viem12.toHex)(8453), {}],
4564
4747
  "prepare"
4565
4748
  );
4566
4749
  if (!final.paymasterAndData?.startsWith("0x"))
@@ -4570,10 +4753,10 @@ async function submitEoa(input) {
4570
4753
  const userOpHash = operationHash(operation);
4571
4754
  await assertOwnerSession(input.state, input.owner);
4572
4755
  assertIntentFresh(input.expiresAt);
4573
- const wallet = (0, import_viem11.createWalletClient)({
4756
+ const wallet = (0, import_viem12.createWalletClient)({
4574
4757
  account: input.owner,
4575
4758
  chain: import_chains3.base,
4576
- transport: (0, import_viem11.custom)(input.state.provider)
4759
+ transport: (0, import_viem12.custom)(input.state.provider)
4577
4760
  });
4578
4761
  const signature = await wallet.signMessage({ message: { raw: userOpHash } });
4579
4762
  operation = { ...operation, signature };
@@ -4651,13 +4834,13 @@ async function submitSmart(input) {
4651
4834
  } catch {
4652
4835
  throw sponsorError("This account cannot check paymaster support on Base.", true);
4653
4836
  }
4654
- 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;
4655
4838
  if (supportsPaymaster !== true)
4656
4839
  throw sponsorError("This account cannot use a paymaster on Base.", true);
4657
4840
  await assertOwnerSession(input.state, input.owner);
4658
4841
  let sent;
4659
4842
  assertIntentFresh(input.expiresAt);
4660
- 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)));
4661
4844
  savePending(input.onPending, { callsId: requestedId }, true);
4662
4845
  await checkpoint(input.onBeforeSubmission, { callsId: requestedId });
4663
4846
  try {
@@ -4666,7 +4849,7 @@ async function submitSmart(input) {
4666
4849
  params: [{
4667
4850
  version: "2.0.0",
4668
4851
  from: input.owner,
4669
- chainId: (0, import_viem11.toHex)(8453),
4852
+ chainId: (0, import_viem12.toHex)(8453),
4670
4853
  id: requestedId,
4671
4854
  atomicRequired: false,
4672
4855
  calls: [{ to: input.agentWallet, value: "0x0", data: input.callData }],
@@ -4741,7 +4924,7 @@ async function submitSmart(input) {
4741
4924
  );
4742
4925
  }
4743
4926
  async function submitYieldseekerSponsoredWithdrawal(input) {
4744
- const owner = (0, import_viem11.getAddress)(input.state.walletAddress);
4927
+ const owner = (0, import_viem12.getAddress)(input.state.walletAddress);
4745
4928
  const { intent, rpcUrl } = await requestIntent({
4746
4929
  apiKey: input.apiKey,
4747
4930
  routingApiBaseUrl: input.routingApiBaseUrl,
@@ -4760,7 +4943,7 @@ async function submitYieldseekerSponsoredWithdrawal(input) {
4760
4943
  return intent.ownerPath === "eoa" ? submitEoa(args) : submitSmart(args);
4761
4944
  }
4762
4945
  async function submitYieldseekerSponsoredUpgrade(input) {
4763
- const owner = (0, import_viem11.getAddress)(input.state.walletAddress);
4946
+ const owner = (0, import_viem12.getAddress)(input.state.walletAddress);
4764
4947
  const { intent, rpcUrl } = await requestIntent({
4765
4948
  apiKey: input.apiKey,
4766
4949
  routingApiBaseUrl: input.routingApiBaseUrl,
@@ -4875,6 +5058,7 @@ var YieldseekerAgent = class {
4875
5058
  receiptClient;
4876
5059
  // A client preparation hint only; the routing API still checks live identity.
4877
5060
  withdrawalVersionCache = /* @__PURE__ */ new Map();
5061
+ pendingUpgrades = /* @__PURE__ */ new Map();
4878
5062
  transactionExecutor;
4879
5063
  unwindReceiptWaiter;
4880
5064
  agentContexts = /* @__PURE__ */ new Map();
@@ -4925,8 +5109,8 @@ var YieldseekerAgent = class {
4925
5109
  if (useCachedV2 && cached && Date.now() - cached.at < 15 * 6e4) return { ...cached.status };
4926
5110
  const status = await readYieldseekerWalletStatus(
4927
5111
  this.getReceiptClient(),
4928
- (0, import_viem12.getAddress)(context.wallet.walletAddress),
4929
- (0, import_viem12.getAddress)(state.walletAddress),
5112
+ (0, import_viem13.getAddress)(context.wallet.walletAddress),
5113
+ (0, import_viem13.getAddress)(state.walletAddress),
4930
5114
  YIELDSEEKER_ASSET_METADATA[asset].address
4931
5115
  );
4932
5116
  this.rememberWalletStatus(key2, status);
@@ -4956,12 +5140,12 @@ var YieldseekerAgent = class {
4956
5140
  const pendingKey2 = `owney.yieldseeker.upgrade:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}`;
4957
5141
  const confirmUpgrade = async (hash, receipt2) => {
4958
5142
  this.withdrawalVersionCache.delete(versionKey);
4959
- 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];
4960
5144
  const lastUpgrade = receipt2?.logs.filter((log) => log.address.toLowerCase() === before.agentWallet.toLowerCase() && log.topics[0] === upgradeTopic).at(-1);
4961
5145
  const expectedUpgrade = (() => {
4962
5146
  if (!lastUpgrade || lastUpgrade.removed) return false;
4963
5147
  try {
4964
- 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 });
4965
5149
  return event.args.implementation.toLowerCase() === YIELDSEEKER_V2_IMPLEMENTATION.toLowerCase();
4966
5150
  } catch {
4967
5151
  return false;
@@ -4990,10 +5174,30 @@ var YieldseekerAgent = class {
4990
5174
  } catch {
4991
5175
  storage5 = void 0;
4992
5176
  }
4993
- const clearConfirmedUpgrade = () => {
5177
+ const clearRecordedUpgrade = () => {
5178
+ this.pendingUpgrades.delete(pendingKey2);
4994
5179
  try {
4995
5180
  storage5?.removeItem(pendingKey2);
4996
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);
4997
5201
  }
4998
5202
  };
4999
5203
  if (!storage5 && (gasMode === "sponsored" || before.ownerPath === "smart"))
@@ -5005,7 +5209,7 @@ var YieldseekerAgent = class {
5005
5209
  );
5006
5210
  let prior = null;
5007
5211
  try {
5008
- const saved = storage5?.getItem(pendingKey2);
5212
+ const saved = this.pendingUpgrades.has(pendingKey2) ? JSON.stringify(this.pendingUpgrades.get(pendingKey2)) : storage5?.getItem(pendingKey2);
5009
5213
  try {
5010
5214
  prior = JSON.parse(saved ?? "null");
5011
5215
  } catch {
@@ -5049,7 +5253,7 @@ var YieldseekerAgent = class {
5049
5253
  this.id
5050
5254
  );
5051
5255
  if (Number(callsStatus?.status) === 400 || Number(callsStatus?.status) === 500) {
5052
- storage5.removeItem(pendingKey2);
5256
+ clearRecordedUpgrade();
5053
5257
  throw new OwneyError(
5054
5258
  "SPONSOR_REQUEST_FAILED",
5055
5259
  "The earlier sponsored upgrade failed.",
@@ -5061,8 +5265,8 @@ var YieldseekerAgent = class {
5061
5265
  earlierHash = hashes.length === 1 ? hashes[0] : void 0;
5062
5266
  }
5063
5267
  if (earlierHash) {
5064
- const recovered = await confirmUpgrade(earlierHash, await this.waitForReceipt(state, chainId, earlierHash));
5065
- clearConfirmedUpgrade();
5268
+ const recovered = await confirmUpgrade(earlierHash, await readUpgradeReceipt(earlierHash));
5269
+ clearRecordedUpgrade();
5066
5270
  return recovered;
5067
5271
  }
5068
5272
  throw new OwneyError(
@@ -5079,7 +5283,7 @@ var YieldseekerAgent = class {
5079
5283
  { version: before.version },
5080
5284
  this.id
5081
5285
  );
5082
- 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" });
5083
5287
  let txHash;
5084
5288
  let receipt;
5085
5289
  if (gasMode === "sponsored") {
@@ -5106,33 +5310,52 @@ var YieldseekerAgent = class {
5106
5310
  asset: YIELDSEEKER_ASSET_METADATA[asset].address,
5107
5311
  callData: data,
5108
5312
  onPending: (pending) => {
5109
- const current = storage5.getItem(pendingKey2);
5110
- storage5.setItem(pendingKey2, JSON.stringify({
5111
- startedAt: Date.now(),
5112
- ...current ? JSON.parse(current) : {},
5113
- ...pending
5114
- }));
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);
5115
5317
  }
5116
5318
  });
5117
5319
  txHash = upgraded.txHash;
5118
5320
  receipt = upgraded.receipt;
5119
5321
  } catch (error) {
5120
- 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();
5121
5323
  throw error;
5122
5324
  }
5123
5325
  } else {
5124
- txHash = await this.submitTransaction(state, chainId, {
5125
- from: (0, import_viem12.getAddress)(state.walletAddress),
5126
- to: before.agentWallet,
5127
- data,
5128
- value: "0",
5129
- chainId
5130
- }, void 0, (value) => {
5131
- receipt = value;
5132
- });
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
+ }
5133
5356
  }
5134
- const confirmed = await confirmUpgrade(txHash, receipt ?? await this.waitForReceipt(state, chainId, txHash));
5135
- if (gasMode === "sponsored") clearConfirmedUpgrade();
5357
+ const confirmed = await confirmUpgrade(txHash, receipt ?? await readUpgradeReceipt(txHash));
5358
+ clearRecordedUpgrade();
5136
5359
  return confirmed;
5137
5360
  }
5138
5361
  async disconnect() {
@@ -5209,12 +5432,12 @@ var YieldseekerAgent = class {
5209
5432
  } else {
5210
5433
  try {
5211
5434
  txHash = await this.submitTransaction(state, chainId, {
5212
- from: (0, import_viem12.getAddress)(state.walletAddress),
5435
+ from: (0, import_viem13.getAddress)(state.walletAddress),
5213
5436
  to: YIELDSEEKER_ASSET_METADATA[asset].address,
5214
- data: (0, import_viem12.encodeFunctionData)({
5215
- abi: import_viem12.erc20Abi,
5437
+ data: (0, import_viem13.encodeFunctionData)({
5438
+ abi: import_viem13.erc20Abi,
5216
5439
  functionName: "transfer",
5217
- args: [(0, import_viem12.getAddress)(context.wallet.walletAddress), BigInt(amount)]
5440
+ args: [(0, import_viem13.getAddress)(context.wallet.walletAddress), BigInt(amount)]
5218
5441
  }),
5219
5442
  value: "0",
5220
5443
  chainId
@@ -5348,7 +5571,7 @@ var YieldseekerAgent = class {
5348
5571
  );
5349
5572
  const positionsRead = this.readWithdrawalPositions(state, chainId, context);
5350
5573
  const metadata = YIELDSEEKER_ASSET_METADATA[asset];
5351
- const agentWallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
5574
+ const agentWallet = (0, import_viem13.getAddress)(context.wallet.walletAddress);
5352
5575
  const readOwnerFunds = async (blockNumber) => {
5353
5576
  const feeTracker = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5354
5577
  address: agentWallet,
@@ -5357,18 +5580,18 @@ var YieldseekerAgent = class {
5357
5580
  blockNumber
5358
5581
  }), chainId, "eth_call", this.id);
5359
5582
  this.assertSession(generation, chainId, asset);
5360
- if (!(0, import_viem12.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
5583
+ if (!(0, import_viem13.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
5361
5584
  throw this.invalidResponse("wallet fee tracker");
5362
5585
  const [idle2, fees] = await Promise.all([
5363
5586
  withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5364
5587
  address: metadata.address,
5365
- abi: import_viem12.erc20Abi,
5588
+ abi: import_viem13.erc20Abi,
5366
5589
  functionName: "balanceOf",
5367
5590
  args: [agentWallet],
5368
5591
  blockNumber
5369
5592
  }), chainId, "eth_call", this.id),
5370
5593
  withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5371
- address: (0, import_viem12.getAddress)(feeTracker),
5594
+ address: (0, import_viem13.getAddress)(feeTracker),
5372
5595
  abi: YIELDSEEKER_FEE_TRACKER_ABI,
5373
5596
  functionName: "getFeesOwed",
5374
5597
  args: [agentWallet],
@@ -5410,13 +5633,13 @@ var YieldseekerAgent = class {
5410
5633
  let active = true;
5411
5634
  const checks = this.readVaultLiquidity(values.map((position2) => position2.yieldOption), agentWallet, blockNumber).catch(() => []);
5412
5635
  const reads = values.map(async (position2, index) => {
5413
- const address = (0, import_viem12.getAddress)(position2.yieldOption.address);
5636
+ const address = (0, import_viem13.getAddress)(position2.yieldOption.address);
5414
5637
  try {
5415
5638
  const group = (await checks).slice(index * 3, index * 3 + 3);
5416
5639
  if (!active || group.length !== 3 || group.some((value) => value.status !== "success")) return null;
5417
5640
  this.assertSession(generation, chainId, asset);
5418
5641
  const [underlying, shares, withdrawable] = group.map((value) => value.result);
5419
- 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;
5420
5643
  const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: withdrawable.toString() };
5421
5644
  if (shares > 0n && withdrawable === 0n) {
5422
5645
  let positiveValue = false;
@@ -5424,7 +5647,7 @@ var YieldseekerAgent = class {
5424
5647
  try {
5425
5648
  const value = await this.getReceiptClient().readContract({
5426
5649
  address,
5427
- abi: import_viem12.erc4626Abi,
5650
+ abi: import_viem13.erc4626Abi,
5428
5651
  functionName,
5429
5652
  args: [shares],
5430
5653
  blockNumber
@@ -5514,6 +5737,18 @@ var YieldseekerAgent = class {
5514
5737
  );
5515
5738
  }
5516
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
+ );
5517
5752
  throw new OwneyError(
5518
5753
  "WITHDRAW_INSUFFICIENT_BALANCE",
5519
5754
  `Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
@@ -5528,6 +5763,7 @@ var YieldseekerAgent = class {
5528
5763
  const requiredIdle = requested + initialFunds.fees;
5529
5764
  let remaining = requiredIdle > idle ? requiredIdle - idle : 0n;
5530
5765
  let grossUnwound = 0n;
5766
+ let confirmedUnwinds = 0n;
5531
5767
  let latestUnwindBlock;
5532
5768
  const unwind = async (position2, assetsRaw) => {
5533
5769
  this.assertSession(generation, chainId, asset);
@@ -5545,7 +5781,10 @@ var YieldseekerAgent = class {
5545
5781
  if (!this.isTransactionHash(response?.transactionHash)) throw this.invalidResponse("position withdrawal");
5546
5782
  unwindTransactionHash = response.transactionHash;
5547
5783
  const unwindReceipt = await this.waitForReceipt(state, chainId, unwindTransactionHash);
5548
- if (unwindReceipt) latestUnwindBlock = unwindReceipt.blockNumber;
5784
+ if (unwindReceipt) {
5785
+ latestUnwindBlock = unwindReceipt.blockNumber;
5786
+ confirmedUnwinds++;
5787
+ }
5549
5788
  } catch (error) {
5550
5789
  this.invalidateFundingRead(state, chainId, asset);
5551
5790
  throw new OwneyError(
@@ -5615,7 +5854,7 @@ var YieldseekerAgent = class {
5615
5854
  const feeRate = feeRateBps ?? 0;
5616
5855
  const maxFee = confirmedMovement && Number.isInteger(feeRate) && feeRate >= 0 && feeRate <= 1e3 ? (grossUnwound * BigInt(feeRate) + 9999n) / 10000n : 0n;
5617
5856
  const shortfall = availableForOwner < requested ? requested - availableForOwner : 0n;
5618
- if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee) {
5857
+ if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee + confirmedUnwinds) {
5619
5858
  this.invalidateFundingRead(state, chainId, asset);
5620
5859
  if (amount === void 0 && positionsStatus !== "empty")
5621
5860
  throw new OwneyError(
@@ -5637,14 +5876,15 @@ var YieldseekerAgent = class {
5637
5876
  delta: maxSettlementBasis.delta - payout,
5638
5877
  expectedNetDeposits: maxSettlementBasis.expectedNetDeposits - payout
5639
5878
  } : this.snapshotMovement(state, chainId, context, -payout);
5640
- const account = (0, import_viem12.getAddress)(state.walletAddress);
5879
+ const account = (0, import_viem13.getAddress)(state.walletAddress);
5641
5880
  let receipt;
5642
- 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" };
5643
- 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)({
5644
5884
  abi: YIELDSEEKER_AGENT_WALLET_ABI,
5645
5885
  functionName: "withdrawAllAssetToUser",
5646
5886
  args: [account, metadata.address]
5647
- }) : (0, import_viem12.encodeFunctionData)({
5887
+ }) : (0, import_viem13.encodeFunctionData)({
5648
5888
  abi: YIELDSEEKER_AGENT_WALLET_ABI,
5649
5889
  functionName: "withdrawAssetToUser",
5650
5890
  args: [account, metadata.address, payout]
@@ -5660,10 +5900,13 @@ var YieldseekerAgent = class {
5660
5900
  refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
5661
5901
  userId: context.user.userId,
5662
5902
  agentId: context.agent.agentId,
5663
- agentWallet: (0, import_viem12.getAddress)(context.wallet.walletAddress),
5903
+ agentWallet: (0, import_viem13.getAddress)(context.wallet.walletAddress),
5664
5904
  asset: metadata.address,
5665
5905
  callData: data,
5666
- onPending: (pending) => notifyFunding(onProgress, { ...base6, ...pending }),
5906
+ onPending: (pending) => {
5907
+ submissionEvidence = { ...submissionEvidence, ...pending };
5908
+ notifyFunding(onProgress, submissionEvidence);
5909
+ },
5667
5910
  onBeforeSubmission: async (pending) => {
5668
5911
  await onBeforeSubmission?.({ ...base6, ...pending });
5669
5912
  onOwnerTransferAttempted?.();
@@ -5675,7 +5918,7 @@ var YieldseekerAgent = class {
5675
5918
  onOwnerTransferAttempted?.();
5676
5919
  txHash = await this.submitTransaction(state, chainId, {
5677
5920
  from: account,
5678
- to: (0, import_viem12.getAddress)(context.wallet.walletAddress),
5921
+ to: (0, import_viem13.getAddress)(context.wallet.walletAddress),
5679
5922
  data,
5680
5923
  value: "0",
5681
5924
  chainId
@@ -5683,10 +5926,20 @@ var YieldseekerAgent = class {
5683
5926
  receipt = value;
5684
5927
  });
5685
5928
  }
5686
- confirmedMovement = true;
5687
- settlement = plannedMovement;
5688
- 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" };
5689
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;
5690
5943
  let completion = maxLiquidity && !maxLiquidity.complete ? "unverified" : void 0;
5691
5944
  if (amount === void 0) {
5692
5945
  const verificationDeadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
@@ -5707,7 +5960,7 @@ var YieldseekerAgent = class {
5707
5960
  positions = proof.positions;
5708
5961
  positionsStatus = hasPositions(positions) ? "remaining" : proof.verified ? "empty" : "unavailable";
5709
5962
  if (positionsStatus === "remaining") {
5710
- const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? payout.toString(), evidence };
5963
+ const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? "0", evidence };
5711
5964
  throw new OwneyError(
5712
5965
  "WITHDRAW_PARTIAL_FAILURE",
5713
5966
  "Yieldseeker MAX could not confirm that all vault positions were cleared.",
@@ -5727,7 +5980,7 @@ var YieldseekerAgent = class {
5727
5980
  }
5728
5981
  if (!proof.verified) completion = "unverified";
5729
5982
  }
5730
- if (evidence.transfer === "failed" && evidence.confirmedAmount === "0")
5983
+ if (explicitEmptyPayout)
5731
5984
  throw new OwneyError(
5732
5985
  "WITHDRAW_INSUFFICIENT_BALANCE",
5733
5986
  "The Yieldseeker withdrawal completed without transferring funds.",
@@ -5742,7 +5995,7 @@ var YieldseekerAgent = class {
5742
5995
  type: amount === void 0 && !completion ? "full" : "partial",
5743
5996
  ...completion ? { completion } : {},
5744
5997
  ...feeAdjustment ? { feeAdjustment } : {},
5745
- amount: evidence.confirmedAmount ?? payout.toString(),
5998
+ amount: evidence.confirmedAmount ?? "0",
5746
5999
  evidence
5747
6000
  };
5748
6001
  } finally {
@@ -5783,17 +6036,17 @@ var YieldseekerAgent = class {
5783
6036
  return proof.available;
5784
6037
  }
5785
6038
  async readVaultLiquidity(options, wallet, blockNumber) {
5786
- const owner = (0, import_viem12.getAddress)(wallet);
6039
+ const owner = (0, import_viem13.getAddress)(wallet);
5787
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));
5788
6041
  const results = await Promise.all(batches.map((batch) => this.getReceiptClient().multicall({
5789
6042
  blockNumber,
5790
6043
  allowFailure: true,
5791
6044
  contracts: batch.flatMap((option) => {
5792
- const address = (0, import_viem12.getAddress)(option.address);
6045
+ const address = (0, import_viem13.getAddress)(option.address);
5793
6046
  return [
5794
- { address, abi: import_viem12.erc4626Abi, functionName: "asset" },
5795
- { address, abi: import_viem12.erc20Abi, functionName: "balanceOf", args: [owner] },
5796
- { 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] }
5797
6050
  ];
5798
6051
  })
5799
6052
  })));
@@ -5804,7 +6057,7 @@ var YieldseekerAgent = class {
5804
6057
  const seen = /* @__PURE__ */ new Set();
5805
6058
  for (const value of values) {
5806
6059
  const option = value?.yieldOption;
5807
- 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))
5808
6061
  throw this.invalidResponse("yield positions");
5809
6062
  seen.add(option.address.toLowerCase());
5810
6063
  }
@@ -5817,7 +6070,7 @@ var YieldseekerAgent = class {
5817
6070
  const historic = this.readCache.get(`historic:${key2}`)?.value?.position;
5818
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()));
5819
6072
  for (const option of this.yieldOptions.get(context.asset)?.value ?? []) {
5820
- 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;
5821
6074
  hints.set(option.address.toLowerCase(), {
5822
6075
  yieldOption: option,
5823
6076
  assetsRaw: "0",
@@ -5869,7 +6122,7 @@ var YieldseekerAgent = class {
5869
6122
  const read2 = async () => {
5870
6123
  const client = this.getReceiptClient();
5871
6124
  const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
5872
- const wallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
6125
+ const wallet = (0, import_viem13.getAddress)(context.wallet.walletAddress);
5873
6126
  assertReadCurrent();
5874
6127
  if (typeof blockNumber !== "bigint") throw unavailable();
5875
6128
  const indexedPositions = this.validateWithdrawalPositions(positions ?? [], chainId, context.asset);
@@ -5879,11 +6132,11 @@ var YieldseekerAgent = class {
5879
6132
  for (const position2 of indexedPositions) candidates.set(position2.yieldOption.address.toLowerCase(), position2.yieldOption);
5880
6133
  const options = [...candidates.values()];
5881
6134
  if (options.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
5882
- const [checks, idle, feeTracker] = await Promise.all([
5883
- 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([
5884
6137
  client.readContract({
5885
6138
  address: metadata.address,
5886
- abi: import_viem12.erc20Abi,
6139
+ abi: import_viem13.erc20Abi,
5887
6140
  functionName: "balanceOf",
5888
6141
  args: [wallet],
5889
6142
  blockNumber
@@ -5895,10 +6148,10 @@ var YieldseekerAgent = class {
5895
6148
  blockNumber
5896
6149
  })
5897
6150
  ]);
5898
- 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();
5899
6152
  assertReadCurrent();
5900
6153
  const fees = await client.readContract({
5901
- address: (0, import_viem12.getAddress)(feeTracker),
6154
+ address: (0, import_viem13.getAddress)(feeTracker),
5902
6155
  abi: YIELDSEEKER_FEE_TRACKER_ABI,
5903
6156
  functionName: "getFeesOwed",
5904
6157
  args: [wallet],
@@ -5908,6 +6161,18 @@ var YieldseekerAgent = class {
5908
6161
  const verified = [];
5909
6162
  const verifiedVaults = [];
5910
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);
5911
6176
  const collect = (options2, results) => {
5912
6177
  assertReadCurrent();
5913
6178
  const known = this.balanceVaults.get(contextKey) ?? /* @__PURE__ */ new Set();
@@ -5923,7 +6188,7 @@ var YieldseekerAgent = class {
5923
6188
  return;
5924
6189
  }
5925
6190
  const [underlying, shares, withdrawable] = group.map((value) => value.result);
5926
- 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) {
5927
6192
  valid2 = false;
5928
6193
  return;
5929
6194
  }
@@ -5940,17 +6205,10 @@ var YieldseekerAgent = class {
5940
6205
  });
5941
6206
  return valid2;
5942
6207
  };
5943
- const allKnownVerified = collect(options, checks);
5944
- const proof = (complete) => ({
5945
- blockNumber,
5946
- idle,
5947
- fees,
5948
- available: idle + deployed > fees ? idle + deployed - fees : 0n,
5949
- positions: [...verified],
5950
- verifiedVaults: [...verifiedVaults],
5951
- indexedPositions,
5952
- complete
5953
- });
6208
+ const checks = await checksRead;
6209
+ assertReadCurrent();
6210
+ if ("error" in checks) throw checks.error;
6211
+ const allKnownVerified = collect(options, checks.value);
5954
6212
  knownProof = proof(false);
5955
6213
  const indexed = new Map(indexedPositions.map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
5956
6214
  const discrepancy = verified.some((position2) => {
@@ -5967,7 +6225,7 @@ var YieldseekerAgent = class {
5967
6225
  assertReadCurrent();
5968
6226
  const eligible = /* @__PURE__ */ new Map();
5969
6227
  for (const option of catalog ?? []) {
5970
- 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))
5971
6229
  eligible.set(option.address.toLowerCase(), option);
5972
6230
  }
5973
6231
  const extra = [...eligible].filter(([address]) => !candidates.has(address)).map(([, option]) => option);
@@ -6018,7 +6276,7 @@ var YieldseekerAgent = class {
6018
6276
  const key2 = this.contextKey(state, evidence.chainId, evidence.token);
6019
6277
  const generation = this.readGeneration, version = this.portfolioVersion(key2);
6020
6278
  if (read2.readVersion !== version) return void 0;
6021
- const wallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
6279
+ const wallet = (0, import_viem13.getAddress)(context.wallet.walletAddress);
6022
6280
  const asset = YIELDSEEKER_ASSET_METADATA[evidence.token];
6023
6281
  const client = this.getReceiptClient();
6024
6282
  const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
@@ -6026,10 +6284,10 @@ var YieldseekerAgent = class {
6026
6284
  const vaults = [...this.balanceVaults.get(key2) ?? []];
6027
6285
  const [invested, idle, tracker] = await Promise.all([
6028
6286
  remainingVaultBalance(client, wallet, asset.address, vaults, blockNumber),
6029
- 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 }),
6030
6288
  client.readContract({ address: wallet, abi: YIELDSEEKER_AGENT_WALLET_ABI, functionName: "feeTracker", blockNumber })
6031
6289
  ]);
6032
- 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;
6033
6291
  const fees = await client.readContract({
6034
6292
  address: tracker,
6035
6293
  abi: YIELDSEEKER_FEE_TRACKER_ABI,
@@ -6042,7 +6300,7 @@ var YieldseekerAgent = class {
6042
6300
  const total = idle + invested > fees ? idle + invested - fees : 0n;
6043
6301
  const displayed = balance.assetBalances?.find((item) => item.chainId === evidence.chainId && item.asset === evidence.token);
6044
6302
  if (!displayed || !evidence.confirmedAmount || !/^\d+$/.test(evidence.confirmedAmount)) return void 0;
6045
- const amount = (0, import_viem12.parseUnits)(displayed.amount, asset.decimals);
6303
+ const amount = (0, import_viem13.parseUnits)(displayed.amount, asset.decimals);
6046
6304
  if (amount < 0n) return void 0;
6047
6305
  const displayUnit = evidence.token === "USDC" ? 5000n : 500000000000n;
6048
6306
  const movementUnit = BigInt(evidence.confirmedAmount) / 1000n;
@@ -6088,9 +6346,9 @@ var YieldseekerAgent = class {
6088
6346
  if (!context) return void 0;
6089
6347
  const idle = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
6090
6348
  address: YIELDSEEKER_ASSET_METADATA[asset].address,
6091
- abi: import_viem12.erc20Abi,
6349
+ abi: import_viem13.erc20Abi,
6092
6350
  functionName: "balanceOf",
6093
- args: [(0, import_viem12.getAddress)(context.wallet.walletAddress)]
6351
+ args: [(0, import_viem13.getAddress)(context.wallet.walletAddress)]
6094
6352
  }), chainId, "eth_call", this.id);
6095
6353
  this.assertSession(generation, chainId, asset);
6096
6354
  if (typeof idle !== "bigint" || idle < 0n) throw this.invalidResponse("wallet deposit balance");
@@ -6098,7 +6356,7 @@ var YieldseekerAgent = class {
6098
6356
  }
6099
6357
  async getEarnings(state, chainId) {
6100
6358
  this.assertChain(chainId);
6101
- return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
6359
+ return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, { includePositions: false }));
6102
6360
  }
6103
6361
  async getAccountApy(state, chainId, days, tokenSymbol) {
6104
6362
  this.assertChain(chainId);
@@ -6110,6 +6368,17 @@ var YieldseekerAgent = class {
6110
6368
  });
6111
6369
  return mapYieldseekerApy(state.walletAddress, contexts, days);
6112
6370
  }
6371
+ async getDailyEarnings(state, chainId, days, tokenSymbol) {
6372
+ this.assertChain(chainId);
6373
+ const asset = tokenSymbol?.toUpperCase();
6374
+ if (asset !== void 0) this.assertAsset(asset);
6375
+ const contexts = await this.loadActivityPortfolio(state, chainId, {
6376
+ ...asset ? { asset } : {},
6377
+ historic: true,
6378
+ includePositions: false
6379
+ });
6380
+ return mapYieldseekerDailyEarnings(state.walletAddress, contexts, days);
6381
+ }
6113
6382
  async getHistory(state, chainId, options) {
6114
6383
  this.assertChain(chainId);
6115
6384
  const asset = options?.tokenSymbol?.toUpperCase();
@@ -6126,7 +6395,7 @@ var YieldseekerAgent = class {
6126
6395
  );
6127
6396
  const vaultAddresses = new Set(
6128
6397
  catalog.flat().filter(
6129
- (yieldOption) => yieldOption.chainId === chainId && (0, import_viem12.isAddress)(yieldOption.address)
6398
+ (yieldOption) => yieldOption.chainId === chainId && (0, import_viem13.isAddress)(yieldOption.address)
6130
6399
  ).map((yieldOption) => yieldOption.address.toLowerCase())
6131
6400
  );
6132
6401
  return mapYieldseekerHistory(contexts, {
@@ -6255,7 +6524,7 @@ var YieldseekerAgent = class {
6255
6524
  `/users/${user.userId}/agents/${agent.agentId}/wallet`
6256
6525
  ).then((walletResponse) => {
6257
6526
  this.assertSession(generation, chainId, asset);
6258
- if (!walletResponse?.agentWallet || !(0, import_viem12.isAddress)(walletResponse.agentWallet.walletAddress)) {
6527
+ if (!walletResponse?.agentWallet || !(0, import_viem13.isAddress)(walletResponse.agentWallet.walletAddress)) {
6259
6528
  throw this.invalidResponse("agent wallet");
6260
6529
  }
6261
6530
  const context = {
@@ -6285,7 +6554,7 @@ var YieldseekerAgent = class {
6285
6554
  this.users.set(key2, persisted);
6286
6555
  return persisted;
6287
6556
  }
6288
- const walletAddress = (0, import_viem12.getAddress)(state.walletAddress);
6557
+ const walletAddress = (0, import_viem13.getAddress)(state.walletAddress);
6289
6558
  const generation = this.readGeneration;
6290
6559
  let user = null;
6291
6560
  try {
@@ -6620,7 +6889,7 @@ var YieldseekerAgent = class {
6620
6889
  }
6621
6890
  async loadPortfolioParts(state, chainId, context, options, contextKey) {
6622
6891
  const [positions, historic, actions] = await Promise.all([
6623
- this.cachedRead(
6892
+ options.includePositions === false ? Promise.resolve({ yieldPositions: [] }) : this.cachedRead(
6624
6893
  `positions:${contextKey}`,
6625
6894
  this.portfolioCacheMs(contextKey),
6626
6895
  async () => {
@@ -6669,7 +6938,7 @@ var YieldseekerAgent = class {
6669
6938
  this.agentPath(context, "deploy"),
6670
6939
  { method: "POST", body: {} }
6671
6940
  );
6672
- 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) {
6673
6942
  throw this.invalidResponse("agent deployment", {
6674
6943
  reason: "Deploy did not return the expected Agent Wallet."
6675
6944
  });
@@ -6772,20 +7041,20 @@ var YieldseekerAgent = class {
6772
7041
  }
6773
7042
  async submitTransaction(state, chainId, transaction, onSubmitted, onReceipt) {
6774
7043
  if (this.transactionExecutor) {
6775
- const hash2 = await this.transactionExecutor(state, chainId, transaction);
7044
+ const hash2 = await this.transactionExecutor(state, chainId, transaction, onReceipt);
6776
7045
  onSubmitted?.(hash2);
6777
7046
  return hash2;
6778
7047
  }
6779
7048
  this.assertTransaction(transaction, state, chainId);
6780
- const account = (0, import_viem12.getAddress)(state.walletAddress);
6781
- const walletClient = (0, import_viem12.createWalletClient)({
7049
+ const account = (0, import_viem13.getAddress)(state.walletAddress);
7050
+ const walletClient = (0, import_viem13.createWalletClient)({
6782
7051
  account,
6783
7052
  chain: import_chains4.base,
6784
- transport: (0, import_viem12.custom)(state.provider)
7053
+ transport: (0, import_viem13.custom)(state.provider)
6785
7054
  });
6786
- const walletChainClient = (0, import_viem12.createPublicClient)({
7055
+ const walletChainClient = (0, import_viem13.createPublicClient)({
6787
7056
  chain: import_chains4.base,
6788
- transport: (0, import_viem12.custom)(state.provider)
7057
+ transport: (0, import_viem13.custom)(state.provider)
6789
7058
  });
6790
7059
  await ensureWalletOnChain(
6791
7060
  walletChainClient,
@@ -6795,7 +7064,7 @@ var YieldseekerAgent = class {
6795
7064
  const hash = await walletClient.sendTransaction({
6796
7065
  account,
6797
7066
  chain: import_chains4.base,
6798
- to: (0, import_viem12.getAddress)(transaction.to),
7067
+ to: (0, import_viem13.getAddress)(transaction.to),
6799
7068
  data: transaction.data,
6800
7069
  value: BigInt(transaction.value)
6801
7070
  });
@@ -6899,12 +7168,12 @@ var YieldseekerAgent = class {
6899
7168
  return receipt;
6900
7169
  }
6901
7170
  assertTransaction(transaction, state, chainId) {
6902
- 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)) {
6903
7172
  throw this.invalidResponse("transaction");
6904
7173
  }
6905
7174
  }
6906
7175
  assertAgent(agent) {
6907
- 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) {
6908
7177
  throw this.invalidResponse("agent");
6909
7178
  }
6910
7179
  }
@@ -7058,15 +7327,24 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
7058
7327
  }
7059
7328
 
7060
7329
  // src/lib/helpers/withdraw-helper.ts
7061
- var import_viem13 = require("viem");
7330
+ var import_viem14 = require("viem");
7062
7331
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
7063
7332
  const target = asset.toUpperCase();
7064
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
+ };
7065
7343
  const agentBalance = aggregated[agent.id];
7066
7344
  const tokenBalance = agentBalance?.tokens.find(
7067
7345
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
7068
7346
  );
7069
- let balance = tokenBalance ? (0, import_viem13.parseUnits)(tokenBalance.amount, decimals) : 0n;
7347
+ let balance = tokenBalance ? readAmount(tokenBalance.amount) : 0n;
7070
7348
  if (agent.balanceComposition === "tokens-plus-positions") {
7071
7349
  const chainNameById = {
7072
7350
  1: "ETHEREUM",
@@ -7078,20 +7356,22 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
7078
7356
  const positionChain = position2.chain.trim().toUpperCase();
7079
7357
  const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
7080
7358
  if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
7081
- 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);
7082
7360
  }
7083
7361
  }
7084
7362
  const assetTotal = agentBalance?.assetBalances?.find(
7085
7363
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
7086
7364
  );
7087
7365
  if (assetTotal) {
7088
- const total = (0, import_viem13.parseUnits)(assetTotal.amount, decimals);
7366
+ const total = readAmount(assetTotal.amount);
7089
7367
  if (total < balance) balance = total > 0n ? total : 0n;
7090
7368
  }
7091
- return { agent, balance };
7369
+ return invalidBalance ? { agent, balance: 0n, invalidBalance: true } : { agent, balance };
7092
7370
  });
7093
7371
  }
7094
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.");
7095
7375
  const plans = balances.map(({ agent, balance }) => ({
7096
7376
  agent,
7097
7377
  balance,
@@ -7114,6 +7394,8 @@ function planProportionalShares(balances, requested, totalAvailable) {
7114
7394
  return plans;
7115
7395
  }
7116
7396
  function planDisabledDrain(disabled, requested) {
7397
+ if (requested < 0n || disabled.some((entry) => entry.balance < 0n))
7398
+ throw new Error("Invalid withdrawal planning amounts.");
7117
7399
  const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
7118
7400
  (a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
7119
7401
  );
@@ -7131,6 +7413,7 @@ function planDisabledDrain(disabled, requested) {
7131
7413
  return { plans, remaining };
7132
7414
  }
7133
7415
  function redistributeShare(plans, fromIndex, amount, candidatePool) {
7416
+ if (amount <= 0n) return;
7134
7417
  const pool = candidatePool ?? plans.slice(fromIndex + 1);
7135
7418
  const candidates = pool.filter((c) => c.balance - c.planned > 0n);
7136
7419
  const totalHeadroom = candidates.reduce(
@@ -7157,7 +7440,13 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
7157
7440
  }
7158
7441
  }
7159
7442
  function sumWithdrawnAmount(results) {
7160
- 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;
7161
7450
  }
7162
7451
 
7163
7452
  // src/lib/helpers/account-apy-helper.ts
@@ -7243,15 +7532,52 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
7243
7532
  return out;
7244
7533
  }
7245
7534
 
7535
+ // src/lib/helpers/daily-earnings-helper.ts
7536
+ function aggregateDailyEarnings(series, tokenSymbol) {
7537
+ const wanted = tokenSymbol?.toUpperCase();
7538
+ const byAsset = /* @__PURE__ */ new Map();
7539
+ for (const account of series) {
7540
+ for (const { asset, points } of account.assets) {
7541
+ const symbol = asset.toUpperCase();
7542
+ if (!points.length || wanted && symbol !== wanted) continue;
7543
+ const components = byAsset.get(symbol) ?? [];
7544
+ components.push([...points].sort((a, b) => a.date.localeCompare(b.date)));
7545
+ byAsset.set(symbol, components);
7546
+ }
7547
+ }
7548
+ return [...byAsset.entries()].map(([asset, components]) => {
7549
+ const dates = [...new Set(components.flatMap((points2) => points2.map((point) => point.date)))].sort();
7550
+ const cursors = components.map((points2) => ({
7551
+ index: 0,
7552
+ // Providers can have different lifetime/window offsets and start dates.
7553
+ // Remove each opening reading before combining their earned amounts.
7554
+ baseline: points2[0].amount,
7555
+ current: points2[0].amount
7556
+ }));
7557
+ const points = dates.map((date) => {
7558
+ let amount = 0;
7559
+ components.forEach((component, index) => {
7560
+ const cursor = cursors[index];
7561
+ while (cursor.index < component.length && component[cursor.index].date <= date) {
7562
+ cursor.current = component[cursor.index++].amount;
7563
+ }
7564
+ amount += cursor.current - cursor.baseline;
7565
+ });
7566
+ return { date, amount };
7567
+ });
7568
+ return { asset, points };
7569
+ }).sort((a, b) => a.asset.localeCompare(b.asset));
7570
+ }
7571
+
7246
7572
  // src/client.ts
7247
- var import_viem17 = require("viem");
7573
+ var import_viem18 = require("viem");
7248
7574
  var import_chains5 = require("viem/chains");
7249
7575
 
7250
7576
  // src/lib/sponsored-token-batch.ts
7251
- var import_viem15 = require("viem");
7577
+ var import_viem16 = require("viem");
7252
7578
 
7253
7579
  // src/lib/permit2-batch.ts
7254
- var import_viem14 = require("viem");
7580
+ var import_viem15 = require("viem");
7255
7581
  var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
7256
7582
  var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
7257
7583
  var ERC2612_PERMIT_TYPES = {
@@ -7263,7 +7589,7 @@ var ERC2612_PERMIT_TYPES = {
7263
7589
  { name: "deadline", type: "uint256" }
7264
7590
  ]
7265
7591
  };
7266
- var ERC2612_READ_ABI = (0, import_viem14.parseAbi)([
7592
+ var ERC2612_READ_ABI = (0, import_viem15.parseAbi)([
7267
7593
  "function name() view returns (string)",
7268
7594
  "function version() view returns (string)",
7269
7595
  "function nonces(address owner) view returns (uint256)"
@@ -7301,7 +7627,7 @@ var PERMIT_BATCH_TYPES = {
7301
7627
  { name: "amount", type: "uint256" }
7302
7628
  ]
7303
7629
  };
7304
- var PERMIT2_BATCH_ABI = (0, import_viem14.parseAbi)([
7630
+ var PERMIT2_BATCH_ABI = (0, import_viem15.parseAbi)([
7305
7631
  "struct TokenPermissions { address token; uint256 amount; }",
7306
7632
  "struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
7307
7633
  "struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
@@ -7334,9 +7660,29 @@ function batchTypedData(b, spender) {
7334
7660
  }
7335
7661
  };
7336
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
+ }
7337
7681
 
7338
7682
  // src/lib/sponsored-token-batch.ts
7339
7683
  var memory = /* @__PURE__ */ new Map();
7684
+ var settledRecords = /* @__PURE__ */ new Map();
7685
+ var expiredRecords = /* @__PURE__ */ new Map();
7340
7686
  var inflight = /* @__PURE__ */ new Map();
7341
7687
  var planOf = (transfers) => JSON.stringify(
7342
7688
  transfers.map((t) => ({
@@ -7344,32 +7690,109 @@ var planOf = (transfers) => JSON.stringify(
7344
7690
  amount: BigInt(t.amount).toString()
7345
7691
  }))
7346
7692
  );
7347
- 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()}`;
7348
7694
  function pendingTokenBatch(i) {
7349
7695
  try {
7350
7696
  const saved = read(pendingKey(i));
7351
7697
  if (!saved) return;
7352
7698
  const body = JSON.parse(saved);
7353
- 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;
7354
- 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 })) };
7701
+ } catch {
7702
+ return;
7703
+ }
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
+ })]);
7355
7765
  } catch {
7356
7766
  return;
7767
+ } finally {
7768
+ expired = true;
7769
+ if (timer) clearTimeout(timer);
7357
7770
  }
7358
7771
  }
7359
7772
  function read(key2) {
7360
- 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;
7361
7775
  }
7362
- function save(key2, body) {
7363
- const value = JSON.stringify({
7776
+ function storedBody(body) {
7777
+ return JSON.stringify({
7364
7778
  ...body,
7365
7779
  transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
7366
7780
  });
7781
+ }
7782
+ function save(key2, body) {
7783
+ const value = storedBody(body);
7367
7784
  if (typeof window === "undefined") memory.set(key2, value);
7368
7785
  else window.localStorage.setItem(key2, value);
7786
+ settledRecords.delete(key2);
7369
7787
  }
7370
- function clear(key2) {
7371
- if (typeof window === "undefined") memory.delete(key2);
7372
- 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
+ }
7373
7796
  }
7374
7797
  function sponsorTokenBatch(i) {
7375
7798
  const key2 = pendingKey(i);
@@ -7436,7 +7859,7 @@ async function execute(i, key2, plan) {
7436
7859
  baseUrl: i.baseUrl,
7437
7860
  body
7438
7861
  });
7439
- 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)
7440
7863
  throw new Error(
7441
7864
  "Sponsorship API did not return a valid prepared permit transaction."
7442
7865
  );
@@ -7447,7 +7870,7 @@ async function execute(i, key2, plan) {
7447
7870
  save(key2, body);
7448
7871
  }
7449
7872
  if (!body.serializedTransaction) {
7450
- 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;
7451
7874
  let prepared = permitHash ? await postSigned("approval", permitHash) : await post();
7452
7875
  if (prepared?.status === "pending") {
7453
7876
  if (!permitHash || prepared.phase !== "approval" || prepared.txHash !== permitHash)
@@ -7458,7 +7881,7 @@ async function execute(i, key2, plan) {
7458
7881
  if (!prepared) prepared = await post();
7459
7882
  if (prepared.status === "pending" && permitHash && prepared.phase === "approval" && prepared.txHash === permitHash)
7460
7883
  throw pendingError("approval", permitHash);
7461
- if (!prepared.serializedTransaction || (0, import_viem15.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
7884
+ if (!prepared.serializedTransaction || (0, import_viem16.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
7462
7885
  throw new Error(
7463
7886
  "Sponsorship API did not return a valid prepared transaction."
7464
7887
  );
@@ -7469,22 +7892,22 @@ async function execute(i, key2, plan) {
7469
7892
  save(key2, body);
7470
7893
  }
7471
7894
  try {
7472
- i.onSubmitted?.((0, import_viem15.keccak256)(body.serializedTransaction));
7895
+ i.onSubmitted?.((0, import_viem16.keccak256)(body.serializedTransaction));
7473
7896
  } catch {
7474
7897
  }
7475
- const hash = (0, import_viem15.keccak256)(body.serializedTransaction);
7898
+ const hash = (0, import_viem16.keccak256)(body.serializedTransaction);
7476
7899
  const existing = await i.pub.getTransactionReceipt({ hash }).catch(() => void 0);
7477
7900
  if (existing) {
7478
7901
  assertReceipt(existing, "deposit");
7479
7902
  void post().catch(() => void 0);
7480
- clear(key2);
7903
+ clear(key2, body);
7481
7904
  return hash;
7482
7905
  }
7483
7906
  let result = await postSigned("deposit", hash);
7484
- 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)) {
7485
7908
  await waitForConfirmation("approval", result.txHash);
7486
7909
  result = await postSigned("deposit", hash);
7487
- 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);
7488
7911
  }
7489
7912
  if (result) {
7490
7913
  if (result.txHash !== hash || result.status === "pending" && result.phase !== "deposit")
@@ -7492,11 +7915,11 @@ async function execute(i, key2, plan) {
7492
7915
  if (result.status === "pending") await waitForConfirmation("deposit", hash);
7493
7916
  }
7494
7917
  if (!result || result.status === "pending") void post().catch(() => void 0);
7495
- clear(key2);
7918
+ clear(key2, body);
7496
7919
  return hash;
7497
7920
  } catch (error) {
7498
7921
  if (!body.serializedTransaction && !body.serializedPermitTransaction || rejectedPreparation(error) || error instanceof OwneyError && error.details?.notSubmitted === true)
7499
- clear(key2);
7922
+ clear(key2, body);
7500
7923
  throw error;
7501
7924
  }
7502
7925
  };
@@ -7505,10 +7928,16 @@ async function execute(i, key2, plan) {
7505
7928
  throw new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit changed. Check its status before depositing again.");
7506
7929
  if (saved) {
7507
7930
  const previous = JSON.parse(saved);
7508
- 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)
7509
7932
  throw new Error(
7510
7933
  "Retry the previous token deposit and agent split first to reconcile its status."
7511
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
+ );
7512
7941
  i.onApproved?.();
7513
7942
  return send({ ...previous, transfers: i.transfers });
7514
7943
  }
@@ -7522,7 +7951,7 @@ async function execute(i, key2, plan) {
7522
7951
  "DEPOSIT_INSUFFICIENT_BALANCE",
7523
7952
  "Insufficient token balance for this deposit."
7524
7953
  );
7525
- 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)))
7526
7955
  throw new OwneyError(
7527
7956
  "PERMIT2_APPROVAL_REQUIRED",
7528
7957
  "token deposits need a one-time Permit2 approval."
@@ -7701,7 +8130,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
7701
8130
  }
7702
8131
 
7703
8132
  // src/lib/sponsored-calls-deposit.ts
7704
- var import_viem16 = require("viem");
8133
+ var import_viem17 = require("viem");
7705
8134
  var DEFAULT_POLL_INTERVAL_MS = 1500;
7706
8135
  var DEFAULT_MAX_POLLS = 30;
7707
8136
  async function paymasterSupported(provider, owner, chainId) {
@@ -7709,7 +8138,7 @@ async function paymasterSupported(provider, owner, chainId) {
7709
8138
  method: "wallet_getCapabilities",
7710
8139
  params: [owner]
7711
8140
  });
7712
- const forChain = caps?.[(0, import_viem16.toHex)(chainId)] ?? caps?.[String(chainId)];
8141
+ const forChain = caps?.[(0, import_viem17.toHex)(chainId)] ?? caps?.[String(chainId)];
7713
8142
  return Boolean(forChain?.paymasterService?.supported);
7714
8143
  }
7715
8144
  function makeSponsoredCallsCallback(deps) {
@@ -7746,8 +8175,8 @@ function makeSponsoredCallsCallback(deps) {
7746
8175
  const calls = transfers.map((transfer) => ({
7747
8176
  to: token,
7748
8177
  value: "0x0",
7749
- data: (0, import_viem16.encodeFunctionData)({
7750
- abi: import_viem16.erc20Abi,
8178
+ data: (0, import_viem17.encodeFunctionData)({
8179
+ abi: import_viem17.erc20Abi,
7751
8180
  functionName: "transfer",
7752
8181
  args: [transfer.to, BigInt(transfer.amount)]
7753
8182
  })
@@ -7779,7 +8208,7 @@ function makeSponsoredCallsCallback(deps) {
7779
8208
  url.searchParams.append("owneyIntent", intent);
7780
8209
  paymasterUrl = url.toString();
7781
8210
  }
7782
- 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)));
7783
8212
  const progress = (id, transfer) => {
7784
8213
  try {
7785
8214
  deps.onCallsProgress?.(id, transfers, transfer);
@@ -7796,7 +8225,7 @@ function makeSponsoredCallsCallback(deps) {
7796
8225
  version: "2.0.0",
7797
8226
  id: requestedId,
7798
8227
  from: deps.ownerAddress,
7799
- chainId: (0, import_viem16.toHex)(chainId),
8228
+ chainId: (0, import_viem17.toHex)(chainId),
7800
8229
  atomicRequired: transfers.length > 1,
7801
8230
  calls,
7802
8231
  capabilities: {
@@ -7929,6 +8358,8 @@ var OwneySDK = class {
7929
8358
  */
7930
8359
  disabledAgents = /* @__PURE__ */ new Set();
7931
8360
  state = null;
8361
+ connectionRevision = 0;
8362
+ disconnectingAgents = null;
7932
8363
  apiKey;
7933
8364
  // The org's agent policy, fetched once per client. Applied to each agent on
7934
8365
  // activation (ROUT-229). `undefined` means "not fetched yet", `null` means
@@ -7950,6 +8381,9 @@ var OwneySDK = class {
7950
8381
  recoveredFundingReads = /* @__PURE__ */ new Set();
7951
8382
  fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
7952
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();
7953
8387
  readAgent(agent, method, fetch2, params = null) {
7954
8388
  const key2 = JSON.stringify([
7955
8389
  this.state?.walletAddress.toLowerCase() ?? null,
@@ -7982,9 +8416,10 @@ var OwneySDK = class {
7982
8416
  */
7983
8417
  // TODO: replace with a proper EIP-1193 Provider type
7984
8418
  async connect(provider) {
7985
- const accounts = await provider.request({
7986
- method: "eth_accounts"
7987
- });
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.");
7988
8423
  const walletAddress = accounts[0];
7989
8424
  if (!walletAddress) {
7990
8425
  throw new OwneyError(
@@ -7992,33 +8427,46 @@ var OwneySDK = class {
7992
8427
  "No accounts found. Ensure the wallet is unlocked and connected."
7993
8428
  );
7994
8429
  }
7995
- this.agentReads.clearInFlight();
7996
- this.activationChecks.clear();
7997
- 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.");
7998
8435
  this.state = { provider, walletAddress, chainId: null };
7999
- this.cachedSponsoredCallback = null;
8000
- this.cachedWethSponsoredCallback = null;
8001
- this.cachedSponsoredCallsCallbacks.clear();
8002
8436
  }
8003
8437
  /**
8004
8438
  * Disconnect from all agents and clear connection state.
8005
8439
  * After calling this, `connect()` must be called again before using agent methods.
8006
8440
  */
8007
8441
  async disconnect() {
8442
+ ++this.connectionRevision;
8443
+ this.clearConnectionState();
8444
+ await this.disconnectAgents();
8445
+ }
8446
+ clearConnectionState() {
8008
8447
  this.state = null;
8009
8448
  this.fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
8010
- for (const agent of this.agents.values()) {
8011
- await agent.disconnect();
8012
- }
8013
8449
  this.activeAgents.clear();
8014
- this.disabledAgents.clear();
8015
8450
  this.agentReads.clearInFlight();
8016
8451
  this.activationChecks.clear();
8017
- this.state = null;
8018
8452
  this.cachedSponsoredCallback = null;
8019
8453
  this.cachedWethSponsoredCallback = null;
8020
8454
  this.cachedSponsoredCallsCallbacks.clear();
8021
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
+ }
8022
8470
  isAgentDisabled(agentId) {
8023
8471
  return this.disabledAgents.has(agentId);
8024
8472
  }
@@ -8081,14 +8529,14 @@ var OwneySDK = class {
8081
8529
  // PublicClient/WalletClient param types — structurally identical at
8082
8530
  // runtime, but the two share a name TS treats as unrelated.
8083
8531
  getPublicClient: (cid) => this.getPaidRpcClient(cid),
8084
- getWalletChainClient: (cid) => (0, import_viem17.createPublicClient)({
8532
+ getWalletChainClient: (cid) => (0, import_viem18.createPublicClient)({
8085
8533
  chain: VIEM_CHAIN2[cid],
8086
- transport: (0, import_viem17.custom)(provider)
8534
+ transport: (0, import_viem18.custom)(provider)
8087
8535
  }),
8088
- getWalletClient: (cid) => (0, import_viem17.createWalletClient)({
8536
+ getWalletClient: (cid) => (0, import_viem18.createWalletClient)({
8089
8537
  account: owner,
8090
8538
  chain: VIEM_CHAIN2[cid],
8091
- transport: (0, import_viem17.custom)(provider)
8539
+ transport: (0, import_viem18.custom)(provider)
8092
8540
  })
8093
8541
  });
8094
8542
  if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedSponsoredCallback = callback;
@@ -8136,14 +8584,14 @@ var OwneySDK = class {
8136
8584
  // PublicClient/WalletClient param types — structurally identical at
8137
8585
  // runtime, but the two share a name TS treats as unrelated.
8138
8586
  getPublicClient: (cid) => this.getPaidRpcClient(cid),
8139
- getWalletChainClient: (cid) => (0, import_viem17.createPublicClient)({
8587
+ getWalletChainClient: (cid) => (0, import_viem18.createPublicClient)({
8140
8588
  chain: VIEM_CHAIN2[cid],
8141
- transport: (0, import_viem17.custom)(provider)
8589
+ transport: (0, import_viem18.custom)(provider)
8142
8590
  }),
8143
- getWalletClient: (cid) => (0, import_viem17.createWalletClient)({
8591
+ getWalletClient: (cid) => (0, import_viem18.createWalletClient)({
8144
8592
  account: owner,
8145
8593
  chain: VIEM_CHAIN2[cid],
8146
- transport: (0, import_viem17.custom)(provider)
8594
+ transport: (0, import_viem18.custom)(provider)
8147
8595
  })
8148
8596
  });
8149
8597
  if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedWethSponsoredCallback = callback;
@@ -8454,6 +8902,12 @@ var OwneySDK = class {
8454
8902
  */
8455
8903
  async deposit(options) {
8456
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
+ );
8457
8911
  const prepared = /* @__PURE__ */ new Map();
8458
8912
  const paidFallbackAgents = /* @__PURE__ */ new Set();
8459
8913
  const onProgress = options.onProgress ? (evidence) => {
@@ -8481,6 +8935,47 @@ var OwneySDK = class {
8481
8935
  throw new OwneyError("DEPOSIT_IN_PROGRESS", "This deposit cannot resume the saved relayer transaction.");
8482
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));
8483
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
+ }
8484
8979
  if (agentId) {
8485
8980
  if (this.isAgentDisabled(agentId)) {
8486
8981
  throw new OwneyError(
@@ -8897,9 +9392,36 @@ var OwneySDK = class {
8897
9392
  const token = SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token);
8898
9393
  if (!token) throw new OwneyError("CHAIN_UNSUPPORTED", "Unsupported funding asset.");
8899
9394
  const rpc2 = this.getPaidRpcClient(chainId);
8900
- 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
+ }
8901
9423
  assertCurrent();
8902
- let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction);
9424
+ let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction, options);
8903
9425
  if (direction === "withdraw" && evidence.agentId === "yieldseeker" && verified.transfer === "unknown" && verified.confirmedAmount && BigInt(verified.confirmedAmount) > 0n) {
8904
9426
  const transaction = await rpc2.getTransaction({ hash: evidence.txHash });
8905
9427
  assertCurrent();
@@ -9073,9 +9595,11 @@ var OwneySDK = class {
9073
9595
  return page;
9074
9596
  }
9075
9597
  async recoverDepositRegistration(evidence) {
9598
+ const { state, assertCurrent } = this.captureReadScope(evidence.chainId);
9076
9599
  const verified = await this.inspectFunding(evidence, "deposit");
9600
+ assertCurrent();
9077
9601
  if (verified.agentId !== "zyfai") return verified;
9078
- return this.getAgent("zyfai").retryDepositRegistration(this.requireState(), verified);
9602
+ return this.getAgent("zyfai").retryDepositRegistration(state, verified);
9079
9603
  }
9080
9604
  // --- Fund operations ---
9081
9605
  /** On-chain version and ownership status for this owner's Yieldseeker agent. */
@@ -9121,10 +9645,24 @@ var OwneySDK = class {
9121
9645
  * @returns {AgentWithdrawResult} for a single agent, or {OwneyWithdrawResult} with per-agent results
9122
9646
  */
9123
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
+ );
9124
9654
  const state = this.requireState();
9125
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
+ );
9126
9663
  const ids = options.agentId ? [options.agentId] : this.getActiveAgentIds().filter((id) => !options.excludeAgentIds?.includes(id));
9127
9664
  const attempted = /* @__PURE__ */ new Set();
9665
+ this.withdrawalSubmissions.add(submissionKey);
9128
9666
  try {
9129
9667
  return await this.withdrawFromAgents(options, state, chainId, attempted);
9130
9668
  } catch (error) {
@@ -9141,6 +9679,8 @@ var OwneySDK = class {
9141
9679
  });
9142
9680
  }
9143
9681
  throw error;
9682
+ } finally {
9683
+ this.withdrawalSubmissions.delete(submissionKey);
9144
9684
  }
9145
9685
  }
9146
9686
  async withdrawFromAgents(options, state, chainId, attempted) {
@@ -9179,7 +9719,19 @@ var OwneySDK = class {
9179
9719
  };
9180
9720
  };
9181
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
+ };
9182
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.");
9183
9735
  attempted.add(agent.id);
9184
9736
  let evidence = {
9185
9737
  agentId: agent.id,
@@ -9191,12 +9743,16 @@ var OwneySDK = class {
9191
9743
  registration: "not-applicable",
9192
9744
  delivery: "pending"
9193
9745
  };
9746
+ let adapterReported = false;
9194
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;
9195
9750
  evidence = { ...evidence, ...next };
9196
9751
  submissionEvidence.set(agent.id, evidence);
9197
9752
  notifyFunding(options.onProgress, evidence);
9198
9753
  };
9199
9754
  progress(evidence);
9755
+ adapterReported = false;
9200
9756
  try {
9201
9757
  if (options.onBeforeSubmission)
9202
9758
  return await agent.withdraw(
@@ -9210,7 +9766,7 @@ var OwneySDK = class {
9210
9766
  );
9211
9767
  return await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
9212
9768
  } catch (error) {
9213
- 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 : "")))
9214
9770
  progress({ ...evidence, transfer: "failed", delivery: "failed" });
9215
9771
  throw error;
9216
9772
  }
@@ -9271,6 +9827,7 @@ var OwneySDK = class {
9271
9827
  }
9272
9828
  }
9273
9829
  if (Object.keys(results2).length === 0) {
9830
+ throwUnconfirmed(agentErrors2);
9274
9831
  if (Object.keys(agentErrors2).length === 0)
9275
9832
  throw new OwneyError(
9276
9833
  "WITHDRAW_INSUFFICIENT_BALANCE",
@@ -9291,7 +9848,7 @@ var OwneySDK = class {
9291
9848
  );
9292
9849
  }
9293
9850
  const amountsKnown = Object.values(results2).every(
9294
- (result) => typeof result.amount === "string" && /^\d+$/.test(result.amount)
9851
+ (result) => confirmedWithdrawalAmount(result) !== void 0
9295
9852
  );
9296
9853
  return {
9297
9854
  agentResult: results2,
@@ -9310,6 +9867,13 @@ var OwneySDK = class {
9310
9867
  asset,
9311
9868
  assetInfo.decimals
9312
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
+ }
9313
9877
  const indexedAvailable = balances.reduce((sum, entry) => sum + entry.balance, 0n);
9314
9878
  await Promise.all(balances.map(async (entry) => {
9315
9879
  if (entry.balance !== 0n && indexedAvailable >= requested || !entry.agent.getWithdrawableBalance) return;
@@ -9322,7 +9886,7 @@ var OwneySDK = class {
9322
9886
  if (this.isUserRejectedWithdrawal(originalFailure)) throw originalFailure;
9323
9887
  if (entry.balance !== 0n && entry.agent.id !== "yieldseeker") {
9324
9888
  const total = aggregated.agentBalances[entry.agent.id]?.assetBalances?.find((value) => value.chainId === chainId && value.asset.toUpperCase() === token);
9325
- 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;
9326
9890
  }
9327
9891
  try {
9328
9892
  const otherAvailable = indexedAvailable - entry.balance;
@@ -9349,12 +9913,15 @@ var OwneySDK = class {
9349
9913
  throw new OwneyError("NOT_CONNECTED", "Account changed while planning withdrawal.");
9350
9914
  const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
9351
9915
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
9352
- if (totalAvailable === 0n && unavailableAgents.length > 0) {
9916
+ if (totalAvailable < requested && unavailableAgents.length > 0) {
9353
9917
  throw new OwneyError(
9354
9918
  "WITHDRAW_BALANCE_UNAVAILABLE",
9355
- `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(", ")}.`,
9356
9920
  {
9357
9921
  asset,
9922
+ requested: amount,
9923
+ available: totalAvailable.toString(),
9924
+ rejectedBeforeSubmission: true,
9358
9925
  unavailableAgents: unavailableAgentIds,
9359
9926
  agentErrors: aggregated.agentErrors
9360
9927
  }
@@ -9371,7 +9938,7 @@ var OwneySDK = class {
9371
9938
  }
9372
9939
  );
9373
9940
  }
9374
- const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
9941
+ const plannedTarget = requested;
9375
9942
  const disabledBalances = balances.filter(
9376
9943
  (b) => this.isAgentDisabled(b.agent.id)
9377
9944
  );
@@ -9390,13 +9957,23 @@ var OwneySDK = class {
9390
9957
  }));
9391
9958
  const plans = [...disabledPlans, ...enabledPlans];
9392
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();
9393
9969
  const results = {};
9394
9970
  const agentErrors = {
9395
9971
  ...aggregated.agentErrors ?? {}
9396
9972
  };
9397
9973
  for (let i = 0; i < orderedPlans.length; i++) {
9398
9974
  const p = orderedPlans[i];
9399
- if (p.planned === 0n) continue;
9975
+ assertPlan();
9976
+ if (p.planned <= 0n) continue;
9400
9977
  try {
9401
9978
  results[p.agent.id] = await submit(p.agent, p.planned.toString());
9402
9979
  notifyAgentResult(p.agent.id, results[p.agent.id]);
@@ -9420,6 +9997,7 @@ var OwneySDK = class {
9420
9997
  }
9421
9998
  }
9422
9999
  if (Object.keys(results).length === 0) {
10000
+ throwUnconfirmed(agentErrors);
9423
10001
  throw new OwneyError(
9424
10002
  "WITHDRAW_ALL_FAILED",
9425
10003
  `Failed to withdraw "${amount}" of "${asset}" from all eligible agents.`,
@@ -9437,13 +10015,17 @@ var OwneySDK = class {
9437
10015
  const adjustment = result?.feeAdjustment;
9438
10016
  const evidence = result?.evidence;
9439
10017
  const submitted = submissionEvidence.get(plan.agent.id);
9440
- 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;
9441
10019
  const fee = BigInt(adjustment.feeAmount);
9442
10020
  const net = BigInt(result.amount);
9443
10021
  const gross = BigInt(adjustment.grossAmount);
9444
10022
  return fee > 0n && net > 0n && gross === plan.planned && net + fee === gross ? total + fee : total;
9445
10023
  }, 0n);
9446
- 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) {
9447
10029
  throw new OwneyError(
9448
10030
  "WITHDRAW_PARTIAL_FAILURE",
9449
10031
  `Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
@@ -9451,6 +10033,7 @@ var OwneySDK = class {
9451
10033
  asset,
9452
10034
  requested: amount,
9453
10035
  withdrawn: withdrawn.toString(),
10036
+ pendingAmount: pendingAmount.toString(),
9454
10037
  partialResults: results,
9455
10038
  agentErrors,
9456
10039
  agentFailures,
@@ -9458,7 +10041,7 @@ var OwneySDK = class {
9458
10041
  }
9459
10042
  );
9460
10043
  }
9461
- return { agentResult: results, totalWithdrawn: withdrawn.toString() };
10044
+ return { agentResult: results, totalWithdrawn: Object.values(results).every((result) => confirmedWithdrawalAmount(result) !== void 0) ? withdrawn.toString() : null };
9462
10045
  }
9463
10046
  // --- Portfolio reads ---
9464
10047
  /**
@@ -9625,10 +10208,8 @@ var OwneySDK = class {
9625
10208
  * @returns {AccountAgentApy} for a single agent, or {OwneyAccountApy} with totalApy and per-agent breakdown
9626
10209
  */
9627
10210
  /**
9628
- * Daily cumulative NET earnings for the selected chain/asset, backing the
9629
- * "recent earnings" subline. Net is computed as Zyfai's own
9630
- * `lifetime + unrealized + current x 0.9`, so the figure reconciles with the
9631
- * balance headline rather than reading ~11% high. (ROUT-452)
10211
+ * Daily cumulative earnings in the selected asset's units, backing the
10212
+ * "recent earnings" subline. Each adapter uses its provider's earnings basis.
9632
10213
  *
9633
10214
  * Unlike getAccountApy this does NOT blend across agents: earnings are
9634
10215
  * summed, not weighted, and an agent that fails to report must not silently
@@ -9661,7 +10242,7 @@ var OwneySDK = class {
9661
10242
  const entries = [...this.getActiveAgents().entries()].filter(
9662
10243
  ([, agent]) => agent.getDailyEarnings
9663
10244
  );
9664
- const series = await Promise.all(
10245
+ const settled = await Promise.allSettled(
9665
10246
  entries.map(
9666
10247
  ([, agent]) => this.readAgent(
9667
10248
  agent,
@@ -9671,23 +10252,15 @@ var OwneySDK = class {
9671
10252
  )
9672
10253
  )
9673
10254
  );
9674
- const byAsset = /* @__PURE__ */ new Map();
9675
- for (const s of series) {
9676
- for (const { asset, points } of s.assets) {
9677
- const byDate = byAsset.get(asset) ?? /* @__PURE__ */ new Map();
9678
- for (const point of points) {
9679
- byDate.set(point.date, (byDate.get(point.date) ?? 0) + point.amount);
9680
- }
9681
- byAsset.set(asset, byDate);
9682
- }
10255
+ const series = settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
10256
+ if (entries.length && !series.length) {
10257
+ const failure = settled.find((result) => result.status === "rejected");
10258
+ if (failure?.status === "rejected") throw failure.reason;
9683
10259
  }
9684
10260
  return {
9685
10261
  walletAddress: series[0]?.walletAddress ?? state.walletAddress ?? "",
9686
10262
  chainId,
9687
- assets: [...byAsset.entries()].map(([asset, byDate]) => ({
9688
- asset,
9689
- points: [...byDate.entries()].map(([date, amount]) => ({ date, amount })).sort((a, b) => a.date.localeCompare(b.date))
9690
- })).sort((a, b) => a.asset.localeCompare(b.asset))
10263
+ assets: aggregateDailyEarnings(series, tokenSymbol)
9691
10264
  };
9692
10265
  }
9693
10266
  async getAccountApy({
@@ -9926,16 +10499,16 @@ var OwneySDK = class {
9926
10499
  );
9927
10500
  }
9928
10501
  const provider = this.requireConnectedProvider();
9929
- const walletChainClient = (0, import_viem17.createPublicClient)({
10502
+ const walletChainClient = (0, import_viem18.createPublicClient)({
9930
10503
  chain: VIEM_CHAIN2[chainId],
9931
- transport: (0, import_viem17.custom)(provider)
10504
+ transport: (0, import_viem18.custom)(provider)
9932
10505
  });
9933
10506
  const publicClient = this.getPaidRpcClient(chainId);
9934
10507
  const approvalAmount = permit2ApprovalAmount(requiredAmount);
9935
- const wallet = (0, import_viem17.createWalletClient)({
10508
+ const wallet = (0, import_viem18.createWalletClient)({
9936
10509
  account: state.walletAddress,
9937
10510
  chain: VIEM_CHAIN2[chainId],
9938
- transport: (0, import_viem17.custom)(provider)
10511
+ transport: (0, import_viem18.custom)(provider)
9939
10512
  });
9940
10513
  await ensureWalletOnChain(walletChainClient, wallet, chainId);
9941
10514
  const hash = await wallet.writeContract({
@@ -10097,7 +10670,7 @@ var OwneySDK = class {
10097
10670
  };
10098
10671
 
10099
10672
  // src/agents/zyfai/zyfai.siwx.ts
10100
- var import_viem18 = require("viem");
10673
+ var import_viem19 = require("viem");
10101
10674
  var import_siwe2 = require("siwe");
10102
10675
  var import_sdk2 = require("@zyfai/sdk");
10103
10676
 
@@ -10224,7 +10797,7 @@ function buildSIWXConfig(deps) {
10224
10797
  issuedAt,
10225
10798
  toString() {
10226
10799
  return new import_siwe2.SiweMessage({
10227
- address: (0, import_viem18.getAddress)(accountAddress),
10800
+ address: (0, import_viem19.getAddress)(accountAddress),
10228
10801
  chainId: numericChainId(chainId),
10229
10802
  domain,
10230
10803
  uri,