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

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;
@@ -232,6 +286,240 @@ var AgentChainIncompatibleError = class extends OwneyError {
232
286
  }
233
287
  };
234
288
 
289
+ // src/lib/transfer-auth.ts
290
+ var import_viem2 = require("viem");
291
+
292
+ // src/lib/sponsor-client.ts
293
+ var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
294
+ async function postPaymasterIntent(input) {
295
+ const base6 = input.baseUrl ?? ROUTING_API_BASE_URL;
296
+ let res;
297
+ try {
298
+ res = await fetch(`${base6}/api/v1/sponsor/paymaster-intent`, {
299
+ method: "POST",
300
+ headers: {
301
+ "content-type": "application/json",
302
+ "x-owney-api-key": input.apiKey,
303
+ Authorization: `Signature ${input.yieldseekerSignature}`
304
+ },
305
+ body: JSON.stringify(input.body)
306
+ });
307
+ } catch (networkError) {
308
+ throw new OwneyError(
309
+ "SPONSOR_REQUEST_FAILED",
310
+ `Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
311
+ { cause: String(networkError), safeToFallback: true }
312
+ );
313
+ }
314
+ const text = await res.text();
315
+ let parsed = null;
316
+ try {
317
+ parsed = JSON.parse(text);
318
+ } catch {
319
+ }
320
+ if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
321
+ throw new OwneyError(
322
+ "SPONSOR_REQUEST_FAILED",
323
+ `Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
324
+ {
325
+ statusCode: res.status,
326
+ responseBody: text.slice(0, 500),
327
+ safeToFallback: true
328
+ }
329
+ );
330
+ }
331
+ return parsed.data;
332
+ }
333
+ async function getSponsorRelayerAddress(input) {
334
+ const base6 = input.baseUrl ?? ROUTING_API_BASE_URL;
335
+ let res;
336
+ try {
337
+ res = await fetch(
338
+ `${base6}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
339
+ {
340
+ headers: { "x-owney-api-key": input.apiKey }
341
+ }
342
+ );
343
+ } catch (networkError) {
344
+ throw new OwneyError(
345
+ "SPONSOR_REQUEST_FAILED",
346
+ `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
347
+ { cause: String(networkError), safeToFallback: true }
348
+ );
349
+ }
350
+ const text = await res.text();
351
+ let parsed = null;
352
+ try {
353
+ parsed = JSON.parse(text);
354
+ } catch {
355
+ }
356
+ if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
357
+ throw new OwneyError(
358
+ "SPONSOR_REQUEST_FAILED",
359
+ `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
360
+ {
361
+ statusCode: res.status,
362
+ responseBody: text.slice(0, 500),
363
+ safeToFallback: true
364
+ }
365
+ );
366
+ }
367
+ return parsed.data.relayer;
368
+ }
369
+ async function postSponsorBatchTransfer(input) {
370
+ let res;
371
+ try {
372
+ res = await fetch(
373
+ `${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch`,
374
+ {
375
+ method: "POST",
376
+ headers: {
377
+ "content-type": "application/json",
378
+ "x-owney-api-key": input.apiKey
379
+ },
380
+ body: JSON.stringify(input.body)
381
+ }
382
+ );
383
+ } catch {
384
+ throw new OwneyError(
385
+ "SPONSOR_REQUEST_FAILED",
386
+ "Deposit status is unknown. Retry the same amount to check it.",
387
+ { safeToFallback: false }
388
+ );
389
+ }
390
+ const parsed = await res.json().catch(() => null);
391
+ if (!res.ok || !parsed?.success || !/^0x[0-9a-fA-F]{64}$/.test(parsed.data?.txHash ?? "")) {
392
+ throw new OwneyError(
393
+ "SPONSOR_REQUEST_FAILED",
394
+ "Deposit could not be confirmed. Retry the same amount to check its status.",
395
+ {
396
+ statusCode: res.status,
397
+ safeToFallback: false,
398
+ notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
399
+ }
400
+ );
401
+ }
402
+ return parsed.data;
403
+ }
404
+
405
+ // src/lib/permit2.ts
406
+ var import_viem3 = require("viem");
407
+ var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
408
+ var MAX_UINT256 = 2n ** 256n - 1n;
409
+ function permit2ApprovalAmount(requiredAmount) {
410
+ if (requiredAmount <= 0n) {
411
+ throw new Error("Permit2 approval requires a positive deposit amount");
412
+ }
413
+ return MAX_UINT256;
414
+ }
415
+ var ERC20_ALLOWANCE_ABI = [
416
+ {
417
+ type: "function",
418
+ name: "allowance",
419
+ stateMutability: "view",
420
+ inputs: [
421
+ { name: "owner", type: "address" },
422
+ { name: "spender", type: "address" }
423
+ ],
424
+ outputs: [{ name: "", type: "uint256" }]
425
+ },
426
+ {
427
+ type: "function",
428
+ name: "approve",
429
+ stateMutability: "nonpayable",
430
+ inputs: [
431
+ { name: "spender", type: "address" },
432
+ { name: "amount", type: "uint256" }
433
+ ],
434
+ outputs: [{ name: "", type: "bool" }]
435
+ },
436
+ {
437
+ type: "function",
438
+ name: "balanceOf",
439
+ stateMutability: "view",
440
+ inputs: [{ name: "account", type: "address" }],
441
+ outputs: [{ name: "", type: "uint256" }]
442
+ }
443
+ ];
444
+ function randomPermit2Nonce() {
445
+ const bytes = new Uint8Array(32);
446
+ globalThis.crypto.getRandomValues(bytes);
447
+ return BigInt((0, import_viem3.bytesToHex)(bytes));
448
+ }
449
+ async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
450
+ return publicClient.readContract({
451
+ address: token,
452
+ abi: ERC20_ALLOWANCE_ABI,
453
+ functionName: "allowance",
454
+ args: [owner, PERMIT2_ADDRESS],
455
+ ...blockNumber === void 0 ? {} : { blockNumber }
456
+ });
457
+ }
458
+ async function readErc20Balance(publicClient, token, owner) {
459
+ return publicClient.readContract({
460
+ address: token,
461
+ abi: ERC20_ALLOWANCE_ABI,
462
+ functionName: "balanceOf",
463
+ args: [owner]
464
+ });
465
+ }
466
+
467
+ // src/lib/chain-guard.ts
468
+ var CHAIN_NAMES = {
469
+ 1: "Ethereum",
470
+ 8453: "Base",
471
+ 42161: "Arbitrum"
472
+ };
473
+ function chainName(chainId) {
474
+ return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
475
+ }
476
+ async function ensureWalletOnChain(pub, wallet, expected) {
477
+ const actual = await pub.getChainId();
478
+ if (actual === expected) return;
479
+ try {
480
+ await wallet.switchChain({ id: expected });
481
+ } catch (error) {
482
+ throw new OwneyError(
483
+ "CHAIN_MISMATCH",
484
+ `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
485
+ {
486
+ expectedChainId: expected,
487
+ actualChainId: actual,
488
+ cause: error instanceof Error ? error.message : String(error)
489
+ }
490
+ );
491
+ }
492
+ const after = await pub.getChainId();
493
+ if (after !== expected) {
494
+ throw new OwneyError(
495
+ "CHAIN_MISMATCH",
496
+ `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
497
+ { expectedChainId: expected, actualChainId: after }
498
+ );
499
+ }
500
+ }
501
+
502
+ // src/lib/sponsored-deposit.ts
503
+ var AUTH_WINDOW_SECONDS = 15 * 60;
504
+ var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
505
+ function provideDepositVerificationContext(callback, context) {
506
+ callback[verificationSetter]?.(context);
507
+ }
508
+ function makeVerificationAwareDepositCallback(implementation) {
509
+ let nextVerification;
510
+ const callback = async (smartWallet, chainId, amount) => {
511
+ const verification = nextVerification;
512
+ nextVerification = void 0;
513
+ return implementation(smartWallet, chainId, amount, verification);
514
+ };
515
+ Object.defineProperty(callback, verificationSetter, {
516
+ value: (context) => {
517
+ nextVerification = context;
518
+ }
519
+ });
520
+ return callback;
521
+ }
522
+
235
523
  // src/lib/rate-limit.ts
236
524
  function rateLimitDelay(error, now = Date.now()) {
237
525
  const seen = /* @__PURE__ */ new Set();
@@ -379,7 +667,7 @@ var AgentReads = class {
379
667
 
380
668
  // src/agents/zyfai/zyfai.withdraw-submission.ts
381
669
  var listenersByClient = /* @__PURE__ */ new WeakMap();
382
- function observeWithdrawalSubmission(sdk, chainId, token) {
670
+ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
383
671
  const client = sdk.httpClient;
384
672
  if (!client || typeof client.get !== "function" || typeof client.post !== "function")
385
673
  return { attempted: () => void 0, rejected: () => false, request: () => void 0, stop: () => {
@@ -396,6 +684,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
396
684
  const withdrawal = method === "get" && url === "/users/withdraw" || method === "post" && url === "/users/partial-withdraw";
397
685
  const payload = method === "get" ? options?.params : options;
398
686
  const matching = withdrawal ? [...observers].filter((observer) => observer.chainId === payload?.chainId && observer.token === payload?.tokenSymbol) : [];
687
+ for (const observer of matching) observer.assertCurrent?.();
399
688
  const startedAt = Date.now();
400
689
  const request2 = {
401
690
  attempted: true,
@@ -440,7 +729,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
440
729
  const status = error?.response?.status;
441
730
  if (request2.httpStatus === void 0 && typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599)
442
731
  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";
732
+ 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
733
  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
734
  throw error;
446
735
  } finally {
@@ -453,7 +742,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
453
742
  let submitted = false;
454
743
  let rejected = false;
455
744
  let request = { attempted: false };
456
- const listener = { chainId, token, notify: (outcome, evidence) => {
745
+ const listener = { chainId, token, assertCurrent, notify: (outcome, evidence) => {
457
746
  request = evidence;
458
747
  if (outcome === "attempted") submitted = true;
459
748
  else if (outcome === "rejected") rejected = true;
@@ -504,10 +793,8 @@ function hasEmptyWithdrawalSnapshot(raw2, chainId, token) {
504
793
  return true;
505
794
  }
506
795
 
507
- // src/agents/zyfai/zyfai.agent.ts
508
- var import_sdk = require("@zyfai/sdk");
796
+ // src/agents/zyfai/zyfai.deposit.ts
509
797
  var import_viem4 = require("viem");
510
- var import_chains = require("viem/chains");
511
798
 
512
799
  // src/types/config.ts
513
800
  var P_SUPPORTED_CHAINS = {
@@ -556,12 +843,51 @@ var SupportedAssets = [
556
843
  }
557
844
  ];
558
845
 
559
- // src/lib/debug.ts
560
- var configuredDebug = false;
561
- function setOwneyDebug(enabled) {
562
- configuredDebug = enabled;
563
- }
564
- function isOwneyDebug() {
846
+ // src/agents/zyfai/zyfai.deposit.ts
847
+ async function submitZyfaiDeposit({ sdk, state, chain, amount, asset, smartWallet, assertCurrent, onBeforeSubmission }) {
848
+ const owner = state.walletAddress;
849
+ const provider = state.provider;
850
+ const token = SupportedAssets.find((value) => value.chainId === chain.id && value.symbol === asset);
851
+ if (!token || !(0, import_viem4.isAddress)(owner) || !(0, import_viem4.isAddress)(smartWallet) || !/^\d+$/.test(amount) || BigInt(amount) <= 0n)
852
+ throw new OwneyError("AGENT_INVALID_RESPONSE", "Invalid Zyfai deposit transfer.", { chainId: chain.id, asset }, "zyfai");
853
+ assertCurrent();
854
+ const prepared = await sdk.prepareEnterPosition({ userAddress: owner, chainId: chain.id, amount, asset, strategy: "aggressive" });
855
+ assertCurrent();
856
+ const expectedData = (0, import_viem4.encodeFunctionData)({ abi: import_viem4.erc20Abi, functionName: "transfer", args: [smartWallet, BigInt(amount)] });
857
+ const transfer = prepared?.transfer;
858
+ 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")
859
+ throw new OwneyError("AGENT_INVALID_RESPONSE", "Zyfai prepared a different deposit transfer.", { chainId: chain.id, asset }, "zyfai");
860
+ const wallet = (0, import_viem4.createWalletClient)({ account: owner, chain, transport: (0, import_viem4.custom)({
861
+ request: async (args) => {
862
+ assertCurrent();
863
+ if (args.method === "eth_sendTransaction") {
864
+ const [accounts, chainId] = await Promise.all([
865
+ provider.request({ method: "eth_accounts" }),
866
+ provider.request({ method: "eth_chainId" })
867
+ ]);
868
+ assertCurrent();
869
+ if (!Array.isArray(accounts) || typeof accounts[0] !== "string" || accounts[0].toLowerCase() !== owner.toLowerCase() || Number(chainId) !== chain.id)
870
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai deposit.", void 0, "zyfai");
871
+ await onBeforeSubmission?.();
872
+ assertCurrent();
873
+ }
874
+ return provider.request(args);
875
+ }
876
+ }, { retryCount: 0 }) });
877
+ return wallet.sendTransaction({ account: owner, chain, to: token.address, data: expectedData, value: 0n });
878
+ }
879
+
880
+ // src/agents/zyfai/zyfai.agent.ts
881
+ var import_sdk = require("@zyfai/sdk");
882
+ var import_viem7 = require("viem");
883
+ var import_chains = require("viem/chains");
884
+
885
+ // src/lib/debug.ts
886
+ var configuredDebug = false;
887
+ function setOwneyDebug(enabled) {
888
+ configuredDebug = enabled;
889
+ }
890
+ function isOwneyDebug() {
565
891
  return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
566
892
  }
567
893
  function debugLog(scope, message, data) {
@@ -600,8 +926,8 @@ var DayFilterMapping = {
600
926
  };
601
927
 
602
928
  // 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);
929
+ var import_viem5 = require("viem");
930
+ var exactHexAmount = (raw2, decimals) => (0, import_viem5.formatUnits)(BigInt(/^0x/i.test(raw2) ? raw2 : `0x${raw2}`), decimals);
605
931
  var DECIMALS_BY_SYMBOL = {
606
932
  usdc: 6,
607
933
  weth: 18,
@@ -615,13 +941,6 @@ function tokenDecimals(symbol, explicit) {
615
941
  return explicit;
616
942
  return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
617
943
  }
618
- function mapDeposit(raw2) {
619
- return {
620
- txHash: raw2.txHash,
621
- smartWallet: raw2.smartWallet,
622
- amount: raw2.amount
623
- };
624
- }
625
944
  function mapWithdraw(raw2) {
626
945
  return {
627
946
  txHash: raw2.txHash,
@@ -707,7 +1026,7 @@ function mapBalances(raw2, _chainId, smartWallet) {
707
1026
  // legacy `amount` fallback is hex. Runtime
708
1027
  // getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
709
1028
  // when present, otherwise fall back to the per-symbol lookup.
710
- amount: p.underlyingAmountWithFee ?? p.underlyingAmount ? (0, import_viem2.formatUnits)(
1029
+ amount: p.underlyingAmountWithFee ?? p.underlyingAmount ? (0, import_viem5.formatUnits)(
711
1030
  BigInt(p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0"),
712
1031
  tokenDecimals(
713
1032
  p.token_symbol,
@@ -1372,7 +1691,7 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
1372
1691
  }
1373
1692
 
1374
1693
  // src/lib/paid-rpc.ts
1375
- var import_viem3 = require("viem");
1694
+ var import_viem6 = require("viem");
1376
1695
  var PAID_RPC_RETRY_COUNT = 3;
1377
1696
  var PAID_RPC_RETRY_DELAY_MS = 1e3;
1378
1697
  function configuredRpcProxyBaseUrl(baseUrl) {
@@ -1408,9 +1727,9 @@ function createPaidRpcClient(chain, rpcUrls) {
1408
1727
  rpcUrls,
1409
1728
  chain.id
1410
1729
  );
1411
- return (0, import_viem3.createPublicClient)({
1730
+ return (0, import_viem6.createPublicClient)({
1412
1731
  chain,
1413
- transport: (0, import_viem3.http)(url, {
1732
+ transport: (0, import_viem6.http)(url, {
1414
1733
  retryCount: PAID_RPC_RETRY_COUNT,
1415
1734
  retryDelay: PAID_RPC_RETRY_DELAY_MS
1416
1735
  })
@@ -1507,7 +1826,7 @@ async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
1507
1826
  }
1508
1827
 
1509
1828
  // src/agents/zyfai/zyfai.agent.ts
1510
- var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem4.parseAbi)([
1829
+ var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem7.parseAbi)([
1511
1830
  "function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
1512
1831
  ]);
1513
1832
  var WETH_ADDRESS_BY_CHAIN = {
@@ -1521,6 +1840,14 @@ var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
1521
1840
  var EARNINGS_REFRESH_COOLDOWN_MS = 60 * 60 * 1e3;
1522
1841
  var WITHDRAW_BALANCE_TIMEOUT_MS = 2e3;
1523
1842
  var ZyfaiAgent = class _ZyfaiAgent {
1843
+ constructor(apiKey, rpcUrls, referralSource) {
1844
+ this.apiKey = apiKey;
1845
+ this.referralSource = referralSource;
1846
+ this.rpcUrls = resolveRpcUrls(rpcUrls);
1847
+ this.sdk = this.createSdk();
1848
+ }
1849
+ apiKey;
1850
+ referralSource;
1524
1851
  id = "zyfai";
1525
1852
  supportedChainIds = [8453, 42161, 1];
1526
1853
  supportedAssets = [
@@ -1565,6 +1892,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
1565
1892
  */
1566
1893
  sessionKeyAttempts = /* @__PURE__ */ new Set();
1567
1894
  connectAuthPromise = null;
1895
+ connectAuthScope = null;
1896
+ connectionGeneration = 0;
1897
+ sdkHasConnected = false;
1568
1898
  rpcUrls;
1569
1899
  publicClients = /* @__PURE__ */ new Map();
1570
1900
  // Receipt-derived amounts and log identities, scoped to the owner and asset.
@@ -1575,17 +1905,20 @@ var ZyfaiAgent = class _ZyfaiAgent {
1575
1905
  earningsSnapshot = null;
1576
1906
  earningsGeneration = 0;
1577
1907
  balanceSnapshots = /* @__PURE__ */ new WeakMap();
1578
- constructor(apiKey, rpcUrls, referralSource) {
1579
- this.rpcUrls = resolveRpcUrls(rpcUrls);
1580
- this.sdk = new import_sdk.ZyfaiSDK({
1581
- apiKey,
1908
+ /** A replaced wallet session gets its own vendor client. In-flight calls
1909
+ * keep their original client; authentication must never change owners under
1910
+ * an operation that is waiting for provider reads or an HTTP response. */
1911
+ createSdk() {
1912
+ const sdk = new import_sdk.ZyfaiSDK({
1913
+ apiKey: this.apiKey,
1582
1914
  rpcUrls: this.rpcUrls,
1583
1915
  // Forwarded to /auth/login by the SDK's own SIWE handshake, so
1584
1916
  // attribution survives without the AppKit SIWX config (which carried it
1585
1917
  // via `createOwneySIWX({ referralSource })` when apps used the modal).
1586
- referralSource
1918
+ referralSource: this.referralSource
1587
1919
  });
1588
- this.installAuthCache();
1920
+ this.installAuthCache(sdk);
1921
+ return sdk;
1589
1922
  }
1590
1923
  getPublicClient(chainId) {
1591
1924
  const cached = this.publicClients.get(chainId);
@@ -1660,7 +1993,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
1660
1993
  }, asset.address, entry.action === "Withdraw" ? "withdraw" : "deposit");
1661
1994
  if (evidence.transfer === "confirmed" && evidence.confirmedAmount) {
1662
1995
  Object.assign(tx, {
1663
- amount: (0, import_viem4.formatUnits)(BigInt(evidence.confirmedAmount), asset.decimals),
1996
+ amount: (0, import_viem7.formatUnits)(BigInt(evidence.confirmedAmount), asset.decimals),
1664
1997
  amountRaw: evidence.confirmedAmount,
1665
1998
  blockNumber: evidence.blockNumber,
1666
1999
  logIndexes: evidence.logIndexes,
@@ -1684,9 +2017,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
1684
2017
  * No-op on the server (no `window`) — the SDK is constructed during SSR
1685
2018
  * for some apps and the patch only matters in the browser anyway.
1686
2019
  */
1687
- installAuthCache() {
2020
+ installAuthCache(sdk = this.sdk) {
1688
2021
  if (typeof window === "undefined") return;
1689
- const internals = this.sdk;
2022
+ const internals = sdk;
1690
2023
  const original = internals.authenticateUser.bind(internals);
1691
2024
  internals.authenticateUser = async () => {
1692
2025
  if (internals.authenticatedUserId !== null) return;
@@ -1731,25 +2064,41 @@ var ZyfaiAgent = class _ZyfaiAgent {
1731
2064
  /** Light connect: SIWE auth only. Re-auths if chain changed. */
1732
2065
  async connectAuth(state, chainId) {
1733
2066
  const validChainId = isValidChainId(chainId);
1734
- if (this.connectedAddress && this.connectedChainId === validChainId) {
2067
+ const owner = state.walletAddress.toLowerCase();
2068
+ const scope = `${owner}:${validChainId}`;
2069
+ if (this.connectedAddress?.toLowerCase() === owner && this.connectedChainId === validChainId) {
1735
2070
  return;
1736
2071
  }
1737
- if (this.connectAuthPromise) {
2072
+ if (this.connectAuthPromise && this.connectAuthScope === scope) {
1738
2073
  await this.connectAuthPromise;
1739
- return;
2074
+ return this.connectAuth(state, chainId);
1740
2075
  }
1741
- this.connectAuthPromise = (async () => {
2076
+ if (this.sdkHasConnected || this.connectedAddress) this.sdk = this.createSdk();
2077
+ this.sdkHasConnected = true;
2078
+ const executionSdk = this.sdk;
2079
+ const generation = ++this.connectionGeneration;
2080
+ this.connectedAddress = null;
2081
+ this.connectedChainId = null;
2082
+ const pending = (async () => {
1742
2083
  const address = await this.connectAccountWithRetry(
1743
2084
  state.provider,
1744
- validChainId
2085
+ validChainId,
2086
+ executionSdk
1745
2087
  );
2088
+ if (generation !== this.connectionGeneration || address.toLowerCase() !== owner)
2089
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while connecting Zyfai.", void 0, this.id);
1746
2090
  this.connectedAddress = address;
1747
2091
  this.connectedChainId = validChainId;
1748
2092
  })();
2093
+ this.connectAuthPromise = pending;
2094
+ this.connectAuthScope = scope;
1749
2095
  try {
1750
- await this.connectAuthPromise;
2096
+ await pending;
1751
2097
  } finally {
1752
- this.connectAuthPromise = null;
2098
+ if (this.connectAuthPromise === pending) {
2099
+ this.connectAuthPromise = null;
2100
+ this.connectAuthScope = null;
2101
+ }
1753
2102
  }
1754
2103
  }
1755
2104
  /**
@@ -1761,16 +2110,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
1761
2110
  * JWT from a misconfigured API key; we retry once either way, and if the
1762
2111
  * second attempt still fails the original error propagates.
1763
2112
  */
1764
- async connectAccountWithRetry(provider, chainId) {
2113
+ async connectAccountWithRetry(provider, chainId, executionSdk = this.sdk) {
1765
2114
  try {
1766
- return await this.sdk.connectAccount(provider, chainId);
2115
+ return await executionSdk.connectAccount(provider, chainId);
1767
2116
  } catch (error) {
1768
2117
  const message = error instanceof Error ? error.message : String(error);
1769
2118
  if (!message.toLowerCase().includes("unauthorized")) throw error;
1770
- const internals = this.sdk;
2119
+ const internals = executionSdk;
1771
2120
  const address = internals.walletClient?.account?.address;
1772
2121
  if (address) clearSession(address, chainId);
1773
- return await this.sdk.connectAccount(provider, chainId);
2122
+ return await executionSdk.connectAccount(provider, chainId);
1774
2123
  }
1775
2124
  }
1776
2125
  /**
@@ -1782,11 +2131,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
1782
2131
  * withdrawFunds) fail with "No account connected".
1783
2132
  */
1784
2133
  async ensureConnected(state, chainId) {
2134
+ const assertOwner = () => {
2135
+ if (this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== chainId)
2136
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while connecting Zyfai.", void 0, this.id);
2137
+ };
1785
2138
  await this.connectAuth(state, chainId);
2139
+ assertOwner();
1786
2140
  const internals = this.sdk;
1787
2141
  if (!internals.walletClient || internals.authenticatedUserId === null) {
1788
2142
  this.connectedChainId = null;
1789
2143
  await this.connectAuth(state, chainId);
2144
+ assertOwner();
1790
2145
  }
1791
2146
  }
1792
2147
  /** A rejected execution session must not be reused by the next foreground attempt. */
@@ -1847,6 +2202,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
1847
2202
  }
1848
2203
  // --- IAgent: Connection lifecycle ---
1849
2204
  async disconnect() {
2205
+ const executionSdk = this.sdk;
2206
+ this.connectionGeneration++;
1850
2207
  this.earningsGeneration++;
1851
2208
  this.earningsReads.clear();
1852
2209
  this.earningsRefreshes.clear();
@@ -1854,10 +2211,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
1854
2211
  if (this.connectedAddress && this.connectedChainId !== null) {
1855
2212
  clearSession(this.connectedAddress, this.connectedChainId);
1856
2213
  }
1857
- await this.sdk.disconnectAccount();
1858
2214
  this.connectedAddress = null;
1859
2215
  this.connectedChainId = null;
2216
+ this.connectAuthPromise = null;
2217
+ this.connectAuthScope = null;
1860
2218
  this.smartWalletCache.clear();
2219
+ this.sdk = this.createSdk();
2220
+ this.sdkHasConnected = false;
2221
+ await executionSdk.disconnectAccount();
1861
2222
  }
1862
2223
  // --- IAgent: Account lifecycle ---
1863
2224
  async activateAgent(state, chainId) {
@@ -2345,94 +2706,91 @@ var ZyfaiAgent = class _ZyfaiAgent {
2345
2706
  await this.ensureSessionKey(ownerAddress, chainId, redeployed.address);
2346
2707
  return redeployed.address;
2347
2708
  }
2348
- async deposit(state, chainId, amount, asset, depositCallback, onProgress) {
2349
- 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 = {
2709
+ async deposit(state, chainId, amount, asset, depositCallback, onProgress, onBeforeSubmission) {
2710
+ const validChainId = isValidChainId(chainId);
2711
+ const owner = state.walletAddress;
2712
+ const provider = state.provider;
2713
+ await this.ensureConnected(state, validChainId);
2714
+ const executionSdk = this.sdk;
2715
+ const generation = this.connectionGeneration;
2716
+ const assertCurrent = () => {
2717
+ const account = executionSdk.walletClient?.account?.address;
2718
+ 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())
2719
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai deposit.", void 0, this.id);
2720
+ };
2721
+ assertCurrent();
2722
+ const smartWallet = await this.ensureWalletDeployed(owner, validChainId);
2723
+ assertCurrent();
2724
+ notifyFunding(onProgress, {
2725
+ agentId: this.id,
2726
+ chainId,
2727
+ token: asset,
2728
+ walletAddress: owner,
2729
+ agentWallet: smartWallet,
2730
+ intendedAmount: amount,
2731
+ transfer: "pending",
2732
+ registration: "pending",
2733
+ delivery: "not-applicable"
2734
+ });
2735
+ const txHash = depositCallback ? await depositCallback(smartWallet, validChainId, amount) : await submitZyfaiDeposit({
2736
+ sdk: executionSdk,
2737
+ state: { ...state, walletAddress: owner, provider },
2738
+ chain: VIEM_CHAIN[validChainId],
2739
+ amount,
2740
+ asset,
2741
+ smartWallet,
2742
+ assertCurrent,
2743
+ onBeforeSubmission: onBeforeSubmission ? () => onBeforeSubmission({
2411
2744
  agentId: this.id,
2412
2745
  chainId,
2413
2746
  token: asset,
2414
- walletAddress: state.walletAddress,
2415
- agentWallet: raw2.smartWallet,
2747
+ walletAddress: owner,
2748
+ agentWallet: smartWallet,
2416
2749
  intendedAmount: amount,
2417
- txHash: raw2.txHash,
2418
- transfer: "unknown",
2419
- registration: raw2.registration ? "confirmed" : "unknown",
2750
+ transfer: "pending",
2751
+ registration: "pending",
2420
2752
  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;
2753
+ }) : void 0
2754
+ });
2755
+ let evidence = {
2756
+ agentId: this.id,
2757
+ chainId,
2758
+ token: asset,
2759
+ walletAddress: owner,
2760
+ agentWallet: smartWallet,
2761
+ intendedAmount: amount,
2762
+ txHash,
2763
+ transfer: "pending",
2764
+ registration: "pending",
2765
+ delivery: "not-applicable"
2766
+ };
2767
+ notifyFunding(onProgress, evidence);
2768
+ try {
2769
+ const receipt = await this.getPublicClient(validChainId).waitForTransactionReceipt({
2770
+ hash: txHash,
2771
+ confirmations: 1,
2772
+ timeout: 9e4,
2773
+ checkReplacement: false
2774
+ });
2775
+ const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
2776
+ evidence = inspectFundingReceipt(receipt, evidence, token.address, "deposit");
2777
+ } catch {
2778
+ evidence = { ...evidence, transfer: "unknown" };
2779
+ }
2780
+ notifyFunding(onProgress, evidence);
2781
+ if (evidence.transfer === "failed")
2782
+ throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
2783
+ if (evidence.transfer !== "confirmed") return { txHash, smartWallet, amount, evidence };
2784
+ try {
2785
+ assertCurrent();
2786
+ const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
2787
+ await (tokenAddress ? executionSdk.logDeposit(validChainId, txHash, amount, tokenAddress) : executionSdk.logDeposit(validChainId, txHash, amount));
2788
+ evidence = { ...evidence, registration: "confirmed" };
2789
+ } catch {
2790
+ evidence = { ...evidence, registration: "unknown" };
2435
2791
  }
2792
+ notifyFunding(onProgress, evidence);
2793
+ return { txHash, smartWallet, amount, evidence };
2436
2794
  }
2437
2795
  // --- IAgent: Fund operations ---
2438
2796
  /** Only after a definitive endpoint rejection, combine a new provider
@@ -2462,7 +2820,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2462
2820
  this.sdk.getPortfolio(owner).catch(() => void 0)
2463
2821
  ]);
2464
2822
  assertScope();
2465
- if (!(0, import_viem4.isAddress)(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
2823
+ if (!(0, import_viem7.isAddress)(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
2466
2824
  throw new OwneyError(
2467
2825
  "AGENT_AUTH_FAILED",
2468
2826
  "Could not verify Zyfai's withdrawal wallet.",
@@ -2473,7 +2831,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2473
2831
  const asset = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === token);
2474
2832
  const idle = asset ? await this.getPublicClient(chainId).readContract({
2475
2833
  address: asset.address,
2476
- abi: import_viem4.erc20Abi,
2834
+ abi: import_viem7.erc20Abi,
2477
2835
  functionName: "balanceOf",
2478
2836
  args: [wallet.address]
2479
2837
  }).catch(() => void 0) : void 0;
@@ -2502,11 +2860,12 @@ var ZyfaiAgent = class _ZyfaiAgent {
2502
2860
  /** Called only after the execution endpoint explicitly rejected a request.
2503
2861
  * A timeout or an accepted request never authorizes repair and resubmission. */
2504
2862
  async recoverWithdrawalSession(state, chainId, token, owner) {
2863
+ const generation = this.connectionGeneration;
2505
2864
  await this.assertNotEmptyRejectedWithdrawal(state, chainId, token, owner);
2506
2865
  const internals = this.sdk;
2507
2866
  const assertScope = () => {
2508
2867
  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())
2868
+ if (generation !== this.connectionGeneration || state.walletAddress.toLowerCase() !== owner.toLowerCase() || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
2510
2869
  throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai withdrawal.", void 0, this.id);
2511
2870
  };
2512
2871
  assertScope();
@@ -2535,6 +2894,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
2535
2894
  }
2536
2895
  }
2537
2896
  async withdraw(state, chainId, token, amount, onProgress) {
2897
+ if (amount !== void 0 && (typeof amount !== "string" || !/^[0-9]+$/.test(amount) || BigInt(amount) <= 0n))
2898
+ throw new OwneyError("WITHDRAW_INVALID_AMOUNT", "Withdrawal amount must be a positive integer in token base units.", void 0, this.id);
2538
2899
  const validChainId = isValidChainId(chainId);
2539
2900
  try {
2540
2901
  await this.ensureConnected(state, validChainId);
@@ -2551,16 +2912,40 @@ var ZyfaiAgent = class _ZyfaiAgent {
2551
2912
  });
2552
2913
  throw error;
2553
2914
  }
2554
- let observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
2555
- const authToken = this.sdk.httpClient?.authToken;
2556
- const owner = this.getAddress();
2915
+ const executionSdk = this.sdk;
2916
+ const internals = executionSdk;
2917
+ const generation = this.connectionGeneration;
2918
+ const owner = state.walletAddress;
2919
+ let staleSessionError;
2920
+ const assertCurrent = () => {
2921
+ const account = internals.walletClient?.account?.address;
2922
+ if (generation !== this.connectionGeneration || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== validChainId || state.chainId !== null && state.chainId !== validChainId || account && account.toLowerCase() !== owner.toLowerCase()) {
2923
+ staleSessionError = new OwneyError(
2924
+ "NOT_CONNECTED",
2925
+ "Wallet changed before Zyfai withdrawal submission.",
2926
+ { phase: "prepare", rejectedBeforeSubmission: true },
2927
+ this.id
2928
+ );
2929
+ throw staleSessionError;
2930
+ }
2931
+ };
2932
+ let observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertCurrent);
2933
+ const authToken = internals.httpClient?.authToken;
2557
2934
  let raw2;
2558
2935
  try {
2559
2936
  for (let attempt = 0; attempt < 2; attempt++) {
2560
2937
  try {
2561
- raw2 = await this.sdk.withdrawFunds(owner, validChainId, amount, token);
2938
+ assertCurrent();
2939
+ raw2 = await executionSdk.withdrawFunds(owner, validChainId, amount, token);
2562
2940
  break;
2563
2941
  } catch (error) {
2942
+ if (observation.rejected()) {
2943
+ try {
2944
+ assertCurrent();
2945
+ } catch {
2946
+ throw error;
2947
+ }
2948
+ }
2564
2949
  if (!amount && observation.rejected() && observation.request()?.failure === "insufficient-balance") {
2565
2950
  try {
2566
2951
  await this.assertNotEmptyRejectedWithdrawal(state, validChainId, token, owner);
@@ -2578,7 +2963,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2578
2963
  );
2579
2964
  await this.recoverWithdrawalSession(state, validChainId, token, owner);
2580
2965
  observation.stop();
2581
- observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
2966
+ observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertCurrent);
2582
2967
  }
2583
2968
  }
2584
2969
  if (!raw2) throw new OwneyError(
@@ -2588,6 +2973,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2588
2973
  this.id
2589
2974
  );
2590
2975
  } catch (error) {
2976
+ if (staleSessionError && observation.attempted() === false) error = staleSessionError;
2591
2977
  this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken);
2592
2978
  if (observation.attempted() !== void 0) {
2593
2979
  const failed = observation.attempted() === false || observation.rejected();
@@ -2608,7 +2994,6 @@ var ZyfaiAgent = class _ZyfaiAgent {
2608
2994
  observation.stop();
2609
2995
  }
2610
2996
  if (!raw2.success) {
2611
- const unresolved = !!raw2.txHash || /already|pending|queued|in.progress/i.test(raw2.message ?? "");
2612
2997
  notifyFunding(onProgress, {
2613
2998
  agentId: this.id,
2614
2999
  chainId,
@@ -2616,9 +3001,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
2616
3001
  walletAddress: state.walletAddress,
2617
3002
  intendedAmount: amount ?? "MAX",
2618
3003
  txHash: raw2.txHash,
2619
- transfer: unresolved ? "unknown" : "failed",
3004
+ transfer: "unknown",
2620
3005
  registration: "not-applicable",
2621
- delivery: unresolved ? "unknown" : "failed",
3006
+ delivery: "unknown",
2622
3007
  withdrawalRequest: observation.request()
2623
3008
  });
2624
3009
  throw new OwneyError(
@@ -2645,8 +3030,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
2645
3030
  }
2646
3031
  /** Registration recovery never signs, transfers, activates, or refreshes authentication. */
2647
3032
  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) {
3033
+ const executionSdk = this.sdk;
3034
+ const internals = executionSdk;
3035
+ 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
3036
  throw new OwneyError("NOT_CONNECTED", "Reconnect the wallet before recovering deposit registration.");
2651
3037
  }
2652
3038
  if (evidence.transfer !== "confirmed" || !evidence.txHash) {
@@ -2654,16 +3040,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
2654
3040
  }
2655
3041
  const cid = isValidChainId(evidence.chainId);
2656
3042
  const tokenAddress = evidence.token === "WETH" ? WETH_ADDRESS_BY_CHAIN[cid] : void 0;
2657
- await this.sdk.logDeposit(cid, evidence.txHash, evidence.intendedAmount, tokenAddress);
3043
+ await executionSdk.logDeposit(cid, evidence.txHash, evidence.intendedAmount, tokenAddress);
2658
3044
  return { ...evidence, registration: "confirmed" };
2659
3045
  }
2660
3046
  // --- IAgent: Portfolio reads ---
2661
3047
  async getFundingWallet(state, chainId) {
2662
3048
  return (await this.sdk.getSmartWalletAddress(state.walletAddress, isValidChainId(chainId))).address;
2663
3049
  }
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. */
3050
+ /** A positive deployed Safe balance is a conservative planning floor. Zero
3051
+ * requires both the mapped Safe's exact idle balance and a fresh complete
3052
+ * empty portfolio, including for an undeployed counterfactual Safe. */
2667
3053
  async getWithdrawableBalance(state, chainId, token) {
2668
3054
  const validChainId = isValidChainId(chainId);
2669
3055
  const asset = SupportedAssets.find((value) => value.chainId === validChainId && value.symbol === token);
@@ -2692,20 +3078,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
2692
3078
  this.sdk.getSmartWalletByEOA(owner)
2693
3079
  ]);
2694
3080
  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();
3081
+ if (!(0, import_viem7.isAddress)(wallet.address) || wallet.isDeployed !== false && (wallet.isDeployed !== true || wallet.isOwner !== true) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase()) throw unavailable();
2696
3082
  const client = this.getPublicClient(validChainId);
2697
3083
  const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
2698
3084
  assertScope();
2699
3085
  const idle = await client.readContract({
2700
3086
  address: asset.address,
2701
- abi: import_viem4.erc20Abi,
3087
+ abi: import_viem7.erc20Abi,
2702
3088
  functionName: "balanceOf",
2703
3089
  args: [wallet.address],
2704
3090
  blockNumber
2705
3091
  });
2706
3092
  assertScope();
2707
- if (typeof idle !== "bigint" || idle <= 0n) throw unavailable();
2708
- return idle;
3093
+ if (typeof idle !== "bigint" || idle < 0n) throw unavailable();
3094
+ if (idle > 0n) {
3095
+ if (wallet.isDeployed !== true || wallet.isOwner !== true) throw unavailable();
3096
+ return idle;
3097
+ }
3098
+ const portfolio = await this.sdk.getPortfolio(owner);
3099
+ assertScope();
3100
+ if (!hasEmptyWithdrawalSnapshot(portfolio, validChainId, token)) throw unavailable();
3101
+ return 0n;
2709
3102
  };
2710
3103
  try {
2711
3104
  return await Promise.race([read2(), new Promise((_, reject) => {
@@ -2748,7 +3141,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
2748
3141
  if (blockNumber < BigInt(evidence.blockNumber)) return void 0;
2749
3142
  const idle = await client.readContract({
2750
3143
  address: asset.address,
2751
- abi: import_viem4.erc20Abi,
3144
+ abi: import_viem7.erc20Abi,
2752
3145
  functionName: "balanceOf",
2753
3146
  args: [balance.smartWallet],
2754
3147
  blockNumber
@@ -2940,259 +3333,25 @@ var ZyfaiAgent = class _ZyfaiAgent {
2940
3333
  return true;
2941
3334
  }
2942
3335
  // --- IAgent: Discovery (no wallet required) ---
2943
- async getAgentApy(days, options) {
2944
- const raw2 = await this.sdk.getAPYPerStrategy(
2945
- false,
2946
- DayFilterMapping[days],
2947
- "aggressive",
2948
- options?.chainId,
2949
- options?.tokenSymbol
2950
- );
2951
- return mapApyByStrategy(raw2);
2952
- }
2953
- };
2954
-
2955
- // src/agents/yieldseeker/yieldseeker.agent.ts
2956
- var import_viem12 = require("viem");
2957
- var import_chains4 = require("viem/chains");
2958
-
2959
- // src/lib/chain-guard.ts
2960
- var CHAIN_NAMES = {
2961
- 1: "Ethereum",
2962
- 8453: "Base",
2963
- 42161: "Arbitrum"
2964
- };
2965
- function chainName(chainId) {
2966
- return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
2967
- }
2968
- async function ensureWalletOnChain(pub, wallet, expected) {
2969
- const actual = await pub.getChainId();
2970
- if (actual === expected) return;
2971
- try {
2972
- await wallet.switchChain({ id: expected });
2973
- } catch (error) {
2974
- throw new OwneyError(
2975
- "CHAIN_MISMATCH",
2976
- `Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
2977
- {
2978
- expectedChainId: expected,
2979
- actualChainId: actual,
2980
- cause: error instanceof Error ? error.message : String(error)
2981
- }
2982
- );
2983
- }
2984
- const after = await pub.getChainId();
2985
- if (after !== expected) {
2986
- throw new OwneyError(
2987
- "CHAIN_MISMATCH",
2988
- `Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
2989
- { expectedChainId: expected, actualChainId: after }
2990
- );
2991
- }
2992
- }
2993
-
2994
- // src/lib/transfer-auth.ts
2995
- var import_viem5 = require("viem");
2996
-
2997
- // src/lib/sponsor-client.ts
2998
- var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
2999
- async function postPaymasterIntent(input) {
3000
- const base6 = input.baseUrl ?? ROUTING_API_BASE_URL;
3001
- let res;
3002
- try {
3003
- res = await fetch(`${base6}/api/v1/sponsor/paymaster-intent`, {
3004
- method: "POST",
3005
- headers: {
3006
- "content-type": "application/json",
3007
- "x-owney-api-key": input.apiKey,
3008
- Authorization: `Signature ${input.yieldseekerSignature}`
3009
- },
3010
- body: JSON.stringify(input.body)
3011
- });
3012
- } catch (networkError) {
3013
- throw new OwneyError(
3014
- "SPONSOR_REQUEST_FAILED",
3015
- `Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
3016
- { cause: String(networkError), safeToFallback: true }
3017
- );
3018
- }
3019
- const text = await res.text();
3020
- let parsed = null;
3021
- try {
3022
- parsed = JSON.parse(text);
3023
- } catch {
3024
- }
3025
- if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
3026
- throw new OwneyError(
3027
- "SPONSOR_REQUEST_FAILED",
3028
- `Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
3029
- {
3030
- statusCode: res.status,
3031
- responseBody: text.slice(0, 500),
3032
- safeToFallback: true
3033
- }
3034
- );
3035
- }
3036
- return parsed.data;
3037
- }
3038
- async function getSponsorRelayerAddress(input) {
3039
- const base6 = input.baseUrl ?? ROUTING_API_BASE_URL;
3040
- let res;
3041
- try {
3042
- res = await fetch(
3043
- `${base6}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
3044
- {
3045
- headers: { "x-owney-api-key": input.apiKey }
3046
- }
3047
- );
3048
- } catch (networkError) {
3049
- throw new OwneyError(
3050
- "SPONSOR_REQUEST_FAILED",
3051
- `Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
3052
- { cause: String(networkError), safeToFallback: true }
3053
- );
3054
- }
3055
- const text = await res.text();
3056
- let parsed = null;
3057
- try {
3058
- parsed = JSON.parse(text);
3059
- } catch {
3060
- }
3061
- if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
3062
- throw new OwneyError(
3063
- "SPONSOR_REQUEST_FAILED",
3064
- `Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
3065
- {
3066
- statusCode: res.status,
3067
- responseBody: text.slice(0, 500),
3068
- safeToFallback: true
3069
- }
3070
- );
3071
- }
3072
- return parsed.data.relayer;
3073
- }
3074
- async function postSponsorBatchTransfer(input) {
3075
- let res;
3076
- try {
3077
- res = await fetch(
3078
- `${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch`,
3079
- {
3080
- method: "POST",
3081
- headers: {
3082
- "content-type": "application/json",
3083
- "x-owney-api-key": input.apiKey
3084
- },
3085
- body: JSON.stringify(input.body)
3086
- }
3087
- );
3088
- } catch {
3089
- throw new OwneyError(
3090
- "SPONSOR_REQUEST_FAILED",
3091
- "Deposit status is unknown. Retry the same amount to check it.",
3092
- { safeToFallback: false }
3093
- );
3094
- }
3095
- const parsed = await res.json().catch(() => null);
3096
- if (!res.ok || !parsed?.success || !/^0x[0-9a-fA-F]{64}$/.test(parsed.data?.txHash ?? "")) {
3097
- throw new OwneyError(
3098
- "SPONSOR_REQUEST_FAILED",
3099
- "Deposit could not be confirmed. Retry the same amount to check its status.",
3100
- {
3101
- statusCode: res.status,
3102
- safeToFallback: false,
3103
- notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
3104
- }
3105
- );
3106
- }
3107
- return parsed.data;
3108
- }
3109
-
3110
- // src/lib/permit2.ts
3111
- var import_viem6 = require("viem");
3112
- var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
3113
- var MAX_UINT256 = 2n ** 256n - 1n;
3114
- function permit2ApprovalAmount(requiredAmount) {
3115
- if (requiredAmount <= 0n) {
3116
- throw new Error("Permit2 approval requires a positive deposit amount");
3117
- }
3118
- return MAX_UINT256;
3119
- }
3120
- var ERC20_ALLOWANCE_ABI = [
3121
- {
3122
- type: "function",
3123
- name: "allowance",
3124
- stateMutability: "view",
3125
- inputs: [
3126
- { name: "owner", type: "address" },
3127
- { name: "spender", type: "address" }
3128
- ],
3129
- outputs: [{ name: "", type: "uint256" }]
3130
- },
3131
- {
3132
- type: "function",
3133
- name: "approve",
3134
- stateMutability: "nonpayable",
3135
- inputs: [
3136
- { name: "spender", type: "address" },
3137
- { name: "amount", type: "uint256" }
3138
- ],
3139
- outputs: [{ name: "", type: "bool" }]
3140
- },
3141
- {
3142
- type: "function",
3143
- name: "balanceOf",
3144
- stateMutability: "view",
3145
- inputs: [{ name: "account", type: "address" }],
3146
- outputs: [{ name: "", type: "uint256" }]
3336
+ async getAgentApy(days, options) {
3337
+ const raw2 = await this.sdk.getAPYPerStrategy(
3338
+ false,
3339
+ DayFilterMapping[days],
3340
+ "aggressive",
3341
+ options?.chainId,
3342
+ options?.tokenSymbol
3343
+ );
3344
+ return mapApyByStrategy(raw2);
3147
3345
  }
3148
- ];
3149
- function randomPermit2Nonce() {
3150
- const bytes = new Uint8Array(32);
3151
- globalThis.crypto.getRandomValues(bytes);
3152
- return BigInt((0, import_viem6.bytesToHex)(bytes));
3153
- }
3154
- async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
3155
- return publicClient.readContract({
3156
- address: token,
3157
- abi: ERC20_ALLOWANCE_ABI,
3158
- functionName: "allowance",
3159
- args: [owner, PERMIT2_ADDRESS],
3160
- ...blockNumber === void 0 ? {} : { blockNumber }
3161
- });
3162
- }
3163
- async function readErc20Balance(publicClient, token, owner) {
3164
- return publicClient.readContract({
3165
- address: token,
3166
- abi: ERC20_ALLOWANCE_ABI,
3167
- functionName: "balanceOf",
3168
- args: [owner]
3169
- });
3170
- }
3346
+ };
3171
3347
 
3172
- // src/lib/sponsored-deposit.ts
3173
- var AUTH_WINDOW_SECONDS = 15 * 60;
3174
- var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
3175
- function provideDepositVerificationContext(callback, context) {
3176
- callback[verificationSetter]?.(context);
3177
- }
3178
- function makeVerificationAwareDepositCallback(implementation) {
3179
- let nextVerification;
3180
- const callback = async (smartWallet, chainId, amount) => {
3181
- const verification = nextVerification;
3182
- nextVerification = void 0;
3183
- return implementation(smartWallet, chainId, amount, verification);
3184
- };
3185
- Object.defineProperty(callback, verificationSetter, {
3186
- value: (context) => {
3187
- nextVerification = context;
3188
- }
3189
- });
3190
- return callback;
3191
- }
3348
+ // src/agents/yieldseeker/yieldseeker.agent.ts
3349
+ var import_viem13 = require("viem");
3350
+ var import_chains4 = require("viem/chains");
3192
3351
 
3193
3352
  // src/agents/yieldseeker/yieldseeker.auth.ts
3194
3353
  var import_siwe = require("siwe");
3195
- var import_viem7 = require("viem");
3354
+ var import_viem8 = require("viem");
3196
3355
  var import_chains2 = require("viem/chains");
3197
3356
 
3198
3357
  // src/agents/yieldseeker/yieldseeker.auth-cache.ts
@@ -3305,7 +3464,7 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
3305
3464
  return new import_siwe.SiweMessage({
3306
3465
  scheme: url.protocol.slice(0, -1),
3307
3466
  domain: url.host,
3308
- address: (0, import_viem7.getAddress)(address),
3467
+ address: (0, import_viem8.getAddress)(address),
3309
3468
  uri: url.origin,
3310
3469
  version: "1",
3311
3470
  chainId,
@@ -3391,15 +3550,15 @@ var YieldseekerAuth = class {
3391
3550
  clearYieldseekerSession(state.walletAddress, chainId);
3392
3551
  }
3393
3552
  async sign(state, chainId) {
3394
- const account = (0, import_viem7.getAddress)(state.walletAddress);
3395
- const publicClient = (0, import_viem7.createPublicClient)({
3553
+ const account = (0, import_viem8.getAddress)(state.walletAddress);
3554
+ const publicClient = (0, import_viem8.createPublicClient)({
3396
3555
  chain: import_chains2.base,
3397
- transport: (0, import_viem7.custom)(state.provider)
3556
+ transport: (0, import_viem8.custom)(state.provider)
3398
3557
  });
3399
- const walletClient = (0, import_viem7.createWalletClient)({
3558
+ const walletClient = (0, import_viem8.createWalletClient)({
3400
3559
  account,
3401
3560
  chain: import_chains2.base,
3402
- transport: (0, import_viem7.custom)(state.provider)
3561
+ transport: (0, import_viem8.custom)(state.provider)
3403
3562
  });
3404
3563
  await ensureWalletOnChain(
3405
3564
  publicClient,
@@ -3566,7 +3725,7 @@ var YieldseekerApiClient = class {
3566
3725
  };
3567
3726
 
3568
3727
  // src/agents/yieldseeker/yieldseeker.mapper.ts
3569
- var import_viem8 = require("viem");
3728
+ var import_viem9 = require("viem");
3570
3729
 
3571
3730
  // src/lib/helpers/snapshot-apy.ts
3572
3731
  var DAY_MS = 864e5;
@@ -3633,10 +3792,18 @@ function raw(value, endpoint) {
3633
3792
  return BigInt(value);
3634
3793
  }
3635
3794
  function decimal(value, decimals, endpoint) {
3636
- return (0, import_viem8.formatUnits)(raw(value, endpoint), decimals);
3795
+ return (0, import_viem9.formatUnits)(raw(value, endpoint), decimals);
3796
+ }
3797
+ function balanceRaw(value, endpoint) {
3798
+ const amount = raw(value, endpoint);
3799
+ if (amount < 0n) return invalid(endpoint, "expected a non-negative balance");
3800
+ return amount;
3801
+ }
3802
+ function balanceDecimal(value, decimals, endpoint) {
3803
+ return (0, import_viem9.formatUnits)(balanceRaw(value, endpoint), decimals);
3637
3804
  }
3638
3805
  function usd(rawAmount, decimals, price) {
3639
- return Number((0, import_viem8.formatUnits)(rawAmount, decimals)) * price;
3806
+ return Number((0, import_viem9.formatUnits)(rawAmount, decimals)) * price;
3640
3807
  }
3641
3808
  function percent(value) {
3642
3809
  const result = Number(value);
@@ -3662,7 +3829,7 @@ function assetAddressValue(record, address) {
3662
3829
  }
3663
3830
  function position(value, asset, baseAssetDecimals) {
3664
3831
  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)) {
3832
+ if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem9.isAddress)(option.address)) {
3666
3833
  return invalid("yield positions", "missing vault metadata");
3667
3834
  }
3668
3835
  return {
@@ -3676,13 +3843,13 @@ function position(value, asset, baseAssetDecimals) {
3676
3843
  // underlying base-asset units in `assetsBase`; pair that value with the
3677
3844
  // snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
3678
3845
  // share quantity separate from the withdrawable underlying asset amount.
3679
- amount: decimal(
3846
+ amount: balanceDecimal(
3680
3847
  value.assetsBase,
3681
3848
  baseAssetDecimals,
3682
3849
  "yield positions"
3683
3850
  ),
3684
- amountRaw: String(value.assetsRaw),
3685
- withdrawableAmountRaw: String(value.withdrawableAssetsRaw),
3851
+ amountRaw: balanceRaw(value.assetsRaw, "yield positions").toString(),
3852
+ withdrawableAmountRaw: balanceRaw(value.withdrawableAssetsRaw, "yield positions").toString(),
3686
3853
  apy: percent(option.riskAdjustedApy),
3687
3854
  tvl: Number(option.totalDepositsUsd),
3688
3855
  liquidity: Number(option.withdrawableDepositsUsd)
@@ -3699,7 +3866,7 @@ function mapYieldseekerBalances(contexts) {
3699
3866
  chain: "BASE",
3700
3867
  chainId: 8453,
3701
3868
  asset: context.asset,
3702
- amount: decimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
3869
+ amount: balanceDecimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
3703
3870
  });
3704
3871
  const idle = assetAddressValue(
3705
3872
  context.snapshot.tokenBalances,
@@ -3709,7 +3876,7 @@ function mapYieldseekerBalances(contexts) {
3709
3876
  chain: "BASE",
3710
3877
  chainId: 8453,
3711
3878
  asset: context.asset,
3712
- amount: decimal(idle, metadata.decimals, "snapshot")
3879
+ amount: balanceDecimal(idle, metadata.decimals, "snapshot")
3713
3880
  });
3714
3881
  positions.push(
3715
3882
  ...context.positions.map(
@@ -3721,7 +3888,7 @@ function mapYieldseekerBalances(contexts) {
3721
3888
  )
3722
3889
  );
3723
3890
  totalUsd += usd(
3724
- raw(context.snapshot.totalValueBase, "snapshot"),
3891
+ balanceRaw(context.snapshot.totalValueBase, "snapshot"),
3725
3892
  context.snapshot.baseAssetDecimals,
3726
3893
  context.snapshot.baseAssetPriceUsd
3727
3894
  );
@@ -3755,7 +3922,7 @@ function mapYieldseekerEarnings(contexts) {
3755
3922
  chain: "BASE",
3756
3923
  chainId: 8453,
3757
3924
  asset: context.asset,
3758
- amount: (0, import_viem8.formatUnits)(amount, context.snapshot.baseAssetDecimals)
3925
+ amount: (0, import_viem9.formatUnits)(amount, context.snapshot.baseAssetDecimals)
3759
3926
  });
3760
3927
  lifetimeEarnings += usd(
3761
3928
  amount,
@@ -3913,7 +4080,7 @@ function allocationEntry(action, context) {
3913
4080
  const leg = deposit;
3914
4081
  const symbol = typeof leg.assetSymbol === "string" ? leg.assetSymbol.toUpperCase() : "";
3915
4082
  if (symbol !== context.asset && !(context.asset === "WETH" && symbol === "ETH")) return void 0;
3916
- if (typeof leg.vaultAddress !== "string" || !(0, import_viem8.isAddress)(leg.vaultAddress)) return void 0;
4083
+ if (typeof leg.vaultAddress !== "string" || !(0, import_viem9.isAddress)(leg.vaultAddress)) return void 0;
3917
4084
  const rawAmount = typeof leg.rawAmount === "string" && /^\d+$/.test(leg.rawAmount) ? BigInt(leg.rawAmount) : typeof leg.rawAmount === "number" && Number.isSafeInteger(leg.rawAmount) ? BigInt(leg.rawAmount) : void 0;
3918
4085
  if (rawAmount === void 0 || rawAmount <= 0n) return void 0;
3919
4086
  const option = context.positions.find((position2) => position2.yieldOption.address.toLowerCase() === leg.vaultAddress.toLowerCase())?.yieldOption;
@@ -3921,7 +4088,7 @@ function allocationEntry(action, context) {
3921
4088
  protocol: typeof leg.vaultProvider === "string" && leg.vaultProvider ? leg.vaultProvider : option?.provider || "Vault",
3922
4089
  pool: typeof leg.vaultName === "string" && leg.vaultName ? leg.vaultName : option?.name,
3923
4090
  tokenSymbol: context.asset,
3924
- amount: (0, import_viem8.formatUnits)(rawAmount, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
4091
+ amount: (0, import_viem9.formatUnits)(rawAmount, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
3925
4092
  });
3926
4093
  amountRaw += rawAmount;
3927
4094
  }
@@ -3940,7 +4107,7 @@ function allocationEntry(action, context) {
3940
4107
  tokenSymbol: context.asset,
3941
4108
  agentWallet: context.wallet.walletAddress,
3942
4109
  amountRaw: String(amountRaw),
3943
- amount: (0, import_viem8.formatUnits)(amountRaw, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
4110
+ amount: (0, import_viem9.formatUnits)(amountRaw, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
3944
4111
  }]
3945
4112
  };
3946
4113
  }
@@ -4064,7 +4231,7 @@ function mapYieldseekerHistory(contexts, options) {
4064
4231
  const known = new Set(target.logIndexes ?? []);
4065
4232
  if (tx.logIndexes?.some((index) => !known.has(index))) {
4066
4233
  target.amountRaw = String(BigInt(target.amountRaw ?? "0") + BigInt(tx.amountRaw));
4067
- target.amount = (0, import_viem8.formatUnits)(
4234
+ target.amount = (0, import_viem9.formatUnits)(
4068
4235
  BigInt(target.amountRaw),
4069
4236
  YIELDSEEKER_ASSET_METADATA[target.tokenSymbol].decimals
4070
4237
  );
@@ -4145,7 +4312,7 @@ function mapYieldseekerAgentApy(options, days) {
4145
4312
  }
4146
4313
 
4147
4314
  // src/lib/remaining-vault-balance.ts
4148
- var import_viem9 = require("viem");
4315
+ var import_viem10 = require("viem");
4149
4316
  async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber) {
4150
4317
  const addresses = [...new Set(vaults.map((value) => value.toLowerCase()))];
4151
4318
  if (addresses.length > 64) return void 0;
@@ -4156,8 +4323,8 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
4156
4323
  blockNumber,
4157
4324
  allowFailure: true,
4158
4325
  contracts: batch.map((address) => ({
4159
- address: (0, import_viem9.getAddress)(address),
4160
- abi: import_viem9.erc20Abi,
4326
+ address: (0, import_viem10.getAddress)(address),
4327
+ abi: import_viem10.erc20Abi,
4161
4328
  functionName: "balanceOf",
4162
4329
  args: [wallet]
4163
4330
  }))
@@ -4167,15 +4334,15 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
4167
4334
  for (let index = 0; index < batch.length; index++) {
4168
4335
  const result = balances[index];
4169
4336
  if (result?.status !== "success" || typeof result.result !== "bigint" || result.result < 0n) return void 0;
4170
- if (result.result > 0n) funded.push({ address: (0, import_viem9.getAddress)(batch[index]), shares: result.result });
4337
+ if (result.result > 0n) funded.push({ address: (0, import_viem10.getAddress)(batch[index]), shares: result.result });
4171
4338
  }
4172
4339
  if (!funded.length) return 0n;
4173
4340
  const valuations = await client.multicall({
4174
4341
  blockNumber,
4175
4342
  allowFailure: true,
4176
4343
  contracts: funded.flatMap(({ address, shares }) => [
4177
- { address, abi: import_viem9.erc4626Abi, functionName: "asset" },
4178
- { address, abi: import_viem9.erc4626Abi, functionName: "convertToAssets", args: [shares] }
4344
+ { address, abi: import_viem10.erc4626Abi, functionName: "asset" },
4345
+ { address, abi: import_viem10.erc4626Abi, functionName: "convertToAssets", args: [shares] }
4179
4346
  ])
4180
4347
  });
4181
4348
  if (valuations.length !== funded.length * 2) return void 0;
@@ -4191,12 +4358,12 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
4191
4358
  }
4192
4359
 
4193
4360
  // src/agents/yieldseeker/yieldseeker.wallet-version.ts
4194
- var import_viem10 = require("viem");
4361
+ var import_viem11 = require("viem");
4195
4362
  var YIELDSEEKER_FACTORY = "0x9c7410a0faCAC60850C46eE5b58B518daec95130";
4196
4363
  var YIELDSEEKER_V1_IMPLEMENTATION = "0x32403455357589523435dB18b414057D7cE05051";
4197
4364
  var YIELDSEEKER_V2_IMPLEMENTATION = "0x613Db9c924ADB7A7A90c11b02Cce20c913E3Fe50";
4198
4365
  var ERC1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
4199
- var walletAbi = (0, import_viem10.parseAbi)([
4366
+ var walletAbi = (0, import_viem11.parseAbi)([
4200
4367
  "function owner() view returns (address)",
4201
4368
  "function ownerAgentIndex() view returns (uint256)",
4202
4369
  "function baseAsset() view returns (address)",
@@ -4204,7 +4371,7 @@ var walletAbi = (0, import_viem10.parseAbi)([
4204
4371
  "function upgradeToLatest()",
4205
4372
  "event Upgraded(address indexed implementation)"
4206
4373
  ]);
4207
- var factoryAbi = (0, import_viem10.parseAbi)([
4374
+ var factoryAbi = (0, import_viem11.parseAbi)([
4208
4375
  "function userWallets(address owner, uint256 index) view returns (address)",
4209
4376
  "function agentWalletImplementation() view returns (address)"
4210
4377
  ]);
@@ -4212,9 +4379,9 @@ function yieldseekerOwnerPath(code) {
4212
4379
  return !code || code === "0x" || /^0xef0100[0-9a-f]{40}$/i.test(code) ? "eoa" : "smart";
4213
4380
  }
4214
4381
  async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNumber) {
4215
- const agentWallet = (0, import_viem10.getAddress)(wallet);
4216
- const expectedOwner = (0, import_viem10.getAddress)(owner);
4217
- const expectedAsset = (0, import_viem10.getAddress)(asset);
4382
+ const agentWallet = (0, import_viem11.getAddress)(wallet);
4383
+ const expectedOwner = (0, import_viem11.getAddress)(owner);
4384
+ const expectedAsset = (0, import_viem11.getAddress)(asset);
4218
4385
  const [code, ownerCode, slot, actualOwner, index, actualAsset, actualFactory, latest] = await Promise.all([
4219
4386
  client.getBytecode({ address: agentWallet, blockNumber }),
4220
4387
  client.getBytecode({ address: expectedOwner, blockNumber }),
@@ -4228,7 +4395,7 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
4228
4395
  if (!code || code === "0x" || !slot || /^0x0+$/.test(slot)) {
4229
4396
  throw new Error("Yieldseeker wallet is not a deployed ERC1967 proxy");
4230
4397
  }
4231
- const implementation = (0, import_viem10.getAddress)(`0x${slot.slice(-40)}`);
4398
+ const implementation = (0, import_viem11.getAddress)(`0x${slot.slice(-40)}`);
4232
4399
  const mapped = await client.readContract({
4233
4400
  address: YIELDSEEKER_FACTORY,
4234
4401
  abi: factoryAbi,
@@ -4236,8 +4403,8 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
4236
4403
  args: [expectedOwner, index],
4237
4404
  blockNumber
4238
4405
  });
4239
- const verified = (0, import_viem10.isAddressEqual)(actualOwner, expectedOwner) && (0, import_viem10.isAddressEqual)(actualAsset, expectedAsset) && (0, import_viem10.isAddressEqual)(actualFactory, YIELDSEEKER_FACTORY) && (0, import_viem10.isAddressEqual)(mapped, agentWallet);
4240
- const version = !verified ? "unknown" : (0, import_viem10.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem10.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
4406
+ 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);
4407
+ const version = !verified ? "unknown" : (0, import_viem11.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem11.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
4241
4408
  return {
4242
4409
  agentWallet,
4243
4410
  owner: actualOwner,
@@ -4246,13 +4413,13 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
4246
4413
  implementation,
4247
4414
  version,
4248
4415
  verified,
4249
- upgradeAvailable: version === "v1" && (0, import_viem10.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
4416
+ upgradeAvailable: version === "v1" && (0, import_viem11.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
4250
4417
  };
4251
4418
  }
4252
4419
  var YIELDSEEKER_UPGRADE_ABI = walletAbi;
4253
4420
 
4254
4421
  // src/agents/yieldseeker/yieldseeker.sponsored-withdraw.ts
4255
- var import_viem11 = require("viem");
4422
+ var import_viem12 = require("viem");
4256
4423
  var import_account_abstraction2 = require("viem/account-abstraction");
4257
4424
  var import_accounts = require("viem/accounts");
4258
4425
  var import_chains3 = require("viem/chains");
@@ -4339,7 +4506,7 @@ function rpcDiagnostics(data) {
4339
4506
  details.revertName = data.revertName;
4340
4507
  if (typeof data?.aaCode === "string" && /^AA[0-9]{2}$/.test(data.aaCode)) details.aaCode = data.aaCode;
4341
4508
  if (typeof data?.panicCode === "string" && /^0x[0-9a-fA-F]{1,2}$/.test(data.panicCode)) {
4342
- const panicCode = (0, import_viem11.toHex)(BigInt(data.panicCode));
4509
+ const panicCode = (0, import_viem12.toHex)(BigInt(data.panicCode));
4343
4510
  if (["0x0", "0x1", "0x11", "0x12", "0x21", "0x22", "0x31", "0x32", "0x41", "0x51"].includes(panicCode))
4344
4511
  details.panicCode = panicCode;
4345
4512
  }
@@ -4559,14 +4726,14 @@ async function submitEoa(input) {
4559
4726
  throw sponsorError("Base did not return EIP-1559 gas fees.", true);
4560
4727
  let operation = {
4561
4728
  sender: input.agentWallet,
4562
- nonce: (0, import_viem11.toHex)(nonce),
4729
+ nonce: (0, import_viem12.toHex)(nonce),
4563
4730
  initCode: "0x",
4564
4731
  callData: input.callData,
4565
- callGasLimit: (0, import_viem11.toHex)(3e5),
4566
- verificationGasLimit: (0, import_viem11.toHex)(3e5),
4567
- preVerificationGas: (0, import_viem11.toHex)(6e4),
4568
- maxFeePerGas: (0, import_viem11.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
4569
- maxPriorityFeePerGas: (0, import_viem11.toHex)(fees.maxPriorityFeePerGas),
4732
+ callGasLimit: (0, import_viem12.toHex)(3e5),
4733
+ verificationGasLimit: (0, import_viem12.toHex)(3e5),
4734
+ preVerificationGas: (0, import_viem12.toHex)(6e4),
4735
+ maxFeePerGas: (0, import_viem12.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
4736
+ maxPriorityFeePerGas: (0, import_viem12.toHex)(fees.maxPriorityFeePerGas),
4570
4737
  paymasterAndData: "0x",
4571
4738
  signature: "0x"
4572
4739
  };
@@ -4574,7 +4741,7 @@ async function submitEoa(input) {
4574
4741
  const stub = await rpc(
4575
4742
  input.rpcUrl,
4576
4743
  "pm_getPaymasterStubData",
4577
- [operation, ENTRY_POINT_V06, (0, import_viem11.toHex)(8453), {}],
4744
+ [operation, ENTRY_POINT_V06, (0, import_viem12.toHex)(8453), {}],
4578
4745
  "prepare"
4579
4746
  );
4580
4747
  if (!stub.paymasterAndData?.startsWith("0x"))
@@ -4589,7 +4756,7 @@ async function submitEoa(input) {
4589
4756
  const final = await rpc(
4590
4757
  input.rpcUrl,
4591
4758
  "pm_getPaymasterData",
4592
- [operation, ENTRY_POINT_V06, (0, import_viem11.toHex)(8453), {}],
4759
+ [operation, ENTRY_POINT_V06, (0, import_viem12.toHex)(8453), {}],
4593
4760
  "prepare"
4594
4761
  );
4595
4762
  if (!final.paymasterAndData?.startsWith("0x"))
@@ -4599,10 +4766,10 @@ async function submitEoa(input) {
4599
4766
  const userOpHash = operationHash(operation);
4600
4767
  await assertOwnerSession(input.state, input.owner);
4601
4768
  assertIntentFresh(input.expiresAt);
4602
- const wallet = (0, import_viem11.createWalletClient)({
4769
+ const wallet = (0, import_viem12.createWalletClient)({
4603
4770
  account: input.owner,
4604
4771
  chain: import_chains3.base,
4605
- transport: (0, import_viem11.custom)(input.state.provider)
4772
+ transport: (0, import_viem12.custom)(input.state.provider)
4606
4773
  });
4607
4774
  const signature = await wallet.signMessage({ message: { raw: userOpHash } });
4608
4775
  operation = { ...operation, signature };
@@ -4680,13 +4847,13 @@ async function submitSmart(input) {
4680
4847
  } catch {
4681
4848
  throw sponsorError("This account cannot check paymaster support on Base.", true);
4682
4849
  }
4683
- const supportsPaymaster = capabilities?.[(0, import_viem11.toHex)(8453)]?.paymasterService?.supported ?? capabilities?.["8453"]?.paymasterService?.supported ?? capabilities?.["0x0"]?.paymasterService?.supported;
4850
+ const supportsPaymaster = capabilities?.[(0, import_viem12.toHex)(8453)]?.paymasterService?.supported ?? capabilities?.["8453"]?.paymasterService?.supported ?? capabilities?.["0x0"]?.paymasterService?.supported;
4684
4851
  if (supportsPaymaster !== true)
4685
4852
  throw sponsorError("This account cannot use a paymaster on Base.", true);
4686
4853
  await assertOwnerSession(input.state, input.owner);
4687
4854
  let sent;
4688
4855
  assertIntentFresh(input.expiresAt);
4689
- const requestedId = (0, import_viem11.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
4856
+ const requestedId = (0, import_viem12.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
4690
4857
  savePending(input.onPending, { callsId: requestedId }, true);
4691
4858
  await checkpoint(input.onBeforeSubmission, { callsId: requestedId });
4692
4859
  try {
@@ -4695,7 +4862,7 @@ async function submitSmart(input) {
4695
4862
  params: [{
4696
4863
  version: "2.0.0",
4697
4864
  from: input.owner,
4698
- chainId: (0, import_viem11.toHex)(8453),
4865
+ chainId: (0, import_viem12.toHex)(8453),
4699
4866
  id: requestedId,
4700
4867
  atomicRequired: false,
4701
4868
  calls: [{ to: input.agentWallet, value: "0x0", data: input.callData }],
@@ -4770,7 +4937,7 @@ async function submitSmart(input) {
4770
4937
  );
4771
4938
  }
4772
4939
  async function submitYieldseekerSponsoredWithdrawal(input) {
4773
- const owner = (0, import_viem11.getAddress)(input.state.walletAddress);
4940
+ const owner = (0, import_viem12.getAddress)(input.state.walletAddress);
4774
4941
  const { intent, rpcUrl } = await requestIntent({
4775
4942
  apiKey: input.apiKey,
4776
4943
  routingApiBaseUrl: input.routingApiBaseUrl,
@@ -4789,7 +4956,7 @@ async function submitYieldseekerSponsoredWithdrawal(input) {
4789
4956
  return intent.ownerPath === "eoa" ? submitEoa(args) : submitSmart(args);
4790
4957
  }
4791
4958
  async function submitYieldseekerSponsoredUpgrade(input) {
4792
- const owner = (0, import_viem11.getAddress)(input.state.walletAddress);
4959
+ const owner = (0, import_viem12.getAddress)(input.state.walletAddress);
4793
4960
  const { intent, rpcUrl } = await requestIntent({
4794
4961
  apiKey: input.apiKey,
4795
4962
  routingApiBaseUrl: input.routingApiBaseUrl,
@@ -4904,6 +5071,7 @@ var YieldseekerAgent = class {
4904
5071
  receiptClient;
4905
5072
  // A client preparation hint only; the routing API still checks live identity.
4906
5073
  withdrawalVersionCache = /* @__PURE__ */ new Map();
5074
+ pendingUpgrades = /* @__PURE__ */ new Map();
4907
5075
  transactionExecutor;
4908
5076
  unwindReceiptWaiter;
4909
5077
  agentContexts = /* @__PURE__ */ new Map();
@@ -4954,8 +5122,8 @@ var YieldseekerAgent = class {
4954
5122
  if (useCachedV2 && cached && Date.now() - cached.at < 15 * 6e4) return { ...cached.status };
4955
5123
  const status = await readYieldseekerWalletStatus(
4956
5124
  this.getReceiptClient(),
4957
- (0, import_viem12.getAddress)(context.wallet.walletAddress),
4958
- (0, import_viem12.getAddress)(state.walletAddress),
5125
+ (0, import_viem13.getAddress)(context.wallet.walletAddress),
5126
+ (0, import_viem13.getAddress)(state.walletAddress),
4959
5127
  YIELDSEEKER_ASSET_METADATA[asset].address
4960
5128
  );
4961
5129
  this.rememberWalletStatus(key2, status);
@@ -4985,12 +5153,12 @@ var YieldseekerAgent = class {
4985
5153
  const pendingKey2 = `owney.yieldseeker.upgrade:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}`;
4986
5154
  const confirmUpgrade = async (hash, receipt2) => {
4987
5155
  this.withdrawalVersionCache.delete(versionKey);
4988
- const upgradeTopic = (0, import_viem12.encodeEventTopics)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded" })[0];
5156
+ const upgradeTopic = (0, import_viem13.encodeEventTopics)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded" })[0];
4989
5157
  const lastUpgrade = receipt2?.logs.filter((log) => log.address.toLowerCase() === before.agentWallet.toLowerCase() && log.topics[0] === upgradeTopic).at(-1);
4990
5158
  const expectedUpgrade = (() => {
4991
5159
  if (!lastUpgrade || lastUpgrade.removed) return false;
4992
5160
  try {
4993
- const event = (0, import_viem12.decodeEventLog)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded", data: lastUpgrade.data, topics: lastUpgrade.topics });
5161
+ const event = (0, import_viem13.decodeEventLog)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded", data: lastUpgrade.data, topics: lastUpgrade.topics });
4994
5162
  return event.args.implementation.toLowerCase() === YIELDSEEKER_V2_IMPLEMENTATION.toLowerCase();
4995
5163
  } catch {
4996
5164
  return false;
@@ -5019,10 +5187,30 @@ var YieldseekerAgent = class {
5019
5187
  } catch {
5020
5188
  storage5 = void 0;
5021
5189
  }
5022
- const clearConfirmedUpgrade = () => {
5190
+ const clearRecordedUpgrade = () => {
5191
+ this.pendingUpgrades.delete(pendingKey2);
5023
5192
  try {
5024
5193
  storage5?.removeItem(pendingKey2);
5025
5194
  } catch {
5195
+ this.pendingUpgrades.set(pendingKey2, null);
5196
+ }
5197
+ };
5198
+ const pendingUpgrade = (hash) => new OwneyError(
5199
+ "WITHDRAW_IN_PROGRESS",
5200
+ "Yieldseeker wallet upgrade is awaiting confirmation. Retry to check its status.",
5201
+ { txHash: hash, reason: "UPGRADE_CONFIRMATION_PENDING", phase: "observe", safeToFallback: false },
5202
+ this.id
5203
+ );
5204
+ const terminalUpgradeFailure = (error) => error instanceof OwneyError && (error.code === "AGENT_TRANSACTION_REVERTED" || error.details?.reason === "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL");
5205
+ const readUpgradeReceipt = async (hash) => {
5206
+ try {
5207
+ return await this.waitForReceipt(state, chainId, hash);
5208
+ } catch (error) {
5209
+ if (terminalUpgradeFailure(error)) {
5210
+ clearRecordedUpgrade();
5211
+ throw error;
5212
+ }
5213
+ throw pendingUpgrade(hash);
5026
5214
  }
5027
5215
  };
5028
5216
  if (!storage5 && (gasMode === "sponsored" || before.ownerPath === "smart"))
@@ -5034,7 +5222,7 @@ var YieldseekerAgent = class {
5034
5222
  );
5035
5223
  let prior = null;
5036
5224
  try {
5037
- const saved = storage5?.getItem(pendingKey2);
5225
+ const saved = this.pendingUpgrades.has(pendingKey2) ? JSON.stringify(this.pendingUpgrades.get(pendingKey2)) : storage5?.getItem(pendingKey2);
5038
5226
  try {
5039
5227
  prior = JSON.parse(saved ?? "null");
5040
5228
  } catch {
@@ -5078,7 +5266,7 @@ var YieldseekerAgent = class {
5078
5266
  this.id
5079
5267
  );
5080
5268
  if (Number(callsStatus?.status) === 400 || Number(callsStatus?.status) === 500) {
5081
- storage5.removeItem(pendingKey2);
5269
+ clearRecordedUpgrade();
5082
5270
  throw new OwneyError(
5083
5271
  "SPONSOR_REQUEST_FAILED",
5084
5272
  "The earlier sponsored upgrade failed.",
@@ -5090,8 +5278,8 @@ var YieldseekerAgent = class {
5090
5278
  earlierHash = hashes.length === 1 ? hashes[0] : void 0;
5091
5279
  }
5092
5280
  if (earlierHash) {
5093
- const recovered = await confirmUpgrade(earlierHash, await this.waitForReceipt(state, chainId, earlierHash));
5094
- clearConfirmedUpgrade();
5281
+ const recovered = await confirmUpgrade(earlierHash, await readUpgradeReceipt(earlierHash));
5282
+ clearRecordedUpgrade();
5095
5283
  return recovered;
5096
5284
  }
5097
5285
  throw new OwneyError(
@@ -5108,7 +5296,7 @@ var YieldseekerAgent = class {
5108
5296
  { version: before.version },
5109
5297
  this.id
5110
5298
  );
5111
- const data = (0, import_viem12.encodeFunctionData)({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
5299
+ const data = (0, import_viem13.encodeFunctionData)({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
5112
5300
  let txHash;
5113
5301
  let receipt;
5114
5302
  if (gasMode === "sponsored") {
@@ -5135,33 +5323,52 @@ var YieldseekerAgent = class {
5135
5323
  asset: YIELDSEEKER_ASSET_METADATA[asset].address,
5136
5324
  callData: data,
5137
5325
  onPending: (pending) => {
5138
- const current = storage5.getItem(pendingKey2);
5139
- storage5.setItem(pendingKey2, JSON.stringify({
5140
- startedAt: Date.now(),
5141
- ...current ? JSON.parse(current) : {},
5142
- ...pending
5143
- }));
5326
+ const current = this.pendingUpgrades.has(pendingKey2) ? this.pendingUpgrades.get(pendingKey2) : JSON.parse(storage5.getItem(pendingKey2) ?? "null");
5327
+ const next = { startedAt: Date.now(), ...current, ...pending };
5328
+ storage5.setItem(pendingKey2, JSON.stringify(next));
5329
+ this.pendingUpgrades.delete(pendingKey2);
5144
5330
  }
5145
5331
  });
5146
5332
  txHash = upgraded.txHash;
5147
5333
  receipt = upgraded.receipt;
5148
5334
  } catch (error) {
5149
- if (error?.code === 4001 || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) storage5.removeItem(pendingKey2);
5335
+ if (error?.code === 4001 || terminalUpgradeFailure(error) || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) clearRecordedUpgrade();
5150
5336
  throw error;
5151
5337
  }
5152
5338
  } else {
5153
- txHash = await this.submitTransaction(state, chainId, {
5154
- from: (0, import_viem12.getAddress)(state.walletAddress),
5155
- to: before.agentWallet,
5156
- data,
5157
- value: "0",
5158
- chainId
5159
- }, void 0, (value) => {
5160
- receipt = value;
5161
- });
5339
+ let submittedHash;
5340
+ try {
5341
+ txHash = await this.submitTransaction(state, chainId, {
5342
+ from: (0, import_viem13.getAddress)(state.walletAddress),
5343
+ to: before.agentWallet,
5344
+ data,
5345
+ value: "0",
5346
+ chainId
5347
+ }, (hash) => {
5348
+ submittedHash = hash;
5349
+ const pending = { txHash: hash };
5350
+ this.pendingUpgrades.set(pendingKey2, pending);
5351
+ try {
5352
+ if (storage5) {
5353
+ storage5.setItem(pendingKey2, JSON.stringify(pending));
5354
+ this.pendingUpgrades.delete(pendingKey2);
5355
+ }
5356
+ } catch {
5357
+ }
5358
+ }, (value) => {
5359
+ receipt = value;
5360
+ });
5361
+ } catch (error) {
5362
+ if (terminalUpgradeFailure(error)) {
5363
+ clearRecordedUpgrade();
5364
+ throw error;
5365
+ }
5366
+ if (submittedHash) throw pendingUpgrade(submittedHash);
5367
+ throw error;
5368
+ }
5162
5369
  }
5163
- const confirmed = await confirmUpgrade(txHash, receipt ?? await this.waitForReceipt(state, chainId, txHash));
5164
- if (gasMode === "sponsored") clearConfirmedUpgrade();
5370
+ const confirmed = await confirmUpgrade(txHash, receipt ?? await readUpgradeReceipt(txHash));
5371
+ clearRecordedUpgrade();
5165
5372
  return confirmed;
5166
5373
  }
5167
5374
  async disconnect() {
@@ -5193,7 +5400,7 @@ var YieldseekerAgent = class {
5193
5400
  this.assertAsset(targetAsset);
5194
5401
  await this.ensureAgent(state, chainId, targetAsset);
5195
5402
  }
5196
- async deposit(state, chainId, amount, asset, depositCallback, onProgress) {
5403
+ async deposit(state, chainId, amount, asset, depositCallback, onProgress, onBeforeSubmission) {
5197
5404
  this.assertChain(chainId);
5198
5405
  this.assertAsset(asset);
5199
5406
  if (BigInt(amount) <= 0n) {
@@ -5238,18 +5445,28 @@ var YieldseekerAgent = class {
5238
5445
  } else {
5239
5446
  try {
5240
5447
  txHash = await this.submitTransaction(state, chainId, {
5241
- from: (0, import_viem12.getAddress)(state.walletAddress),
5448
+ from: (0, import_viem13.getAddress)(state.walletAddress),
5242
5449
  to: YIELDSEEKER_ASSET_METADATA[asset].address,
5243
- data: (0, import_viem12.encodeFunctionData)({
5244
- abi: import_viem12.erc20Abi,
5450
+ data: (0, import_viem13.encodeFunctionData)({
5451
+ abi: import_viem13.erc20Abi,
5245
5452
  functionName: "transfer",
5246
- args: [(0, import_viem12.getAddress)(context.wallet.walletAddress), BigInt(amount)]
5453
+ args: [(0, import_viem13.getAddress)(context.wallet.walletAddress), BigInt(amount)]
5247
5454
  }),
5248
5455
  value: "0",
5249
5456
  chainId
5250
5457
  }, submitted, (value) => {
5251
5458
  receipt = value;
5252
- });
5459
+ }, onBeforeSubmission ? () => onBeforeSubmission({
5460
+ agentId: this.id,
5461
+ chainId,
5462
+ token: asset,
5463
+ walletAddress: state.walletAddress,
5464
+ agentWallet: context.wallet.walletAddress,
5465
+ intendedAmount: amount,
5466
+ transfer: "pending",
5467
+ registration: "not-applicable",
5468
+ delivery: "not-applicable"
5469
+ }) : void 0);
5253
5470
  } catch (error) {
5254
5471
  if (error instanceof OwneyError && error.details?.ownerTransferNotExecuted === true && error.details.reason === "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL") {
5255
5472
  notifyFunding(onProgress, {
@@ -5377,7 +5594,7 @@ var YieldseekerAgent = class {
5377
5594
  );
5378
5595
  const positionsRead = this.readWithdrawalPositions(state, chainId, context);
5379
5596
  const metadata = YIELDSEEKER_ASSET_METADATA[asset];
5380
- const agentWallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
5597
+ const agentWallet = (0, import_viem13.getAddress)(context.wallet.walletAddress);
5381
5598
  const readOwnerFunds = async (blockNumber) => {
5382
5599
  const feeTracker = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5383
5600
  address: agentWallet,
@@ -5386,18 +5603,18 @@ var YieldseekerAgent = class {
5386
5603
  blockNumber
5387
5604
  }), chainId, "eth_call", this.id);
5388
5605
  this.assertSession(generation, chainId, asset);
5389
- if (!(0, import_viem12.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
5606
+ if (!(0, import_viem13.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
5390
5607
  throw this.invalidResponse("wallet fee tracker");
5391
5608
  const [idle2, fees] = await Promise.all([
5392
5609
  withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5393
5610
  address: metadata.address,
5394
- abi: import_viem12.erc20Abi,
5611
+ abi: import_viem13.erc20Abi,
5395
5612
  functionName: "balanceOf",
5396
5613
  args: [agentWallet],
5397
5614
  blockNumber
5398
5615
  }), chainId, "eth_call", this.id),
5399
5616
  withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
5400
- address: (0, import_viem12.getAddress)(feeTracker),
5617
+ address: (0, import_viem13.getAddress)(feeTracker),
5401
5618
  abi: YIELDSEEKER_FEE_TRACKER_ABI,
5402
5619
  functionName: "getFeesOwed",
5403
5620
  args: [agentWallet],
@@ -5439,13 +5656,13 @@ var YieldseekerAgent = class {
5439
5656
  let active = true;
5440
5657
  const checks = this.readVaultLiquidity(values.map((position2) => position2.yieldOption), agentWallet, blockNumber).catch(() => []);
5441
5658
  const reads = values.map(async (position2, index) => {
5442
- const address = (0, import_viem12.getAddress)(position2.yieldOption.address);
5659
+ const address = (0, import_viem13.getAddress)(position2.yieldOption.address);
5443
5660
  try {
5444
5661
  const group = (await checks).slice(index * 3, index * 3 + 3);
5445
5662
  if (!active || group.length !== 3 || group.some((value) => value.status !== "success")) return null;
5446
5663
  this.assertSession(generation, chainId, asset);
5447
5664
  const [underlying, shares, withdrawable] = group.map((value) => value.result);
5448
- if (typeof underlying !== "string" || !(0, import_viem12.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
5665
+ if (typeof underlying !== "string" || !(0, import_viem13.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
5449
5666
  const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: withdrawable.toString() };
5450
5667
  if (shares > 0n && withdrawable === 0n) {
5451
5668
  let positiveValue = false;
@@ -5453,7 +5670,7 @@ var YieldseekerAgent = class {
5453
5670
  try {
5454
5671
  const value = await this.getReceiptClient().readContract({
5455
5672
  address,
5456
- abi: import_viem12.erc4626Abi,
5673
+ abi: import_viem13.erc4626Abi,
5457
5674
  functionName,
5458
5675
  args: [shares],
5459
5676
  blockNumber
@@ -5543,6 +5760,18 @@ var YieldseekerAgent = class {
5543
5760
  );
5544
5761
  }
5545
5762
  if (requested > totalAvailable) {
5763
+ if (!funds.complete)
5764
+ throw new OwneyError(
5765
+ "WITHDRAW_BALANCE_UNAVAILABLE",
5766
+ "Yieldseeker withdrawal liquidity could not be verified for the requested amount.",
5767
+ {
5768
+ asset,
5769
+ requested: requested.toString(),
5770
+ verifiedAvailable: totalAvailable.toString(),
5771
+ reason: "YIELDSEEKER_LIQUIDITY_UNVERIFIED"
5772
+ },
5773
+ this.id
5774
+ );
5546
5775
  throw new OwneyError(
5547
5776
  "WITHDRAW_INSUFFICIENT_BALANCE",
5548
5777
  `Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
@@ -5557,6 +5786,7 @@ var YieldseekerAgent = class {
5557
5786
  const requiredIdle = requested + initialFunds.fees;
5558
5787
  let remaining = requiredIdle > idle ? requiredIdle - idle : 0n;
5559
5788
  let grossUnwound = 0n;
5789
+ let confirmedUnwinds = 0n;
5560
5790
  let latestUnwindBlock;
5561
5791
  const unwind = async (position2, assetsRaw) => {
5562
5792
  this.assertSession(generation, chainId, asset);
@@ -5574,7 +5804,10 @@ var YieldseekerAgent = class {
5574
5804
  if (!this.isTransactionHash(response?.transactionHash)) throw this.invalidResponse("position withdrawal");
5575
5805
  unwindTransactionHash = response.transactionHash;
5576
5806
  const unwindReceipt = await this.waitForReceipt(state, chainId, unwindTransactionHash);
5577
- if (unwindReceipt) latestUnwindBlock = unwindReceipt.blockNumber;
5807
+ if (unwindReceipt) {
5808
+ latestUnwindBlock = unwindReceipt.blockNumber;
5809
+ confirmedUnwinds++;
5810
+ }
5578
5811
  } catch (error) {
5579
5812
  this.invalidateFundingRead(state, chainId, asset);
5580
5813
  throw new OwneyError(
@@ -5644,7 +5877,7 @@ var YieldseekerAgent = class {
5644
5877
  const feeRate = feeRateBps ?? 0;
5645
5878
  const maxFee = confirmedMovement && Number.isInteger(feeRate) && feeRate >= 0 && feeRate <= 1e3 ? (grossUnwound * BigInt(feeRate) + 9999n) / 10000n : 0n;
5646
5879
  const shortfall = availableForOwner < requested ? requested - availableForOwner : 0n;
5647
- if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee) {
5880
+ if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee + confirmedUnwinds) {
5648
5881
  this.invalidateFundingRead(state, chainId, asset);
5649
5882
  if (amount === void 0 && positionsStatus !== "empty")
5650
5883
  throw new OwneyError(
@@ -5666,14 +5899,15 @@ var YieldseekerAgent = class {
5666
5899
  delta: maxSettlementBasis.delta - payout,
5667
5900
  expectedNetDeposits: maxSettlementBasis.expectedNetDeposits - payout
5668
5901
  } : this.snapshotMovement(state, chainId, context, -payout);
5669
- const account = (0, import_viem12.getAddress)(state.walletAddress);
5902
+ const account = (0, import_viem13.getAddress)(state.walletAddress);
5670
5903
  let receipt;
5671
- const base6 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount === void 0 ? "MAX" : payout.toString(), transfer: "pending", registration: "not-applicable", delivery: "pending" };
5672
- const data = amount === void 0 ? (0, import_viem12.encodeFunctionData)({
5904
+ 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" };
5905
+ let submissionEvidence = base6;
5906
+ const data = amount === void 0 ? (0, import_viem13.encodeFunctionData)({
5673
5907
  abi: YIELDSEEKER_AGENT_WALLET_ABI,
5674
5908
  functionName: "withdrawAllAssetToUser",
5675
5909
  args: [account, metadata.address]
5676
- }) : (0, import_viem12.encodeFunctionData)({
5910
+ }) : (0, import_viem13.encodeFunctionData)({
5677
5911
  abi: YIELDSEEKER_AGENT_WALLET_ABI,
5678
5912
  functionName: "withdrawAssetToUser",
5679
5913
  args: [account, metadata.address, payout]
@@ -5689,10 +5923,13 @@ var YieldseekerAgent = class {
5689
5923
  refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
5690
5924
  userId: context.user.userId,
5691
5925
  agentId: context.agent.agentId,
5692
- agentWallet: (0, import_viem12.getAddress)(context.wallet.walletAddress),
5926
+ agentWallet: (0, import_viem13.getAddress)(context.wallet.walletAddress),
5693
5927
  asset: metadata.address,
5694
5928
  callData: data,
5695
- onPending: (pending) => notifyFunding(onProgress, { ...base6, ...pending }),
5929
+ onPending: (pending) => {
5930
+ submissionEvidence = { ...submissionEvidence, ...pending };
5931
+ notifyFunding(onProgress, submissionEvidence);
5932
+ },
5696
5933
  onBeforeSubmission: async (pending) => {
5697
5934
  await onBeforeSubmission?.({ ...base6, ...pending });
5698
5935
  onOwnerTransferAttempted?.();
@@ -5704,7 +5941,7 @@ var YieldseekerAgent = class {
5704
5941
  onOwnerTransferAttempted?.();
5705
5942
  txHash = await this.submitTransaction(state, chainId, {
5706
5943
  from: account,
5707
- to: (0, import_viem12.getAddress)(context.wallet.walletAddress),
5944
+ to: (0, import_viem13.getAddress)(context.wallet.walletAddress),
5708
5945
  data,
5709
5946
  value: "0",
5710
5947
  chainId
@@ -5712,10 +5949,20 @@ var YieldseekerAgent = class {
5712
5949
  receipt = value;
5713
5950
  });
5714
5951
  }
5715
- confirmedMovement = true;
5716
- settlement = plannedMovement;
5717
- const evidence = receipt ? inspectFundingReceipt(receipt, { ...base6, txHash }, metadata.address, "withdraw") : { ...base6, txHash, confirmedAmount: payout.toString(), transfer: "confirmed", delivery: "confirmed" };
5952
+ const evidence = receipt ? inspectFundingReceipt(receipt, { ...submissionEvidence, txHash }, metadata.address, "withdraw") : { ...submissionEvidence, txHash, transfer: "pending", delivery: "pending" };
5718
5953
  notifyFunding(onProgress, evidence);
5954
+ const explicitEmptyPayout = evidence.transfer === "failed" && evidence.confirmedAmount === "0" && evidence.transferLogs?.some((log) => log.amountRaw === "0");
5955
+ if (evidence.transfer === "failed" && !explicitEmptyPayout)
5956
+ throw new OwneyError(
5957
+ "AGENT_TRANSACTION_REVERTED",
5958
+ "The Yieldseeker withdrawal completed without transferring funds.",
5959
+ { asset, available: "0", txHash },
5960
+ this.id
5961
+ );
5962
+ if (!explicitEmptyPayout && (evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || !evidence.confirmedAmount))
5963
+ return { txHash, type: "partial", completion: "unverified", amount: "0", evidence };
5964
+ confirmedMovement = true;
5965
+ settlement = evidence.confirmedAmount === payout.toString() ? plannedMovement : null;
5719
5966
  let completion = maxLiquidity && !maxLiquidity.complete ? "unverified" : void 0;
5720
5967
  if (amount === void 0) {
5721
5968
  const verificationDeadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
@@ -5736,7 +5983,7 @@ var YieldseekerAgent = class {
5736
5983
  positions = proof.positions;
5737
5984
  positionsStatus = hasPositions(positions) ? "remaining" : proof.verified ? "empty" : "unavailable";
5738
5985
  if (positionsStatus === "remaining") {
5739
- const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? payout.toString(), evidence };
5986
+ const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? "0", evidence };
5740
5987
  throw new OwneyError(
5741
5988
  "WITHDRAW_PARTIAL_FAILURE",
5742
5989
  "Yieldseeker MAX could not confirm that all vault positions were cleared.",
@@ -5756,7 +6003,7 @@ var YieldseekerAgent = class {
5756
6003
  }
5757
6004
  if (!proof.verified) completion = "unverified";
5758
6005
  }
5759
- if (evidence.transfer === "failed" && evidence.confirmedAmount === "0")
6006
+ if (explicitEmptyPayout)
5760
6007
  throw new OwneyError(
5761
6008
  "WITHDRAW_INSUFFICIENT_BALANCE",
5762
6009
  "The Yieldseeker withdrawal completed without transferring funds.",
@@ -5771,7 +6018,7 @@ var YieldseekerAgent = class {
5771
6018
  type: amount === void 0 && !completion ? "full" : "partial",
5772
6019
  ...completion ? { completion } : {},
5773
6020
  ...feeAdjustment ? { feeAdjustment } : {},
5774
- amount: evidence.confirmedAmount ?? payout.toString(),
6021
+ amount: evidence.confirmedAmount ?? "0",
5775
6022
  evidence
5776
6023
  };
5777
6024
  } finally {
@@ -5812,17 +6059,17 @@ var YieldseekerAgent = class {
5812
6059
  return proof.available;
5813
6060
  }
5814
6061
  async readVaultLiquidity(options, wallet, blockNumber) {
5815
- const owner = (0, import_viem12.getAddress)(wallet);
6062
+ const owner = (0, import_viem13.getAddress)(wallet);
5816
6063
  const batches = Array.from({ length: Math.ceil(options.length / YIELDSEEKER_VAULTS_PER_RPC_BATCH) }, (_, index) => options.slice(index * YIELDSEEKER_VAULTS_PER_RPC_BATCH, (index + 1) * YIELDSEEKER_VAULTS_PER_RPC_BATCH));
5817
6064
  const results = await Promise.all(batches.map((batch) => this.getReceiptClient().multicall({
5818
6065
  blockNumber,
5819
6066
  allowFailure: true,
5820
6067
  contracts: batch.flatMap((option) => {
5821
- const address = (0, import_viem12.getAddress)(option.address);
6068
+ const address = (0, import_viem13.getAddress)(option.address);
5822
6069
  return [
5823
- { address, abi: import_viem12.erc4626Abi, functionName: "asset" },
5824
- { address, abi: import_viem12.erc20Abi, functionName: "balanceOf", args: [owner] },
5825
- { address, abi: import_viem12.erc4626Abi, functionName: "maxWithdraw", args: [owner] }
6070
+ { address, abi: import_viem13.erc4626Abi, functionName: "asset" },
6071
+ { address, abi: import_viem13.erc20Abi, functionName: "balanceOf", args: [owner] },
6072
+ { address, abi: import_viem13.erc4626Abi, functionName: "maxWithdraw", args: [owner] }
5826
6073
  ];
5827
6074
  })
5828
6075
  })));
@@ -5833,7 +6080,7 @@ var YieldseekerAgent = class {
5833
6080
  const seen = /* @__PURE__ */ new Set();
5834
6081
  for (const value of values) {
5835
6082
  const option = value?.yieldOption;
5836
- if (!option || typeof option.address !== "string" || !(0, import_viem12.isAddress)(option.address) || seen.has(option.address.toLowerCase()) || option.chainId !== void 0 && option.chainId !== chainId || option.assetAddress !== void 0 && (typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== underlying) || typeof value.assetsRaw !== "string" || !/^\d+$/.test(value.assetsRaw) || typeof value.withdrawableAssetsRaw !== "string" || !/^\d+$/.test(value.withdrawableAssetsRaw))
6083
+ if (!option || typeof option.address !== "string" || !(0, import_viem13.isAddress)(option.address) || seen.has(option.address.toLowerCase()) || option.chainId !== void 0 && option.chainId !== chainId || option.assetAddress !== void 0 && (typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== underlying) || typeof value.assetsRaw !== "string" || !/^\d+$/.test(value.assetsRaw) || typeof value.withdrawableAssetsRaw !== "string" || !/^\d+$/.test(value.withdrawableAssetsRaw))
5837
6084
  throw this.invalidResponse("yield positions");
5838
6085
  seen.add(option.address.toLowerCase());
5839
6086
  }
@@ -5846,7 +6093,7 @@ var YieldseekerAgent = class {
5846
6093
  const historic = this.readCache.get(`historic:${key2}`)?.value?.position;
5847
6094
  const receivedAssets = new Set((historic?.movements ?? []).filter((movement) => movement.chainId === chainId && movement.agentWalletId === context.wallet.agentWalletId && movement.toAddress?.toLowerCase() === context.wallet.walletAddress.toLowerCase()).filter((movement) => typeof movement.assetAddress === "string").map((movement) => movement.assetAddress.toLowerCase()));
5848
6095
  for (const option of this.yieldOptions.get(context.asset)?.value ?? []) {
5849
- if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !(0, import_viem12.isAddress)(option.address) || !receivedAssets.has(option.address.toLowerCase()) || hints.has(option.address.toLowerCase())) continue;
6096
+ if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !(0, import_viem13.isAddress)(option.address) || !receivedAssets.has(option.address.toLowerCase()) || hints.has(option.address.toLowerCase())) continue;
5850
6097
  hints.set(option.address.toLowerCase(), {
5851
6098
  yieldOption: option,
5852
6099
  assetsRaw: "0",
@@ -5898,7 +6145,7 @@ var YieldseekerAgent = class {
5898
6145
  const read2 = async () => {
5899
6146
  const client = this.getReceiptClient();
5900
6147
  const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
5901
- const wallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
6148
+ const wallet = (0, import_viem13.getAddress)(context.wallet.walletAddress);
5902
6149
  assertReadCurrent();
5903
6150
  if (typeof blockNumber !== "bigint") throw unavailable();
5904
6151
  const indexedPositions = this.validateWithdrawalPositions(positions ?? [], chainId, context.asset);
@@ -5908,11 +6155,11 @@ var YieldseekerAgent = class {
5908
6155
  for (const position2 of indexedPositions) candidates.set(position2.yieldOption.address.toLowerCase(), position2.yieldOption);
5909
6156
  const options = [...candidates.values()];
5910
6157
  if (options.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
5911
- const [checks, idle, feeTracker] = await Promise.all([
5912
- this.readVaultLiquidity(options, wallet, blockNumber),
6158
+ const checksRead = this.readVaultLiquidity(options, wallet, blockNumber).then((value) => ({ value }), (error) => ({ error }));
6159
+ const [idle, feeTracker] = await Promise.all([
5913
6160
  client.readContract({
5914
6161
  address: metadata.address,
5915
- abi: import_viem12.erc20Abi,
6162
+ abi: import_viem13.erc20Abi,
5916
6163
  functionName: "balanceOf",
5917
6164
  args: [wallet],
5918
6165
  blockNumber
@@ -5924,10 +6171,10 @@ var YieldseekerAgent = class {
5924
6171
  blockNumber
5925
6172
  })
5926
6173
  ]);
5927
- if (typeof idle !== "bigint" || idle < 0n || !(0, import_viem12.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker)) throw unavailable();
6174
+ if (typeof idle !== "bigint" || idle < 0n || !(0, import_viem13.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker)) throw unavailable();
5928
6175
  assertReadCurrent();
5929
6176
  const fees = await client.readContract({
5930
- address: (0, import_viem12.getAddress)(feeTracker),
6177
+ address: (0, import_viem13.getAddress)(feeTracker),
5931
6178
  abi: YIELDSEEKER_FEE_TRACKER_ABI,
5932
6179
  functionName: "getFeesOwed",
5933
6180
  args: [wallet],
@@ -5937,6 +6184,18 @@ var YieldseekerAgent = class {
5937
6184
  const verified = [];
5938
6185
  const verifiedVaults = [];
5939
6186
  let deployed = 0n;
6187
+ const proof = (complete) => ({
6188
+ blockNumber,
6189
+ idle,
6190
+ fees,
6191
+ available: idle + deployed > fees ? idle + deployed - fees : 0n,
6192
+ positions: [...verified],
6193
+ verifiedVaults: [...verifiedVaults],
6194
+ indexedPositions,
6195
+ complete
6196
+ });
6197
+ assertReadCurrent();
6198
+ knownProof = proof(false);
5940
6199
  const collect = (options2, results) => {
5941
6200
  assertReadCurrent();
5942
6201
  const known = this.balanceVaults.get(contextKey) ?? /* @__PURE__ */ new Set();
@@ -5952,7 +6211,7 @@ var YieldseekerAgent = class {
5952
6211
  return;
5953
6212
  }
5954
6213
  const [underlying, shares, withdrawable] = group.map((value) => value.result);
5955
- if (typeof underlying !== "string" || !(0, import_viem12.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n || shares === 0n && withdrawable > 0n) {
6214
+ if (typeof underlying !== "string" || !(0, import_viem13.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n || shares === 0n && withdrawable > 0n) {
5956
6215
  valid2 = false;
5957
6216
  return;
5958
6217
  }
@@ -5969,17 +6228,10 @@ var YieldseekerAgent = class {
5969
6228
  });
5970
6229
  return valid2;
5971
6230
  };
5972
- const allKnownVerified = collect(options, checks);
5973
- const proof = (complete) => ({
5974
- blockNumber,
5975
- idle,
5976
- fees,
5977
- available: idle + deployed > fees ? idle + deployed - fees : 0n,
5978
- positions: [...verified],
5979
- verifiedVaults: [...verifiedVaults],
5980
- indexedPositions,
5981
- complete
5982
- });
6231
+ const checks = await checksRead;
6232
+ assertReadCurrent();
6233
+ if ("error" in checks) throw checks.error;
6234
+ const allKnownVerified = collect(options, checks.value);
5983
6235
  knownProof = proof(false);
5984
6236
  const indexed = new Map(indexedPositions.map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
5985
6237
  const discrepancy = verified.some((position2) => {
@@ -5996,7 +6248,7 @@ var YieldseekerAgent = class {
5996
6248
  assertReadCurrent();
5997
6249
  const eligible = /* @__PURE__ */ new Map();
5998
6250
  for (const option of catalog ?? []) {
5999
- if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() && (0, import_viem12.isAddress)(option.address))
6251
+ if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() && (0, import_viem13.isAddress)(option.address))
6000
6252
  eligible.set(option.address.toLowerCase(), option);
6001
6253
  }
6002
6254
  const extra = [...eligible].filter(([address]) => !candidates.has(address)).map(([, option]) => option);
@@ -6047,7 +6299,7 @@ var YieldseekerAgent = class {
6047
6299
  const key2 = this.contextKey(state, evidence.chainId, evidence.token);
6048
6300
  const generation = this.readGeneration, version = this.portfolioVersion(key2);
6049
6301
  if (read2.readVersion !== version) return void 0;
6050
- const wallet = (0, import_viem12.getAddress)(context.wallet.walletAddress);
6302
+ const wallet = (0, import_viem13.getAddress)(context.wallet.walletAddress);
6051
6303
  const asset = YIELDSEEKER_ASSET_METADATA[evidence.token];
6052
6304
  const client = this.getReceiptClient();
6053
6305
  const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
@@ -6055,10 +6307,10 @@ var YieldseekerAgent = class {
6055
6307
  const vaults = [...this.balanceVaults.get(key2) ?? []];
6056
6308
  const [invested, idle, tracker] = await Promise.all([
6057
6309
  remainingVaultBalance(client, wallet, asset.address, vaults, blockNumber),
6058
- client.readContract({ address: asset.address, abi: import_viem12.erc20Abi, functionName: "balanceOf", args: [wallet], blockNumber }),
6310
+ client.readContract({ address: asset.address, abi: import_viem13.erc20Abi, functionName: "balanceOf", args: [wallet], blockNumber }),
6059
6311
  client.readContract({ address: wallet, abi: YIELDSEEKER_AGENT_WALLET_ABI, functionName: "feeTracker", blockNumber })
6060
6312
  ]);
6061
- if (invested === void 0 || typeof idle !== "bigint" || idle < 0n || !(0, import_viem12.isAddress)(tracker) || /^0x0{40}$/i.test(tracker)) return void 0;
6313
+ if (invested === void 0 || typeof idle !== "bigint" || idle < 0n || !(0, import_viem13.isAddress)(tracker) || /^0x0{40}$/i.test(tracker)) return void 0;
6062
6314
  const fees = await client.readContract({
6063
6315
  address: tracker,
6064
6316
  abi: YIELDSEEKER_FEE_TRACKER_ABI,
@@ -6071,7 +6323,7 @@ var YieldseekerAgent = class {
6071
6323
  const total = idle + invested > fees ? idle + invested - fees : 0n;
6072
6324
  const displayed = balance.assetBalances?.find((item) => item.chainId === evidence.chainId && item.asset === evidence.token);
6073
6325
  if (!displayed || !evidence.confirmedAmount || !/^\d+$/.test(evidence.confirmedAmount)) return void 0;
6074
- const amount = (0, import_viem12.parseUnits)(displayed.amount, asset.decimals);
6326
+ const amount = (0, import_viem13.parseUnits)(displayed.amount, asset.decimals);
6075
6327
  if (amount < 0n) return void 0;
6076
6328
  const displayUnit = evidence.token === "USDC" ? 5000n : 500000000000n;
6077
6329
  const movementUnit = BigInt(evidence.confirmedAmount) / 1000n;
@@ -6117,9 +6369,9 @@ var YieldseekerAgent = class {
6117
6369
  if (!context) return void 0;
6118
6370
  const idle = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
6119
6371
  address: YIELDSEEKER_ASSET_METADATA[asset].address,
6120
- abi: import_viem12.erc20Abi,
6372
+ abi: import_viem13.erc20Abi,
6121
6373
  functionName: "balanceOf",
6122
- args: [(0, import_viem12.getAddress)(context.wallet.walletAddress)]
6374
+ args: [(0, import_viem13.getAddress)(context.wallet.walletAddress)]
6123
6375
  }), chainId, "eth_call", this.id);
6124
6376
  this.assertSession(generation, chainId, asset);
6125
6377
  if (typeof idle !== "bigint" || idle < 0n) throw this.invalidResponse("wallet deposit balance");
@@ -6127,7 +6379,7 @@ var YieldseekerAgent = class {
6127
6379
  }
6128
6380
  async getEarnings(state, chainId) {
6129
6381
  this.assertChain(chainId);
6130
- return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
6382
+ return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, { includePositions: false }));
6131
6383
  }
6132
6384
  async getAccountApy(state, chainId, days, tokenSymbol) {
6133
6385
  this.assertChain(chainId);
@@ -6166,7 +6418,7 @@ var YieldseekerAgent = class {
6166
6418
  );
6167
6419
  const vaultAddresses = new Set(
6168
6420
  catalog.flat().filter(
6169
- (yieldOption) => yieldOption.chainId === chainId && (0, import_viem12.isAddress)(yieldOption.address)
6421
+ (yieldOption) => yieldOption.chainId === chainId && (0, import_viem13.isAddress)(yieldOption.address)
6170
6422
  ).map((yieldOption) => yieldOption.address.toLowerCase())
6171
6423
  );
6172
6424
  return mapYieldseekerHistory(contexts, {
@@ -6295,7 +6547,7 @@ var YieldseekerAgent = class {
6295
6547
  `/users/${user.userId}/agents/${agent.agentId}/wallet`
6296
6548
  ).then((walletResponse) => {
6297
6549
  this.assertSession(generation, chainId, asset);
6298
- if (!walletResponse?.agentWallet || !(0, import_viem12.isAddress)(walletResponse.agentWallet.walletAddress)) {
6550
+ if (!walletResponse?.agentWallet || !(0, import_viem13.isAddress)(walletResponse.agentWallet.walletAddress)) {
6299
6551
  throw this.invalidResponse("agent wallet");
6300
6552
  }
6301
6553
  const context = {
@@ -6325,7 +6577,7 @@ var YieldseekerAgent = class {
6325
6577
  this.users.set(key2, persisted);
6326
6578
  return persisted;
6327
6579
  }
6328
- const walletAddress = (0, import_viem12.getAddress)(state.walletAddress);
6580
+ const walletAddress = (0, import_viem13.getAddress)(state.walletAddress);
6329
6581
  const generation = this.readGeneration;
6330
6582
  let user = null;
6331
6583
  try {
@@ -6709,7 +6961,7 @@ var YieldseekerAgent = class {
6709
6961
  this.agentPath(context, "deploy"),
6710
6962
  { method: "POST", body: {} }
6711
6963
  );
6712
- if (!deployed?.agentWallet || !(0, import_viem12.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
6964
+ if (!deployed?.agentWallet || !(0, import_viem13.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
6713
6965
  throw this.invalidResponse("agent deployment", {
6714
6966
  reason: "Deploy did not return the expected Agent Wallet."
6715
6967
  });
@@ -6810,22 +7062,26 @@ var YieldseekerAgent = class {
6810
7062
  this.id
6811
7063
  );
6812
7064
  }
6813
- async submitTransaction(state, chainId, transaction, onSubmitted, onReceipt) {
7065
+ async submitTransaction(state, chainId, transaction, onSubmitted, onReceipt, onBeforeSubmission) {
6814
7066
  if (this.transactionExecutor) {
6815
- const hash2 = await this.transactionExecutor(state, chainId, transaction);
7067
+ await onBeforeSubmission?.();
7068
+ const hash2 = await this.transactionExecutor(state, chainId, transaction, onReceipt);
6816
7069
  onSubmitted?.(hash2);
6817
7070
  return hash2;
6818
7071
  }
6819
7072
  this.assertTransaction(transaction, state, chainId);
6820
- const account = (0, import_viem12.getAddress)(state.walletAddress);
6821
- const walletClient = (0, import_viem12.createWalletClient)({
7073
+ const account = (0, import_viem13.getAddress)(state.walletAddress);
7074
+ const walletClient = (0, import_viem13.createWalletClient)({
6822
7075
  account,
6823
7076
  chain: import_chains4.base,
6824
- transport: (0, import_viem12.custom)(state.provider)
7077
+ transport: onBeforeSubmission ? (0, import_viem13.custom)({ request: async (args) => {
7078
+ if (args.method === "eth_sendTransaction") await onBeforeSubmission();
7079
+ return state.provider.request(args);
7080
+ } }, { retryCount: 0 }) : (0, import_viem13.custom)(state.provider)
6825
7081
  });
6826
- const walletChainClient = (0, import_viem12.createPublicClient)({
7082
+ const walletChainClient = (0, import_viem13.createPublicClient)({
6827
7083
  chain: import_chains4.base,
6828
- transport: (0, import_viem12.custom)(state.provider)
7084
+ transport: (0, import_viem13.custom)(state.provider)
6829
7085
  });
6830
7086
  await ensureWalletOnChain(
6831
7087
  walletChainClient,
@@ -6835,7 +7091,7 @@ var YieldseekerAgent = class {
6835
7091
  const hash = await walletClient.sendTransaction({
6836
7092
  account,
6837
7093
  chain: import_chains4.base,
6838
- to: (0, import_viem12.getAddress)(transaction.to),
7094
+ to: (0, import_viem13.getAddress)(transaction.to),
6839
7095
  data: transaction.data,
6840
7096
  value: BigInt(transaction.value)
6841
7097
  });
@@ -6939,12 +7195,12 @@ var YieldseekerAgent = class {
6939
7195
  return receipt;
6940
7196
  }
6941
7197
  assertTransaction(transaction, state, chainId) {
6942
- if (!transaction || typeof transaction.from !== "string" || !(0, import_viem12.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem12.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem12.getAddress)(transaction.from) !== (0, import_viem12.getAddress)(state.walletAddress)) {
7198
+ if (!transaction || typeof transaction.from !== "string" || !(0, import_viem13.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem13.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem13.getAddress)(transaction.from) !== (0, import_viem13.getAddress)(state.walletAddress)) {
6943
7199
  throw this.invalidResponse("transaction");
6944
7200
  }
6945
7201
  }
6946
7202
  assertAgent(agent) {
6947
- if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem12.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
7203
+ if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem13.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
6948
7204
  throw this.invalidResponse("agent");
6949
7205
  }
6950
7206
  }
@@ -7098,15 +7354,24 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
7098
7354
  }
7099
7355
 
7100
7356
  // src/lib/helpers/withdraw-helper.ts
7101
- var import_viem13 = require("viem");
7357
+ var import_viem14 = require("viem");
7102
7358
  function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
7103
7359
  const target = asset.toUpperCase();
7104
7360
  return agents.map((agent) => {
7361
+ let invalidBalance = false;
7362
+ const readAmount = (value, raw2 = false) => {
7363
+ if (typeof value !== "string" || !(raw2 ? /^\d+$/ : /^\d+(?:\.\d+)?$/).test(value)) {
7364
+ invalidBalance = true;
7365
+ return 0n;
7366
+ }
7367
+ const [whole, fraction] = value.split(".");
7368
+ return raw2 ? BigInt(value) : (0, import_viem14.parseUnits)(fraction === void 0 ? whole : `${whole}.${fraction.slice(0, decimals)}`, decimals);
7369
+ };
7105
7370
  const agentBalance = aggregated[agent.id];
7106
7371
  const tokenBalance = agentBalance?.tokens.find(
7107
7372
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
7108
7373
  );
7109
- let balance = tokenBalance ? (0, import_viem13.parseUnits)(tokenBalance.amount, decimals) : 0n;
7374
+ let balance = tokenBalance ? readAmount(tokenBalance.amount) : 0n;
7110
7375
  if (agent.balanceComposition === "tokens-plus-positions") {
7111
7376
  const chainNameById = {
7112
7377
  1: "ETHEREUM",
@@ -7118,20 +7383,22 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
7118
7383
  const positionChain = position2.chain.trim().toUpperCase();
7119
7384
  const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
7120
7385
  if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
7121
- balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0, import_viem13.parseUnits)(position2.amount, decimals);
7386
+ balance += position2.withdrawableAmountRaw !== void 0 ? readAmount(position2.withdrawableAmountRaw, true) : readAmount(position2.amount);
7122
7387
  }
7123
7388
  }
7124
7389
  const assetTotal = agentBalance?.assetBalances?.find(
7125
7390
  (t) => t.chainId === chainId && t.asset.toUpperCase() === target
7126
7391
  );
7127
7392
  if (assetTotal) {
7128
- const total = (0, import_viem13.parseUnits)(assetTotal.amount, decimals);
7393
+ const total = readAmount(assetTotal.amount);
7129
7394
  if (total < balance) balance = total > 0n ? total : 0n;
7130
7395
  }
7131
- return { agent, balance };
7396
+ return invalidBalance ? { agent, balance: 0n, invalidBalance: true } : { agent, balance };
7132
7397
  });
7133
7398
  }
7134
7399
  function planProportionalShares(balances, requested, totalAvailable) {
7400
+ if (requested < 0n || totalAvailable < 0n || balances.some((entry) => entry.balance < 0n) || balances.reduce((sum, entry) => sum + entry.balance, 0n) !== totalAvailable || requested > totalAvailable)
7401
+ throw new Error("Invalid withdrawal planning amounts.");
7135
7402
  const plans = balances.map(({ agent, balance }) => ({
7136
7403
  agent,
7137
7404
  balance,
@@ -7154,6 +7421,8 @@ function planProportionalShares(balances, requested, totalAvailable) {
7154
7421
  return plans;
7155
7422
  }
7156
7423
  function planDisabledDrain(disabled, requested) {
7424
+ if (requested < 0n || disabled.some((entry) => entry.balance < 0n))
7425
+ throw new Error("Invalid withdrawal planning amounts.");
7157
7426
  const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
7158
7427
  (a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
7159
7428
  );
@@ -7171,6 +7440,7 @@ function planDisabledDrain(disabled, requested) {
7171
7440
  return { plans, remaining };
7172
7441
  }
7173
7442
  function redistributeShare(plans, fromIndex, amount, candidatePool) {
7443
+ if (amount <= 0n) return;
7174
7444
  const pool = candidatePool ?? plans.slice(fromIndex + 1);
7175
7445
  const candidates = pool.filter((c) => c.balance - c.planned > 0n);
7176
7446
  const totalHeadroom = candidates.reduce(
@@ -7197,7 +7467,13 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
7197
7467
  }
7198
7468
  }
7199
7469
  function sumWithdrawnAmount(results) {
7200
- return Object.values(results).reduce((sum, r) => sum + BigInt(r.amount), 0n);
7470
+ return Object.values(results).reduce((sum, result) => sum + (confirmedWithdrawalAmount(result) ?? 0n), 0n);
7471
+ }
7472
+ function confirmedWithdrawalAmount(result) {
7473
+ const evidence = result.evidence;
7474
+ if (evidence && (evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed")) return void 0;
7475
+ const value = evidence ? evidence.confirmedAmount : result.amount;
7476
+ return typeof value === "string" && /^\d+$/.test(value) ? BigInt(value) : void 0;
7201
7477
  }
7202
7478
 
7203
7479
  // src/lib/helpers/account-apy-helper.ts
@@ -7321,14 +7597,14 @@ function aggregateDailyEarnings(series, tokenSymbol) {
7321
7597
  }
7322
7598
 
7323
7599
  // src/client.ts
7324
- var import_viem17 = require("viem");
7600
+ var import_viem18 = require("viem");
7325
7601
  var import_chains5 = require("viem/chains");
7326
7602
 
7327
7603
  // src/lib/sponsored-token-batch.ts
7328
- var import_viem15 = require("viem");
7604
+ var import_viem16 = require("viem");
7329
7605
 
7330
7606
  // src/lib/permit2-batch.ts
7331
- var import_viem14 = require("viem");
7607
+ var import_viem15 = require("viem");
7332
7608
  var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
7333
7609
  var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
7334
7610
  var ERC2612_PERMIT_TYPES = {
@@ -7340,7 +7616,7 @@ var ERC2612_PERMIT_TYPES = {
7340
7616
  { name: "deadline", type: "uint256" }
7341
7617
  ]
7342
7618
  };
7343
- var ERC2612_READ_ABI = (0, import_viem14.parseAbi)([
7619
+ var ERC2612_READ_ABI = (0, import_viem15.parseAbi)([
7344
7620
  "function name() view returns (string)",
7345
7621
  "function version() view returns (string)",
7346
7622
  "function nonces(address owner) view returns (uint256)"
@@ -7378,7 +7654,7 @@ var PERMIT_BATCH_TYPES = {
7378
7654
  { name: "amount", type: "uint256" }
7379
7655
  ]
7380
7656
  };
7381
- var PERMIT2_BATCH_ABI = (0, import_viem14.parseAbi)([
7657
+ var PERMIT2_BATCH_ABI = (0, import_viem15.parseAbi)([
7382
7658
  "struct TokenPermissions { address token; uint256 amount; }",
7383
7659
  "struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
7384
7660
  "struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
@@ -7411,9 +7687,29 @@ function batchTypedData(b, spender) {
7411
7687
  }
7412
7688
  };
7413
7689
  }
7690
+ var BATCH_WITNESS_TYPE = "Deposit witness)Deposit(address[] recipients)TokenPermissions(address token,uint256 amount)";
7691
+ function batchWitness(b) {
7692
+ return (0, import_viem15.hashStruct)({
7693
+ types: PERMIT_BATCH_TYPES,
7694
+ primaryType: "Deposit",
7695
+ data: { recipients: b.transfers.map((t) => t.to) }
7696
+ });
7697
+ }
7698
+ function batchArgs(b) {
7699
+ return [
7700
+ batchPermit(b),
7701
+ b.transfers.map((t) => ({ to: t.to, requestedAmount: BigInt(t.amount) })),
7702
+ b.from,
7703
+ batchWitness(b),
7704
+ BATCH_WITNESS_TYPE,
7705
+ b.signature
7706
+ ];
7707
+ }
7414
7708
 
7415
7709
  // src/lib/sponsored-token-batch.ts
7416
7710
  var memory = /* @__PURE__ */ new Map();
7711
+ var settledRecords = /* @__PURE__ */ new Map();
7712
+ var expiredRecords = /* @__PURE__ */ new Map();
7417
7713
  var inflight = /* @__PURE__ */ new Map();
7418
7714
  var planOf = (transfers) => JSON.stringify(
7419
7715
  transfers.map((t) => ({
@@ -7421,32 +7717,109 @@ var planOf = (transfers) => JSON.stringify(
7421
7717
  amount: BigInt(t.amount).toString()
7422
7718
  }))
7423
7719
  );
7424
- var pendingKey = (i) => `owney.token-batch.v1:${(0, import_viem15.keccak256)((0, import_viem15.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
7720
+ var pendingKey = (i) => `owney.token-batch.v1:${(0, import_viem16.keccak256)((0, import_viem16.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
7425
7721
  function pendingTokenBatch(i) {
7426
7722
  try {
7427
7723
  const saved = read(pendingKey(i));
7428
7724
  if (!saved) return;
7429
7725
  const body = JSON.parse(saved);
7430
- if (!body.serializedTransaction || body.chainId !== i.chainId || !(0, import_viem15.isAddressEqual)(body.from, i.owner) || !(0, import_viem15.isAddressEqual)(body.token, i.token) || !body.transfers?.length) return;
7431
- return { txHash: (0, import_viem15.keccak256)(body.serializedTransaction), transfers: body.transfers.map(({ to, amount }) => ({ to, amount })) };
7726
+ 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;
7727
+ return { txHash: (0, import_viem16.keccak256)(body.serializedTransaction), transfers: body.transfers.map(({ to, amount }) => ({ to, amount })) };
7728
+ } catch {
7729
+ return;
7730
+ }
7731
+ }
7732
+ async function releaseExpiredTokenBatch(i) {
7733
+ const key2 = pendingKey(i);
7734
+ let timer;
7735
+ let expired = false;
7736
+ const check = async () => {
7737
+ let active = read(key2);
7738
+ if (active && i.expectedTxHash) {
7739
+ const current = JSON.parse(active);
7740
+ if (!current.serializedTransaction || (0, import_viem16.keccak256)(current.serializedTransaction).toLowerCase() !== i.expectedTxHash.toLowerCase())
7741
+ active = void 0;
7742
+ }
7743
+ const archiveKey = i.expectedTxHash ? `${key2}:expired:${i.expectedTxHash.toLowerCase()}` : void 0;
7744
+ const saved = active ?? (archiveKey && (expiredRecords.get(archiveKey) ?? (typeof window === "undefined" ? void 0 : window.localStorage.getItem(archiveKey))));
7745
+ if (!saved) return;
7746
+ const body = JSON.parse(saved);
7747
+ 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;
7748
+ const txHash = (0, import_viem16.keccak256)(body.serializedTransaction);
7749
+ if (i.expectedTxHash && txHash.toLowerCase() !== i.expectedTxHash.toLowerCase()) return;
7750
+ const transaction = (0, import_viem16.parseTransaction)(body.serializedTransaction);
7751
+ 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)({
7752
+ abi: PERMIT2_BATCH_ABI,
7753
+ functionName: "permitWitnessTransferFrom",
7754
+ args: batchArgs(body)
7755
+ })) return;
7756
+ const nonce = BigInt(body.nonce);
7757
+ const deadline = BigInt(body.deadline);
7758
+ if (nonce < 0n || nonce > MAX_UINT256 || deadline < 0n) return;
7759
+ const block = await i.pub.getBlock({ blockTag: "finalized" });
7760
+ if (expired || typeof block.number !== "bigint" || typeof block.timestamp !== "bigint" || block.timestamp <= deadline) return;
7761
+ const bitmap = await i.pub.readContract({
7762
+ address: BATCH_PERMIT2_ADDRESS,
7763
+ abi: PERMIT2_BATCH_ABI,
7764
+ functionName: "nonceBitmap",
7765
+ args: [i.owner, nonce >> 8n],
7766
+ blockNumber: block.number
7767
+ });
7768
+ if (expired || typeof bitmap !== "bigint" || bitmap < 0n || bitmap > MAX_UINT256 || (bitmap & 1n << (nonce & 255n)) !== 0n || active && read(key2) !== active) return;
7769
+ if (active) {
7770
+ const terminalKey = `${key2}:expired:${txHash.toLowerCase()}`;
7771
+ expiredRecords.set(terminalKey, saved);
7772
+ let persisted = typeof window === "undefined";
7773
+ try {
7774
+ if (typeof window !== "undefined") {
7775
+ window.localStorage.setItem(terminalKey, saved);
7776
+ persisted = window.localStorage.getItem(terminalKey) === saved;
7777
+ }
7778
+ } catch {
7779
+ }
7780
+ if (!persisted) settledRecords.set(key2, saved);
7781
+ else clear(key2, body);
7782
+ }
7783
+ return { txHash, transfers: body.transfers.map(({ to, amount }) => ({ to, amount })), blockNumber: block.number };
7784
+ };
7785
+ try {
7786
+ return await Promise.race([check(), new Promise((resolve) => {
7787
+ timer = setTimeout(() => {
7788
+ expired = true;
7789
+ resolve(void 0);
7790
+ }, 8e3);
7791
+ })]);
7432
7792
  } catch {
7433
7793
  return;
7794
+ } finally {
7795
+ expired = true;
7796
+ if (timer) clearTimeout(timer);
7434
7797
  }
7435
7798
  }
7436
7799
  function read(key2) {
7437
- return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
7800
+ const value = typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
7801
+ return value && settledRecords.get(key2) === value ? void 0 : value;
7438
7802
  }
7439
- function save(key2, body) {
7440
- const value = JSON.stringify({
7803
+ function storedBody(body) {
7804
+ return JSON.stringify({
7441
7805
  ...body,
7442
7806
  transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
7443
7807
  });
7808
+ }
7809
+ function save(key2, body) {
7810
+ const value = storedBody(body);
7444
7811
  if (typeof window === "undefined") memory.set(key2, value);
7445
7812
  else window.localStorage.setItem(key2, value);
7813
+ settledRecords.delete(key2);
7446
7814
  }
7447
- function clear(key2) {
7448
- if (typeof window === "undefined") memory.delete(key2);
7449
- else window.localStorage.removeItem(key2);
7815
+ function clear(key2, body) {
7816
+ try {
7817
+ if (typeof window === "undefined") memory.delete(key2);
7818
+ else window.localStorage.removeItem(key2);
7819
+ settledRecords.delete(key2);
7820
+ } catch {
7821
+ settledRecords.set(key2, storedBody(body));
7822
+ }
7450
7823
  }
7451
7824
  function sponsorTokenBatch(i) {
7452
7825
  const key2 = pendingKey(i);
@@ -7513,7 +7886,7 @@ async function execute(i, key2, plan) {
7513
7886
  baseUrl: i.baseUrl,
7514
7887
  body
7515
7888
  });
7516
- if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0, import_viem15.keccak256)(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
7889
+ if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0, import_viem16.keccak256)(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
7517
7890
  throw new Error(
7518
7891
  "Sponsorship API did not return a valid prepared permit transaction."
7519
7892
  );
@@ -7524,7 +7897,7 @@ async function execute(i, key2, plan) {
7524
7897
  save(key2, body);
7525
7898
  }
7526
7899
  if (!body.serializedTransaction) {
7527
- const permitHash = body.serializedPermitTransaction ? (0, import_viem15.keccak256)(body.serializedPermitTransaction) : void 0;
7900
+ const permitHash = body.serializedPermitTransaction ? (0, import_viem16.keccak256)(body.serializedPermitTransaction) : void 0;
7528
7901
  let prepared = permitHash ? await postSigned("approval", permitHash) : await post();
7529
7902
  if (prepared?.status === "pending") {
7530
7903
  if (!permitHash || prepared.phase !== "approval" || prepared.txHash !== permitHash)
@@ -7535,7 +7908,7 @@ async function execute(i, key2, plan) {
7535
7908
  if (!prepared) prepared = await post();
7536
7909
  if (prepared.status === "pending" && permitHash && prepared.phase === "approval" && prepared.txHash === permitHash)
7537
7910
  throw pendingError("approval", permitHash);
7538
- if (!prepared.serializedTransaction || (0, import_viem15.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
7911
+ if (!prepared.serializedTransaction || (0, import_viem16.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
7539
7912
  throw new Error(
7540
7913
  "Sponsorship API did not return a valid prepared transaction."
7541
7914
  );
@@ -7545,23 +7918,24 @@ async function execute(i, key2, plan) {
7545
7918
  };
7546
7919
  save(key2, body);
7547
7920
  }
7921
+ await i.onBeforeSubmission?.((0, import_viem16.keccak256)(body.serializedTransaction));
7548
7922
  try {
7549
- i.onSubmitted?.((0, import_viem15.keccak256)(body.serializedTransaction));
7923
+ i.onSubmitted?.((0, import_viem16.keccak256)(body.serializedTransaction));
7550
7924
  } catch {
7551
7925
  }
7552
- const hash = (0, import_viem15.keccak256)(body.serializedTransaction);
7926
+ const hash = (0, import_viem16.keccak256)(body.serializedTransaction);
7553
7927
  const existing = await i.pub.getTransactionReceipt({ hash }).catch(() => void 0);
7554
7928
  if (existing) {
7555
7929
  assertReceipt(existing, "deposit");
7556
7930
  void post().catch(() => void 0);
7557
- clear(key2);
7931
+ clear(key2, body);
7558
7932
  return hash;
7559
7933
  }
7560
7934
  let result = await postSigned("deposit", hash);
7561
- if (result?.status === "pending" && result.phase === "approval" && body.serializedPermitTransaction && result.txHash === (0, import_viem15.keccak256)(body.serializedPermitTransaction)) {
7935
+ if (result?.status === "pending" && result.phase === "approval" && body.serializedPermitTransaction && result.txHash === (0, import_viem16.keccak256)(body.serializedPermitTransaction)) {
7562
7936
  await waitForConfirmation("approval", result.txHash);
7563
7937
  result = await postSigned("deposit", hash);
7564
- if (result?.status === "pending" && result.phase === "approval" && result.txHash === (0, import_viem15.keccak256)(body.serializedPermitTransaction)) throw pendingError("approval", result.txHash);
7938
+ if (result?.status === "pending" && result.phase === "approval" && result.txHash === (0, import_viem16.keccak256)(body.serializedPermitTransaction)) throw pendingError("approval", result.txHash);
7565
7939
  }
7566
7940
  if (result) {
7567
7941
  if (result.txHash !== hash || result.status === "pending" && result.phase !== "deposit")
@@ -7569,11 +7943,18 @@ async function execute(i, key2, plan) {
7569
7943
  if (result.status === "pending") await waitForConfirmation("deposit", hash);
7570
7944
  }
7571
7945
  if (!result || result.status === "pending") void post().catch(() => void 0);
7572
- clear(key2);
7946
+ clear(key2, body);
7573
7947
  return hash;
7574
7948
  } catch (error) {
7949
+ if (initial.serializedTransaction && error instanceof OwneyError && error.code === "DEPOSIT_CHECKPOINT_FAILED") {
7950
+ throw new OwneyError(
7951
+ error.code,
7952
+ "Could not save recovery of the existing deposit.",
7953
+ { ...error.details, notSubmitted: false, safeToFallback: false }
7954
+ );
7955
+ }
7575
7956
  if (!body.serializedTransaction && !body.serializedPermitTransaction || rejectedPreparation(error) || error instanceof OwneyError && error.details?.notSubmitted === true)
7576
- clear(key2);
7957
+ clear(key2, body);
7577
7958
  throw error;
7578
7959
  }
7579
7960
  };
@@ -7582,10 +7963,16 @@ async function execute(i, key2, plan) {
7582
7963
  throw new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit changed. Check its status before depositing again.");
7583
7964
  if (saved) {
7584
7965
  const previous = JSON.parse(saved);
7585
- if (previous.chainId !== i.chainId || !(0, import_viem15.isAddressEqual)(previous.from, i.owner) || !(0, import_viem15.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
7966
+ if (previous.chainId !== i.chainId || !(0, import_viem16.isAddressEqual)(previous.from, i.owner) || !(0, import_viem16.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
7586
7967
  throw new Error(
7587
7968
  "Retry the previous token deposit and agent split first to reconcile its status."
7588
7969
  );
7970
+ if (await releaseExpiredTokenBatch({ ...i, expectedTxHash: previous.serializedTransaction ? (0, import_viem16.keccak256)(previous.serializedTransaction) : void 0 }))
7971
+ throw new OwneyError(
7972
+ "SPONSOR_REQUEST_FAILED",
7973
+ "The deposit authorization expired without transferring funds. Start a new deposit.",
7974
+ { notSubmitted: true, safeToFallback: false }
7975
+ );
7589
7976
  i.onApproved?.();
7590
7977
  return send({ ...previous, transfers: i.transfers });
7591
7978
  }
@@ -7599,7 +7986,7 @@ async function execute(i, key2, plan) {
7599
7986
  "DEPOSIT_INSUFFICIENT_BALANCE",
7600
7987
  "Insufficient token balance for this deposit."
7601
7988
  );
7602
- if (allowance < total && (i.chainId !== 8453 || !(0, import_viem15.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
7989
+ if (allowance < total && (i.chainId !== 8453 || !(0, import_viem16.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
7603
7990
  throw new OwneyError(
7604
7991
  "PERMIT2_APPROVAL_REQUIRED",
7605
7992
  "token deposits need a one-time Permit2 approval."
@@ -7692,6 +8079,7 @@ function makeSponsoredTokenCallback(deps) {
7692
8079
  wallet,
7693
8080
  onApproved: deps.onApproved,
7694
8081
  onSubmitted: (hash) => deps.onSubmitted?.(hash, transfers),
8082
+ onBeforeSubmission: (hash) => deps.onBeforeSubmission?.(hash, transfers),
7695
8083
  resumeTxHash: deps.resumeTxHash
7696
8084
  });
7697
8085
  };
@@ -7778,7 +8166,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
7778
8166
  }
7779
8167
 
7780
8168
  // src/lib/sponsored-calls-deposit.ts
7781
- var import_viem16 = require("viem");
8169
+ var import_viem17 = require("viem");
7782
8170
  var DEFAULT_POLL_INTERVAL_MS = 1500;
7783
8171
  var DEFAULT_MAX_POLLS = 30;
7784
8172
  async function paymasterSupported(provider, owner, chainId) {
@@ -7786,7 +8174,7 @@ async function paymasterSupported(provider, owner, chainId) {
7786
8174
  method: "wallet_getCapabilities",
7787
8175
  params: [owner]
7788
8176
  });
7789
- const forChain = caps?.[(0, import_viem16.toHex)(chainId)] ?? caps?.[String(chainId)];
8177
+ const forChain = caps?.[(0, import_viem17.toHex)(chainId)] ?? caps?.[String(chainId)];
7790
8178
  return Boolean(forChain?.paymasterService?.supported);
7791
8179
  }
7792
8180
  function makeSponsoredCallsCallback(deps) {
@@ -7823,8 +8211,8 @@ function makeSponsoredCallsCallback(deps) {
7823
8211
  const calls = transfers.map((transfer) => ({
7824
8212
  to: token,
7825
8213
  value: "0x0",
7826
- data: (0, import_viem16.encodeFunctionData)({
7827
- abi: import_viem16.erc20Abi,
8214
+ data: (0, import_viem17.encodeFunctionData)({
8215
+ abi: import_viem17.erc20Abi,
7828
8216
  functionName: "transfer",
7829
8217
  args: [transfer.to, BigInt(transfer.amount)]
7830
8218
  })
@@ -7856,13 +8244,14 @@ function makeSponsoredCallsCallback(deps) {
7856
8244
  url.searchParams.append("owneyIntent", intent);
7857
8245
  paymasterUrl = url.toString();
7858
8246
  }
7859
- const requestedId = (0, import_viem16.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
8247
+ const requestedId = (0, import_viem17.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
7860
8248
  const progress = (id, transfer) => {
7861
8249
  try {
7862
8250
  deps.onCallsProgress?.(id, transfers, transfer);
7863
8251
  } catch {
7864
8252
  }
7865
8253
  };
8254
+ await deps.onBeforeSubmission?.(requestedId, transfers);
7866
8255
  progress(requestedId, "pending");
7867
8256
  let sendResult;
7868
8257
  try {
@@ -7873,7 +8262,7 @@ function makeSponsoredCallsCallback(deps) {
7873
8262
  version: "2.0.0",
7874
8263
  id: requestedId,
7875
8264
  from: deps.ownerAddress,
7876
- chainId: (0, import_viem16.toHex)(chainId),
8265
+ chainId: (0, import_viem17.toHex)(chainId),
7877
8266
  atomicRequired: transfers.length > 1,
7878
8267
  calls,
7879
8268
  capabilities: {
@@ -8006,6 +8395,8 @@ var OwneySDK = class {
8006
8395
  */
8007
8396
  disabledAgents = /* @__PURE__ */ new Set();
8008
8397
  state = null;
8398
+ connectionRevision = 0;
8399
+ disconnectingAgents = null;
8009
8400
  apiKey;
8010
8401
  // The org's agent policy, fetched once per client. Applied to each agent on
8011
8402
  // activation (ROUT-229). `undefined` means "not fetched yet", `null` means
@@ -8027,6 +8418,9 @@ var OwneySDK = class {
8027
8418
  recoveredFundingReads = /* @__PURE__ */ new Set();
8028
8419
  fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
8029
8420
  activationChecks = /* @__PURE__ */ new Map();
8421
+ // Protect the SDK entry point as well as the mini-app hook. This owns only
8422
+ // a running call, never a saved/pending provider outcome or a later retry.
8423
+ withdrawalSubmissions = /* @__PURE__ */ new Set();
8030
8424
  readAgent(agent, method, fetch2, params = null) {
8031
8425
  const key2 = JSON.stringify([
8032
8426
  this.state?.walletAddress.toLowerCase() ?? null,
@@ -8059,9 +8453,10 @@ var OwneySDK = class {
8059
8453
  */
8060
8454
  // TODO: replace with a proper EIP-1193 Provider type
8061
8455
  async connect(provider) {
8062
- const accounts = await provider.request({
8063
- method: "eth_accounts"
8064
- });
8456
+ const revision = ++this.connectionRevision;
8457
+ const accounts = await provider.request({ method: "eth_accounts" });
8458
+ if (revision !== this.connectionRevision)
8459
+ throw new OwneyError("NOT_CONNECTED", "Wallet connection changed while connecting.");
8065
8460
  const walletAddress = accounts[0];
8066
8461
  if (!walletAddress) {
8067
8462
  throw new OwneyError(
@@ -8069,33 +8464,46 @@ var OwneySDK = class {
8069
8464
  "No accounts found. Ensure the wallet is unlocked and connected."
8070
8465
  );
8071
8466
  }
8072
- this.agentReads.clearInFlight();
8073
- this.activationChecks.clear();
8074
- this.fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
8467
+ const replacingOwner = this.state !== null && this.state.walletAddress.toLowerCase() !== walletAddress.toLowerCase();
8468
+ this.clearConnectionState();
8469
+ await (replacingOwner ? this.disconnectAgents() : this.disconnectingAgents);
8470
+ if (revision !== this.connectionRevision)
8471
+ throw new OwneyError("NOT_CONNECTED", "Wallet connection changed while connecting.");
8075
8472
  this.state = { provider, walletAddress, chainId: null };
8076
- this.cachedSponsoredCallback = null;
8077
- this.cachedWethSponsoredCallback = null;
8078
- this.cachedSponsoredCallsCallbacks.clear();
8079
8473
  }
8080
8474
  /**
8081
8475
  * Disconnect from all agents and clear connection state.
8082
8476
  * After calling this, `connect()` must be called again before using agent methods.
8083
8477
  */
8084
8478
  async disconnect() {
8479
+ ++this.connectionRevision;
8480
+ this.clearConnectionState();
8481
+ await this.disconnectAgents();
8482
+ }
8483
+ clearConnectionState() {
8085
8484
  this.state = null;
8086
8485
  this.fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
8087
- for (const agent of this.agents.values()) {
8088
- await agent.disconnect();
8089
- }
8090
8486
  this.activeAgents.clear();
8091
- this.disabledAgents.clear();
8092
8487
  this.agentReads.clearInFlight();
8093
8488
  this.activationChecks.clear();
8094
- this.state = null;
8095
8489
  this.cachedSponsoredCallback = null;
8096
8490
  this.cachedWethSponsoredCallback = null;
8097
8491
  this.cachedSponsoredCallsCallbacks.clear();
8098
8492
  }
8493
+ disconnectAgents() {
8494
+ if (this.disconnectingAgents) return this.disconnectingAgents;
8495
+ const pending = Promise.allSettled([...this.agents.values()].map(async (agent) => agent.disconnect())).then((results) => {
8496
+ const failure = results.find((result) => result.status === "rejected");
8497
+ if (failure?.status === "rejected") throw failure.reason;
8498
+ });
8499
+ this.disconnectingAgents = pending;
8500
+ void pending.then(() => {
8501
+ if (this.disconnectingAgents === pending) this.disconnectingAgents = null;
8502
+ }, () => {
8503
+ if (this.disconnectingAgents === pending) this.disconnectingAgents = null;
8504
+ });
8505
+ return pending;
8506
+ }
8099
8507
  isAgentDisabled(agentId) {
8100
8508
  return this.disabledAgents.has(agentId);
8101
8509
  }
@@ -8141,8 +8549,8 @@ var OwneySDK = class {
8141
8549
  return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
8142
8550
  }
8143
8551
  /** Builds the default USDC batch callback for the connected wallet. */
8144
- getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash) {
8145
- if (!onApproved && !onSubmitted && !resumeTxHash && this.cachedSponsoredCallback)
8552
+ getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission) {
8553
+ if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && this.cachedSponsoredCallback)
8146
8554
  return this.cachedSponsoredCallback;
8147
8555
  const provider = this.requireConnectedProvider();
8148
8556
  const owner = this.state.walletAddress;
@@ -8152,29 +8560,30 @@ var OwneySDK = class {
8152
8560
  ownerAddress: owner,
8153
8561
  onApproved,
8154
8562
  onSubmitted,
8563
+ onBeforeSubmission,
8155
8564
  resumeTxHash,
8156
8565
  tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
8157
8566
  // Casts work around viem's chain-narrowed Client vs the generic
8158
8567
  // PublicClient/WalletClient param types — structurally identical at
8159
8568
  // runtime, but the two share a name TS treats as unrelated.
8160
8569
  getPublicClient: (cid) => this.getPaidRpcClient(cid),
8161
- getWalletChainClient: (cid) => (0, import_viem17.createPublicClient)({
8570
+ getWalletChainClient: (cid) => (0, import_viem18.createPublicClient)({
8162
8571
  chain: VIEM_CHAIN2[cid],
8163
- transport: (0, import_viem17.custom)(provider)
8572
+ transport: (0, import_viem18.custom)(provider)
8164
8573
  }),
8165
- getWalletClient: (cid) => (0, import_viem17.createWalletClient)({
8574
+ getWalletClient: (cid) => (0, import_viem18.createWalletClient)({
8166
8575
  account: owner,
8167
8576
  chain: VIEM_CHAIN2[cid],
8168
- transport: (0, import_viem17.custom)(provider)
8577
+ transport: (0, import_viem18.custom)(provider)
8169
8578
  })
8170
8579
  });
8171
- if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedSponsoredCallback = callback;
8580
+ if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission) this.cachedSponsoredCallback = callback;
8172
8581
  return callback;
8173
8582
  }
8174
8583
  /** Builds the wallet-native sponsored calls callback for compatible paymasters. */
8175
- getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress) {
8584
+ getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress, onBeforeSubmission) {
8176
8585
  const cached = this.cachedSponsoredCallsCallbacks.get(asset);
8177
- if (!onApproved && !onSubmitted && !onCallsProgress && cached) return cached;
8586
+ if (!onApproved && !onSubmitted && !onCallsProgress && !onBeforeSubmission && cached) return cached;
8178
8587
  const provider = this.requireConnectedProvider();
8179
8588
  const callback = makeSponsoredCallsCallback({
8180
8589
  apiKey: this.apiKey,
@@ -8185,9 +8594,10 @@ var OwneySDK = class {
8185
8594
  onApproved,
8186
8595
  onSubmitted,
8187
8596
  onCallsProgress,
8597
+ onBeforeSubmission,
8188
8598
  tokenAddressByChain: sponsoredTokensFor(asset)
8189
8599
  });
8190
- if (!onApproved && !onSubmitted && !onCallsProgress) this.cachedSponsoredCallsCallbacks.set(asset, callback);
8600
+ if (!onApproved && !onSubmitted && !onCallsProgress && !onBeforeSubmission) this.cachedSponsoredCallsCallbacks.set(asset, callback);
8191
8601
  return callback;
8192
8602
  }
8193
8603
  /**
@@ -8196,8 +8606,8 @@ var OwneySDK = class {
8196
8606
  * Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
8197
8607
  * single-use batch authorization instead of an EIP-3009 authorization.
8198
8608
  */
8199
- getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash) {
8200
- if (!onApproved && !onSubmitted && !resumeTxHash && this.cachedWethSponsoredCallback)
8609
+ getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission) {
8610
+ if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && this.cachedWethSponsoredCallback)
8201
8611
  return this.cachedWethSponsoredCallback;
8202
8612
  const provider = this.requireConnectedProvider();
8203
8613
  const owner = this.state.walletAddress;
@@ -8207,23 +8617,24 @@ var OwneySDK = class {
8207
8617
  ownerAddress: owner,
8208
8618
  onApproved,
8209
8619
  onSubmitted,
8620
+ onBeforeSubmission,
8210
8621
  resumeTxHash,
8211
8622
  tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
8212
8623
  // Casts work around viem's chain-narrowed Client vs the generic
8213
8624
  // PublicClient/WalletClient param types — structurally identical at
8214
8625
  // runtime, but the two share a name TS treats as unrelated.
8215
8626
  getPublicClient: (cid) => this.getPaidRpcClient(cid),
8216
- getWalletChainClient: (cid) => (0, import_viem17.createPublicClient)({
8627
+ getWalletChainClient: (cid) => (0, import_viem18.createPublicClient)({
8217
8628
  chain: VIEM_CHAIN2[cid],
8218
- transport: (0, import_viem17.custom)(provider)
8629
+ transport: (0, import_viem18.custom)(provider)
8219
8630
  }),
8220
- getWalletClient: (cid) => (0, import_viem17.createWalletClient)({
8631
+ getWalletClient: (cid) => (0, import_viem18.createWalletClient)({
8221
8632
  account: owner,
8222
8633
  chain: VIEM_CHAIN2[cid],
8223
- transport: (0, import_viem17.custom)(provider)
8634
+ transport: (0, import_viem18.custom)(provider)
8224
8635
  })
8225
8636
  });
8226
- if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedWethSponsoredCallback = callback;
8637
+ if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission) this.cachedWethSponsoredCallback = callback;
8227
8638
  return callback;
8228
8639
  }
8229
8640
  getAgent(agentId) {
@@ -8531,10 +8942,20 @@ var OwneySDK = class {
8531
8942
  */
8532
8943
  async deposit(options) {
8533
8944
  const { amount, asset, depositCallback, agentId, onApproved } = options;
8945
+ if (!isFundingAmount(amount, true))
8946
+ throw new OwneyError(
8947
+ "DEPOSIT_INVALID_AMOUNT",
8948
+ "Deposit amount must be an unsigned base-unit integer.",
8949
+ { phase: "prepare", rejectedBeforeSubmission: true }
8950
+ );
8534
8951
  const prepared = /* @__PURE__ */ new Map();
8535
8952
  const paidFallbackAgents = /* @__PURE__ */ new Set();
8536
- const onProgress = options.onProgress ? (evidence) => {
8537
- if (evidence.agentWallet) prepared.set(evidence.agentWallet.toLowerCase(), evidence);
8953
+ const onProgress = options.onProgress || options.onBeforeSubmission ? (evidence) => {
8954
+ if (evidence.agentWallet) {
8955
+ const wallet = evidence.agentWallet.toLowerCase();
8956
+ evidence = { ...prepared.get(wallet), ...evidence };
8957
+ prepared.set(wallet, evidence);
8958
+ }
8538
8959
  notifyFunding(options.onProgress, evidence);
8539
8960
  } : void 0;
8540
8961
  const onSubmitted = onProgress ? (hash, destinations) => {
@@ -8554,10 +8975,87 @@ var OwneySDK = class {
8554
8975
  }
8555
8976
  const state = this.requireState();
8556
8977
  const chainId = this.requireChainId();
8978
+ let checkpointFailure;
8979
+ let checkpointFailedEvidence = [];
8980
+ const checkpoint2 = options.onBeforeSubmission ? async (evidence) => {
8981
+ try {
8982
+ if (this.state !== state || state.chainId !== chainId) throw new Error("Wallet changed before deposit submission.");
8983
+ const submitting = evidence.map((leg) => ({ ...leg, depositSubmission: "started" }));
8984
+ await options.onBeforeSubmission(submitting);
8985
+ if (this.state !== state || state.chainId !== chainId) throw new Error("Wallet changed before deposit submission.");
8986
+ submitting.forEach((leg) => notifyFunding(onProgress, leg));
8987
+ } catch (cause) {
8988
+ checkpointFailedEvidence = evidence;
8989
+ checkpointFailure = new OwneyError(
8990
+ "DEPOSIT_CHECKPOINT_FAILED",
8991
+ "Could not safely save this deposit before submission.",
8992
+ { notSubmitted: !options.resumeTxHash, safeToFallback: false, cause }
8993
+ );
8994
+ throw checkpointFailure;
8995
+ }
8996
+ } : void 0;
8997
+ const checkpointDestinations = checkpoint2 ? async (destinations, identifier) => {
8998
+ const evidence = destinations.map((destination) => {
8999
+ const leg = prepared.get(destination.to.toLowerCase());
9000
+ if (!leg || leg.intendedAmount !== destination.amount)
9001
+ throw new OwneyError("DEPOSIT_CHECKPOINT_FAILED", "Missing prepared deposit destination.", { notSubmitted: true, safeToFallback: false });
9002
+ return { ...leg, ...identifier, transfer: "pending" };
9003
+ });
9004
+ await checkpoint2(evidence);
9005
+ } : void 0;
9006
+ const checkpointHash = checkpointDestinations ? (hash, destinations) => checkpointDestinations(destinations, { txHash: hash }) : void 0;
9007
+ const checkpointCalls = checkpointDestinations ? (id, destinations) => checkpointDestinations(destinations, { callsId: id }) : void 0;
9008
+ const checkpointPaid = checkpoint2 ? (evidence) => checkpoint2([evidence]) : void 0;
9009
+ const customCallback = depositCallback && checkpointDestinations ? makeVerificationAwareDepositCallback(async (to, cid, value, verification) => {
9010
+ if (verification) provideDepositVerificationContext(depositCallback, verification);
9011
+ await checkpointDestinations([{ to, amount: value }], {});
9012
+ return depositCallback(to, cid, value);
9013
+ }) : depositCallback;
8557
9014
  if (options.resumeTxHash && (depositCallback || this.paymasterServiceUrl || !/^0x[\da-f]{64}$/i.test(options.resumeTxHash)))
8558
9015
  throw new OwneyError("DEPOSIT_IN_PROGRESS", "This deposit cannot resume the saved relayer transaction.");
8559
- 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));
9016
+ const effectiveCallback = customCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress, checkpointCalls) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash, checkpointHash) : this.getDefaultSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash, checkpointHash));
8560
9017
  try {
9018
+ if (options.resumeTxHash) {
9019
+ const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
9020
+ const pending = token && pendingTokenBatch({
9021
+ apiKey: this.apiKey,
9022
+ baseUrl: this.routingApiBaseUrl,
9023
+ owner: state.walletAddress,
9024
+ chainId,
9025
+ token: token.address
9026
+ });
9027
+ const invalid2 = () => new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit does not match this request. Check its status before depositing again.");
9028
+ 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();
9029
+ const candidates = agentId ? [this.getAgent(agentId)] : this.getEligibleAgents(chainId, asset);
9030
+ const wallets = await Promise.all(candidates.map(async (agent) => {
9031
+ this.validateAssetSupport(agent, chainId, asset);
9032
+ const wallet = await (agent.getFundingWallet ? agent.getFundingWallet(state, chainId, asset) : agent.getUserProfile(state, chainId).then((profile) => profile.smartWallet)).catch(() => void 0);
9033
+ return { agent, wallet };
9034
+ }));
9035
+ if (this.state !== state || this.state.chainId !== chainId)
9036
+ throw new OwneyError("NOT_CONNECTED", "Wallet changed while recovering the deposit.");
9037
+ const usedAgents = /* @__PURE__ */ new Set();
9038
+ const legs = pending.transfers.map((transfer) => {
9039
+ const matches = wallets.filter((value) => value.wallet?.toLowerCase() === transfer.to.toLowerCase());
9040
+ if (matches.length !== 1 || usedAgents.has(matches[0].agent.id)) throw invalid2();
9041
+ const { agent } = matches[0];
9042
+ usedAgents.add(agent.id);
9043
+ return {
9044
+ id: agent.id,
9045
+ amount: transfer.amount,
9046
+ run: (callback) => withFailureReporting(
9047
+ this.apiKey,
9048
+ agent.id,
9049
+ () => agent.deposit(state, chainId, transfer.amount, asset, callback, onProgress),
9050
+ this.routingApiBaseUrl
9051
+ )
9052
+ };
9053
+ });
9054
+ const batchTransfer2 = getDepositBatchTransfer(effectiveCallback);
9055
+ if (!batchTransfer2) throw invalid2();
9056
+ const result = await runAgentDepositBatch(chainId, legs, batchTransfer2);
9057
+ return agentId ? result.agentResults[agentId] : result;
9058
+ }
8561
9059
  if (agentId) {
8562
9060
  if (this.isAgentDisabled(agentId)) {
8563
9061
  throw new OwneyError(
@@ -8579,7 +9077,8 @@ var OwneySDK = class {
8579
9077
  depositCallback,
8580
9078
  onProgress,
8581
9079
  () => paidFallbackAgents.add(agent.id),
8582
- !!options.resumeTxHash
9080
+ !!options.resumeTxHash,
9081
+ checkpointPaid
8583
9082
  );
8584
9083
  }
8585
9084
  const totalAmount = BigInt(amount);
@@ -8681,7 +9180,8 @@ var OwneySDK = class {
8681
9180
  depositCallback,
8682
9181
  onProgress,
8683
9182
  () => paidFallbackAgents.add(agent.id),
8684
- !!options.resumeTxHash
9183
+ !!options.resumeTxHash,
9184
+ checkpointPaid
8685
9185
  );
8686
9186
  } catch (error) {
8687
9187
  if (Object.keys(agentResults).length === 0) throw error;
@@ -8701,12 +9201,22 @@ var OwneySDK = class {
8701
9201
  }
8702
9202
  return { agentResults };
8703
9203
  } catch (error) {
9204
+ if (checkpointFailure && !(error instanceof OwneyError && error.details?.notSubmitted === false)) {
9205
+ error = error instanceof OwneyError && error.code === "DEPOSIT_PARTIAL_FAILURE" ? new OwneyError(
9206
+ error.code,
9207
+ "Some deposits completed before another agent could be submitted.",
9208
+ { ...error.details, cause: checkpointFailure },
9209
+ error.agentId
9210
+ ) : checkpointFailure;
9211
+ if (!options.resumeTxHash) for (const evidence of checkpointFailedEvidence)
9212
+ notifyFunding(onProgress, { ...evidence, transfer: "failed" });
9213
+ }
8704
9214
  if (!depositCallback && onProgress && !options.resumeTxHash) {
8705
9215
  const ids = agentId ? [agentId] : this.getEligibleAgents(chainId, asset).map((agent) => agent.id);
8706
9216
  for (const id of ids) {
8707
9217
  const evidence = [...prepared.values()].find((value) => value.agentId === id);
8708
- if (paidFallbackAgents.has(id) || evidence?.transfer === "confirmed" || evidence?.transfer === "failed") continue;
8709
9218
  const rejected = error instanceof OwneyError && error.details?.notSubmitted === true;
9219
+ if (!rejected && paidFallbackAgents.has(id) || evidence?.transfer === "confirmed" || evidence?.transfer === "failed") continue;
8710
9220
  if (!rejected && (evidence?.txHash || evidence?.userOpHash || evidence?.callsId)) continue;
8711
9221
  notifyFunding(onProgress, { ...evidence ?? {
8712
9222
  agentId: id,
@@ -8749,11 +9259,11 @@ var OwneySDK = class {
8749
9259
  * propagates unchanged. Scoped per agent invocation so one agent's failure
8750
9260
  * doesn't force a retry of sibling agents in a multi-agent split.
8751
9261
  */
8752
- async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress, onPaidFallback, resumeOnly = false) {
9262
+ async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress, onPaidFallback, resumeOnly = false, onBeforeSubmission) {
8753
9263
  const attempt = (callback) => withFailureReporting(
8754
9264
  this.apiKey,
8755
9265
  agent.id,
8756
- () => onProgress ? agent.deposit(state, chainId, amount, asset, callback, onProgress) : agent.deposit(state, chainId, amount, asset, callback),
9266
+ () => onBeforeSubmission ? agent.deposit(state, chainId, amount, asset, callback, onProgress, onBeforeSubmission) : onProgress ? agent.deposit(state, chainId, amount, asset, callback, onProgress) : agent.deposit(state, chainId, amount, asset, callback),
8757
9267
  this.routingApiBaseUrl
8758
9268
  );
8759
9269
  let approvalAttempted = false;
@@ -8974,9 +9484,36 @@ var OwneySDK = class {
8974
9484
  const token = SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token);
8975
9485
  if (!token) throw new OwneyError("CHAIN_UNSUPPORTED", "Unsupported funding asset.");
8976
9486
  const rpc2 = this.getPaidRpcClient(chainId);
8977
- const receipt = await rpc2.getTransactionReceipt({ hash: evidence.txHash });
9487
+ let receipt;
9488
+ try {
9489
+ receipt = await rpc2.getTransactionReceipt({ hash: evidence.txHash });
9490
+ } catch (error) {
9491
+ if (direction === "deposit") {
9492
+ const expired = await releaseExpiredTokenBatch({
9493
+ apiKey: this.apiKey,
9494
+ baseUrl: this.routingApiBaseUrl,
9495
+ owner: state.walletAddress,
9496
+ chainId,
9497
+ token: token.address,
9498
+ pub: rpc2,
9499
+ expectedTxHash: evidence.txHash
9500
+ });
9501
+ assertCurrent();
9502
+ if (expired?.transfers.some((transfer) => transfer.to.toLowerCase() === agentWallet.toLowerCase() && transfer.amount === evidence.intendedAmount))
9503
+ return {
9504
+ ...evidence,
9505
+ agentWallet,
9506
+ transfer: "failed",
9507
+ delivery: "not-applicable",
9508
+ registration: "not-applicable",
9509
+ confirmedAmount: "0",
9510
+ blockNumber: expired.blockNumber.toString()
9511
+ };
9512
+ }
9513
+ throw error;
9514
+ }
8978
9515
  assertCurrent();
8979
- let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction);
9516
+ let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction, options);
8980
9517
  if (direction === "withdraw" && evidence.agentId === "yieldseeker" && verified.transfer === "unknown" && verified.confirmedAmount && BigInt(verified.confirmedAmount) > 0n) {
8981
9518
  const transaction = await rpc2.getTransaction({ hash: evidence.txHash });
8982
9519
  assertCurrent();
@@ -9150,9 +9687,11 @@ var OwneySDK = class {
9150
9687
  return page;
9151
9688
  }
9152
9689
  async recoverDepositRegistration(evidence) {
9690
+ const { state, assertCurrent } = this.captureReadScope(evidence.chainId);
9153
9691
  const verified = await this.inspectFunding(evidence, "deposit");
9692
+ assertCurrent();
9154
9693
  if (verified.agentId !== "zyfai") return verified;
9155
- return this.getAgent("zyfai").retryDepositRegistration(this.requireState(), verified);
9694
+ return this.getAgent("zyfai").retryDepositRegistration(state, verified);
9156
9695
  }
9157
9696
  // --- Fund operations ---
9158
9697
  /** On-chain version and ownership status for this owner's Yieldseeker agent. */
@@ -9198,10 +9737,24 @@ var OwneySDK = class {
9198
9737
  * @returns {AgentWithdrawResult} for a single agent, or {OwneyWithdrawResult} with per-agent results
9199
9738
  */
9200
9739
  async withdraw(options) {
9740
+ if (options.amount !== void 0 && !isFundingAmount(options.amount))
9741
+ throw new OwneyError(
9742
+ "WITHDRAW_INVALID_AMOUNT",
9743
+ "Withdrawal amount must be a positive base-unit integer.",
9744
+ { phase: "prepare", rejectedBeforeSubmission: true }
9745
+ );
9201
9746
  const state = this.requireState();
9202
9747
  const chainId = this.requireChainId();
9748
+ const submissionKey = `${state.walletAddress.toLowerCase()}:${chainId}:${options.asset.toUpperCase()}`;
9749
+ if (this.withdrawalSubmissions.has(submissionKey))
9750
+ throw new OwneyError(
9751
+ "WITHDRAW_IN_PROGRESS",
9752
+ "A withdrawal request is already running.",
9753
+ { chainId, asset: options.asset, phase: "prepare", rejectedBeforeSubmission: true }
9754
+ );
9203
9755
  const ids = options.agentId ? [options.agentId] : this.getActiveAgentIds().filter((id) => !options.excludeAgentIds?.includes(id));
9204
9756
  const attempted = /* @__PURE__ */ new Set();
9757
+ this.withdrawalSubmissions.add(submissionKey);
9205
9758
  try {
9206
9759
  return await this.withdrawFromAgents(options, state, chainId, attempted);
9207
9760
  } catch (error) {
@@ -9218,6 +9771,8 @@ var OwneySDK = class {
9218
9771
  });
9219
9772
  }
9220
9773
  throw error;
9774
+ } finally {
9775
+ this.withdrawalSubmissions.delete(submissionKey);
9221
9776
  }
9222
9777
  }
9223
9778
  async withdrawFromAgents(options, state, chainId, attempted) {
@@ -9256,7 +9811,19 @@ var OwneySDK = class {
9256
9811
  };
9257
9812
  };
9258
9813
  const submissionEvidence = /* @__PURE__ */ new Map();
9814
+ 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)));
9815
+ const throwUnconfirmed = (agentErrors2) => {
9816
+ const pending = pendingSubmissions();
9817
+ if (Object.keys(pending).length)
9818
+ throw new OwneyError(
9819
+ "WITHDRAW_UNCONFIRMED",
9820
+ "The withdrawal was submitted, but its outcome could not be confirmed yet.",
9821
+ { asset, amount, agentErrors: agentErrors2, agentFailures, pendingSubmissions: pending }
9822
+ );
9823
+ };
9259
9824
  const submit = async (agent, requested2) => {
9825
+ if (this.state !== state || this.state.chainId !== chainId)
9826
+ throw new OwneyError("NOT_CONNECTED", "Wallet connection changed before withdrawal submission.");
9260
9827
  attempted.add(agent.id);
9261
9828
  let evidence = {
9262
9829
  agentId: agent.id,
@@ -9268,12 +9835,16 @@ var OwneySDK = class {
9268
9835
  registration: "not-applicable",
9269
9836
  delivery: "pending"
9270
9837
  };
9838
+ let adapterReported = false;
9271
9839
  const progress = (next) => {
9840
+ if (next.agentId !== agent.id || next.chainId !== chainId || next.token !== token || next.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase()) return;
9841
+ adapterReported = true;
9272
9842
  evidence = { ...evidence, ...next };
9273
9843
  submissionEvidence.set(agent.id, evidence);
9274
9844
  notifyFunding(options.onProgress, evidence);
9275
9845
  };
9276
9846
  progress(evidence);
9847
+ adapterReported = false;
9277
9848
  try {
9278
9849
  if (options.onBeforeSubmission)
9279
9850
  return await agent.withdraw(
@@ -9287,7 +9858,7 @@ var OwneySDK = class {
9287
9858
  );
9288
9859
  return await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
9289
9860
  } catch (error) {
9290
- if (!evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
9861
+ if (!adapterReported && !evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
9291
9862
  progress({ ...evidence, transfer: "failed", delivery: "failed" });
9292
9863
  throw error;
9293
9864
  }
@@ -9348,6 +9919,7 @@ var OwneySDK = class {
9348
9919
  }
9349
9920
  }
9350
9921
  if (Object.keys(results2).length === 0) {
9922
+ throwUnconfirmed(agentErrors2);
9351
9923
  if (Object.keys(agentErrors2).length === 0)
9352
9924
  throw new OwneyError(
9353
9925
  "WITHDRAW_INSUFFICIENT_BALANCE",
@@ -9368,7 +9940,7 @@ var OwneySDK = class {
9368
9940
  );
9369
9941
  }
9370
9942
  const amountsKnown = Object.values(results2).every(
9371
- (result) => typeof result.amount === "string" && /^\d+$/.test(result.amount)
9943
+ (result) => confirmedWithdrawalAmount(result) !== void 0
9372
9944
  );
9373
9945
  return {
9374
9946
  agentResult: results2,
@@ -9387,6 +9959,13 @@ var OwneySDK = class {
9387
9959
  asset,
9388
9960
  assetInfo.decimals
9389
9961
  );
9962
+ for (const entry of balances) if (entry.invalidBalance) {
9963
+ if (!unavailableAgents.includes(entry.agent)) unavailableAgents.push(entry.agent);
9964
+ aggregated.agentErrors = {
9965
+ ...aggregated.agentErrors,
9966
+ [entry.agent.id]: "Provider returned an invalid withdrawal balance."
9967
+ };
9968
+ }
9390
9969
  const indexedAvailable = balances.reduce((sum, entry) => sum + entry.balance, 0n);
9391
9970
  await Promise.all(balances.map(async (entry) => {
9392
9971
  if (entry.balance !== 0n && indexedAvailable >= requested || !entry.agent.getWithdrawableBalance) return;
@@ -9399,7 +9978,7 @@ var OwneySDK = class {
9399
9978
  if (this.isUserRejectedWithdrawal(originalFailure)) throw originalFailure;
9400
9979
  if (entry.balance !== 0n && entry.agent.id !== "yieldseeker") {
9401
9980
  const total = aggregated.agentBalances[entry.agent.id]?.assetBalances?.find((value) => value.chainId === chainId && value.asset.toUpperCase() === token);
9402
- if (!total || (0, import_viem17.parseUnits)(total.amount, assetInfo.decimals) <= entry.balance) return;
9981
+ if (!total || (0, import_viem18.parseUnits)(total.amount, assetInfo.decimals) <= entry.balance) return;
9403
9982
  }
9404
9983
  try {
9405
9984
  const otherAvailable = indexedAvailable - entry.balance;
@@ -9426,12 +10005,15 @@ var OwneySDK = class {
9426
10005
  throw new OwneyError("NOT_CONNECTED", "Account changed while planning withdrawal.");
9427
10006
  const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
9428
10007
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
9429
- if (totalAvailable === 0n && unavailableAgents.length > 0) {
10008
+ if (totalAvailable < requested && unavailableAgents.length > 0) {
9430
10009
  throw new OwneyError(
9431
10010
  "WITHDRAW_BALANCE_UNAVAILABLE",
9432
- `Cannot safely plan the withdrawal because balance data is unavailable for: ${unavailableAgentIds.join(", ")}.`,
10011
+ `Cannot cover the requested withdrawal with verified available balances: ${unavailableAgentIds.join(", ")}.`,
9433
10012
  {
9434
10013
  asset,
10014
+ requested: amount,
10015
+ available: totalAvailable.toString(),
10016
+ rejectedBeforeSubmission: true,
9435
10017
  unavailableAgents: unavailableAgentIds,
9436
10018
  agentErrors: aggregated.agentErrors
9437
10019
  }
@@ -9448,7 +10030,7 @@ var OwneySDK = class {
9448
10030
  }
9449
10031
  );
9450
10032
  }
9451
- const plannedTarget = totalAvailable < requested ? totalAvailable : requested;
10033
+ const plannedTarget = requested;
9452
10034
  const disabledBalances = balances.filter(
9453
10035
  (b) => this.isAgentDisabled(b.agent.id)
9454
10036
  );
@@ -9467,13 +10049,23 @@ var OwneySDK = class {
9467
10049
  }));
9468
10050
  const plans = [...disabledPlans, ...enabledPlans];
9469
10051
  const orderedPlans = this.orderAgentsForWithdrawal(plans.map((p) => p.agent)).map((agent) => plans.find((plan) => plan.agent === agent));
10052
+ const assertPlan = () => {
10053
+ if (orderedPlans.some((plan) => plan.planned < 0n || plan.planned > plan.balance) || orderedPlans.reduce((sum, plan) => sum + plan.planned, 0n) > plannedTarget)
10054
+ throw new OwneyError(
10055
+ "WITHDRAW_BALANCE_UNAVAILABLE",
10056
+ "Withdrawal plan failed amount validation.",
10057
+ { asset, rejectedBeforeSubmission: true }
10058
+ );
10059
+ };
10060
+ assertPlan();
9470
10061
  const results = {};
9471
10062
  const agentErrors = {
9472
10063
  ...aggregated.agentErrors ?? {}
9473
10064
  };
9474
10065
  for (let i = 0; i < orderedPlans.length; i++) {
9475
10066
  const p = orderedPlans[i];
9476
- if (p.planned === 0n) continue;
10067
+ assertPlan();
10068
+ if (p.planned <= 0n) continue;
9477
10069
  try {
9478
10070
  results[p.agent.id] = await submit(p.agent, p.planned.toString());
9479
10071
  notifyAgentResult(p.agent.id, results[p.agent.id]);
@@ -9497,6 +10089,7 @@ var OwneySDK = class {
9497
10089
  }
9498
10090
  }
9499
10091
  if (Object.keys(results).length === 0) {
10092
+ throwUnconfirmed(agentErrors);
9500
10093
  throw new OwneyError(
9501
10094
  "WITHDRAW_ALL_FAILED",
9502
10095
  `Failed to withdraw "${amount}" of "${asset}" from all eligible agents.`,
@@ -9514,13 +10107,17 @@ var OwneySDK = class {
9514
10107
  const adjustment = result?.feeAdjustment;
9515
10108
  const evidence = result?.evidence;
9516
10109
  const submitted = submissionEvidence.get(plan.agent.id);
9517
- if (plan.agent.id !== "yieldseeker" || !adjustment || !evidence || !submitted || typeof result.amount !== "string" || !/^\d+$/.test(result.amount) || typeof adjustment.grossAmount !== "string" || !/^\d+$/.test(adjustment.grossAmount) || typeof adjustment.feeAmount !== "string" || !/^\d+$/.test(adjustment.feeAmount) || evidence.agentId !== plan.agent.id || evidence.chainId !== chainId || evidence.token !== token || typeof evidence.walletAddress !== "string" || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || typeof evidence.agentWallet !== "string" || !/^0x[\da-f]{40}$/i.test(evidence.agentWallet) || typeof result.txHash !== "string" || !/^0x[\da-f]{64}$/i.test(result.txHash) || typeof evidence.txHash !== "string" || typeof submitted.txHash !== "string" || evidence.txHash?.toLowerCase() !== result.txHash.toLowerCase() || submitted.txHash?.toLowerCase() !== result.txHash.toLowerCase() || submitted.agentId !== evidence.agentId || submitted.chainId !== chainId || submitted.token !== token || typeof submitted.walletAddress !== "string" || typeof submitted.agentWallet !== "string" || submitted.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || submitted.agentWallet?.toLowerCase() !== evidence.agentWallet.toLowerCase() || evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || submitted.transfer !== "confirmed" || submitted.delivery !== "confirmed" || evidence.confirmedAmount !== result.amount || submitted.confirmedAmount !== result.amount || evidence.intendedAmount !== result.amount || submitted.intendedAmount !== result.amount) return total;
10110
+ if (plan.agent.id !== "yieldseeker" || !adjustment || !evidence || !submitted || typeof result.amount !== "string" || !/^\d+$/.test(result.amount) || typeof adjustment.grossAmount !== "string" || !/^\d+$/.test(adjustment.grossAmount) || typeof adjustment.feeAmount !== "string" || !/^\d+$/.test(adjustment.feeAmount) || evidence.agentId !== plan.agent.id || evidence.chainId !== chainId || evidence.token !== token || typeof evidence.walletAddress !== "string" || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || typeof evidence.agentWallet !== "string" || !/^0x[\da-f]{40}$/i.test(evidence.agentWallet) || typeof result.txHash !== "string" || !/^0x[\da-f]{64}$/i.test(result.txHash) || typeof evidence.txHash !== "string" || typeof submitted.txHash !== "string" || evidence.txHash?.toLowerCase() !== result.txHash.toLowerCase() || submitted.txHash?.toLowerCase() !== result.txHash.toLowerCase() || submitted.agentId !== evidence.agentId || submitted.chainId !== chainId || submitted.token !== token || typeof submitted.walletAddress !== "string" || typeof submitted.agentWallet !== "string" || submitted.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || submitted.agentWallet?.toLowerCase() !== evidence.agentWallet.toLowerCase() || evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || submitted.transfer !== "confirmed" || submitted.delivery !== "confirmed" || evidence.confirmedAmount !== result.amount || submitted.confirmedAmount !== result.amount || ![result.amount, adjustment.grossAmount].includes(evidence.intendedAmount) || ![result.amount, adjustment.grossAmount].includes(submitted.intendedAmount)) return total;
9518
10111
  const fee = BigInt(adjustment.feeAmount);
9519
10112
  const net = BigInt(result.amount);
9520
10113
  const gross = BigInt(adjustment.grossAmount);
9521
10114
  return fee > 0n && net > 0n && gross === plan.planned && net + fee === gross ? total + fee : total;
9522
10115
  }, 0n);
9523
- if (withdrawn + verifiedFeeDeductions < requested) {
10116
+ const pendingAmount = orderedPlans.reduce((sum, plan) => {
10117
+ const evidence = results[plan.agent.id]?.evidence;
10118
+ return evidence && (evidence.transfer === "pending" || evidence.transfer === "unknown") && evidence.delivery !== "failed" ? sum + plan.planned : sum;
10119
+ }, 0n);
10120
+ if (withdrawn + verifiedFeeDeductions + pendingAmount < requested) {
9524
10121
  throw new OwneyError(
9525
10122
  "WITHDRAW_PARTIAL_FAILURE",
9526
10123
  `Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
@@ -9528,6 +10125,7 @@ var OwneySDK = class {
9528
10125
  asset,
9529
10126
  requested: amount,
9530
10127
  withdrawn: withdrawn.toString(),
10128
+ pendingAmount: pendingAmount.toString(),
9531
10129
  partialResults: results,
9532
10130
  agentErrors,
9533
10131
  agentFailures,
@@ -9535,7 +10133,7 @@ var OwneySDK = class {
9535
10133
  }
9536
10134
  );
9537
10135
  }
9538
- return { agentResult: results, totalWithdrawn: withdrawn.toString() };
10136
+ return { agentResult: results, totalWithdrawn: Object.values(results).every((result) => confirmedWithdrawalAmount(result) !== void 0) ? withdrawn.toString() : null };
9539
10137
  }
9540
10138
  // --- Portfolio reads ---
9541
10139
  /**
@@ -9993,16 +10591,16 @@ var OwneySDK = class {
9993
10591
  );
9994
10592
  }
9995
10593
  const provider = this.requireConnectedProvider();
9996
- const walletChainClient = (0, import_viem17.createPublicClient)({
10594
+ const walletChainClient = (0, import_viem18.createPublicClient)({
9997
10595
  chain: VIEM_CHAIN2[chainId],
9998
- transport: (0, import_viem17.custom)(provider)
10596
+ transport: (0, import_viem18.custom)(provider)
9999
10597
  });
10000
10598
  const publicClient = this.getPaidRpcClient(chainId);
10001
10599
  const approvalAmount = permit2ApprovalAmount(requiredAmount);
10002
- const wallet = (0, import_viem17.createWalletClient)({
10600
+ const wallet = (0, import_viem18.createWalletClient)({
10003
10601
  account: state.walletAddress,
10004
10602
  chain: VIEM_CHAIN2[chainId],
10005
- transport: (0, import_viem17.custom)(provider)
10603
+ transport: (0, import_viem18.custom)(provider)
10006
10604
  });
10007
10605
  await ensureWalletOnChain(walletChainClient, wallet, chainId);
10008
10606
  const hash = await wallet.writeContract({
@@ -10164,7 +10762,7 @@ var OwneySDK = class {
10164
10762
  };
10165
10763
 
10166
10764
  // src/agents/zyfai/zyfai.siwx.ts
10167
- var import_viem18 = require("viem");
10765
+ var import_viem19 = require("viem");
10168
10766
  var import_siwe2 = require("siwe");
10169
10767
  var import_sdk2 = require("@zyfai/sdk");
10170
10768
 
@@ -10291,7 +10889,7 @@ function buildSIWXConfig(deps) {
10291
10889
  issuedAt,
10292
10890
  toString() {
10293
10891
  return new import_siwe2.SiweMessage({
10294
- address: (0, import_viem18.getAddress)(accountAddress),
10892
+ address: (0, import_viem19.getAddress)(accountAddress),
10295
10893
  chainId: numericChainId(chainId),
10296
10894
  domain,
10297
10895
  uri,