@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 +1254 -656
- package/dist/index.d.cts +26 -8
- package/dist/index.d.ts +26 -8
- package/dist/index.js +1176 -576
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16,6 +16,29 @@ var accountExecutionAbi = parseAbi([
|
|
|
16
16
|
]);
|
|
17
17
|
var entryPoint06 = "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789";
|
|
18
18
|
var entryPoint07 = "0x0000000071727de22e5e9d8baf0edac6f37da032";
|
|
19
|
+
function yieldseekerExecution(receipt, evidence) {
|
|
20
|
+
const operationEvents = receipt.logs.flatMap((log) => {
|
|
21
|
+
if (![entryPoint06, entryPoint07].includes(log.address.toLowerCase())) return [];
|
|
22
|
+
try {
|
|
23
|
+
const event = decodeEventLog({ abi: entryPoint06Abi, data: log.data, topics: log.topics });
|
|
24
|
+
return event.eventName === "UserOperationEvent" ? [{ ...event.args, logIndex: log.logIndex }] : [];
|
|
25
|
+
} catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}).sort((a, b) => a.logIndex - b.logIndex);
|
|
29
|
+
const bundled = !!evidence.userOpHash || !!evidence.callsId || operationEvents.length > 0 || [entryPoint06, entryPoint07].includes(receipt.to?.toLowerCase() ?? "");
|
|
30
|
+
if (!bundled) return { status: "confirmed", logs: receipt.logs };
|
|
31
|
+
const senders = evidence.userOperation?.sender ? [evidence.userOperation.sender.toLowerCase()] : evidence.callsId ? [evidence.walletAddress.toLowerCase()] : [evidence.walletAddress.toLowerCase(), evidence.agentWallet?.toLowerCase()];
|
|
32
|
+
const matches = operationEvents.filter((event) => senders.includes(event.sender.toLowerCase()) && (!evidence.userOpHash || event.userOpHash.toLowerCase() === evidence.userOpHash.toLowerCase()));
|
|
33
|
+
if (matches.length !== 1) return { status: "unknown", logs: [] };
|
|
34
|
+
const operation = matches[0];
|
|
35
|
+
if (!operation.success) return { status: "failed", logs: [] };
|
|
36
|
+
const previous = operationEvents.filter((event) => event.logIndex < operation.logIndex).at(-1)?.logIndex ?? -1;
|
|
37
|
+
return {
|
|
38
|
+
status: "confirmed",
|
|
39
|
+
logs: receipt.logs.filter((log) => log.logIndex > previous && log.logIndex < operation.logIndex)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
19
42
|
function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress, receipt) {
|
|
20
43
|
if (evidence.agentId !== "yieldseeker" || transaction.chainId !== evidence.chainId || transaction.hash.toLowerCase() !== evidence.txHash?.toLowerCase() || !evidence.agentWallet) return false;
|
|
21
44
|
const matchesFullCall = (data) => {
|
|
@@ -61,8 +84,27 @@ function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress, receip
|
|
|
61
84
|
return false;
|
|
62
85
|
}
|
|
63
86
|
}
|
|
64
|
-
function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
87
|
+
function inspectFundingReceipt(receipt, evidence, tokenAddress, direction, options = {}) {
|
|
88
|
+
evidence = {
|
|
89
|
+
...evidence,
|
|
90
|
+
confirmedAmount: void 0,
|
|
91
|
+
amountCompletion: void 0,
|
|
92
|
+
shortfallAmount: void 0,
|
|
93
|
+
blockNumber: void 0,
|
|
94
|
+
logIndexes: void 0,
|
|
95
|
+
transferLogs: void 0
|
|
96
|
+
};
|
|
97
|
+
if (evidence.txHash && receipt.transactionHash && evidence.txHash.toLowerCase() !== receipt.transactionHash.toLowerCase())
|
|
98
|
+
return { ...evidence, transfer: "unknown", delivery: direction === "withdraw" ? "unknown" : "not-applicable" };
|
|
65
99
|
if (receipt.status !== "success") return { ...evidence, transfer: "failed", delivery: "failed" };
|
|
100
|
+
const execution = direction === "withdraw" && evidence.agentId === "yieldseeker" ? yieldseekerExecution(receipt, evidence) : { status: "confirmed", logs: receipt.logs };
|
|
101
|
+
if (execution.status !== "confirmed") return {
|
|
102
|
+
...evidence,
|
|
103
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
104
|
+
...execution.status === "failed" ? { confirmedAmount: "0" } : {},
|
|
105
|
+
transfer: execution.status,
|
|
106
|
+
delivery: execution.status
|
|
107
|
+
};
|
|
66
108
|
let amount = 0n;
|
|
67
109
|
const seen = /* @__PURE__ */ new Set();
|
|
68
110
|
const attributed = /* @__PURE__ */ new Map();
|
|
@@ -73,7 +115,7 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
|
73
115
|
const logIndexes = [];
|
|
74
116
|
const transferLogs = [];
|
|
75
117
|
const zeroTransferLogs = [];
|
|
76
|
-
for (const log of [...
|
|
118
|
+
for (const log of [...execution.logs].sort((a, b) => a.logIndex - b.logIndex)) {
|
|
77
119
|
if (log.address.toLowerCase() !== tokenAddress.toLowerCase()) continue;
|
|
78
120
|
if (seen.has(log.logIndex)) continue;
|
|
79
121
|
seen.add(log.logIndex);
|
|
@@ -99,7 +141,7 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
|
99
141
|
}
|
|
100
142
|
}
|
|
101
143
|
const expected = /^\d+$/.test(evidence.intendedAmount) ? BigInt(evidence.intendedAmount) : void 0;
|
|
102
|
-
const emptyPayout = direction === "withdraw" && evidence.agentId === "yieldseeker" && amount === 0n && zeroTransferLogs.length > 0 &&
|
|
144
|
+
const emptyPayout = direction === "withdraw" && evidence.agentId === "yieldseeker" && amount === 0n && zeroTransferLogs.length > 0 && execution.logs.some((log) => {
|
|
103
145
|
if (log.address.toLowerCase() !== agent) return false;
|
|
104
146
|
try {
|
|
105
147
|
const { args } = decodeEventLog({ abi: withdrawalEventAbi, data: log.data, topics: log.topics });
|
|
@@ -117,18 +159,30 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
|
117
159
|
transfer: "failed",
|
|
118
160
|
delivery: "failed"
|
|
119
161
|
};
|
|
120
|
-
const
|
|
162
|
+
const exactReceipt = !!evidence.txHash && /^0x[\da-f]{64}$/i.test(evidence.txHash) && receipt.transactionHash?.toLowerCase() === evidence.txHash.toLowerCase();
|
|
163
|
+
const delivered = amount > 0n && (options.requireAmountMatch ? expected === void 0 || amount === expected : direction === "withdraw" && exactReceipt || expected === void 0 || amount >= expected);
|
|
121
164
|
return {
|
|
122
165
|
...evidence,
|
|
123
166
|
blockNumber: receipt.blockNumber.toString(),
|
|
124
167
|
logIndexes,
|
|
125
168
|
transferLogs,
|
|
126
169
|
...amount > 0n ? { confirmedAmount: amount.toString() } : {},
|
|
170
|
+
...delivered && direction === "withdraw" && expected !== void 0 ? {
|
|
171
|
+
amountCompletion: amount < expected ? "shortfall" : "complete",
|
|
172
|
+
shortfallAmount: (amount < expected ? expected - amount : 0n).toString()
|
|
173
|
+
} : {},
|
|
127
174
|
transfer: delivered ? "confirmed" : "unknown",
|
|
128
175
|
delivery: direction === "withdraw" ? delivered ? "confirmed" : "unknown" : "not-applicable"
|
|
129
176
|
};
|
|
130
177
|
}
|
|
131
178
|
|
|
179
|
+
// src/lib/helpers/funding-amount.ts
|
|
180
|
+
function isFundingAmount(value, allowZero = false) {
|
|
181
|
+
if (typeof value !== "string" || !/^\d{1,78}$/.test(value)) return false;
|
|
182
|
+
const amount = BigInt(value);
|
|
183
|
+
return (allowZero ? amount >= 0n : amount > 0n) && amount <= (1n << 256n) - 1n;
|
|
184
|
+
}
|
|
185
|
+
|
|
132
186
|
// src/lib/deposit-batch-callback.ts
|
|
133
187
|
var batchCallbacks = /* @__PURE__ */ new WeakMap();
|
|
134
188
|
var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
|
|
@@ -198,6 +252,240 @@ var AgentChainIncompatibleError = class extends OwneyError {
|
|
|
198
252
|
}
|
|
199
253
|
};
|
|
200
254
|
|
|
255
|
+
// src/lib/transfer-auth.ts
|
|
256
|
+
import { bytesToHex } from "viem";
|
|
257
|
+
|
|
258
|
+
// src/lib/sponsor-client.ts
|
|
259
|
+
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
260
|
+
async function postPaymasterIntent(input) {
|
|
261
|
+
const base6 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
262
|
+
let res;
|
|
263
|
+
try {
|
|
264
|
+
res = await fetch(`${base6}/api/v1/sponsor/paymaster-intent`, {
|
|
265
|
+
method: "POST",
|
|
266
|
+
headers: {
|
|
267
|
+
"content-type": "application/json",
|
|
268
|
+
"x-owney-api-key": input.apiKey,
|
|
269
|
+
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
270
|
+
},
|
|
271
|
+
body: JSON.stringify(input.body)
|
|
272
|
+
});
|
|
273
|
+
} catch (networkError) {
|
|
274
|
+
throw new OwneyError(
|
|
275
|
+
"SPONSOR_REQUEST_FAILED",
|
|
276
|
+
`Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
277
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
const text = await res.text();
|
|
281
|
+
let parsed = null;
|
|
282
|
+
try {
|
|
283
|
+
parsed = JSON.parse(text);
|
|
284
|
+
} catch {
|
|
285
|
+
}
|
|
286
|
+
if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
|
|
287
|
+
throw new OwneyError(
|
|
288
|
+
"SPONSOR_REQUEST_FAILED",
|
|
289
|
+
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
290
|
+
{
|
|
291
|
+
statusCode: res.status,
|
|
292
|
+
responseBody: text.slice(0, 500),
|
|
293
|
+
safeToFallback: true
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
return parsed.data;
|
|
298
|
+
}
|
|
299
|
+
async function getSponsorRelayerAddress(input) {
|
|
300
|
+
const base6 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
301
|
+
let res;
|
|
302
|
+
try {
|
|
303
|
+
res = await fetch(
|
|
304
|
+
`${base6}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
305
|
+
{
|
|
306
|
+
headers: { "x-owney-api-key": input.apiKey }
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
} catch (networkError) {
|
|
310
|
+
throw new OwneyError(
|
|
311
|
+
"SPONSOR_REQUEST_FAILED",
|
|
312
|
+
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
313
|
+
{ cause: String(networkError), safeToFallback: true }
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
const text = await res.text();
|
|
317
|
+
let parsed = null;
|
|
318
|
+
try {
|
|
319
|
+
parsed = JSON.parse(text);
|
|
320
|
+
} catch {
|
|
321
|
+
}
|
|
322
|
+
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
323
|
+
throw new OwneyError(
|
|
324
|
+
"SPONSOR_REQUEST_FAILED",
|
|
325
|
+
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
326
|
+
{
|
|
327
|
+
statusCode: res.status,
|
|
328
|
+
responseBody: text.slice(0, 500),
|
|
329
|
+
safeToFallback: true
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
return parsed.data.relayer;
|
|
334
|
+
}
|
|
335
|
+
async function postSponsorBatchTransfer(input) {
|
|
336
|
+
let res;
|
|
337
|
+
try {
|
|
338
|
+
res = await fetch(
|
|
339
|
+
`${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch`,
|
|
340
|
+
{
|
|
341
|
+
method: "POST",
|
|
342
|
+
headers: {
|
|
343
|
+
"content-type": "application/json",
|
|
344
|
+
"x-owney-api-key": input.apiKey
|
|
345
|
+
},
|
|
346
|
+
body: JSON.stringify(input.body)
|
|
347
|
+
}
|
|
348
|
+
);
|
|
349
|
+
} catch {
|
|
350
|
+
throw new OwneyError(
|
|
351
|
+
"SPONSOR_REQUEST_FAILED",
|
|
352
|
+
"Deposit status is unknown. Retry the same amount to check it.",
|
|
353
|
+
{ safeToFallback: false }
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
const parsed = await res.json().catch(() => null);
|
|
357
|
+
if (!res.ok || !parsed?.success || !/^0x[0-9a-fA-F]{64}$/.test(parsed.data?.txHash ?? "")) {
|
|
358
|
+
throw new OwneyError(
|
|
359
|
+
"SPONSOR_REQUEST_FAILED",
|
|
360
|
+
"Deposit could not be confirmed. Retry the same amount to check its status.",
|
|
361
|
+
{
|
|
362
|
+
statusCode: res.status,
|
|
363
|
+
safeToFallback: false,
|
|
364
|
+
notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
|
|
365
|
+
}
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
return parsed.data;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/lib/permit2.ts
|
|
372
|
+
import { bytesToHex as bytesToHex2 } from "viem";
|
|
373
|
+
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
374
|
+
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
375
|
+
function permit2ApprovalAmount(requiredAmount) {
|
|
376
|
+
if (requiredAmount <= 0n) {
|
|
377
|
+
throw new Error("Permit2 approval requires a positive deposit amount");
|
|
378
|
+
}
|
|
379
|
+
return MAX_UINT256;
|
|
380
|
+
}
|
|
381
|
+
var ERC20_ALLOWANCE_ABI = [
|
|
382
|
+
{
|
|
383
|
+
type: "function",
|
|
384
|
+
name: "allowance",
|
|
385
|
+
stateMutability: "view",
|
|
386
|
+
inputs: [
|
|
387
|
+
{ name: "owner", type: "address" },
|
|
388
|
+
{ name: "spender", type: "address" }
|
|
389
|
+
],
|
|
390
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
type: "function",
|
|
394
|
+
name: "approve",
|
|
395
|
+
stateMutability: "nonpayable",
|
|
396
|
+
inputs: [
|
|
397
|
+
{ name: "spender", type: "address" },
|
|
398
|
+
{ name: "amount", type: "uint256" }
|
|
399
|
+
],
|
|
400
|
+
outputs: [{ name: "", type: "bool" }]
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
type: "function",
|
|
404
|
+
name: "balanceOf",
|
|
405
|
+
stateMutability: "view",
|
|
406
|
+
inputs: [{ name: "account", type: "address" }],
|
|
407
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
408
|
+
}
|
|
409
|
+
];
|
|
410
|
+
function randomPermit2Nonce() {
|
|
411
|
+
const bytes = new Uint8Array(32);
|
|
412
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
413
|
+
return BigInt(bytesToHex2(bytes));
|
|
414
|
+
}
|
|
415
|
+
async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
|
|
416
|
+
return publicClient.readContract({
|
|
417
|
+
address: token,
|
|
418
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
419
|
+
functionName: "allowance",
|
|
420
|
+
args: [owner, PERMIT2_ADDRESS],
|
|
421
|
+
...blockNumber === void 0 ? {} : { blockNumber }
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
async function readErc20Balance(publicClient, token, owner) {
|
|
425
|
+
return publicClient.readContract({
|
|
426
|
+
address: token,
|
|
427
|
+
abi: ERC20_ALLOWANCE_ABI,
|
|
428
|
+
functionName: "balanceOf",
|
|
429
|
+
args: [owner]
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// src/lib/chain-guard.ts
|
|
434
|
+
var CHAIN_NAMES = {
|
|
435
|
+
1: "Ethereum",
|
|
436
|
+
8453: "Base",
|
|
437
|
+
42161: "Arbitrum"
|
|
438
|
+
};
|
|
439
|
+
function chainName(chainId) {
|
|
440
|
+
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
441
|
+
}
|
|
442
|
+
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
443
|
+
const actual = await pub.getChainId();
|
|
444
|
+
if (actual === expected) return;
|
|
445
|
+
try {
|
|
446
|
+
await wallet.switchChain({ id: expected });
|
|
447
|
+
} catch (error) {
|
|
448
|
+
throw new OwneyError(
|
|
449
|
+
"CHAIN_MISMATCH",
|
|
450
|
+
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
451
|
+
{
|
|
452
|
+
expectedChainId: expected,
|
|
453
|
+
actualChainId: actual,
|
|
454
|
+
cause: error instanceof Error ? error.message : String(error)
|
|
455
|
+
}
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
const after = await pub.getChainId();
|
|
459
|
+
if (after !== expected) {
|
|
460
|
+
throw new OwneyError(
|
|
461
|
+
"CHAIN_MISMATCH",
|
|
462
|
+
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
463
|
+
{ expectedChainId: expected, actualChainId: after }
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/lib/sponsored-deposit.ts
|
|
469
|
+
var AUTH_WINDOW_SECONDS = 15 * 60;
|
|
470
|
+
var verificationSetter = /* @__PURE__ */ Symbol("owney.depositVerificationSetter");
|
|
471
|
+
function provideDepositVerificationContext(callback, context) {
|
|
472
|
+
callback[verificationSetter]?.(context);
|
|
473
|
+
}
|
|
474
|
+
function makeVerificationAwareDepositCallback(implementation) {
|
|
475
|
+
let nextVerification;
|
|
476
|
+
const callback = async (smartWallet, chainId, amount) => {
|
|
477
|
+
const verification = nextVerification;
|
|
478
|
+
nextVerification = void 0;
|
|
479
|
+
return implementation(smartWallet, chainId, amount, verification);
|
|
480
|
+
};
|
|
481
|
+
Object.defineProperty(callback, verificationSetter, {
|
|
482
|
+
value: (context) => {
|
|
483
|
+
nextVerification = context;
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
return callback;
|
|
487
|
+
}
|
|
488
|
+
|
|
201
489
|
// src/lib/rate-limit.ts
|
|
202
490
|
function rateLimitDelay(error, now = Date.now()) {
|
|
203
491
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -345,7 +633,7 @@ var AgentReads = class {
|
|
|
345
633
|
|
|
346
634
|
// src/agents/zyfai/zyfai.withdraw-submission.ts
|
|
347
635
|
var listenersByClient = /* @__PURE__ */ new WeakMap();
|
|
348
|
-
function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
636
|
+
function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
349
637
|
const client = sdk.httpClient;
|
|
350
638
|
if (!client || typeof client.get !== "function" || typeof client.post !== "function")
|
|
351
639
|
return { attempted: () => void 0, rejected: () => false, request: () => void 0, stop: () => {
|
|
@@ -362,6 +650,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
|
362
650
|
const withdrawal = method === "get" && url === "/users/withdraw" || method === "post" && url === "/users/partial-withdraw";
|
|
363
651
|
const payload = method === "get" ? options?.params : options;
|
|
364
652
|
const matching = withdrawal ? [...observers].filter((observer) => observer.chainId === payload?.chainId && observer.token === payload?.tokenSymbol) : [];
|
|
653
|
+
for (const observer of matching) observer.assertCurrent?.();
|
|
365
654
|
const startedAt = Date.now();
|
|
366
655
|
const request2 = {
|
|
367
656
|
attempted: true,
|
|
@@ -406,7 +695,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
|
406
695
|
const status = error?.response?.status;
|
|
407
696
|
if (request2.httpStatus === void 0 && typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599)
|
|
408
697
|
request2.httpStatus = status;
|
|
409
|
-
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";
|
|
698
|
+
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";
|
|
410
699
|
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");
|
|
411
700
|
throw error;
|
|
412
701
|
} finally {
|
|
@@ -419,7 +708,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
|
419
708
|
let submitted = false;
|
|
420
709
|
let rejected = false;
|
|
421
710
|
let request = { attempted: false };
|
|
422
|
-
const listener = { chainId, token, notify: (outcome, evidence) => {
|
|
711
|
+
const listener = { chainId, token, assertCurrent, notify: (outcome, evidence) => {
|
|
423
712
|
request = evidence;
|
|
424
713
|
if (outcome === "attempted") submitted = true;
|
|
425
714
|
else if (outcome === "rejected") rejected = true;
|
|
@@ -470,10 +759,8 @@ function hasEmptyWithdrawalSnapshot(raw2, chainId, token) {
|
|
|
470
759
|
return true;
|
|
471
760
|
}
|
|
472
761
|
|
|
473
|
-
// src/agents/zyfai/zyfai.
|
|
474
|
-
import {
|
|
475
|
-
import { erc20Abi as erc20Abi2, formatUnits as formatUnits2, isAddress, parseAbi as parseAbi2 } from "viem";
|
|
476
|
-
import { base, arbitrum, mainnet } from "viem/chains";
|
|
762
|
+
// src/agents/zyfai/zyfai.deposit.ts
|
|
763
|
+
import { createWalletClient, custom, encodeFunctionData, erc20Abi as erc20Abi2, isAddress } from "viem";
|
|
477
764
|
|
|
478
765
|
// src/types/config.ts
|
|
479
766
|
var P_SUPPORTED_CHAINS = {
|
|
@@ -522,11 +809,50 @@ var SupportedAssets = [
|
|
|
522
809
|
}
|
|
523
810
|
];
|
|
524
811
|
|
|
525
|
-
// src/
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
812
|
+
// src/agents/zyfai/zyfai.deposit.ts
|
|
813
|
+
async function submitZyfaiDeposit({ sdk, state, chain, amount, asset, smartWallet, assertCurrent, onBeforeSubmission }) {
|
|
814
|
+
const owner = state.walletAddress;
|
|
815
|
+
const provider = state.provider;
|
|
816
|
+
const token = SupportedAssets.find((value) => value.chainId === chain.id && value.symbol === asset);
|
|
817
|
+
if (!token || !isAddress(owner) || !isAddress(smartWallet) || !/^\d+$/.test(amount) || BigInt(amount) <= 0n)
|
|
818
|
+
throw new OwneyError("AGENT_INVALID_RESPONSE", "Invalid Zyfai deposit transfer.", { chainId: chain.id, asset }, "zyfai");
|
|
819
|
+
assertCurrent();
|
|
820
|
+
const prepared = await sdk.prepareEnterPosition({ userAddress: owner, chainId: chain.id, amount, asset, strategy: "aggressive" });
|
|
821
|
+
assertCurrent();
|
|
822
|
+
const expectedData = encodeFunctionData({ abi: erc20Abi2, functionName: "transfer", args: [smartWallet, BigInt(amount)] });
|
|
823
|
+
const transfer = prepared?.transfer;
|
|
824
|
+
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")
|
|
825
|
+
throw new OwneyError("AGENT_INVALID_RESPONSE", "Zyfai prepared a different deposit transfer.", { chainId: chain.id, asset }, "zyfai");
|
|
826
|
+
const wallet = createWalletClient({ account: owner, chain, transport: custom({
|
|
827
|
+
request: async (args) => {
|
|
828
|
+
assertCurrent();
|
|
829
|
+
if (args.method === "eth_sendTransaction") {
|
|
830
|
+
const [accounts, chainId] = await Promise.all([
|
|
831
|
+
provider.request({ method: "eth_accounts" }),
|
|
832
|
+
provider.request({ method: "eth_chainId" })
|
|
833
|
+
]);
|
|
834
|
+
assertCurrent();
|
|
835
|
+
if (!Array.isArray(accounts) || typeof accounts[0] !== "string" || accounts[0].toLowerCase() !== owner.toLowerCase() || Number(chainId) !== chain.id)
|
|
836
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai deposit.", void 0, "zyfai");
|
|
837
|
+
await onBeforeSubmission?.();
|
|
838
|
+
assertCurrent();
|
|
839
|
+
}
|
|
840
|
+
return provider.request(args);
|
|
841
|
+
}
|
|
842
|
+
}, { retryCount: 0 }) });
|
|
843
|
+
return wallet.sendTransaction({ account: owner, chain, to: token.address, data: expectedData, value: 0n });
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// src/agents/zyfai/zyfai.agent.ts
|
|
847
|
+
import { SMART_SESSIONS_VALIDATOR, ZyfaiSDK } from "@zyfai/sdk";
|
|
848
|
+
import { erc20Abi as erc20Abi3, formatUnits as formatUnits2, isAddress as isAddress2, parseAbi as parseAbi2 } from "viem";
|
|
849
|
+
import { base, arbitrum, mainnet } from "viem/chains";
|
|
850
|
+
|
|
851
|
+
// src/lib/debug.ts
|
|
852
|
+
var configuredDebug = false;
|
|
853
|
+
function setOwneyDebug(enabled) {
|
|
854
|
+
configuredDebug = enabled;
|
|
855
|
+
}
|
|
530
856
|
function isOwneyDebug() {
|
|
531
857
|
return globalThis.__OWNEY_DEBUG__ === true || configuredDebug;
|
|
532
858
|
}
|
|
@@ -581,13 +907,6 @@ function tokenDecimals(symbol, explicit) {
|
|
|
581
907
|
return explicit;
|
|
582
908
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
583
909
|
}
|
|
584
|
-
function mapDeposit(raw2) {
|
|
585
|
-
return {
|
|
586
|
-
txHash: raw2.txHash,
|
|
587
|
-
smartWallet: raw2.smartWallet,
|
|
588
|
-
amount: raw2.amount
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
910
|
function mapWithdraw(raw2) {
|
|
592
911
|
return {
|
|
593
912
|
txHash: raw2.txHash,
|
|
@@ -1490,6 +1809,14 @@ var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
|
|
|
1490
1809
|
var EARNINGS_REFRESH_COOLDOWN_MS = 60 * 60 * 1e3;
|
|
1491
1810
|
var WITHDRAW_BALANCE_TIMEOUT_MS = 2e3;
|
|
1492
1811
|
var ZyfaiAgent = class _ZyfaiAgent {
|
|
1812
|
+
constructor(apiKey, rpcUrls, referralSource) {
|
|
1813
|
+
this.apiKey = apiKey;
|
|
1814
|
+
this.referralSource = referralSource;
|
|
1815
|
+
this.rpcUrls = resolveRpcUrls(rpcUrls);
|
|
1816
|
+
this.sdk = this.createSdk();
|
|
1817
|
+
}
|
|
1818
|
+
apiKey;
|
|
1819
|
+
referralSource;
|
|
1493
1820
|
id = "zyfai";
|
|
1494
1821
|
supportedChainIds = [8453, 42161, 1];
|
|
1495
1822
|
supportedAssets = [
|
|
@@ -1534,6 +1861,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1534
1861
|
*/
|
|
1535
1862
|
sessionKeyAttempts = /* @__PURE__ */ new Set();
|
|
1536
1863
|
connectAuthPromise = null;
|
|
1864
|
+
connectAuthScope = null;
|
|
1865
|
+
connectionGeneration = 0;
|
|
1866
|
+
sdkHasConnected = false;
|
|
1537
1867
|
rpcUrls;
|
|
1538
1868
|
publicClients = /* @__PURE__ */ new Map();
|
|
1539
1869
|
// Receipt-derived amounts and log identities, scoped to the owner and asset.
|
|
@@ -1544,17 +1874,20 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1544
1874
|
earningsSnapshot = null;
|
|
1545
1875
|
earningsGeneration = 0;
|
|
1546
1876
|
balanceSnapshots = /* @__PURE__ */ new WeakMap();
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1877
|
+
/** A replaced wallet session gets its own vendor client. In-flight calls
|
|
1878
|
+
* keep their original client; authentication must never change owners under
|
|
1879
|
+
* an operation that is waiting for provider reads or an HTTP response. */
|
|
1880
|
+
createSdk() {
|
|
1881
|
+
const sdk = new ZyfaiSDK({
|
|
1882
|
+
apiKey: this.apiKey,
|
|
1551
1883
|
rpcUrls: this.rpcUrls,
|
|
1552
1884
|
// Forwarded to /auth/login by the SDK's own SIWE handshake, so
|
|
1553
1885
|
// attribution survives without the AppKit SIWX config (which carried it
|
|
1554
1886
|
// via `createOwneySIWX({ referralSource })` when apps used the modal).
|
|
1555
|
-
referralSource
|
|
1887
|
+
referralSource: this.referralSource
|
|
1556
1888
|
});
|
|
1557
|
-
this.installAuthCache();
|
|
1889
|
+
this.installAuthCache(sdk);
|
|
1890
|
+
return sdk;
|
|
1558
1891
|
}
|
|
1559
1892
|
getPublicClient(chainId) {
|
|
1560
1893
|
const cached = this.publicClients.get(chainId);
|
|
@@ -1653,9 +1986,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1653
1986
|
* No-op on the server (no `window`) — the SDK is constructed during SSR
|
|
1654
1987
|
* for some apps and the patch only matters in the browser anyway.
|
|
1655
1988
|
*/
|
|
1656
|
-
installAuthCache() {
|
|
1989
|
+
installAuthCache(sdk = this.sdk) {
|
|
1657
1990
|
if (typeof window === "undefined") return;
|
|
1658
|
-
const internals =
|
|
1991
|
+
const internals = sdk;
|
|
1659
1992
|
const original = internals.authenticateUser.bind(internals);
|
|
1660
1993
|
internals.authenticateUser = async () => {
|
|
1661
1994
|
if (internals.authenticatedUserId !== null) return;
|
|
@@ -1700,25 +2033,41 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1700
2033
|
/** Light connect: SIWE auth only. Re-auths if chain changed. */
|
|
1701
2034
|
async connectAuth(state, chainId) {
|
|
1702
2035
|
const validChainId = isValidChainId(chainId);
|
|
1703
|
-
|
|
2036
|
+
const owner = state.walletAddress.toLowerCase();
|
|
2037
|
+
const scope = `${owner}:${validChainId}`;
|
|
2038
|
+
if (this.connectedAddress?.toLowerCase() === owner && this.connectedChainId === validChainId) {
|
|
1704
2039
|
return;
|
|
1705
2040
|
}
|
|
1706
|
-
if (this.connectAuthPromise) {
|
|
2041
|
+
if (this.connectAuthPromise && this.connectAuthScope === scope) {
|
|
1707
2042
|
await this.connectAuthPromise;
|
|
1708
|
-
return;
|
|
2043
|
+
return this.connectAuth(state, chainId);
|
|
1709
2044
|
}
|
|
1710
|
-
this.
|
|
2045
|
+
if (this.sdkHasConnected || this.connectedAddress) this.sdk = this.createSdk();
|
|
2046
|
+
this.sdkHasConnected = true;
|
|
2047
|
+
const executionSdk = this.sdk;
|
|
2048
|
+
const generation = ++this.connectionGeneration;
|
|
2049
|
+
this.connectedAddress = null;
|
|
2050
|
+
this.connectedChainId = null;
|
|
2051
|
+
const pending = (async () => {
|
|
1711
2052
|
const address = await this.connectAccountWithRetry(
|
|
1712
2053
|
state.provider,
|
|
1713
|
-
validChainId
|
|
2054
|
+
validChainId,
|
|
2055
|
+
executionSdk
|
|
1714
2056
|
);
|
|
2057
|
+
if (generation !== this.connectionGeneration || address.toLowerCase() !== owner)
|
|
2058
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while connecting Zyfai.", void 0, this.id);
|
|
1715
2059
|
this.connectedAddress = address;
|
|
1716
2060
|
this.connectedChainId = validChainId;
|
|
1717
2061
|
})();
|
|
2062
|
+
this.connectAuthPromise = pending;
|
|
2063
|
+
this.connectAuthScope = scope;
|
|
1718
2064
|
try {
|
|
1719
|
-
await
|
|
2065
|
+
await pending;
|
|
1720
2066
|
} finally {
|
|
1721
|
-
this.connectAuthPromise
|
|
2067
|
+
if (this.connectAuthPromise === pending) {
|
|
2068
|
+
this.connectAuthPromise = null;
|
|
2069
|
+
this.connectAuthScope = null;
|
|
2070
|
+
}
|
|
1722
2071
|
}
|
|
1723
2072
|
}
|
|
1724
2073
|
/**
|
|
@@ -1730,16 +2079,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1730
2079
|
* JWT from a misconfigured API key; we retry once either way, and if the
|
|
1731
2080
|
* second attempt still fails the original error propagates.
|
|
1732
2081
|
*/
|
|
1733
|
-
async connectAccountWithRetry(provider, chainId) {
|
|
2082
|
+
async connectAccountWithRetry(provider, chainId, executionSdk = this.sdk) {
|
|
1734
2083
|
try {
|
|
1735
|
-
return await
|
|
2084
|
+
return await executionSdk.connectAccount(provider, chainId);
|
|
1736
2085
|
} catch (error) {
|
|
1737
2086
|
const message = error instanceof Error ? error.message : String(error);
|
|
1738
2087
|
if (!message.toLowerCase().includes("unauthorized")) throw error;
|
|
1739
|
-
const internals =
|
|
2088
|
+
const internals = executionSdk;
|
|
1740
2089
|
const address = internals.walletClient?.account?.address;
|
|
1741
2090
|
if (address) clearSession(address, chainId);
|
|
1742
|
-
return await
|
|
2091
|
+
return await executionSdk.connectAccount(provider, chainId);
|
|
1743
2092
|
}
|
|
1744
2093
|
}
|
|
1745
2094
|
/**
|
|
@@ -1751,11 +2100,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1751
2100
|
* withdrawFunds) fail with "No account connected".
|
|
1752
2101
|
*/
|
|
1753
2102
|
async ensureConnected(state, chainId) {
|
|
2103
|
+
const assertOwner = () => {
|
|
2104
|
+
if (this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== chainId)
|
|
2105
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while connecting Zyfai.", void 0, this.id);
|
|
2106
|
+
};
|
|
1754
2107
|
await this.connectAuth(state, chainId);
|
|
2108
|
+
assertOwner();
|
|
1755
2109
|
const internals = this.sdk;
|
|
1756
2110
|
if (!internals.walletClient || internals.authenticatedUserId === null) {
|
|
1757
2111
|
this.connectedChainId = null;
|
|
1758
2112
|
await this.connectAuth(state, chainId);
|
|
2113
|
+
assertOwner();
|
|
1759
2114
|
}
|
|
1760
2115
|
}
|
|
1761
2116
|
/** A rejected execution session must not be reused by the next foreground attempt. */
|
|
@@ -1816,6 +2171,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1816
2171
|
}
|
|
1817
2172
|
// --- IAgent: Connection lifecycle ---
|
|
1818
2173
|
async disconnect() {
|
|
2174
|
+
const executionSdk = this.sdk;
|
|
2175
|
+
this.connectionGeneration++;
|
|
1819
2176
|
this.earningsGeneration++;
|
|
1820
2177
|
this.earningsReads.clear();
|
|
1821
2178
|
this.earningsRefreshes.clear();
|
|
@@ -1823,10 +2180,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1823
2180
|
if (this.connectedAddress && this.connectedChainId !== null) {
|
|
1824
2181
|
clearSession(this.connectedAddress, this.connectedChainId);
|
|
1825
2182
|
}
|
|
1826
|
-
await this.sdk.disconnectAccount();
|
|
1827
2183
|
this.connectedAddress = null;
|
|
1828
2184
|
this.connectedChainId = null;
|
|
2185
|
+
this.connectAuthPromise = null;
|
|
2186
|
+
this.connectAuthScope = null;
|
|
1829
2187
|
this.smartWalletCache.clear();
|
|
2188
|
+
this.sdk = this.createSdk();
|
|
2189
|
+
this.sdkHasConnected = false;
|
|
2190
|
+
await executionSdk.disconnectAccount();
|
|
1830
2191
|
}
|
|
1831
2192
|
// --- IAgent: Account lifecycle ---
|
|
1832
2193
|
async activateAgent(state, chainId) {
|
|
@@ -2314,94 +2675,91 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2314
2675
|
await this.ensureSessionKey(ownerAddress, chainId, redeployed.address);
|
|
2315
2676
|
return redeployed.address;
|
|
2316
2677
|
}
|
|
2317
|
-
async deposit(state, chainId, amount, asset, depositCallback, onProgress) {
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
const receipt = await this.getPublicClient(validChainId).getTransactionReceipt({ hash: txHash });
|
|
2353
|
-
const token = SupportedAssets.find((token2) => token2.chainId === chainId && token2.symbol === asset);
|
|
2354
|
-
evidence2 = inspectFundingReceipt(receipt, evidence2, token.address, "deposit");
|
|
2355
|
-
} catch {
|
|
2356
|
-
evidence2 = { ...evidence2, transfer: "unknown" };
|
|
2357
|
-
}
|
|
2358
|
-
notifyFunding(onProgress, evidence2);
|
|
2359
|
-
if (evidence2.transfer === "failed")
|
|
2360
|
-
throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
|
|
2361
|
-
try {
|
|
2362
|
-
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
2363
|
-
await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
|
|
2364
|
-
evidence2 = { ...evidence2, registration: "confirmed" };
|
|
2365
|
-
} catch {
|
|
2366
|
-
evidence2 = { ...evidence2, registration: "unknown" };
|
|
2367
|
-
}
|
|
2368
|
-
notifyFunding(onProgress, evidence2);
|
|
2369
|
-
return { txHash, smartWallet, amount, evidence: evidence2 };
|
|
2370
|
-
}
|
|
2371
|
-
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
2372
|
-
const raw2 = await this.sdk.sendDeposit(
|
|
2373
|
-
this.getAddress(),
|
|
2374
|
-
validChainId,
|
|
2375
|
-
amount,
|
|
2376
|
-
asset,
|
|
2377
|
-
"aggressive"
|
|
2378
|
-
);
|
|
2379
|
-
let evidence = {
|
|
2678
|
+
async deposit(state, chainId, amount, asset, depositCallback, onProgress, onBeforeSubmission) {
|
|
2679
|
+
const validChainId = isValidChainId(chainId);
|
|
2680
|
+
const owner = state.walletAddress;
|
|
2681
|
+
const provider = state.provider;
|
|
2682
|
+
await this.ensureConnected(state, validChainId);
|
|
2683
|
+
const executionSdk = this.sdk;
|
|
2684
|
+
const generation = this.connectionGeneration;
|
|
2685
|
+
const assertCurrent = () => {
|
|
2686
|
+
const account = executionSdk.walletClient?.account?.address;
|
|
2687
|
+
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())
|
|
2688
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai deposit.", void 0, this.id);
|
|
2689
|
+
};
|
|
2690
|
+
assertCurrent();
|
|
2691
|
+
const smartWallet = await this.ensureWalletDeployed(owner, validChainId);
|
|
2692
|
+
assertCurrent();
|
|
2693
|
+
notifyFunding(onProgress, {
|
|
2694
|
+
agentId: this.id,
|
|
2695
|
+
chainId,
|
|
2696
|
+
token: asset,
|
|
2697
|
+
walletAddress: owner,
|
|
2698
|
+
agentWallet: smartWallet,
|
|
2699
|
+
intendedAmount: amount,
|
|
2700
|
+
transfer: "pending",
|
|
2701
|
+
registration: "pending",
|
|
2702
|
+
delivery: "not-applicable"
|
|
2703
|
+
});
|
|
2704
|
+
const txHash = depositCallback ? await depositCallback(smartWallet, validChainId, amount) : await submitZyfaiDeposit({
|
|
2705
|
+
sdk: executionSdk,
|
|
2706
|
+
state: { ...state, walletAddress: owner, provider },
|
|
2707
|
+
chain: VIEM_CHAIN[validChainId],
|
|
2708
|
+
amount,
|
|
2709
|
+
asset,
|
|
2710
|
+
smartWallet,
|
|
2711
|
+
assertCurrent,
|
|
2712
|
+
onBeforeSubmission: onBeforeSubmission ? () => onBeforeSubmission({
|
|
2380
2713
|
agentId: this.id,
|
|
2381
2714
|
chainId,
|
|
2382
2715
|
token: asset,
|
|
2383
|
-
walletAddress:
|
|
2384
|
-
agentWallet:
|
|
2716
|
+
walletAddress: owner,
|
|
2717
|
+
agentWallet: smartWallet,
|
|
2385
2718
|
intendedAmount: amount,
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
registration: raw2.registration ? "confirmed" : "unknown",
|
|
2719
|
+
transfer: "pending",
|
|
2720
|
+
registration: "pending",
|
|
2389
2721
|
delivery: "not-applicable"
|
|
2390
|
-
}
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2722
|
+
}) : void 0
|
|
2723
|
+
});
|
|
2724
|
+
let evidence = {
|
|
2725
|
+
agentId: this.id,
|
|
2726
|
+
chainId,
|
|
2727
|
+
token: asset,
|
|
2728
|
+
walletAddress: owner,
|
|
2729
|
+
agentWallet: smartWallet,
|
|
2730
|
+
intendedAmount: amount,
|
|
2731
|
+
txHash,
|
|
2732
|
+
transfer: "pending",
|
|
2733
|
+
registration: "pending",
|
|
2734
|
+
delivery: "not-applicable"
|
|
2735
|
+
};
|
|
2736
|
+
notifyFunding(onProgress, evidence);
|
|
2737
|
+
try {
|
|
2738
|
+
const receipt = await this.getPublicClient(validChainId).waitForTransactionReceipt({
|
|
2739
|
+
hash: txHash,
|
|
2740
|
+
confirmations: 1,
|
|
2741
|
+
timeout: 9e4,
|
|
2742
|
+
checkReplacement: false
|
|
2743
|
+
});
|
|
2744
|
+
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
2745
|
+
evidence = inspectFundingReceipt(receipt, evidence, token.address, "deposit");
|
|
2746
|
+
} catch {
|
|
2747
|
+
evidence = { ...evidence, transfer: "unknown" };
|
|
2748
|
+
}
|
|
2749
|
+
notifyFunding(onProgress, evidence);
|
|
2750
|
+
if (evidence.transfer === "failed")
|
|
2751
|
+
throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
|
|
2752
|
+
if (evidence.transfer !== "confirmed") return { txHash, smartWallet, amount, evidence };
|
|
2753
|
+
try {
|
|
2754
|
+
assertCurrent();
|
|
2755
|
+
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
2756
|
+
await (tokenAddress ? executionSdk.logDeposit(validChainId, txHash, amount, tokenAddress) : executionSdk.logDeposit(validChainId, txHash, amount));
|
|
2757
|
+
evidence = { ...evidence, registration: "confirmed" };
|
|
2758
|
+
} catch {
|
|
2759
|
+
evidence = { ...evidence, registration: "unknown" };
|
|
2404
2760
|
}
|
|
2761
|
+
notifyFunding(onProgress, evidence);
|
|
2762
|
+
return { txHash, smartWallet, amount, evidence };
|
|
2405
2763
|
}
|
|
2406
2764
|
// --- IAgent: Fund operations ---
|
|
2407
2765
|
/** Only after a definitive endpoint rejection, combine a new provider
|
|
@@ -2431,7 +2789,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2431
2789
|
this.sdk.getPortfolio(owner).catch(() => void 0)
|
|
2432
2790
|
]);
|
|
2433
2791
|
assertScope();
|
|
2434
|
-
if (!
|
|
2792
|
+
if (!isAddress2(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
|
|
2435
2793
|
throw new OwneyError(
|
|
2436
2794
|
"AGENT_AUTH_FAILED",
|
|
2437
2795
|
"Could not verify Zyfai's withdrawal wallet.",
|
|
@@ -2442,7 +2800,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2442
2800
|
const asset = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === token);
|
|
2443
2801
|
const idle = asset ? await this.getPublicClient(chainId).readContract({
|
|
2444
2802
|
address: asset.address,
|
|
2445
|
-
abi:
|
|
2803
|
+
abi: erc20Abi3,
|
|
2446
2804
|
functionName: "balanceOf",
|
|
2447
2805
|
args: [wallet.address]
|
|
2448
2806
|
}).catch(() => void 0) : void 0;
|
|
@@ -2471,11 +2829,12 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2471
2829
|
/** Called only after the execution endpoint explicitly rejected a request.
|
|
2472
2830
|
* A timeout or an accepted request never authorizes repair and resubmission. */
|
|
2473
2831
|
async recoverWithdrawalSession(state, chainId, token, owner) {
|
|
2832
|
+
const generation = this.connectionGeneration;
|
|
2474
2833
|
await this.assertNotEmptyRejectedWithdrawal(state, chainId, token, owner);
|
|
2475
2834
|
const internals = this.sdk;
|
|
2476
2835
|
const assertScope = () => {
|
|
2477
2836
|
const account = internals.walletClient?.account?.address;
|
|
2478
|
-
if (state.walletAddress.toLowerCase() !== owner.toLowerCase() || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
|
|
2837
|
+
if (generation !== this.connectionGeneration || state.walletAddress.toLowerCase() !== owner.toLowerCase() || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
|
|
2479
2838
|
throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai withdrawal.", void 0, this.id);
|
|
2480
2839
|
};
|
|
2481
2840
|
assertScope();
|
|
@@ -2504,6 +2863,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2504
2863
|
}
|
|
2505
2864
|
}
|
|
2506
2865
|
async withdraw(state, chainId, token, amount, onProgress) {
|
|
2866
|
+
if (amount !== void 0 && (typeof amount !== "string" || !/^[0-9]+$/.test(amount) || BigInt(amount) <= 0n))
|
|
2867
|
+
throw new OwneyError("WITHDRAW_INVALID_AMOUNT", "Withdrawal amount must be a positive integer in token base units.", void 0, this.id);
|
|
2507
2868
|
const validChainId = isValidChainId(chainId);
|
|
2508
2869
|
try {
|
|
2509
2870
|
await this.ensureConnected(state, validChainId);
|
|
@@ -2520,16 +2881,40 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2520
2881
|
});
|
|
2521
2882
|
throw error;
|
|
2522
2883
|
}
|
|
2523
|
-
|
|
2524
|
-
const
|
|
2525
|
-
const
|
|
2884
|
+
const executionSdk = this.sdk;
|
|
2885
|
+
const internals = executionSdk;
|
|
2886
|
+
const generation = this.connectionGeneration;
|
|
2887
|
+
const owner = state.walletAddress;
|
|
2888
|
+
let staleSessionError;
|
|
2889
|
+
const assertCurrent = () => {
|
|
2890
|
+
const account = internals.walletClient?.account?.address;
|
|
2891
|
+
if (generation !== this.connectionGeneration || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== validChainId || state.chainId !== null && state.chainId !== validChainId || account && account.toLowerCase() !== owner.toLowerCase()) {
|
|
2892
|
+
staleSessionError = new OwneyError(
|
|
2893
|
+
"NOT_CONNECTED",
|
|
2894
|
+
"Wallet changed before Zyfai withdrawal submission.",
|
|
2895
|
+
{ phase: "prepare", rejectedBeforeSubmission: true },
|
|
2896
|
+
this.id
|
|
2897
|
+
);
|
|
2898
|
+
throw staleSessionError;
|
|
2899
|
+
}
|
|
2900
|
+
};
|
|
2901
|
+
let observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertCurrent);
|
|
2902
|
+
const authToken = internals.httpClient?.authToken;
|
|
2526
2903
|
let raw2;
|
|
2527
2904
|
try {
|
|
2528
2905
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
2529
2906
|
try {
|
|
2530
|
-
|
|
2907
|
+
assertCurrent();
|
|
2908
|
+
raw2 = await executionSdk.withdrawFunds(owner, validChainId, amount, token);
|
|
2531
2909
|
break;
|
|
2532
2910
|
} catch (error) {
|
|
2911
|
+
if (observation.rejected()) {
|
|
2912
|
+
try {
|
|
2913
|
+
assertCurrent();
|
|
2914
|
+
} catch {
|
|
2915
|
+
throw error;
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2533
2918
|
if (!amount && observation.rejected() && observation.request()?.failure === "insufficient-balance") {
|
|
2534
2919
|
try {
|
|
2535
2920
|
await this.assertNotEmptyRejectedWithdrawal(state, validChainId, token, owner);
|
|
@@ -2547,7 +2932,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2547
2932
|
);
|
|
2548
2933
|
await this.recoverWithdrawalSession(state, validChainId, token, owner);
|
|
2549
2934
|
observation.stop();
|
|
2550
|
-
observation = observeWithdrawalSubmission(
|
|
2935
|
+
observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertCurrent);
|
|
2551
2936
|
}
|
|
2552
2937
|
}
|
|
2553
2938
|
if (!raw2) throw new OwneyError(
|
|
@@ -2557,6 +2942,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2557
2942
|
this.id
|
|
2558
2943
|
);
|
|
2559
2944
|
} catch (error) {
|
|
2945
|
+
if (staleSessionError && observation.attempted() === false) error = staleSessionError;
|
|
2560
2946
|
this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken);
|
|
2561
2947
|
if (observation.attempted() !== void 0) {
|
|
2562
2948
|
const failed = observation.attempted() === false || observation.rejected();
|
|
@@ -2577,7 +2963,6 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2577
2963
|
observation.stop();
|
|
2578
2964
|
}
|
|
2579
2965
|
if (!raw2.success) {
|
|
2580
|
-
const unresolved = !!raw2.txHash || /already|pending|queued|in.progress/i.test(raw2.message ?? "");
|
|
2581
2966
|
notifyFunding(onProgress, {
|
|
2582
2967
|
agentId: this.id,
|
|
2583
2968
|
chainId,
|
|
@@ -2585,9 +2970,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2585
2970
|
walletAddress: state.walletAddress,
|
|
2586
2971
|
intendedAmount: amount ?? "MAX",
|
|
2587
2972
|
txHash: raw2.txHash,
|
|
2588
|
-
transfer:
|
|
2973
|
+
transfer: "unknown",
|
|
2589
2974
|
registration: "not-applicable",
|
|
2590
|
-
delivery:
|
|
2975
|
+
delivery: "unknown",
|
|
2591
2976
|
withdrawalRequest: observation.request()
|
|
2592
2977
|
});
|
|
2593
2978
|
throw new OwneyError(
|
|
@@ -2614,8 +2999,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2614
2999
|
}
|
|
2615
3000
|
/** Registration recovery never signs, transfers, activates, or refreshes authentication. */
|
|
2616
3001
|
async retryDepositRegistration(state, evidence) {
|
|
2617
|
-
const
|
|
2618
|
-
|
|
3002
|
+
const executionSdk = this.sdk;
|
|
3003
|
+
const internals = executionSdk;
|
|
3004
|
+
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) {
|
|
2619
3005
|
throw new OwneyError("NOT_CONNECTED", "Reconnect the wallet before recovering deposit registration.");
|
|
2620
3006
|
}
|
|
2621
3007
|
if (evidence.transfer !== "confirmed" || !evidence.txHash) {
|
|
@@ -2623,16 +3009,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2623
3009
|
}
|
|
2624
3010
|
const cid = isValidChainId(evidence.chainId);
|
|
2625
3011
|
const tokenAddress = evidence.token === "WETH" ? WETH_ADDRESS_BY_CHAIN[cid] : void 0;
|
|
2626
|
-
await
|
|
3012
|
+
await executionSdk.logDeposit(cid, evidence.txHash, evidence.intendedAmount, tokenAddress);
|
|
2627
3013
|
return { ...evidence, registration: "confirmed" };
|
|
2628
3014
|
}
|
|
2629
3015
|
// --- IAgent: Portfolio reads ---
|
|
2630
3016
|
async getFundingWallet(state, chainId) {
|
|
2631
3017
|
return (await this.sdk.getSmartWalletAddress(state.walletAddress, isValidChainId(chainId))).address;
|
|
2632
3018
|
}
|
|
2633
|
-
/** A positive Safe balance is a
|
|
2634
|
-
*
|
|
2635
|
-
*
|
|
3019
|
+
/** A positive deployed Safe balance is a conservative planning floor. Zero
|
|
3020
|
+
* requires both the mapped Safe's exact idle balance and a fresh complete
|
|
3021
|
+
* empty portfolio, including for an undeployed counterfactual Safe. */
|
|
2636
3022
|
async getWithdrawableBalance(state, chainId, token) {
|
|
2637
3023
|
const validChainId = isValidChainId(chainId);
|
|
2638
3024
|
const asset = SupportedAssets.find((value) => value.chainId === validChainId && value.symbol === token);
|
|
@@ -2661,20 +3047,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2661
3047
|
this.sdk.getSmartWalletByEOA(owner)
|
|
2662
3048
|
]);
|
|
2663
3049
|
assertScope();
|
|
2664
|
-
if (!
|
|
3050
|
+
if (!isAddress2(wallet.address) || wallet.isDeployed !== false && (wallet.isDeployed !== true || wallet.isOwner !== true) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase()) throw unavailable();
|
|
2665
3051
|
const client = this.getPublicClient(validChainId);
|
|
2666
3052
|
const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
|
|
2667
3053
|
assertScope();
|
|
2668
3054
|
const idle = await client.readContract({
|
|
2669
3055
|
address: asset.address,
|
|
2670
|
-
abi:
|
|
3056
|
+
abi: erc20Abi3,
|
|
2671
3057
|
functionName: "balanceOf",
|
|
2672
3058
|
args: [wallet.address],
|
|
2673
3059
|
blockNumber
|
|
2674
3060
|
});
|
|
2675
3061
|
assertScope();
|
|
2676
|
-
if (typeof idle !== "bigint" || idle
|
|
2677
|
-
|
|
3062
|
+
if (typeof idle !== "bigint" || idle < 0n) throw unavailable();
|
|
3063
|
+
if (idle > 0n) {
|
|
3064
|
+
if (wallet.isDeployed !== true || wallet.isOwner !== true) throw unavailable();
|
|
3065
|
+
return idle;
|
|
3066
|
+
}
|
|
3067
|
+
const portfolio = await this.sdk.getPortfolio(owner);
|
|
3068
|
+
assertScope();
|
|
3069
|
+
if (!hasEmptyWithdrawalSnapshot(portfolio, validChainId, token)) throw unavailable();
|
|
3070
|
+
return 0n;
|
|
2678
3071
|
};
|
|
2679
3072
|
try {
|
|
2680
3073
|
return await Promise.race([read2(), new Promise((_, reject) => {
|
|
@@ -2717,7 +3110,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2717
3110
|
if (blockNumber < BigInt(evidence.blockNumber)) return void 0;
|
|
2718
3111
|
const idle = await client.readContract({
|
|
2719
3112
|
address: asset.address,
|
|
2720
|
-
abi:
|
|
3113
|
+
abi: erc20Abi3,
|
|
2721
3114
|
functionName: "balanceOf",
|
|
2722
3115
|
args: [balance.smartWallet],
|
|
2723
3116
|
blockNumber
|
|
@@ -2908,275 +3301,41 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2908
3301
|
await this.sdk.updateUserProfile(update);
|
|
2909
3302
|
return true;
|
|
2910
3303
|
}
|
|
2911
|
-
// --- IAgent: Discovery (no wallet required) ---
|
|
2912
|
-
async getAgentApy(days, options) {
|
|
2913
|
-
const raw2 = await this.sdk.getAPYPerStrategy(
|
|
2914
|
-
false,
|
|
2915
|
-
DayFilterMapping[days],
|
|
2916
|
-
"aggressive",
|
|
2917
|
-
options?.chainId,
|
|
2918
|
-
options?.tokenSymbol
|
|
2919
|
-
);
|
|
2920
|
-
return mapApyByStrategy(raw2);
|
|
2921
|
-
}
|
|
2922
|
-
};
|
|
2923
|
-
|
|
2924
|
-
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2925
|
-
import {
|
|
2926
|
-
createPublicClient as createPublicClient3,
|
|
2927
|
-
createWalletClient as createWalletClient3,
|
|
2928
|
-
custom as custom3,
|
|
2929
|
-
decodeEventLog as decodeEventLog2,
|
|
2930
|
-
encodeEventTopics,
|
|
2931
|
-
encodeFunctionData,
|
|
2932
|
-
erc20Abi as erc20Abi4,
|
|
2933
|
-
erc4626Abi as erc4626Abi2,
|
|
2934
|
-
getAddress as getAddress5,
|
|
2935
|
-
isAddress as isAddress3,
|
|
2936
|
-
parseUnits
|
|
2937
|
-
} from "viem";
|
|
2938
|
-
import { base as base4 } from "viem/chains";
|
|
2939
|
-
|
|
2940
|
-
// src/lib/chain-guard.ts
|
|
2941
|
-
var CHAIN_NAMES = {
|
|
2942
|
-
1: "Ethereum",
|
|
2943
|
-
8453: "Base",
|
|
2944
|
-
42161: "Arbitrum"
|
|
2945
|
-
};
|
|
2946
|
-
function chainName(chainId) {
|
|
2947
|
-
return CHAIN_NAMES[chainId] ?? `chain ${chainId}`;
|
|
2948
|
-
}
|
|
2949
|
-
async function ensureWalletOnChain(pub, wallet, expected) {
|
|
2950
|
-
const actual = await pub.getChainId();
|
|
2951
|
-
if (actual === expected) return;
|
|
2952
|
-
try {
|
|
2953
|
-
await wallet.switchChain({ id: expected });
|
|
2954
|
-
} catch (error) {
|
|
2955
|
-
throw new OwneyError(
|
|
2956
|
-
"CHAIN_MISMATCH",
|
|
2957
|
-
`Your wallet is on ${chainName(actual)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2958
|
-
{
|
|
2959
|
-
expectedChainId: expected,
|
|
2960
|
-
actualChainId: actual,
|
|
2961
|
-
cause: error instanceof Error ? error.message : String(error)
|
|
2962
|
-
}
|
|
2963
|
-
);
|
|
2964
|
-
}
|
|
2965
|
-
const after = await pub.getChainId();
|
|
2966
|
-
if (after !== expected) {
|
|
2967
|
-
throw new OwneyError(
|
|
2968
|
-
"CHAIN_MISMATCH",
|
|
2969
|
-
`Your wallet is still on ${chainName(after)}. Switch it to ${chainName(expected)} and try again.`,
|
|
2970
|
-
{ expectedChainId: expected, actualChainId: after }
|
|
2971
|
-
);
|
|
2972
|
-
}
|
|
2973
|
-
}
|
|
2974
|
-
|
|
2975
|
-
// src/lib/transfer-auth.ts
|
|
2976
|
-
import { bytesToHex } from "viem";
|
|
2977
|
-
|
|
2978
|
-
// src/lib/sponsor-client.ts
|
|
2979
|
-
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2980
|
-
async function postPaymasterIntent(input) {
|
|
2981
|
-
const base6 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2982
|
-
let res;
|
|
2983
|
-
try {
|
|
2984
|
-
res = await fetch(`${base6}/api/v1/sponsor/paymaster-intent`, {
|
|
2985
|
-
method: "POST",
|
|
2986
|
-
headers: {
|
|
2987
|
-
"content-type": "application/json",
|
|
2988
|
-
"x-owney-api-key": input.apiKey,
|
|
2989
|
-
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
2990
|
-
},
|
|
2991
|
-
body: JSON.stringify(input.body)
|
|
2992
|
-
});
|
|
2993
|
-
} catch (networkError) {
|
|
2994
|
-
throw new OwneyError(
|
|
2995
|
-
"SPONSOR_REQUEST_FAILED",
|
|
2996
|
-
`Paymaster intent network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
2997
|
-
{ cause: String(networkError), safeToFallback: true }
|
|
2998
|
-
);
|
|
2999
|
-
}
|
|
3000
|
-
const text = await res.text();
|
|
3001
|
-
let parsed = null;
|
|
3002
|
-
try {
|
|
3003
|
-
parsed = JSON.parse(text);
|
|
3004
|
-
} catch {
|
|
3005
|
-
}
|
|
3006
|
-
if (!res.ok || !parsed?.success || typeof parsed.data?.intent !== "string" || typeof parsed.data.expiresAt !== "number") {
|
|
3007
|
-
throw new OwneyError(
|
|
3008
|
-
"SPONSOR_REQUEST_FAILED",
|
|
3009
|
-
`Paymaster intent API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
3010
|
-
{
|
|
3011
|
-
statusCode: res.status,
|
|
3012
|
-
responseBody: text.slice(0, 500),
|
|
3013
|
-
safeToFallback: true
|
|
3014
|
-
}
|
|
3015
|
-
);
|
|
3016
|
-
}
|
|
3017
|
-
return parsed.data;
|
|
3018
|
-
}
|
|
3019
|
-
async function getSponsorRelayerAddress(input) {
|
|
3020
|
-
const base6 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
3021
|
-
let res;
|
|
3022
|
-
try {
|
|
3023
|
-
res = await fetch(
|
|
3024
|
-
`${base6}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
3025
|
-
{
|
|
3026
|
-
headers: { "x-owney-api-key": input.apiKey }
|
|
3027
|
-
}
|
|
3028
|
-
);
|
|
3029
|
-
} catch (networkError) {
|
|
3030
|
-
throw new OwneyError(
|
|
3031
|
-
"SPONSOR_REQUEST_FAILED",
|
|
3032
|
-
`Sponsor API network error: ${networkError instanceof Error ? networkError.message : String(networkError)}`,
|
|
3033
|
-
{ cause: String(networkError), safeToFallback: true }
|
|
3034
|
-
);
|
|
3035
|
-
}
|
|
3036
|
-
const text = await res.text();
|
|
3037
|
-
let parsed = null;
|
|
3038
|
-
try {
|
|
3039
|
-
parsed = JSON.parse(text);
|
|
3040
|
-
} catch {
|
|
3041
|
-
}
|
|
3042
|
-
if (!res.ok || !parsed?.success || !parsed.data?.relayer) {
|
|
3043
|
-
throw new OwneyError(
|
|
3044
|
-
"SPONSOR_REQUEST_FAILED",
|
|
3045
|
-
`Sponsor API error ${res.status}: ${parsed?.message ?? text.slice(0, 500)}`,
|
|
3046
|
-
{
|
|
3047
|
-
statusCode: res.status,
|
|
3048
|
-
responseBody: text.slice(0, 500),
|
|
3049
|
-
safeToFallback: true
|
|
3050
|
-
}
|
|
3051
|
-
);
|
|
3052
|
-
}
|
|
3053
|
-
return parsed.data.relayer;
|
|
3054
|
-
}
|
|
3055
|
-
async function postSponsorBatchTransfer(input) {
|
|
3056
|
-
let res;
|
|
3057
|
-
try {
|
|
3058
|
-
res = await fetch(
|
|
3059
|
-
`${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch`,
|
|
3060
|
-
{
|
|
3061
|
-
method: "POST",
|
|
3062
|
-
headers: {
|
|
3063
|
-
"content-type": "application/json",
|
|
3064
|
-
"x-owney-api-key": input.apiKey
|
|
3065
|
-
},
|
|
3066
|
-
body: JSON.stringify(input.body)
|
|
3067
|
-
}
|
|
3068
|
-
);
|
|
3069
|
-
} catch {
|
|
3070
|
-
throw new OwneyError(
|
|
3071
|
-
"SPONSOR_REQUEST_FAILED",
|
|
3072
|
-
"Deposit status is unknown. Retry the same amount to check it.",
|
|
3073
|
-
{ safeToFallback: false }
|
|
3074
|
-
);
|
|
3075
|
-
}
|
|
3076
|
-
const parsed = await res.json().catch(() => null);
|
|
3077
|
-
if (!res.ok || !parsed?.success || !/^0x[0-9a-fA-F]{64}$/.test(parsed.data?.txHash ?? "")) {
|
|
3078
|
-
throw new OwneyError(
|
|
3079
|
-
"SPONSOR_REQUEST_FAILED",
|
|
3080
|
-
"Deposit could not be confirmed. Retry the same amount to check its status.",
|
|
3081
|
-
{
|
|
3082
|
-
statusCode: res.status,
|
|
3083
|
-
safeToFallback: false,
|
|
3084
|
-
notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
|
|
3085
|
-
}
|
|
3086
|
-
);
|
|
3087
|
-
}
|
|
3088
|
-
return parsed.data;
|
|
3089
|
-
}
|
|
3090
|
-
|
|
3091
|
-
// src/lib/permit2.ts
|
|
3092
|
-
import { bytesToHex as bytesToHex2 } from "viem";
|
|
3093
|
-
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
3094
|
-
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
3095
|
-
function permit2ApprovalAmount(requiredAmount) {
|
|
3096
|
-
if (requiredAmount <= 0n) {
|
|
3097
|
-
throw new Error("Permit2 approval requires a positive deposit amount");
|
|
3098
|
-
}
|
|
3099
|
-
return MAX_UINT256;
|
|
3100
|
-
}
|
|
3101
|
-
var ERC20_ALLOWANCE_ABI = [
|
|
3102
|
-
{
|
|
3103
|
-
type: "function",
|
|
3104
|
-
name: "allowance",
|
|
3105
|
-
stateMutability: "view",
|
|
3106
|
-
inputs: [
|
|
3107
|
-
{ name: "owner", type: "address" },
|
|
3108
|
-
{ name: "spender", type: "address" }
|
|
3109
|
-
],
|
|
3110
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
3111
|
-
},
|
|
3112
|
-
{
|
|
3113
|
-
type: "function",
|
|
3114
|
-
name: "approve",
|
|
3115
|
-
stateMutability: "nonpayable",
|
|
3116
|
-
inputs: [
|
|
3117
|
-
{ name: "spender", type: "address" },
|
|
3118
|
-
{ name: "amount", type: "uint256" }
|
|
3119
|
-
],
|
|
3120
|
-
outputs: [{ name: "", type: "bool" }]
|
|
3121
|
-
},
|
|
3122
|
-
{
|
|
3123
|
-
type: "function",
|
|
3124
|
-
name: "balanceOf",
|
|
3125
|
-
stateMutability: "view",
|
|
3126
|
-
inputs: [{ name: "account", type: "address" }],
|
|
3127
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
3128
|
-
}
|
|
3129
|
-
];
|
|
3130
|
-
function randomPermit2Nonce() {
|
|
3131
|
-
const bytes = new Uint8Array(32);
|
|
3132
|
-
globalThis.crypto.getRandomValues(bytes);
|
|
3133
|
-
return BigInt(bytesToHex2(bytes));
|
|
3134
|
-
}
|
|
3135
|
-
async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
|
|
3136
|
-
return publicClient.readContract({
|
|
3137
|
-
address: token,
|
|
3138
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
3139
|
-
functionName: "allowance",
|
|
3140
|
-
args: [owner, PERMIT2_ADDRESS],
|
|
3141
|
-
...blockNumber === void 0 ? {} : { blockNumber }
|
|
3142
|
-
});
|
|
3143
|
-
}
|
|
3144
|
-
async function readErc20Balance(publicClient, token, owner) {
|
|
3145
|
-
return publicClient.readContract({
|
|
3146
|
-
address: token,
|
|
3147
|
-
abi: ERC20_ALLOWANCE_ABI,
|
|
3148
|
-
functionName: "balanceOf",
|
|
3149
|
-
args: [owner]
|
|
3150
|
-
});
|
|
3151
|
-
}
|
|
3304
|
+
// --- IAgent: Discovery (no wallet required) ---
|
|
3305
|
+
async getAgentApy(days, options) {
|
|
3306
|
+
const raw2 = await this.sdk.getAPYPerStrategy(
|
|
3307
|
+
false,
|
|
3308
|
+
DayFilterMapping[days],
|
|
3309
|
+
"aggressive",
|
|
3310
|
+
options?.chainId,
|
|
3311
|
+
options?.tokenSymbol
|
|
3312
|
+
);
|
|
3313
|
+
return mapApyByStrategy(raw2);
|
|
3314
|
+
}
|
|
3315
|
+
};
|
|
3152
3316
|
|
|
3153
|
-
// src/
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
nextVerification = context;
|
|
3169
|
-
}
|
|
3170
|
-
});
|
|
3171
|
-
return callback;
|
|
3172
|
-
}
|
|
3317
|
+
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
3318
|
+
import {
|
|
3319
|
+
createPublicClient as createPublicClient3,
|
|
3320
|
+
createWalletClient as createWalletClient4,
|
|
3321
|
+
custom as custom4,
|
|
3322
|
+
decodeEventLog as decodeEventLog2,
|
|
3323
|
+
encodeEventTopics,
|
|
3324
|
+
encodeFunctionData as encodeFunctionData2,
|
|
3325
|
+
erc20Abi as erc20Abi5,
|
|
3326
|
+
erc4626Abi as erc4626Abi2,
|
|
3327
|
+
getAddress as getAddress5,
|
|
3328
|
+
isAddress as isAddress4,
|
|
3329
|
+
parseUnits
|
|
3330
|
+
} from "viem";
|
|
3331
|
+
import { base as base4 } from "viem/chains";
|
|
3173
3332
|
|
|
3174
3333
|
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
3175
3334
|
import { SiweMessage, generateNonce } from "siwe";
|
|
3176
3335
|
import {
|
|
3177
3336
|
createPublicClient as createPublicClient2,
|
|
3178
|
-
createWalletClient,
|
|
3179
|
-
custom,
|
|
3337
|
+
createWalletClient as createWalletClient2,
|
|
3338
|
+
custom as custom2,
|
|
3180
3339
|
getAddress
|
|
3181
3340
|
} from "viem";
|
|
3182
3341
|
import { base as base2 } from "viem/chains";
|
|
@@ -3380,12 +3539,12 @@ var YieldseekerAuth = class {
|
|
|
3380
3539
|
const account = getAddress(state.walletAddress);
|
|
3381
3540
|
const publicClient = createPublicClient2({
|
|
3382
3541
|
chain: base2,
|
|
3383
|
-
transport:
|
|
3542
|
+
transport: custom2(state.provider)
|
|
3384
3543
|
});
|
|
3385
|
-
const walletClient =
|
|
3544
|
+
const walletClient = createWalletClient2({
|
|
3386
3545
|
account,
|
|
3387
3546
|
chain: base2,
|
|
3388
|
-
transport:
|
|
3547
|
+
transport: custom2(state.provider)
|
|
3389
3548
|
});
|
|
3390
3549
|
await ensureWalletOnChain(
|
|
3391
3550
|
publicClient,
|
|
@@ -3552,7 +3711,7 @@ var YieldseekerApiClient = class {
|
|
|
3552
3711
|
};
|
|
3553
3712
|
|
|
3554
3713
|
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
3555
|
-
import { formatUnits as formatUnits3, isAddress as
|
|
3714
|
+
import { formatUnits as formatUnits3, isAddress as isAddress3 } from "viem";
|
|
3556
3715
|
|
|
3557
3716
|
// src/lib/helpers/snapshot-apy.ts
|
|
3558
3717
|
var DAY_MS = 864e5;
|
|
@@ -3621,6 +3780,14 @@ function raw(value, endpoint) {
|
|
|
3621
3780
|
function decimal(value, decimals, endpoint) {
|
|
3622
3781
|
return formatUnits3(raw(value, endpoint), decimals);
|
|
3623
3782
|
}
|
|
3783
|
+
function balanceRaw(value, endpoint) {
|
|
3784
|
+
const amount = raw(value, endpoint);
|
|
3785
|
+
if (amount < 0n) return invalid(endpoint, "expected a non-negative balance");
|
|
3786
|
+
return amount;
|
|
3787
|
+
}
|
|
3788
|
+
function balanceDecimal(value, decimals, endpoint) {
|
|
3789
|
+
return formatUnits3(balanceRaw(value, endpoint), decimals);
|
|
3790
|
+
}
|
|
3624
3791
|
function usd(rawAmount, decimals, price) {
|
|
3625
3792
|
return Number(formatUnits3(rawAmount, decimals)) * price;
|
|
3626
3793
|
}
|
|
@@ -3648,7 +3815,7 @@ function assetAddressValue(record, address) {
|
|
|
3648
3815
|
}
|
|
3649
3816
|
function position(value, asset, baseAssetDecimals) {
|
|
3650
3817
|
const option = value?.yieldOption;
|
|
3651
|
-
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !
|
|
3818
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !isAddress3(option.address)) {
|
|
3652
3819
|
return invalid("yield positions", "missing vault metadata");
|
|
3653
3820
|
}
|
|
3654
3821
|
return {
|
|
@@ -3662,13 +3829,13 @@ function position(value, asset, baseAssetDecimals) {
|
|
|
3662
3829
|
// underlying base-asset units in `assetsBase`; pair that value with the
|
|
3663
3830
|
// snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
|
|
3664
3831
|
// share quantity separate from the withdrawable underlying asset amount.
|
|
3665
|
-
amount:
|
|
3832
|
+
amount: balanceDecimal(
|
|
3666
3833
|
value.assetsBase,
|
|
3667
3834
|
baseAssetDecimals,
|
|
3668
3835
|
"yield positions"
|
|
3669
3836
|
),
|
|
3670
|
-
amountRaw:
|
|
3671
|
-
withdrawableAmountRaw:
|
|
3837
|
+
amountRaw: balanceRaw(value.assetsRaw, "yield positions").toString(),
|
|
3838
|
+
withdrawableAmountRaw: balanceRaw(value.withdrawableAssetsRaw, "yield positions").toString(),
|
|
3672
3839
|
apy: percent(option.riskAdjustedApy),
|
|
3673
3840
|
tvl: Number(option.totalDepositsUsd),
|
|
3674
3841
|
liquidity: Number(option.withdrawableDepositsUsd)
|
|
@@ -3685,7 +3852,7 @@ function mapYieldseekerBalances(contexts) {
|
|
|
3685
3852
|
chain: "BASE",
|
|
3686
3853
|
chainId: 8453,
|
|
3687
3854
|
asset: context.asset,
|
|
3688
|
-
amount:
|
|
3855
|
+
amount: balanceDecimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
|
|
3689
3856
|
});
|
|
3690
3857
|
const idle = assetAddressValue(
|
|
3691
3858
|
context.snapshot.tokenBalances,
|
|
@@ -3695,7 +3862,7 @@ function mapYieldseekerBalances(contexts) {
|
|
|
3695
3862
|
chain: "BASE",
|
|
3696
3863
|
chainId: 8453,
|
|
3697
3864
|
asset: context.asset,
|
|
3698
|
-
amount:
|
|
3865
|
+
amount: balanceDecimal(idle, metadata.decimals, "snapshot")
|
|
3699
3866
|
});
|
|
3700
3867
|
positions.push(
|
|
3701
3868
|
...context.positions.map(
|
|
@@ -3707,7 +3874,7 @@ function mapYieldseekerBalances(contexts) {
|
|
|
3707
3874
|
)
|
|
3708
3875
|
);
|
|
3709
3876
|
totalUsd += usd(
|
|
3710
|
-
|
|
3877
|
+
balanceRaw(context.snapshot.totalValueBase, "snapshot"),
|
|
3711
3878
|
context.snapshot.baseAssetDecimals,
|
|
3712
3879
|
context.snapshot.baseAssetPriceUsd
|
|
3713
3880
|
);
|
|
@@ -3899,7 +4066,7 @@ function allocationEntry(action, context) {
|
|
|
3899
4066
|
const leg = deposit;
|
|
3900
4067
|
const symbol = typeof leg.assetSymbol === "string" ? leg.assetSymbol.toUpperCase() : "";
|
|
3901
4068
|
if (symbol !== context.asset && !(context.asset === "WETH" && symbol === "ETH")) return void 0;
|
|
3902
|
-
if (typeof leg.vaultAddress !== "string" || !
|
|
4069
|
+
if (typeof leg.vaultAddress !== "string" || !isAddress3(leg.vaultAddress)) return void 0;
|
|
3903
4070
|
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;
|
|
3904
4071
|
if (rawAmount === void 0 || rawAmount <= 0n) return void 0;
|
|
3905
4072
|
const option = context.positions.find((position2) => position2.yieldOption.address.toLowerCase() === leg.vaultAddress.toLowerCase())?.yieldOption;
|
|
@@ -4131,7 +4298,7 @@ function mapYieldseekerAgentApy(options, days) {
|
|
|
4131
4298
|
}
|
|
4132
4299
|
|
|
4133
4300
|
// src/lib/remaining-vault-balance.ts
|
|
4134
|
-
import { erc20Abi as
|
|
4301
|
+
import { erc20Abi as erc20Abi4, erc4626Abi, getAddress as getAddress2 } from "viem";
|
|
4135
4302
|
async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber) {
|
|
4136
4303
|
const addresses = [...new Set(vaults.map((value) => value.toLowerCase()))];
|
|
4137
4304
|
if (addresses.length > 64) return void 0;
|
|
@@ -4143,7 +4310,7 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
|
|
|
4143
4310
|
allowFailure: true,
|
|
4144
4311
|
contracts: batch.map((address) => ({
|
|
4145
4312
|
address: getAddress2(address),
|
|
4146
|
-
abi:
|
|
4313
|
+
abi: erc20Abi4,
|
|
4147
4314
|
functionName: "balanceOf",
|
|
4148
4315
|
args: [wallet]
|
|
4149
4316
|
}))
|
|
@@ -4243,8 +4410,8 @@ var YIELDSEEKER_UPGRADE_ABI = walletAbi;
|
|
|
4243
4410
|
|
|
4244
4411
|
// src/agents/yieldseeker/yieldseeker.sponsored-withdraw.ts
|
|
4245
4412
|
import {
|
|
4246
|
-
createWalletClient as
|
|
4247
|
-
custom as
|
|
4413
|
+
createWalletClient as createWalletClient3,
|
|
4414
|
+
custom as custom3,
|
|
4248
4415
|
getAddress as getAddress4,
|
|
4249
4416
|
toHex
|
|
4250
4417
|
} from "viem";
|
|
@@ -4594,10 +4761,10 @@ async function submitEoa(input) {
|
|
|
4594
4761
|
const userOpHash = operationHash(operation);
|
|
4595
4762
|
await assertOwnerSession(input.state, input.owner);
|
|
4596
4763
|
assertIntentFresh(input.expiresAt);
|
|
4597
|
-
const wallet =
|
|
4764
|
+
const wallet = createWalletClient3({
|
|
4598
4765
|
account: input.owner,
|
|
4599
4766
|
chain: base3,
|
|
4600
|
-
transport:
|
|
4767
|
+
transport: custom3(input.state.provider)
|
|
4601
4768
|
});
|
|
4602
4769
|
const signature = await wallet.signMessage({ message: { raw: userOpHash } });
|
|
4603
4770
|
operation = { ...operation, signature };
|
|
@@ -4899,6 +5066,7 @@ var YieldseekerAgent = class {
|
|
|
4899
5066
|
receiptClient;
|
|
4900
5067
|
// A client preparation hint only; the routing API still checks live identity.
|
|
4901
5068
|
withdrawalVersionCache = /* @__PURE__ */ new Map();
|
|
5069
|
+
pendingUpgrades = /* @__PURE__ */ new Map();
|
|
4902
5070
|
transactionExecutor;
|
|
4903
5071
|
unwindReceiptWaiter;
|
|
4904
5072
|
agentContexts = /* @__PURE__ */ new Map();
|
|
@@ -5014,10 +5182,30 @@ var YieldseekerAgent = class {
|
|
|
5014
5182
|
} catch {
|
|
5015
5183
|
storage5 = void 0;
|
|
5016
5184
|
}
|
|
5017
|
-
const
|
|
5185
|
+
const clearRecordedUpgrade = () => {
|
|
5186
|
+
this.pendingUpgrades.delete(pendingKey2);
|
|
5018
5187
|
try {
|
|
5019
5188
|
storage5?.removeItem(pendingKey2);
|
|
5020
5189
|
} catch {
|
|
5190
|
+
this.pendingUpgrades.set(pendingKey2, null);
|
|
5191
|
+
}
|
|
5192
|
+
};
|
|
5193
|
+
const pendingUpgrade = (hash) => new OwneyError(
|
|
5194
|
+
"WITHDRAW_IN_PROGRESS",
|
|
5195
|
+
"Yieldseeker wallet upgrade is awaiting confirmation. Retry to check its status.",
|
|
5196
|
+
{ txHash: hash, reason: "UPGRADE_CONFIRMATION_PENDING", phase: "observe", safeToFallback: false },
|
|
5197
|
+
this.id
|
|
5198
|
+
);
|
|
5199
|
+
const terminalUpgradeFailure = (error) => error instanceof OwneyError && (error.code === "AGENT_TRANSACTION_REVERTED" || error.details?.reason === "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL");
|
|
5200
|
+
const readUpgradeReceipt = async (hash) => {
|
|
5201
|
+
try {
|
|
5202
|
+
return await this.waitForReceipt(state, chainId, hash);
|
|
5203
|
+
} catch (error) {
|
|
5204
|
+
if (terminalUpgradeFailure(error)) {
|
|
5205
|
+
clearRecordedUpgrade();
|
|
5206
|
+
throw error;
|
|
5207
|
+
}
|
|
5208
|
+
throw pendingUpgrade(hash);
|
|
5021
5209
|
}
|
|
5022
5210
|
};
|
|
5023
5211
|
if (!storage5 && (gasMode === "sponsored" || before.ownerPath === "smart"))
|
|
@@ -5029,7 +5217,7 @@ var YieldseekerAgent = class {
|
|
|
5029
5217
|
);
|
|
5030
5218
|
let prior = null;
|
|
5031
5219
|
try {
|
|
5032
|
-
const saved = storage5?.getItem(pendingKey2);
|
|
5220
|
+
const saved = this.pendingUpgrades.has(pendingKey2) ? JSON.stringify(this.pendingUpgrades.get(pendingKey2)) : storage5?.getItem(pendingKey2);
|
|
5033
5221
|
try {
|
|
5034
5222
|
prior = JSON.parse(saved ?? "null");
|
|
5035
5223
|
} catch {
|
|
@@ -5073,7 +5261,7 @@ var YieldseekerAgent = class {
|
|
|
5073
5261
|
this.id
|
|
5074
5262
|
);
|
|
5075
5263
|
if (Number(callsStatus?.status) === 400 || Number(callsStatus?.status) === 500) {
|
|
5076
|
-
|
|
5264
|
+
clearRecordedUpgrade();
|
|
5077
5265
|
throw new OwneyError(
|
|
5078
5266
|
"SPONSOR_REQUEST_FAILED",
|
|
5079
5267
|
"The earlier sponsored upgrade failed.",
|
|
@@ -5085,8 +5273,8 @@ var YieldseekerAgent = class {
|
|
|
5085
5273
|
earlierHash = hashes.length === 1 ? hashes[0] : void 0;
|
|
5086
5274
|
}
|
|
5087
5275
|
if (earlierHash) {
|
|
5088
|
-
const recovered = await confirmUpgrade(earlierHash, await
|
|
5089
|
-
|
|
5276
|
+
const recovered = await confirmUpgrade(earlierHash, await readUpgradeReceipt(earlierHash));
|
|
5277
|
+
clearRecordedUpgrade();
|
|
5090
5278
|
return recovered;
|
|
5091
5279
|
}
|
|
5092
5280
|
throw new OwneyError(
|
|
@@ -5103,7 +5291,7 @@ var YieldseekerAgent = class {
|
|
|
5103
5291
|
{ version: before.version },
|
|
5104
5292
|
this.id
|
|
5105
5293
|
);
|
|
5106
|
-
const data =
|
|
5294
|
+
const data = encodeFunctionData2({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
|
|
5107
5295
|
let txHash;
|
|
5108
5296
|
let receipt;
|
|
5109
5297
|
if (gasMode === "sponsored") {
|
|
@@ -5130,33 +5318,52 @@ var YieldseekerAgent = class {
|
|
|
5130
5318
|
asset: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
5131
5319
|
callData: data,
|
|
5132
5320
|
onPending: (pending) => {
|
|
5133
|
-
const current = storage5.getItem(pendingKey2);
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
...pending
|
|
5138
|
-
}));
|
|
5321
|
+
const current = this.pendingUpgrades.has(pendingKey2) ? this.pendingUpgrades.get(pendingKey2) : JSON.parse(storage5.getItem(pendingKey2) ?? "null");
|
|
5322
|
+
const next = { startedAt: Date.now(), ...current, ...pending };
|
|
5323
|
+
storage5.setItem(pendingKey2, JSON.stringify(next));
|
|
5324
|
+
this.pendingUpgrades.delete(pendingKey2);
|
|
5139
5325
|
}
|
|
5140
5326
|
});
|
|
5141
5327
|
txHash = upgraded.txHash;
|
|
5142
5328
|
receipt = upgraded.receipt;
|
|
5143
5329
|
} catch (error) {
|
|
5144
|
-
if (error?.code === 4001 || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true))
|
|
5330
|
+
if (error?.code === 4001 || terminalUpgradeFailure(error) || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) clearRecordedUpgrade();
|
|
5145
5331
|
throw error;
|
|
5146
5332
|
}
|
|
5147
5333
|
} else {
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5334
|
+
let submittedHash;
|
|
5335
|
+
try {
|
|
5336
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
5337
|
+
from: getAddress5(state.walletAddress),
|
|
5338
|
+
to: before.agentWallet,
|
|
5339
|
+
data,
|
|
5340
|
+
value: "0",
|
|
5341
|
+
chainId
|
|
5342
|
+
}, (hash) => {
|
|
5343
|
+
submittedHash = hash;
|
|
5344
|
+
const pending = { txHash: hash };
|
|
5345
|
+
this.pendingUpgrades.set(pendingKey2, pending);
|
|
5346
|
+
try {
|
|
5347
|
+
if (storage5) {
|
|
5348
|
+
storage5.setItem(pendingKey2, JSON.stringify(pending));
|
|
5349
|
+
this.pendingUpgrades.delete(pendingKey2);
|
|
5350
|
+
}
|
|
5351
|
+
} catch {
|
|
5352
|
+
}
|
|
5353
|
+
}, (value) => {
|
|
5354
|
+
receipt = value;
|
|
5355
|
+
});
|
|
5356
|
+
} catch (error) {
|
|
5357
|
+
if (terminalUpgradeFailure(error)) {
|
|
5358
|
+
clearRecordedUpgrade();
|
|
5359
|
+
throw error;
|
|
5360
|
+
}
|
|
5361
|
+
if (submittedHash) throw pendingUpgrade(submittedHash);
|
|
5362
|
+
throw error;
|
|
5363
|
+
}
|
|
5157
5364
|
}
|
|
5158
|
-
const confirmed = await confirmUpgrade(txHash, receipt ?? await
|
|
5159
|
-
|
|
5365
|
+
const confirmed = await confirmUpgrade(txHash, receipt ?? await readUpgradeReceipt(txHash));
|
|
5366
|
+
clearRecordedUpgrade();
|
|
5160
5367
|
return confirmed;
|
|
5161
5368
|
}
|
|
5162
5369
|
async disconnect() {
|
|
@@ -5188,7 +5395,7 @@ var YieldseekerAgent = class {
|
|
|
5188
5395
|
this.assertAsset(targetAsset);
|
|
5189
5396
|
await this.ensureAgent(state, chainId, targetAsset);
|
|
5190
5397
|
}
|
|
5191
|
-
async deposit(state, chainId, amount, asset, depositCallback, onProgress) {
|
|
5398
|
+
async deposit(state, chainId, amount, asset, depositCallback, onProgress, onBeforeSubmission) {
|
|
5192
5399
|
this.assertChain(chainId);
|
|
5193
5400
|
this.assertAsset(asset);
|
|
5194
5401
|
if (BigInt(amount) <= 0n) {
|
|
@@ -5235,8 +5442,8 @@ var YieldseekerAgent = class {
|
|
|
5235
5442
|
txHash = await this.submitTransaction(state, chainId, {
|
|
5236
5443
|
from: getAddress5(state.walletAddress),
|
|
5237
5444
|
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
5238
|
-
data:
|
|
5239
|
-
abi:
|
|
5445
|
+
data: encodeFunctionData2({
|
|
5446
|
+
abi: erc20Abi5,
|
|
5240
5447
|
functionName: "transfer",
|
|
5241
5448
|
args: [getAddress5(context.wallet.walletAddress), BigInt(amount)]
|
|
5242
5449
|
}),
|
|
@@ -5244,7 +5451,17 @@ var YieldseekerAgent = class {
|
|
|
5244
5451
|
chainId
|
|
5245
5452
|
}, submitted, (value) => {
|
|
5246
5453
|
receipt = value;
|
|
5247
|
-
})
|
|
5454
|
+
}, onBeforeSubmission ? () => onBeforeSubmission({
|
|
5455
|
+
agentId: this.id,
|
|
5456
|
+
chainId,
|
|
5457
|
+
token: asset,
|
|
5458
|
+
walletAddress: state.walletAddress,
|
|
5459
|
+
agentWallet: context.wallet.walletAddress,
|
|
5460
|
+
intendedAmount: amount,
|
|
5461
|
+
transfer: "pending",
|
|
5462
|
+
registration: "not-applicable",
|
|
5463
|
+
delivery: "not-applicable"
|
|
5464
|
+
}) : void 0);
|
|
5248
5465
|
} catch (error) {
|
|
5249
5466
|
if (error instanceof OwneyError && error.details?.ownerTransferNotExecuted === true && error.details.reason === "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL") {
|
|
5250
5467
|
notifyFunding(onProgress, {
|
|
@@ -5381,12 +5598,12 @@ var YieldseekerAgent = class {
|
|
|
5381
5598
|
blockNumber
|
|
5382
5599
|
}), chainId, "eth_call", this.id);
|
|
5383
5600
|
this.assertSession(generation, chainId, asset);
|
|
5384
|
-
if (!
|
|
5601
|
+
if (!isAddress4(feeTracker) || /^0x0{40}$/i.test(feeTracker))
|
|
5385
5602
|
throw this.invalidResponse("wallet fee tracker");
|
|
5386
5603
|
const [idle2, fees] = await Promise.all([
|
|
5387
5604
|
withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5388
5605
|
address: metadata.address,
|
|
5389
|
-
abi:
|
|
5606
|
+
abi: erc20Abi5,
|
|
5390
5607
|
functionName: "balanceOf",
|
|
5391
5608
|
args: [agentWallet],
|
|
5392
5609
|
blockNumber
|
|
@@ -5440,7 +5657,7 @@ var YieldseekerAgent = class {
|
|
|
5440
5657
|
if (!active || group.length !== 3 || group.some((value) => value.status !== "success")) return null;
|
|
5441
5658
|
this.assertSession(generation, chainId, asset);
|
|
5442
5659
|
const [underlying, shares, withdrawable] = group.map((value) => value.result);
|
|
5443
|
-
if (typeof underlying !== "string" || !
|
|
5660
|
+
if (typeof underlying !== "string" || !isAddress4(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
|
|
5444
5661
|
const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: withdrawable.toString() };
|
|
5445
5662
|
if (shares > 0n && withdrawable === 0n) {
|
|
5446
5663
|
let positiveValue = false;
|
|
@@ -5538,6 +5755,18 @@ var YieldseekerAgent = class {
|
|
|
5538
5755
|
);
|
|
5539
5756
|
}
|
|
5540
5757
|
if (requested > totalAvailable) {
|
|
5758
|
+
if (!funds.complete)
|
|
5759
|
+
throw new OwneyError(
|
|
5760
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
5761
|
+
"Yieldseeker withdrawal liquidity could not be verified for the requested amount.",
|
|
5762
|
+
{
|
|
5763
|
+
asset,
|
|
5764
|
+
requested: requested.toString(),
|
|
5765
|
+
verifiedAvailable: totalAvailable.toString(),
|
|
5766
|
+
reason: "YIELDSEEKER_LIQUIDITY_UNVERIFIED"
|
|
5767
|
+
},
|
|
5768
|
+
this.id
|
|
5769
|
+
);
|
|
5541
5770
|
throw new OwneyError(
|
|
5542
5771
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
5543
5772
|
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
@@ -5552,6 +5781,7 @@ var YieldseekerAgent = class {
|
|
|
5552
5781
|
const requiredIdle = requested + initialFunds.fees;
|
|
5553
5782
|
let remaining = requiredIdle > idle ? requiredIdle - idle : 0n;
|
|
5554
5783
|
let grossUnwound = 0n;
|
|
5784
|
+
let confirmedUnwinds = 0n;
|
|
5555
5785
|
let latestUnwindBlock;
|
|
5556
5786
|
const unwind = async (position2, assetsRaw) => {
|
|
5557
5787
|
this.assertSession(generation, chainId, asset);
|
|
@@ -5569,7 +5799,10 @@ var YieldseekerAgent = class {
|
|
|
5569
5799
|
if (!this.isTransactionHash(response?.transactionHash)) throw this.invalidResponse("position withdrawal");
|
|
5570
5800
|
unwindTransactionHash = response.transactionHash;
|
|
5571
5801
|
const unwindReceipt = await this.waitForReceipt(state, chainId, unwindTransactionHash);
|
|
5572
|
-
if (unwindReceipt)
|
|
5802
|
+
if (unwindReceipt) {
|
|
5803
|
+
latestUnwindBlock = unwindReceipt.blockNumber;
|
|
5804
|
+
confirmedUnwinds++;
|
|
5805
|
+
}
|
|
5573
5806
|
} catch (error) {
|
|
5574
5807
|
this.invalidateFundingRead(state, chainId, asset);
|
|
5575
5808
|
throw new OwneyError(
|
|
@@ -5639,7 +5872,7 @@ var YieldseekerAgent = class {
|
|
|
5639
5872
|
const feeRate = feeRateBps ?? 0;
|
|
5640
5873
|
const maxFee = confirmedMovement && Number.isInteger(feeRate) && feeRate >= 0 && feeRate <= 1e3 ? (grossUnwound * BigInt(feeRate) + 9999n) / 10000n : 0n;
|
|
5641
5874
|
const shortfall = availableForOwner < requested ? requested - availableForOwner : 0n;
|
|
5642
|
-
if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee) {
|
|
5875
|
+
if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee + confirmedUnwinds) {
|
|
5643
5876
|
this.invalidateFundingRead(state, chainId, asset);
|
|
5644
5877
|
if (amount === void 0 && positionsStatus !== "empty")
|
|
5645
5878
|
throw new OwneyError(
|
|
@@ -5663,12 +5896,13 @@ var YieldseekerAgent = class {
|
|
|
5663
5896
|
} : this.snapshotMovement(state, chainId, context, -payout);
|
|
5664
5897
|
const account = getAddress5(state.walletAddress);
|
|
5665
5898
|
let receipt;
|
|
5666
|
-
const base6 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount
|
|
5667
|
-
|
|
5899
|
+
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" };
|
|
5900
|
+
let submissionEvidence = base6;
|
|
5901
|
+
const data = amount === void 0 ? encodeFunctionData2({
|
|
5668
5902
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5669
5903
|
functionName: "withdrawAllAssetToUser",
|
|
5670
5904
|
args: [account, metadata.address]
|
|
5671
|
-
}) :
|
|
5905
|
+
}) : encodeFunctionData2({
|
|
5672
5906
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5673
5907
|
functionName: "withdrawAssetToUser",
|
|
5674
5908
|
args: [account, metadata.address, payout]
|
|
@@ -5687,7 +5921,10 @@ var YieldseekerAgent = class {
|
|
|
5687
5921
|
agentWallet: getAddress5(context.wallet.walletAddress),
|
|
5688
5922
|
asset: metadata.address,
|
|
5689
5923
|
callData: data,
|
|
5690
|
-
onPending: (pending) =>
|
|
5924
|
+
onPending: (pending) => {
|
|
5925
|
+
submissionEvidence = { ...submissionEvidence, ...pending };
|
|
5926
|
+
notifyFunding(onProgress, submissionEvidence);
|
|
5927
|
+
},
|
|
5691
5928
|
onBeforeSubmission: async (pending) => {
|
|
5692
5929
|
await onBeforeSubmission?.({ ...base6, ...pending });
|
|
5693
5930
|
onOwnerTransferAttempted?.();
|
|
@@ -5707,10 +5944,20 @@ var YieldseekerAgent = class {
|
|
|
5707
5944
|
receipt = value;
|
|
5708
5945
|
});
|
|
5709
5946
|
}
|
|
5710
|
-
|
|
5711
|
-
settlement = plannedMovement;
|
|
5712
|
-
const evidence = receipt ? inspectFundingReceipt(receipt, { ...base6, txHash }, metadata.address, "withdraw") : { ...base6, txHash, confirmedAmount: payout.toString(), transfer: "confirmed", delivery: "confirmed" };
|
|
5947
|
+
const evidence = receipt ? inspectFundingReceipt(receipt, { ...submissionEvidence, txHash }, metadata.address, "withdraw") : { ...submissionEvidence, txHash, transfer: "pending", delivery: "pending" };
|
|
5713
5948
|
notifyFunding(onProgress, evidence);
|
|
5949
|
+
const explicitEmptyPayout = evidence.transfer === "failed" && evidence.confirmedAmount === "0" && evidence.transferLogs?.some((log) => log.amountRaw === "0");
|
|
5950
|
+
if (evidence.transfer === "failed" && !explicitEmptyPayout)
|
|
5951
|
+
throw new OwneyError(
|
|
5952
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
5953
|
+
"The Yieldseeker withdrawal completed without transferring funds.",
|
|
5954
|
+
{ asset, available: "0", txHash },
|
|
5955
|
+
this.id
|
|
5956
|
+
);
|
|
5957
|
+
if (!explicitEmptyPayout && (evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || !evidence.confirmedAmount))
|
|
5958
|
+
return { txHash, type: "partial", completion: "unverified", amount: "0", evidence };
|
|
5959
|
+
confirmedMovement = true;
|
|
5960
|
+
settlement = evidence.confirmedAmount === payout.toString() ? plannedMovement : null;
|
|
5714
5961
|
let completion = maxLiquidity && !maxLiquidity.complete ? "unverified" : void 0;
|
|
5715
5962
|
if (amount === void 0) {
|
|
5716
5963
|
const verificationDeadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
|
|
@@ -5731,7 +5978,7 @@ var YieldseekerAgent = class {
|
|
|
5731
5978
|
positions = proof.positions;
|
|
5732
5979
|
positionsStatus = hasPositions(positions) ? "remaining" : proof.verified ? "empty" : "unavailable";
|
|
5733
5980
|
if (positionsStatus === "remaining") {
|
|
5734
|
-
const result = { txHash, type: "partial", amount: evidence.confirmedAmount ??
|
|
5981
|
+
const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? "0", evidence };
|
|
5735
5982
|
throw new OwneyError(
|
|
5736
5983
|
"WITHDRAW_PARTIAL_FAILURE",
|
|
5737
5984
|
"Yieldseeker MAX could not confirm that all vault positions were cleared.",
|
|
@@ -5751,7 +5998,7 @@ var YieldseekerAgent = class {
|
|
|
5751
5998
|
}
|
|
5752
5999
|
if (!proof.verified) completion = "unverified";
|
|
5753
6000
|
}
|
|
5754
|
-
if (
|
|
6001
|
+
if (explicitEmptyPayout)
|
|
5755
6002
|
throw new OwneyError(
|
|
5756
6003
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
5757
6004
|
"The Yieldseeker withdrawal completed without transferring funds.",
|
|
@@ -5766,7 +6013,7 @@ var YieldseekerAgent = class {
|
|
|
5766
6013
|
type: amount === void 0 && !completion ? "full" : "partial",
|
|
5767
6014
|
...completion ? { completion } : {},
|
|
5768
6015
|
...feeAdjustment ? { feeAdjustment } : {},
|
|
5769
|
-
amount: evidence.confirmedAmount ??
|
|
6016
|
+
amount: evidence.confirmedAmount ?? "0",
|
|
5770
6017
|
evidence
|
|
5771
6018
|
};
|
|
5772
6019
|
} finally {
|
|
@@ -5816,7 +6063,7 @@ var YieldseekerAgent = class {
|
|
|
5816
6063
|
const address = getAddress5(option.address);
|
|
5817
6064
|
return [
|
|
5818
6065
|
{ address, abi: erc4626Abi2, functionName: "asset" },
|
|
5819
|
-
{ address, abi:
|
|
6066
|
+
{ address, abi: erc20Abi5, functionName: "balanceOf", args: [owner] },
|
|
5820
6067
|
{ address, abi: erc4626Abi2, functionName: "maxWithdraw", args: [owner] }
|
|
5821
6068
|
];
|
|
5822
6069
|
})
|
|
@@ -5828,7 +6075,7 @@ var YieldseekerAgent = class {
|
|
|
5828
6075
|
const seen = /* @__PURE__ */ new Set();
|
|
5829
6076
|
for (const value of values) {
|
|
5830
6077
|
const option = value?.yieldOption;
|
|
5831
|
-
if (!option || typeof option.address !== "string" || !
|
|
6078
|
+
if (!option || typeof option.address !== "string" || !isAddress4(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))
|
|
5832
6079
|
throw this.invalidResponse("yield positions");
|
|
5833
6080
|
seen.add(option.address.toLowerCase());
|
|
5834
6081
|
}
|
|
@@ -5841,7 +6088,7 @@ var YieldseekerAgent = class {
|
|
|
5841
6088
|
const historic = this.readCache.get(`historic:${key2}`)?.value?.position;
|
|
5842
6089
|
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()));
|
|
5843
6090
|
for (const option of this.yieldOptions.get(context.asset)?.value ?? []) {
|
|
5844
|
-
if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !
|
|
6091
|
+
if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !isAddress4(option.address) || !receivedAssets.has(option.address.toLowerCase()) || hints.has(option.address.toLowerCase())) continue;
|
|
5845
6092
|
hints.set(option.address.toLowerCase(), {
|
|
5846
6093
|
yieldOption: option,
|
|
5847
6094
|
assetsRaw: "0",
|
|
@@ -5903,11 +6150,11 @@ var YieldseekerAgent = class {
|
|
|
5903
6150
|
for (const position2 of indexedPositions) candidates.set(position2.yieldOption.address.toLowerCase(), position2.yieldOption);
|
|
5904
6151
|
const options = [...candidates.values()];
|
|
5905
6152
|
if (options.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
|
|
5906
|
-
const
|
|
5907
|
-
|
|
6153
|
+
const checksRead = this.readVaultLiquidity(options, wallet, blockNumber).then((value) => ({ value }), (error) => ({ error }));
|
|
6154
|
+
const [idle, feeTracker] = await Promise.all([
|
|
5908
6155
|
client.readContract({
|
|
5909
6156
|
address: metadata.address,
|
|
5910
|
-
abi:
|
|
6157
|
+
abi: erc20Abi5,
|
|
5911
6158
|
functionName: "balanceOf",
|
|
5912
6159
|
args: [wallet],
|
|
5913
6160
|
blockNumber
|
|
@@ -5919,7 +6166,7 @@ var YieldseekerAgent = class {
|
|
|
5919
6166
|
blockNumber
|
|
5920
6167
|
})
|
|
5921
6168
|
]);
|
|
5922
|
-
if (typeof idle !== "bigint" || idle < 0n || !
|
|
6169
|
+
if (typeof idle !== "bigint" || idle < 0n || !isAddress4(feeTracker) || /^0x0{40}$/i.test(feeTracker)) throw unavailable();
|
|
5923
6170
|
assertReadCurrent();
|
|
5924
6171
|
const fees = await client.readContract({
|
|
5925
6172
|
address: getAddress5(feeTracker),
|
|
@@ -5932,6 +6179,18 @@ var YieldseekerAgent = class {
|
|
|
5932
6179
|
const verified = [];
|
|
5933
6180
|
const verifiedVaults = [];
|
|
5934
6181
|
let deployed = 0n;
|
|
6182
|
+
const proof = (complete) => ({
|
|
6183
|
+
blockNumber,
|
|
6184
|
+
idle,
|
|
6185
|
+
fees,
|
|
6186
|
+
available: idle + deployed > fees ? idle + deployed - fees : 0n,
|
|
6187
|
+
positions: [...verified],
|
|
6188
|
+
verifiedVaults: [...verifiedVaults],
|
|
6189
|
+
indexedPositions,
|
|
6190
|
+
complete
|
|
6191
|
+
});
|
|
6192
|
+
assertReadCurrent();
|
|
6193
|
+
knownProof = proof(false);
|
|
5935
6194
|
const collect = (options2, results) => {
|
|
5936
6195
|
assertReadCurrent();
|
|
5937
6196
|
const known = this.balanceVaults.get(contextKey) ?? /* @__PURE__ */ new Set();
|
|
@@ -5947,7 +6206,7 @@ var YieldseekerAgent = class {
|
|
|
5947
6206
|
return;
|
|
5948
6207
|
}
|
|
5949
6208
|
const [underlying, shares, withdrawable] = group.map((value) => value.result);
|
|
5950
|
-
if (typeof underlying !== "string" || !
|
|
6209
|
+
if (typeof underlying !== "string" || !isAddress4(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n || shares === 0n && withdrawable > 0n) {
|
|
5951
6210
|
valid2 = false;
|
|
5952
6211
|
return;
|
|
5953
6212
|
}
|
|
@@ -5964,17 +6223,10 @@ var YieldseekerAgent = class {
|
|
|
5964
6223
|
});
|
|
5965
6224
|
return valid2;
|
|
5966
6225
|
};
|
|
5967
|
-
const
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
fees,
|
|
5972
|
-
available: idle + deployed > fees ? idle + deployed - fees : 0n,
|
|
5973
|
-
positions: [...verified],
|
|
5974
|
-
verifiedVaults: [...verifiedVaults],
|
|
5975
|
-
indexedPositions,
|
|
5976
|
-
complete
|
|
5977
|
-
});
|
|
6226
|
+
const checks = await checksRead;
|
|
6227
|
+
assertReadCurrent();
|
|
6228
|
+
if ("error" in checks) throw checks.error;
|
|
6229
|
+
const allKnownVerified = collect(options, checks.value);
|
|
5978
6230
|
knownProof = proof(false);
|
|
5979
6231
|
const indexed = new Map(indexedPositions.map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
|
|
5980
6232
|
const discrepancy = verified.some((position2) => {
|
|
@@ -5991,7 +6243,7 @@ var YieldseekerAgent = class {
|
|
|
5991
6243
|
assertReadCurrent();
|
|
5992
6244
|
const eligible = /* @__PURE__ */ new Map();
|
|
5993
6245
|
for (const option of catalog ?? []) {
|
|
5994
|
-
if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() &&
|
|
6246
|
+
if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() && isAddress4(option.address))
|
|
5995
6247
|
eligible.set(option.address.toLowerCase(), option);
|
|
5996
6248
|
}
|
|
5997
6249
|
const extra = [...eligible].filter(([address]) => !candidates.has(address)).map(([, option]) => option);
|
|
@@ -6050,10 +6302,10 @@ var YieldseekerAgent = class {
|
|
|
6050
6302
|
const vaults = [...this.balanceVaults.get(key2) ?? []];
|
|
6051
6303
|
const [invested, idle, tracker] = await Promise.all([
|
|
6052
6304
|
remainingVaultBalance(client, wallet, asset.address, vaults, blockNumber),
|
|
6053
|
-
client.readContract({ address: asset.address, abi:
|
|
6305
|
+
client.readContract({ address: asset.address, abi: erc20Abi5, functionName: "balanceOf", args: [wallet], blockNumber }),
|
|
6054
6306
|
client.readContract({ address: wallet, abi: YIELDSEEKER_AGENT_WALLET_ABI, functionName: "feeTracker", blockNumber })
|
|
6055
6307
|
]);
|
|
6056
|
-
if (invested === void 0 || typeof idle !== "bigint" || idle < 0n || !
|
|
6308
|
+
if (invested === void 0 || typeof idle !== "bigint" || idle < 0n || !isAddress4(tracker) || /^0x0{40}$/i.test(tracker)) return void 0;
|
|
6057
6309
|
const fees = await client.readContract({
|
|
6058
6310
|
address: tracker,
|
|
6059
6311
|
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
@@ -6112,7 +6364,7 @@ var YieldseekerAgent = class {
|
|
|
6112
6364
|
if (!context) return void 0;
|
|
6113
6365
|
const idle = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
6114
6366
|
address: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
6115
|
-
abi:
|
|
6367
|
+
abi: erc20Abi5,
|
|
6116
6368
|
functionName: "balanceOf",
|
|
6117
6369
|
args: [getAddress5(context.wallet.walletAddress)]
|
|
6118
6370
|
}), chainId, "eth_call", this.id);
|
|
@@ -6122,7 +6374,7 @@ var YieldseekerAgent = class {
|
|
|
6122
6374
|
}
|
|
6123
6375
|
async getEarnings(state, chainId) {
|
|
6124
6376
|
this.assertChain(chainId);
|
|
6125
|
-
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
6377
|
+
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, { includePositions: false }));
|
|
6126
6378
|
}
|
|
6127
6379
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
6128
6380
|
this.assertChain(chainId);
|
|
@@ -6161,7 +6413,7 @@ var YieldseekerAgent = class {
|
|
|
6161
6413
|
);
|
|
6162
6414
|
const vaultAddresses = new Set(
|
|
6163
6415
|
catalog.flat().filter(
|
|
6164
|
-
(yieldOption) => yieldOption.chainId === chainId &&
|
|
6416
|
+
(yieldOption) => yieldOption.chainId === chainId && isAddress4(yieldOption.address)
|
|
6165
6417
|
).map((yieldOption) => yieldOption.address.toLowerCase())
|
|
6166
6418
|
);
|
|
6167
6419
|
return mapYieldseekerHistory(contexts, {
|
|
@@ -6290,7 +6542,7 @@ var YieldseekerAgent = class {
|
|
|
6290
6542
|
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
6291
6543
|
).then((walletResponse) => {
|
|
6292
6544
|
this.assertSession(generation, chainId, asset);
|
|
6293
|
-
if (!walletResponse?.agentWallet || !
|
|
6545
|
+
if (!walletResponse?.agentWallet || !isAddress4(walletResponse.agentWallet.walletAddress)) {
|
|
6294
6546
|
throw this.invalidResponse("agent wallet");
|
|
6295
6547
|
}
|
|
6296
6548
|
const context = {
|
|
@@ -6704,7 +6956,7 @@ var YieldseekerAgent = class {
|
|
|
6704
6956
|
this.agentPath(context, "deploy"),
|
|
6705
6957
|
{ method: "POST", body: {} }
|
|
6706
6958
|
);
|
|
6707
|
-
if (!deployed?.agentWallet || !
|
|
6959
|
+
if (!deployed?.agentWallet || !isAddress4(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
|
|
6708
6960
|
throw this.invalidResponse("agent deployment", {
|
|
6709
6961
|
reason: "Deploy did not return the expected Agent Wallet."
|
|
6710
6962
|
});
|
|
@@ -6805,22 +7057,26 @@ var YieldseekerAgent = class {
|
|
|
6805
7057
|
this.id
|
|
6806
7058
|
);
|
|
6807
7059
|
}
|
|
6808
|
-
async submitTransaction(state, chainId, transaction, onSubmitted, onReceipt) {
|
|
7060
|
+
async submitTransaction(state, chainId, transaction, onSubmitted, onReceipt, onBeforeSubmission) {
|
|
6809
7061
|
if (this.transactionExecutor) {
|
|
6810
|
-
|
|
7062
|
+
await onBeforeSubmission?.();
|
|
7063
|
+
const hash2 = await this.transactionExecutor(state, chainId, transaction, onReceipt);
|
|
6811
7064
|
onSubmitted?.(hash2);
|
|
6812
7065
|
return hash2;
|
|
6813
7066
|
}
|
|
6814
7067
|
this.assertTransaction(transaction, state, chainId);
|
|
6815
7068
|
const account = getAddress5(state.walletAddress);
|
|
6816
|
-
const walletClient =
|
|
7069
|
+
const walletClient = createWalletClient4({
|
|
6817
7070
|
account,
|
|
6818
7071
|
chain: base4,
|
|
6819
|
-
transport:
|
|
7072
|
+
transport: onBeforeSubmission ? custom4({ request: async (args) => {
|
|
7073
|
+
if (args.method === "eth_sendTransaction") await onBeforeSubmission();
|
|
7074
|
+
return state.provider.request(args);
|
|
7075
|
+
} }, { retryCount: 0 }) : custom4(state.provider)
|
|
6820
7076
|
});
|
|
6821
7077
|
const walletChainClient = createPublicClient3({
|
|
6822
7078
|
chain: base4,
|
|
6823
|
-
transport:
|
|
7079
|
+
transport: custom4(state.provider)
|
|
6824
7080
|
});
|
|
6825
7081
|
await ensureWalletOnChain(
|
|
6826
7082
|
walletChainClient,
|
|
@@ -6934,12 +7190,12 @@ var YieldseekerAgent = class {
|
|
|
6934
7190
|
return receipt;
|
|
6935
7191
|
}
|
|
6936
7192
|
assertTransaction(transaction, state, chainId) {
|
|
6937
|
-
if (!transaction || typeof transaction.from !== "string" || !
|
|
7193
|
+
if (!transaction || typeof transaction.from !== "string" || !isAddress4(transaction.from) || typeof transaction.to !== "string" || !isAddress4(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 || getAddress5(transaction.from) !== getAddress5(state.walletAddress)) {
|
|
6938
7194
|
throw this.invalidResponse("transaction");
|
|
6939
7195
|
}
|
|
6940
7196
|
}
|
|
6941
7197
|
assertAgent(agent) {
|
|
6942
|
-
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !
|
|
7198
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !isAddress4(agent.assetAddress) || agent.chainId !== 8453) {
|
|
6943
7199
|
throw this.invalidResponse("agent");
|
|
6944
7200
|
}
|
|
6945
7201
|
}
|
|
@@ -7097,11 +7353,20 @@ import { parseUnits as parseUnits2 } from "viem";
|
|
|
7097
7353
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
7098
7354
|
const target = asset.toUpperCase();
|
|
7099
7355
|
return agents.map((agent) => {
|
|
7356
|
+
let invalidBalance = false;
|
|
7357
|
+
const readAmount = (value, raw2 = false) => {
|
|
7358
|
+
if (typeof value !== "string" || !(raw2 ? /^\d+$/ : /^\d+(?:\.\d+)?$/).test(value)) {
|
|
7359
|
+
invalidBalance = true;
|
|
7360
|
+
return 0n;
|
|
7361
|
+
}
|
|
7362
|
+
const [whole, fraction] = value.split(".");
|
|
7363
|
+
return raw2 ? BigInt(value) : parseUnits2(fraction === void 0 ? whole : `${whole}.${fraction.slice(0, decimals)}`, decimals);
|
|
7364
|
+
};
|
|
7100
7365
|
const agentBalance = aggregated[agent.id];
|
|
7101
7366
|
const tokenBalance = agentBalance?.tokens.find(
|
|
7102
7367
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
7103
7368
|
);
|
|
7104
|
-
let balance = tokenBalance ?
|
|
7369
|
+
let balance = tokenBalance ? readAmount(tokenBalance.amount) : 0n;
|
|
7105
7370
|
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
7106
7371
|
const chainNameById = {
|
|
7107
7372
|
1: "ETHEREUM",
|
|
@@ -7113,20 +7378,22 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
7113
7378
|
const positionChain = position2.chain.trim().toUpperCase();
|
|
7114
7379
|
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
7115
7380
|
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
7116
|
-
balance += position2.withdrawableAmountRaw !== void 0 ?
|
|
7381
|
+
balance += position2.withdrawableAmountRaw !== void 0 ? readAmount(position2.withdrawableAmountRaw, true) : readAmount(position2.amount);
|
|
7117
7382
|
}
|
|
7118
7383
|
}
|
|
7119
7384
|
const assetTotal = agentBalance?.assetBalances?.find(
|
|
7120
7385
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
7121
7386
|
);
|
|
7122
7387
|
if (assetTotal) {
|
|
7123
|
-
const total =
|
|
7388
|
+
const total = readAmount(assetTotal.amount);
|
|
7124
7389
|
if (total < balance) balance = total > 0n ? total : 0n;
|
|
7125
7390
|
}
|
|
7126
|
-
return { agent, balance };
|
|
7391
|
+
return invalidBalance ? { agent, balance: 0n, invalidBalance: true } : { agent, balance };
|
|
7127
7392
|
});
|
|
7128
7393
|
}
|
|
7129
7394
|
function planProportionalShares(balances, requested, totalAvailable) {
|
|
7395
|
+
if (requested < 0n || totalAvailable < 0n || balances.some((entry) => entry.balance < 0n) || balances.reduce((sum, entry) => sum + entry.balance, 0n) !== totalAvailable || requested > totalAvailable)
|
|
7396
|
+
throw new Error("Invalid withdrawal planning amounts.");
|
|
7130
7397
|
const plans = balances.map(({ agent, balance }) => ({
|
|
7131
7398
|
agent,
|
|
7132
7399
|
balance,
|
|
@@ -7149,6 +7416,8 @@ function planProportionalShares(balances, requested, totalAvailable) {
|
|
|
7149
7416
|
return plans;
|
|
7150
7417
|
}
|
|
7151
7418
|
function planDisabledDrain(disabled, requested) {
|
|
7419
|
+
if (requested < 0n || disabled.some((entry) => entry.balance < 0n))
|
|
7420
|
+
throw new Error("Invalid withdrawal planning amounts.");
|
|
7152
7421
|
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
7153
7422
|
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
7154
7423
|
);
|
|
@@ -7166,6 +7435,7 @@ function planDisabledDrain(disabled, requested) {
|
|
|
7166
7435
|
return { plans, remaining };
|
|
7167
7436
|
}
|
|
7168
7437
|
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
7438
|
+
if (amount <= 0n) return;
|
|
7169
7439
|
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
7170
7440
|
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
7171
7441
|
const totalHeadroom = candidates.reduce(
|
|
@@ -7192,7 +7462,13 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
|
7192
7462
|
}
|
|
7193
7463
|
}
|
|
7194
7464
|
function sumWithdrawnAmount(results) {
|
|
7195
|
-
return Object.values(results).reduce((sum,
|
|
7465
|
+
return Object.values(results).reduce((sum, result) => sum + (confirmedWithdrawalAmount(result) ?? 0n), 0n);
|
|
7466
|
+
}
|
|
7467
|
+
function confirmedWithdrawalAmount(result) {
|
|
7468
|
+
const evidence = result.evidence;
|
|
7469
|
+
if (evidence && (evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed")) return void 0;
|
|
7470
|
+
const value = evidence ? evidence.confirmedAmount : result.amount;
|
|
7471
|
+
return typeof value === "string" && /^\d+$/.test(value) ? BigInt(value) : void 0;
|
|
7196
7472
|
}
|
|
7197
7473
|
|
|
7198
7474
|
// src/lib/helpers/account-apy-helper.ts
|
|
@@ -7319,15 +7595,17 @@ function aggregateDailyEarnings(series, tokenSymbol) {
|
|
|
7319
7595
|
import {
|
|
7320
7596
|
parseUnits as parseUnits3,
|
|
7321
7597
|
createPublicClient as createPublicClient4,
|
|
7322
|
-
createWalletClient as
|
|
7323
|
-
custom as
|
|
7598
|
+
createWalletClient as createWalletClient5,
|
|
7599
|
+
custom as custom5
|
|
7324
7600
|
} from "viem";
|
|
7325
7601
|
import { base as base5, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
7326
7602
|
|
|
7327
7603
|
// src/lib/sponsored-token-batch.ts
|
|
7328
7604
|
import {
|
|
7329
7605
|
isAddressEqual as isAddressEqual2,
|
|
7606
|
+
encodeFunctionData as encodeFunctionData3,
|
|
7330
7607
|
keccak256,
|
|
7608
|
+
parseTransaction,
|
|
7331
7609
|
toBytes
|
|
7332
7610
|
} from "viem";
|
|
7333
7611
|
|
|
@@ -7415,9 +7693,29 @@ function batchTypedData(b, spender) {
|
|
|
7415
7693
|
}
|
|
7416
7694
|
};
|
|
7417
7695
|
}
|
|
7696
|
+
var BATCH_WITNESS_TYPE = "Deposit witness)Deposit(address[] recipients)TokenPermissions(address token,uint256 amount)";
|
|
7697
|
+
function batchWitness(b) {
|
|
7698
|
+
return hashStruct({
|
|
7699
|
+
types: PERMIT_BATCH_TYPES,
|
|
7700
|
+
primaryType: "Deposit",
|
|
7701
|
+
data: { recipients: b.transfers.map((t) => t.to) }
|
|
7702
|
+
});
|
|
7703
|
+
}
|
|
7704
|
+
function batchArgs(b) {
|
|
7705
|
+
return [
|
|
7706
|
+
batchPermit(b),
|
|
7707
|
+
b.transfers.map((t) => ({ to: t.to, requestedAmount: BigInt(t.amount) })),
|
|
7708
|
+
b.from,
|
|
7709
|
+
batchWitness(b),
|
|
7710
|
+
BATCH_WITNESS_TYPE,
|
|
7711
|
+
b.signature
|
|
7712
|
+
];
|
|
7713
|
+
}
|
|
7418
7714
|
|
|
7419
7715
|
// src/lib/sponsored-token-batch.ts
|
|
7420
7716
|
var memory = /* @__PURE__ */ new Map();
|
|
7717
|
+
var settledRecords = /* @__PURE__ */ new Map();
|
|
7718
|
+
var expiredRecords = /* @__PURE__ */ new Map();
|
|
7421
7719
|
var inflight = /* @__PURE__ */ new Map();
|
|
7422
7720
|
var planOf = (transfers) => JSON.stringify(
|
|
7423
7721
|
transfers.map((t) => ({
|
|
@@ -7437,20 +7735,97 @@ function pendingTokenBatch(i) {
|
|
|
7437
7735
|
return;
|
|
7438
7736
|
}
|
|
7439
7737
|
}
|
|
7738
|
+
async function releaseExpiredTokenBatch(i) {
|
|
7739
|
+
const key2 = pendingKey(i);
|
|
7740
|
+
let timer;
|
|
7741
|
+
let expired = false;
|
|
7742
|
+
const check = async () => {
|
|
7743
|
+
let active = read(key2);
|
|
7744
|
+
if (active && i.expectedTxHash) {
|
|
7745
|
+
const current = JSON.parse(active);
|
|
7746
|
+
if (!current.serializedTransaction || keccak256(current.serializedTransaction).toLowerCase() !== i.expectedTxHash.toLowerCase())
|
|
7747
|
+
active = void 0;
|
|
7748
|
+
}
|
|
7749
|
+
const archiveKey = i.expectedTxHash ? `${key2}:expired:${i.expectedTxHash.toLowerCase()}` : void 0;
|
|
7750
|
+
const saved = active ?? (archiveKey && (expiredRecords.get(archiveKey) ?? (typeof window === "undefined" ? void 0 : window.localStorage.getItem(archiveKey))));
|
|
7751
|
+
if (!saved) return;
|
|
7752
|
+
const body = JSON.parse(saved);
|
|
7753
|
+
if (!body.serializedTransaction || body.chainId !== i.chainId || !isAddressEqual2(body.from, i.owner) || !isAddressEqual2(body.token, i.token) || !body.transfers?.length) return;
|
|
7754
|
+
const txHash = keccak256(body.serializedTransaction);
|
|
7755
|
+
if (i.expectedTxHash && txHash.toLowerCase() !== i.expectedTxHash.toLowerCase()) return;
|
|
7756
|
+
const transaction = parseTransaction(body.serializedTransaction);
|
|
7757
|
+
if (transaction.chainId !== i.chainId || !transaction.to || !isAddressEqual2(transaction.to, BATCH_PERMIT2_ADDRESS) || (transaction.value ?? 0n) !== 0n || transaction.data !== encodeFunctionData3({
|
|
7758
|
+
abi: PERMIT2_BATCH_ABI,
|
|
7759
|
+
functionName: "permitWitnessTransferFrom",
|
|
7760
|
+
args: batchArgs(body)
|
|
7761
|
+
})) return;
|
|
7762
|
+
const nonce = BigInt(body.nonce);
|
|
7763
|
+
const deadline = BigInt(body.deadline);
|
|
7764
|
+
if (nonce < 0n || nonce > MAX_UINT256 || deadline < 0n) return;
|
|
7765
|
+
const block = await i.pub.getBlock({ blockTag: "finalized" });
|
|
7766
|
+
if (expired || typeof block.number !== "bigint" || typeof block.timestamp !== "bigint" || block.timestamp <= deadline) return;
|
|
7767
|
+
const bitmap = await i.pub.readContract({
|
|
7768
|
+
address: BATCH_PERMIT2_ADDRESS,
|
|
7769
|
+
abi: PERMIT2_BATCH_ABI,
|
|
7770
|
+
functionName: "nonceBitmap",
|
|
7771
|
+
args: [i.owner, nonce >> 8n],
|
|
7772
|
+
blockNumber: block.number
|
|
7773
|
+
});
|
|
7774
|
+
if (expired || typeof bitmap !== "bigint" || bitmap < 0n || bitmap > MAX_UINT256 || (bitmap & 1n << (nonce & 255n)) !== 0n || active && read(key2) !== active) return;
|
|
7775
|
+
if (active) {
|
|
7776
|
+
const terminalKey = `${key2}:expired:${txHash.toLowerCase()}`;
|
|
7777
|
+
expiredRecords.set(terminalKey, saved);
|
|
7778
|
+
let persisted = typeof window === "undefined";
|
|
7779
|
+
try {
|
|
7780
|
+
if (typeof window !== "undefined") {
|
|
7781
|
+
window.localStorage.setItem(terminalKey, saved);
|
|
7782
|
+
persisted = window.localStorage.getItem(terminalKey) === saved;
|
|
7783
|
+
}
|
|
7784
|
+
} catch {
|
|
7785
|
+
}
|
|
7786
|
+
if (!persisted) settledRecords.set(key2, saved);
|
|
7787
|
+
else clear(key2, body);
|
|
7788
|
+
}
|
|
7789
|
+
return { txHash, transfers: body.transfers.map(({ to, amount }) => ({ to, amount })), blockNumber: block.number };
|
|
7790
|
+
};
|
|
7791
|
+
try {
|
|
7792
|
+
return await Promise.race([check(), new Promise((resolve) => {
|
|
7793
|
+
timer = setTimeout(() => {
|
|
7794
|
+
expired = true;
|
|
7795
|
+
resolve(void 0);
|
|
7796
|
+
}, 8e3);
|
|
7797
|
+
})]);
|
|
7798
|
+
} catch {
|
|
7799
|
+
return;
|
|
7800
|
+
} finally {
|
|
7801
|
+
expired = true;
|
|
7802
|
+
if (timer) clearTimeout(timer);
|
|
7803
|
+
}
|
|
7804
|
+
}
|
|
7440
7805
|
function read(key2) {
|
|
7441
|
-
|
|
7806
|
+
const value = typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
|
|
7807
|
+
return value && settledRecords.get(key2) === value ? void 0 : value;
|
|
7442
7808
|
}
|
|
7443
|
-
function
|
|
7444
|
-
|
|
7809
|
+
function storedBody(body) {
|
|
7810
|
+
return JSON.stringify({
|
|
7445
7811
|
...body,
|
|
7446
7812
|
transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
|
|
7447
7813
|
});
|
|
7814
|
+
}
|
|
7815
|
+
function save(key2, body) {
|
|
7816
|
+
const value = storedBody(body);
|
|
7448
7817
|
if (typeof window === "undefined") memory.set(key2, value);
|
|
7449
7818
|
else window.localStorage.setItem(key2, value);
|
|
7819
|
+
settledRecords.delete(key2);
|
|
7450
7820
|
}
|
|
7451
|
-
function clear(key2) {
|
|
7452
|
-
|
|
7453
|
-
|
|
7821
|
+
function clear(key2, body) {
|
|
7822
|
+
try {
|
|
7823
|
+
if (typeof window === "undefined") memory.delete(key2);
|
|
7824
|
+
else window.localStorage.removeItem(key2);
|
|
7825
|
+
settledRecords.delete(key2);
|
|
7826
|
+
} catch {
|
|
7827
|
+
settledRecords.set(key2, storedBody(body));
|
|
7828
|
+
}
|
|
7454
7829
|
}
|
|
7455
7830
|
function sponsorTokenBatch(i) {
|
|
7456
7831
|
const key2 = pendingKey(i);
|
|
@@ -7549,6 +7924,7 @@ async function execute(i, key2, plan) {
|
|
|
7549
7924
|
};
|
|
7550
7925
|
save(key2, body);
|
|
7551
7926
|
}
|
|
7927
|
+
await i.onBeforeSubmission?.(keccak256(body.serializedTransaction));
|
|
7552
7928
|
try {
|
|
7553
7929
|
i.onSubmitted?.(keccak256(body.serializedTransaction));
|
|
7554
7930
|
} catch {
|
|
@@ -7558,7 +7934,7 @@ async function execute(i, key2, plan) {
|
|
|
7558
7934
|
if (existing) {
|
|
7559
7935
|
assertReceipt(existing, "deposit");
|
|
7560
7936
|
void post().catch(() => void 0);
|
|
7561
|
-
clear(key2);
|
|
7937
|
+
clear(key2, body);
|
|
7562
7938
|
return hash;
|
|
7563
7939
|
}
|
|
7564
7940
|
let result = await postSigned("deposit", hash);
|
|
@@ -7573,11 +7949,18 @@ async function execute(i, key2, plan) {
|
|
|
7573
7949
|
if (result.status === "pending") await waitForConfirmation("deposit", hash);
|
|
7574
7950
|
}
|
|
7575
7951
|
if (!result || result.status === "pending") void post().catch(() => void 0);
|
|
7576
|
-
clear(key2);
|
|
7952
|
+
clear(key2, body);
|
|
7577
7953
|
return hash;
|
|
7578
7954
|
} catch (error) {
|
|
7955
|
+
if (initial.serializedTransaction && error instanceof OwneyError && error.code === "DEPOSIT_CHECKPOINT_FAILED") {
|
|
7956
|
+
throw new OwneyError(
|
|
7957
|
+
error.code,
|
|
7958
|
+
"Could not save recovery of the existing deposit.",
|
|
7959
|
+
{ ...error.details, notSubmitted: false, safeToFallback: false }
|
|
7960
|
+
);
|
|
7961
|
+
}
|
|
7579
7962
|
if (!body.serializedTransaction && !body.serializedPermitTransaction || rejectedPreparation(error) || error instanceof OwneyError && error.details?.notSubmitted === true)
|
|
7580
|
-
clear(key2);
|
|
7963
|
+
clear(key2, body);
|
|
7581
7964
|
throw error;
|
|
7582
7965
|
}
|
|
7583
7966
|
};
|
|
@@ -7590,6 +7973,12 @@ async function execute(i, key2, plan) {
|
|
|
7590
7973
|
throw new Error(
|
|
7591
7974
|
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
7592
7975
|
);
|
|
7976
|
+
if (await releaseExpiredTokenBatch({ ...i, expectedTxHash: previous.serializedTransaction ? keccak256(previous.serializedTransaction) : void 0 }))
|
|
7977
|
+
throw new OwneyError(
|
|
7978
|
+
"SPONSOR_REQUEST_FAILED",
|
|
7979
|
+
"The deposit authorization expired without transferring funds. Start a new deposit.",
|
|
7980
|
+
{ notSubmitted: true, safeToFallback: false }
|
|
7981
|
+
);
|
|
7593
7982
|
i.onApproved?.();
|
|
7594
7983
|
return send({ ...previous, transfers: i.transfers });
|
|
7595
7984
|
}
|
|
@@ -7696,6 +8085,7 @@ function makeSponsoredTokenCallback(deps) {
|
|
|
7696
8085
|
wallet,
|
|
7697
8086
|
onApproved: deps.onApproved,
|
|
7698
8087
|
onSubmitted: (hash) => deps.onSubmitted?.(hash, transfers),
|
|
8088
|
+
onBeforeSubmission: (hash) => deps.onBeforeSubmission?.(hash, transfers),
|
|
7699
8089
|
resumeTxHash: deps.resumeTxHash
|
|
7700
8090
|
});
|
|
7701
8091
|
};
|
|
@@ -7782,7 +8172,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
|
7782
8172
|
}
|
|
7783
8173
|
|
|
7784
8174
|
// src/lib/sponsored-calls-deposit.ts
|
|
7785
|
-
import { encodeFunctionData as
|
|
8175
|
+
import { encodeFunctionData as encodeFunctionData4, erc20Abi as erc20Abi6, toHex as toHex2 } from "viem";
|
|
7786
8176
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
7787
8177
|
var DEFAULT_MAX_POLLS = 30;
|
|
7788
8178
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -7827,8 +8217,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
7827
8217
|
const calls = transfers.map((transfer) => ({
|
|
7828
8218
|
to: token,
|
|
7829
8219
|
value: "0x0",
|
|
7830
|
-
data:
|
|
7831
|
-
abi:
|
|
8220
|
+
data: encodeFunctionData4({
|
|
8221
|
+
abi: erc20Abi6,
|
|
7832
8222
|
functionName: "transfer",
|
|
7833
8223
|
args: [transfer.to, BigInt(transfer.amount)]
|
|
7834
8224
|
})
|
|
@@ -7867,6 +8257,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
7867
8257
|
} catch {
|
|
7868
8258
|
}
|
|
7869
8259
|
};
|
|
8260
|
+
await deps.onBeforeSubmission?.(requestedId, transfers);
|
|
7870
8261
|
progress(requestedId, "pending");
|
|
7871
8262
|
let sendResult;
|
|
7872
8263
|
try {
|
|
@@ -8010,6 +8401,8 @@ var OwneySDK = class {
|
|
|
8010
8401
|
*/
|
|
8011
8402
|
disabledAgents = /* @__PURE__ */ new Set();
|
|
8012
8403
|
state = null;
|
|
8404
|
+
connectionRevision = 0;
|
|
8405
|
+
disconnectingAgents = null;
|
|
8013
8406
|
apiKey;
|
|
8014
8407
|
// The org's agent policy, fetched once per client. Applied to each agent on
|
|
8015
8408
|
// activation (ROUT-229). `undefined` means "not fetched yet", `null` means
|
|
@@ -8031,6 +8424,9 @@ var OwneySDK = class {
|
|
|
8031
8424
|
recoveredFundingReads = /* @__PURE__ */ new Set();
|
|
8032
8425
|
fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
|
|
8033
8426
|
activationChecks = /* @__PURE__ */ new Map();
|
|
8427
|
+
// Protect the SDK entry point as well as the mini-app hook. This owns only
|
|
8428
|
+
// a running call, never a saved/pending provider outcome or a later retry.
|
|
8429
|
+
withdrawalSubmissions = /* @__PURE__ */ new Set();
|
|
8034
8430
|
readAgent(agent, method, fetch2, params = null) {
|
|
8035
8431
|
const key2 = JSON.stringify([
|
|
8036
8432
|
this.state?.walletAddress.toLowerCase() ?? null,
|
|
@@ -8063,9 +8459,10 @@ var OwneySDK = class {
|
|
|
8063
8459
|
*/
|
|
8064
8460
|
// TODO: replace with a proper EIP-1193 Provider type
|
|
8065
8461
|
async connect(provider) {
|
|
8066
|
-
const
|
|
8067
|
-
|
|
8068
|
-
|
|
8462
|
+
const revision = ++this.connectionRevision;
|
|
8463
|
+
const accounts = await provider.request({ method: "eth_accounts" });
|
|
8464
|
+
if (revision !== this.connectionRevision)
|
|
8465
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed while connecting.");
|
|
8069
8466
|
const walletAddress = accounts[0];
|
|
8070
8467
|
if (!walletAddress) {
|
|
8071
8468
|
throw new OwneyError(
|
|
@@ -8073,33 +8470,46 @@ var OwneySDK = class {
|
|
|
8073
8470
|
"No accounts found. Ensure the wallet is unlocked and connected."
|
|
8074
8471
|
);
|
|
8075
8472
|
}
|
|
8076
|
-
this.
|
|
8077
|
-
this.
|
|
8078
|
-
|
|
8473
|
+
const replacingOwner = this.state !== null && this.state.walletAddress.toLowerCase() !== walletAddress.toLowerCase();
|
|
8474
|
+
this.clearConnectionState();
|
|
8475
|
+
await (replacingOwner ? this.disconnectAgents() : this.disconnectingAgents);
|
|
8476
|
+
if (revision !== this.connectionRevision)
|
|
8477
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed while connecting.");
|
|
8079
8478
|
this.state = { provider, walletAddress, chainId: null };
|
|
8080
|
-
this.cachedSponsoredCallback = null;
|
|
8081
|
-
this.cachedWethSponsoredCallback = null;
|
|
8082
|
-
this.cachedSponsoredCallsCallbacks.clear();
|
|
8083
8479
|
}
|
|
8084
8480
|
/**
|
|
8085
8481
|
* Disconnect from all agents and clear connection state.
|
|
8086
8482
|
* After calling this, `connect()` must be called again before using agent methods.
|
|
8087
8483
|
*/
|
|
8088
8484
|
async disconnect() {
|
|
8485
|
+
++this.connectionRevision;
|
|
8486
|
+
this.clearConnectionState();
|
|
8487
|
+
await this.disconnectAgents();
|
|
8488
|
+
}
|
|
8489
|
+
clearConnectionState() {
|
|
8089
8490
|
this.state = null;
|
|
8090
8491
|
this.fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
|
|
8091
|
-
for (const agent of this.agents.values()) {
|
|
8092
|
-
await agent.disconnect();
|
|
8093
|
-
}
|
|
8094
8492
|
this.activeAgents.clear();
|
|
8095
|
-
this.disabledAgents.clear();
|
|
8096
8493
|
this.agentReads.clearInFlight();
|
|
8097
8494
|
this.activationChecks.clear();
|
|
8098
|
-
this.state = null;
|
|
8099
8495
|
this.cachedSponsoredCallback = null;
|
|
8100
8496
|
this.cachedWethSponsoredCallback = null;
|
|
8101
8497
|
this.cachedSponsoredCallsCallbacks.clear();
|
|
8102
8498
|
}
|
|
8499
|
+
disconnectAgents() {
|
|
8500
|
+
if (this.disconnectingAgents) return this.disconnectingAgents;
|
|
8501
|
+
const pending = Promise.allSettled([...this.agents.values()].map(async (agent) => agent.disconnect())).then((results) => {
|
|
8502
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
8503
|
+
if (failure?.status === "rejected") throw failure.reason;
|
|
8504
|
+
});
|
|
8505
|
+
this.disconnectingAgents = pending;
|
|
8506
|
+
void pending.then(() => {
|
|
8507
|
+
if (this.disconnectingAgents === pending) this.disconnectingAgents = null;
|
|
8508
|
+
}, () => {
|
|
8509
|
+
if (this.disconnectingAgents === pending) this.disconnectingAgents = null;
|
|
8510
|
+
});
|
|
8511
|
+
return pending;
|
|
8512
|
+
}
|
|
8103
8513
|
isAgentDisabled(agentId) {
|
|
8104
8514
|
return this.disabledAgents.has(agentId);
|
|
8105
8515
|
}
|
|
@@ -8145,8 +8555,8 @@ var OwneySDK = class {
|
|
|
8145
8555
|
return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
|
|
8146
8556
|
}
|
|
8147
8557
|
/** Builds the default USDC batch callback for the connected wallet. */
|
|
8148
|
-
getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash) {
|
|
8149
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && this.cachedSponsoredCallback)
|
|
8558
|
+
getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission) {
|
|
8559
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && this.cachedSponsoredCallback)
|
|
8150
8560
|
return this.cachedSponsoredCallback;
|
|
8151
8561
|
const provider = this.requireConnectedProvider();
|
|
8152
8562
|
const owner = this.state.walletAddress;
|
|
@@ -8156,6 +8566,7 @@ var OwneySDK = class {
|
|
|
8156
8566
|
ownerAddress: owner,
|
|
8157
8567
|
onApproved,
|
|
8158
8568
|
onSubmitted,
|
|
8569
|
+
onBeforeSubmission,
|
|
8159
8570
|
resumeTxHash,
|
|
8160
8571
|
tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
|
|
8161
8572
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
@@ -8164,21 +8575,21 @@ var OwneySDK = class {
|
|
|
8164
8575
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
8165
8576
|
getWalletChainClient: (cid) => createPublicClient4({
|
|
8166
8577
|
chain: VIEM_CHAIN2[cid],
|
|
8167
|
-
transport:
|
|
8578
|
+
transport: custom5(provider)
|
|
8168
8579
|
}),
|
|
8169
|
-
getWalletClient: (cid) =>
|
|
8580
|
+
getWalletClient: (cid) => createWalletClient5({
|
|
8170
8581
|
account: owner,
|
|
8171
8582
|
chain: VIEM_CHAIN2[cid],
|
|
8172
|
-
transport:
|
|
8583
|
+
transport: custom5(provider)
|
|
8173
8584
|
})
|
|
8174
8585
|
});
|
|
8175
|
-
if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedSponsoredCallback = callback;
|
|
8586
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission) this.cachedSponsoredCallback = callback;
|
|
8176
8587
|
return callback;
|
|
8177
8588
|
}
|
|
8178
8589
|
/** Builds the wallet-native sponsored calls callback for compatible paymasters. */
|
|
8179
|
-
getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress) {
|
|
8590
|
+
getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress, onBeforeSubmission) {
|
|
8180
8591
|
const cached = this.cachedSponsoredCallsCallbacks.get(asset);
|
|
8181
|
-
if (!onApproved && !onSubmitted && !onCallsProgress && cached) return cached;
|
|
8592
|
+
if (!onApproved && !onSubmitted && !onCallsProgress && !onBeforeSubmission && cached) return cached;
|
|
8182
8593
|
const provider = this.requireConnectedProvider();
|
|
8183
8594
|
const callback = makeSponsoredCallsCallback({
|
|
8184
8595
|
apiKey: this.apiKey,
|
|
@@ -8189,9 +8600,10 @@ var OwneySDK = class {
|
|
|
8189
8600
|
onApproved,
|
|
8190
8601
|
onSubmitted,
|
|
8191
8602
|
onCallsProgress,
|
|
8603
|
+
onBeforeSubmission,
|
|
8192
8604
|
tokenAddressByChain: sponsoredTokensFor(asset)
|
|
8193
8605
|
});
|
|
8194
|
-
if (!onApproved && !onSubmitted && !onCallsProgress) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
8606
|
+
if (!onApproved && !onSubmitted && !onCallsProgress && !onBeforeSubmission) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
8195
8607
|
return callback;
|
|
8196
8608
|
}
|
|
8197
8609
|
/**
|
|
@@ -8200,8 +8612,8 @@ var OwneySDK = class {
|
|
|
8200
8612
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
8201
8613
|
* single-use batch authorization instead of an EIP-3009 authorization.
|
|
8202
8614
|
*/
|
|
8203
|
-
getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash) {
|
|
8204
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && this.cachedWethSponsoredCallback)
|
|
8615
|
+
getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission) {
|
|
8616
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && this.cachedWethSponsoredCallback)
|
|
8205
8617
|
return this.cachedWethSponsoredCallback;
|
|
8206
8618
|
const provider = this.requireConnectedProvider();
|
|
8207
8619
|
const owner = this.state.walletAddress;
|
|
@@ -8211,6 +8623,7 @@ var OwneySDK = class {
|
|
|
8211
8623
|
ownerAddress: owner,
|
|
8212
8624
|
onApproved,
|
|
8213
8625
|
onSubmitted,
|
|
8626
|
+
onBeforeSubmission,
|
|
8214
8627
|
resumeTxHash,
|
|
8215
8628
|
tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
|
|
8216
8629
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
@@ -8219,15 +8632,15 @@ var OwneySDK = class {
|
|
|
8219
8632
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
8220
8633
|
getWalletChainClient: (cid) => createPublicClient4({
|
|
8221
8634
|
chain: VIEM_CHAIN2[cid],
|
|
8222
|
-
transport:
|
|
8635
|
+
transport: custom5(provider)
|
|
8223
8636
|
}),
|
|
8224
|
-
getWalletClient: (cid) =>
|
|
8637
|
+
getWalletClient: (cid) => createWalletClient5({
|
|
8225
8638
|
account: owner,
|
|
8226
8639
|
chain: VIEM_CHAIN2[cid],
|
|
8227
|
-
transport:
|
|
8640
|
+
transport: custom5(provider)
|
|
8228
8641
|
})
|
|
8229
8642
|
});
|
|
8230
|
-
if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedWethSponsoredCallback = callback;
|
|
8643
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission) this.cachedWethSponsoredCallback = callback;
|
|
8231
8644
|
return callback;
|
|
8232
8645
|
}
|
|
8233
8646
|
getAgent(agentId) {
|
|
@@ -8535,10 +8948,20 @@ var OwneySDK = class {
|
|
|
8535
8948
|
*/
|
|
8536
8949
|
async deposit(options) {
|
|
8537
8950
|
const { amount, asset, depositCallback, agentId, onApproved } = options;
|
|
8951
|
+
if (!isFundingAmount(amount, true))
|
|
8952
|
+
throw new OwneyError(
|
|
8953
|
+
"DEPOSIT_INVALID_AMOUNT",
|
|
8954
|
+
"Deposit amount must be an unsigned base-unit integer.",
|
|
8955
|
+
{ phase: "prepare", rejectedBeforeSubmission: true }
|
|
8956
|
+
);
|
|
8538
8957
|
const prepared = /* @__PURE__ */ new Map();
|
|
8539
8958
|
const paidFallbackAgents = /* @__PURE__ */ new Set();
|
|
8540
|
-
const onProgress = options.onProgress ? (evidence) => {
|
|
8541
|
-
if (evidence.agentWallet)
|
|
8959
|
+
const onProgress = options.onProgress || options.onBeforeSubmission ? (evidence) => {
|
|
8960
|
+
if (evidence.agentWallet) {
|
|
8961
|
+
const wallet = evidence.agentWallet.toLowerCase();
|
|
8962
|
+
evidence = { ...prepared.get(wallet), ...evidence };
|
|
8963
|
+
prepared.set(wallet, evidence);
|
|
8964
|
+
}
|
|
8542
8965
|
notifyFunding(options.onProgress, evidence);
|
|
8543
8966
|
} : void 0;
|
|
8544
8967
|
const onSubmitted = onProgress ? (hash, destinations) => {
|
|
@@ -8558,10 +8981,87 @@ var OwneySDK = class {
|
|
|
8558
8981
|
}
|
|
8559
8982
|
const state = this.requireState();
|
|
8560
8983
|
const chainId = this.requireChainId();
|
|
8984
|
+
let checkpointFailure;
|
|
8985
|
+
let checkpointFailedEvidence = [];
|
|
8986
|
+
const checkpoint2 = options.onBeforeSubmission ? async (evidence) => {
|
|
8987
|
+
try {
|
|
8988
|
+
if (this.state !== state || state.chainId !== chainId) throw new Error("Wallet changed before deposit submission.");
|
|
8989
|
+
const submitting = evidence.map((leg) => ({ ...leg, depositSubmission: "started" }));
|
|
8990
|
+
await options.onBeforeSubmission(submitting);
|
|
8991
|
+
if (this.state !== state || state.chainId !== chainId) throw new Error("Wallet changed before deposit submission.");
|
|
8992
|
+
submitting.forEach((leg) => notifyFunding(onProgress, leg));
|
|
8993
|
+
} catch (cause) {
|
|
8994
|
+
checkpointFailedEvidence = evidence;
|
|
8995
|
+
checkpointFailure = new OwneyError(
|
|
8996
|
+
"DEPOSIT_CHECKPOINT_FAILED",
|
|
8997
|
+
"Could not safely save this deposit before submission.",
|
|
8998
|
+
{ notSubmitted: !options.resumeTxHash, safeToFallback: false, cause }
|
|
8999
|
+
);
|
|
9000
|
+
throw checkpointFailure;
|
|
9001
|
+
}
|
|
9002
|
+
} : void 0;
|
|
9003
|
+
const checkpointDestinations = checkpoint2 ? async (destinations, identifier) => {
|
|
9004
|
+
const evidence = destinations.map((destination) => {
|
|
9005
|
+
const leg = prepared.get(destination.to.toLowerCase());
|
|
9006
|
+
if (!leg || leg.intendedAmount !== destination.amount)
|
|
9007
|
+
throw new OwneyError("DEPOSIT_CHECKPOINT_FAILED", "Missing prepared deposit destination.", { notSubmitted: true, safeToFallback: false });
|
|
9008
|
+
return { ...leg, ...identifier, transfer: "pending" };
|
|
9009
|
+
});
|
|
9010
|
+
await checkpoint2(evidence);
|
|
9011
|
+
} : void 0;
|
|
9012
|
+
const checkpointHash = checkpointDestinations ? (hash, destinations) => checkpointDestinations(destinations, { txHash: hash }) : void 0;
|
|
9013
|
+
const checkpointCalls = checkpointDestinations ? (id, destinations) => checkpointDestinations(destinations, { callsId: id }) : void 0;
|
|
9014
|
+
const checkpointPaid = checkpoint2 ? (evidence) => checkpoint2([evidence]) : void 0;
|
|
9015
|
+
const customCallback = depositCallback && checkpointDestinations ? makeVerificationAwareDepositCallback(async (to, cid, value, verification) => {
|
|
9016
|
+
if (verification) provideDepositVerificationContext(depositCallback, verification);
|
|
9017
|
+
await checkpointDestinations([{ to, amount: value }], {});
|
|
9018
|
+
return depositCallback(to, cid, value);
|
|
9019
|
+
}) : depositCallback;
|
|
8561
9020
|
if (options.resumeTxHash && (depositCallback || this.paymasterServiceUrl || !/^0x[\da-f]{64}$/i.test(options.resumeTxHash)))
|
|
8562
9021
|
throw new OwneyError("DEPOSIT_IN_PROGRESS", "This deposit cannot resume the saved relayer transaction.");
|
|
8563
|
-
const effectiveCallback =
|
|
9022
|
+
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));
|
|
8564
9023
|
try {
|
|
9024
|
+
if (options.resumeTxHash) {
|
|
9025
|
+
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
9026
|
+
const pending = token && pendingTokenBatch({
|
|
9027
|
+
apiKey: this.apiKey,
|
|
9028
|
+
baseUrl: this.routingApiBaseUrl,
|
|
9029
|
+
owner: state.walletAddress,
|
|
9030
|
+
chainId,
|
|
9031
|
+
token: token.address
|
|
9032
|
+
});
|
|
9033
|
+
const invalid2 = () => new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit does not match this request. Check its status before depositing again.");
|
|
9034
|
+
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();
|
|
9035
|
+
const candidates = agentId ? [this.getAgent(agentId)] : this.getEligibleAgents(chainId, asset);
|
|
9036
|
+
const wallets = await Promise.all(candidates.map(async (agent) => {
|
|
9037
|
+
this.validateAssetSupport(agent, chainId, asset);
|
|
9038
|
+
const wallet = await (agent.getFundingWallet ? agent.getFundingWallet(state, chainId, asset) : agent.getUserProfile(state, chainId).then((profile) => profile.smartWallet)).catch(() => void 0);
|
|
9039
|
+
return { agent, wallet };
|
|
9040
|
+
}));
|
|
9041
|
+
if (this.state !== state || this.state.chainId !== chainId)
|
|
9042
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while recovering the deposit.");
|
|
9043
|
+
const usedAgents = /* @__PURE__ */ new Set();
|
|
9044
|
+
const legs = pending.transfers.map((transfer) => {
|
|
9045
|
+
const matches = wallets.filter((value) => value.wallet?.toLowerCase() === transfer.to.toLowerCase());
|
|
9046
|
+
if (matches.length !== 1 || usedAgents.has(matches[0].agent.id)) throw invalid2();
|
|
9047
|
+
const { agent } = matches[0];
|
|
9048
|
+
usedAgents.add(agent.id);
|
|
9049
|
+
return {
|
|
9050
|
+
id: agent.id,
|
|
9051
|
+
amount: transfer.amount,
|
|
9052
|
+
run: (callback) => withFailureReporting(
|
|
9053
|
+
this.apiKey,
|
|
9054
|
+
agent.id,
|
|
9055
|
+
() => agent.deposit(state, chainId, transfer.amount, asset, callback, onProgress),
|
|
9056
|
+
this.routingApiBaseUrl
|
|
9057
|
+
)
|
|
9058
|
+
};
|
|
9059
|
+
});
|
|
9060
|
+
const batchTransfer2 = getDepositBatchTransfer(effectiveCallback);
|
|
9061
|
+
if (!batchTransfer2) throw invalid2();
|
|
9062
|
+
const result = await runAgentDepositBatch(chainId, legs, batchTransfer2);
|
|
9063
|
+
return agentId ? result.agentResults[agentId] : result;
|
|
9064
|
+
}
|
|
8565
9065
|
if (agentId) {
|
|
8566
9066
|
if (this.isAgentDisabled(agentId)) {
|
|
8567
9067
|
throw new OwneyError(
|
|
@@ -8583,7 +9083,8 @@ var OwneySDK = class {
|
|
|
8583
9083
|
depositCallback,
|
|
8584
9084
|
onProgress,
|
|
8585
9085
|
() => paidFallbackAgents.add(agent.id),
|
|
8586
|
-
!!options.resumeTxHash
|
|
9086
|
+
!!options.resumeTxHash,
|
|
9087
|
+
checkpointPaid
|
|
8587
9088
|
);
|
|
8588
9089
|
}
|
|
8589
9090
|
const totalAmount = BigInt(amount);
|
|
@@ -8685,7 +9186,8 @@ var OwneySDK = class {
|
|
|
8685
9186
|
depositCallback,
|
|
8686
9187
|
onProgress,
|
|
8687
9188
|
() => paidFallbackAgents.add(agent.id),
|
|
8688
|
-
!!options.resumeTxHash
|
|
9189
|
+
!!options.resumeTxHash,
|
|
9190
|
+
checkpointPaid
|
|
8689
9191
|
);
|
|
8690
9192
|
} catch (error) {
|
|
8691
9193
|
if (Object.keys(agentResults).length === 0) throw error;
|
|
@@ -8705,12 +9207,22 @@ var OwneySDK = class {
|
|
|
8705
9207
|
}
|
|
8706
9208
|
return { agentResults };
|
|
8707
9209
|
} catch (error) {
|
|
9210
|
+
if (checkpointFailure && !(error instanceof OwneyError && error.details?.notSubmitted === false)) {
|
|
9211
|
+
error = error instanceof OwneyError && error.code === "DEPOSIT_PARTIAL_FAILURE" ? new OwneyError(
|
|
9212
|
+
error.code,
|
|
9213
|
+
"Some deposits completed before another agent could be submitted.",
|
|
9214
|
+
{ ...error.details, cause: checkpointFailure },
|
|
9215
|
+
error.agentId
|
|
9216
|
+
) : checkpointFailure;
|
|
9217
|
+
if (!options.resumeTxHash) for (const evidence of checkpointFailedEvidence)
|
|
9218
|
+
notifyFunding(onProgress, { ...evidence, transfer: "failed" });
|
|
9219
|
+
}
|
|
8708
9220
|
if (!depositCallback && onProgress && !options.resumeTxHash) {
|
|
8709
9221
|
const ids = agentId ? [agentId] : this.getEligibleAgents(chainId, asset).map((agent) => agent.id);
|
|
8710
9222
|
for (const id of ids) {
|
|
8711
9223
|
const evidence = [...prepared.values()].find((value) => value.agentId === id);
|
|
8712
|
-
if (paidFallbackAgents.has(id) || evidence?.transfer === "confirmed" || evidence?.transfer === "failed") continue;
|
|
8713
9224
|
const rejected = error instanceof OwneyError && error.details?.notSubmitted === true;
|
|
9225
|
+
if (!rejected && paidFallbackAgents.has(id) || evidence?.transfer === "confirmed" || evidence?.transfer === "failed") continue;
|
|
8714
9226
|
if (!rejected && (evidence?.txHash || evidence?.userOpHash || evidence?.callsId)) continue;
|
|
8715
9227
|
notifyFunding(onProgress, { ...evidence ?? {
|
|
8716
9228
|
agentId: id,
|
|
@@ -8753,11 +9265,11 @@ var OwneySDK = class {
|
|
|
8753
9265
|
* propagates unchanged. Scoped per agent invocation so one agent's failure
|
|
8754
9266
|
* doesn't force a retry of sibling agents in a multi-agent split.
|
|
8755
9267
|
*/
|
|
8756
|
-
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress, onPaidFallback, resumeOnly = false) {
|
|
9268
|
+
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress, onPaidFallback, resumeOnly = false, onBeforeSubmission) {
|
|
8757
9269
|
const attempt = (callback) => withFailureReporting(
|
|
8758
9270
|
this.apiKey,
|
|
8759
9271
|
agent.id,
|
|
8760
|
-
() => onProgress ? agent.deposit(state, chainId, amount, asset, callback, onProgress) : agent.deposit(state, chainId, amount, asset, callback),
|
|
9272
|
+
() => 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),
|
|
8761
9273
|
this.routingApiBaseUrl
|
|
8762
9274
|
);
|
|
8763
9275
|
let approvalAttempted = false;
|
|
@@ -8978,9 +9490,36 @@ var OwneySDK = class {
|
|
|
8978
9490
|
const token = SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token);
|
|
8979
9491
|
if (!token) throw new OwneyError("CHAIN_UNSUPPORTED", "Unsupported funding asset.");
|
|
8980
9492
|
const rpc2 = this.getPaidRpcClient(chainId);
|
|
8981
|
-
|
|
9493
|
+
let receipt;
|
|
9494
|
+
try {
|
|
9495
|
+
receipt = await rpc2.getTransactionReceipt({ hash: evidence.txHash });
|
|
9496
|
+
} catch (error) {
|
|
9497
|
+
if (direction === "deposit") {
|
|
9498
|
+
const expired = await releaseExpiredTokenBatch({
|
|
9499
|
+
apiKey: this.apiKey,
|
|
9500
|
+
baseUrl: this.routingApiBaseUrl,
|
|
9501
|
+
owner: state.walletAddress,
|
|
9502
|
+
chainId,
|
|
9503
|
+
token: token.address,
|
|
9504
|
+
pub: rpc2,
|
|
9505
|
+
expectedTxHash: evidence.txHash
|
|
9506
|
+
});
|
|
9507
|
+
assertCurrent();
|
|
9508
|
+
if (expired?.transfers.some((transfer) => transfer.to.toLowerCase() === agentWallet.toLowerCase() && transfer.amount === evidence.intendedAmount))
|
|
9509
|
+
return {
|
|
9510
|
+
...evidence,
|
|
9511
|
+
agentWallet,
|
|
9512
|
+
transfer: "failed",
|
|
9513
|
+
delivery: "not-applicable",
|
|
9514
|
+
registration: "not-applicable",
|
|
9515
|
+
confirmedAmount: "0",
|
|
9516
|
+
blockNumber: expired.blockNumber.toString()
|
|
9517
|
+
};
|
|
9518
|
+
}
|
|
9519
|
+
throw error;
|
|
9520
|
+
}
|
|
8982
9521
|
assertCurrent();
|
|
8983
|
-
let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction);
|
|
9522
|
+
let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction, options);
|
|
8984
9523
|
if (direction === "withdraw" && evidence.agentId === "yieldseeker" && verified.transfer === "unknown" && verified.confirmedAmount && BigInt(verified.confirmedAmount) > 0n) {
|
|
8985
9524
|
const transaction = await rpc2.getTransaction({ hash: evidence.txHash });
|
|
8986
9525
|
assertCurrent();
|
|
@@ -9154,9 +9693,11 @@ var OwneySDK = class {
|
|
|
9154
9693
|
return page;
|
|
9155
9694
|
}
|
|
9156
9695
|
async recoverDepositRegistration(evidence) {
|
|
9696
|
+
const { state, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
9157
9697
|
const verified = await this.inspectFunding(evidence, "deposit");
|
|
9698
|
+
assertCurrent();
|
|
9158
9699
|
if (verified.agentId !== "zyfai") return verified;
|
|
9159
|
-
return this.getAgent("zyfai").retryDepositRegistration(
|
|
9700
|
+
return this.getAgent("zyfai").retryDepositRegistration(state, verified);
|
|
9160
9701
|
}
|
|
9161
9702
|
// --- Fund operations ---
|
|
9162
9703
|
/** On-chain version and ownership status for this owner's Yieldseeker agent. */
|
|
@@ -9202,10 +9743,24 @@ var OwneySDK = class {
|
|
|
9202
9743
|
* @returns {AgentWithdrawResult} for a single agent, or {OwneyWithdrawResult} with per-agent results
|
|
9203
9744
|
*/
|
|
9204
9745
|
async withdraw(options) {
|
|
9746
|
+
if (options.amount !== void 0 && !isFundingAmount(options.amount))
|
|
9747
|
+
throw new OwneyError(
|
|
9748
|
+
"WITHDRAW_INVALID_AMOUNT",
|
|
9749
|
+
"Withdrawal amount must be a positive base-unit integer.",
|
|
9750
|
+
{ phase: "prepare", rejectedBeforeSubmission: true }
|
|
9751
|
+
);
|
|
9205
9752
|
const state = this.requireState();
|
|
9206
9753
|
const chainId = this.requireChainId();
|
|
9754
|
+
const submissionKey = `${state.walletAddress.toLowerCase()}:${chainId}:${options.asset.toUpperCase()}`;
|
|
9755
|
+
if (this.withdrawalSubmissions.has(submissionKey))
|
|
9756
|
+
throw new OwneyError(
|
|
9757
|
+
"WITHDRAW_IN_PROGRESS",
|
|
9758
|
+
"A withdrawal request is already running.",
|
|
9759
|
+
{ chainId, asset: options.asset, phase: "prepare", rejectedBeforeSubmission: true }
|
|
9760
|
+
);
|
|
9207
9761
|
const ids = options.agentId ? [options.agentId] : this.getActiveAgentIds().filter((id) => !options.excludeAgentIds?.includes(id));
|
|
9208
9762
|
const attempted = /* @__PURE__ */ new Set();
|
|
9763
|
+
this.withdrawalSubmissions.add(submissionKey);
|
|
9209
9764
|
try {
|
|
9210
9765
|
return await this.withdrawFromAgents(options, state, chainId, attempted);
|
|
9211
9766
|
} catch (error) {
|
|
@@ -9222,6 +9777,8 @@ var OwneySDK = class {
|
|
|
9222
9777
|
});
|
|
9223
9778
|
}
|
|
9224
9779
|
throw error;
|
|
9780
|
+
} finally {
|
|
9781
|
+
this.withdrawalSubmissions.delete(submissionKey);
|
|
9225
9782
|
}
|
|
9226
9783
|
}
|
|
9227
9784
|
async withdrawFromAgents(options, state, chainId, attempted) {
|
|
@@ -9260,7 +9817,19 @@ var OwneySDK = class {
|
|
|
9260
9817
|
};
|
|
9261
9818
|
};
|
|
9262
9819
|
const submissionEvidence = /* @__PURE__ */ new Map();
|
|
9820
|
+
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)));
|
|
9821
|
+
const throwUnconfirmed = (agentErrors2) => {
|
|
9822
|
+
const pending = pendingSubmissions();
|
|
9823
|
+
if (Object.keys(pending).length)
|
|
9824
|
+
throw new OwneyError(
|
|
9825
|
+
"WITHDRAW_UNCONFIRMED",
|
|
9826
|
+
"The withdrawal was submitted, but its outcome could not be confirmed yet.",
|
|
9827
|
+
{ asset, amount, agentErrors: agentErrors2, agentFailures, pendingSubmissions: pending }
|
|
9828
|
+
);
|
|
9829
|
+
};
|
|
9263
9830
|
const submit = async (agent, requested2) => {
|
|
9831
|
+
if (this.state !== state || this.state.chainId !== chainId)
|
|
9832
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed before withdrawal submission.");
|
|
9264
9833
|
attempted.add(agent.id);
|
|
9265
9834
|
let evidence = {
|
|
9266
9835
|
agentId: agent.id,
|
|
@@ -9272,12 +9841,16 @@ var OwneySDK = class {
|
|
|
9272
9841
|
registration: "not-applicable",
|
|
9273
9842
|
delivery: "pending"
|
|
9274
9843
|
};
|
|
9844
|
+
let adapterReported = false;
|
|
9275
9845
|
const progress = (next) => {
|
|
9846
|
+
if (next.agentId !== agent.id || next.chainId !== chainId || next.token !== token || next.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase()) return;
|
|
9847
|
+
adapterReported = true;
|
|
9276
9848
|
evidence = { ...evidence, ...next };
|
|
9277
9849
|
submissionEvidence.set(agent.id, evidence);
|
|
9278
9850
|
notifyFunding(options.onProgress, evidence);
|
|
9279
9851
|
};
|
|
9280
9852
|
progress(evidence);
|
|
9853
|
+
adapterReported = false;
|
|
9281
9854
|
try {
|
|
9282
9855
|
if (options.onBeforeSubmission)
|
|
9283
9856
|
return await agent.withdraw(
|
|
@@ -9291,7 +9864,7 @@ var OwneySDK = class {
|
|
|
9291
9864
|
);
|
|
9292
9865
|
return await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
|
|
9293
9866
|
} catch (error) {
|
|
9294
|
-
if (!evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
9867
|
+
if (!adapterReported && !evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
9295
9868
|
progress({ ...evidence, transfer: "failed", delivery: "failed" });
|
|
9296
9869
|
throw error;
|
|
9297
9870
|
}
|
|
@@ -9352,6 +9925,7 @@ var OwneySDK = class {
|
|
|
9352
9925
|
}
|
|
9353
9926
|
}
|
|
9354
9927
|
if (Object.keys(results2).length === 0) {
|
|
9928
|
+
throwUnconfirmed(agentErrors2);
|
|
9355
9929
|
if (Object.keys(agentErrors2).length === 0)
|
|
9356
9930
|
throw new OwneyError(
|
|
9357
9931
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
@@ -9372,7 +9946,7 @@ var OwneySDK = class {
|
|
|
9372
9946
|
);
|
|
9373
9947
|
}
|
|
9374
9948
|
const amountsKnown = Object.values(results2).every(
|
|
9375
|
-
(result) =>
|
|
9949
|
+
(result) => confirmedWithdrawalAmount(result) !== void 0
|
|
9376
9950
|
);
|
|
9377
9951
|
return {
|
|
9378
9952
|
agentResult: results2,
|
|
@@ -9391,6 +9965,13 @@ var OwneySDK = class {
|
|
|
9391
9965
|
asset,
|
|
9392
9966
|
assetInfo.decimals
|
|
9393
9967
|
);
|
|
9968
|
+
for (const entry of balances) if (entry.invalidBalance) {
|
|
9969
|
+
if (!unavailableAgents.includes(entry.agent)) unavailableAgents.push(entry.agent);
|
|
9970
|
+
aggregated.agentErrors = {
|
|
9971
|
+
...aggregated.agentErrors,
|
|
9972
|
+
[entry.agent.id]: "Provider returned an invalid withdrawal balance."
|
|
9973
|
+
};
|
|
9974
|
+
}
|
|
9394
9975
|
const indexedAvailable = balances.reduce((sum, entry) => sum + entry.balance, 0n);
|
|
9395
9976
|
await Promise.all(balances.map(async (entry) => {
|
|
9396
9977
|
if (entry.balance !== 0n && indexedAvailable >= requested || !entry.agent.getWithdrawableBalance) return;
|
|
@@ -9430,12 +10011,15 @@ var OwneySDK = class {
|
|
|
9430
10011
|
throw new OwneyError("NOT_CONNECTED", "Account changed while planning withdrawal.");
|
|
9431
10012
|
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
9432
10013
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
9433
|
-
if (totalAvailable
|
|
10014
|
+
if (totalAvailable < requested && unavailableAgents.length > 0) {
|
|
9434
10015
|
throw new OwneyError(
|
|
9435
10016
|
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
9436
|
-
`Cannot
|
|
10017
|
+
`Cannot cover the requested withdrawal with verified available balances: ${unavailableAgentIds.join(", ")}.`,
|
|
9437
10018
|
{
|
|
9438
10019
|
asset,
|
|
10020
|
+
requested: amount,
|
|
10021
|
+
available: totalAvailable.toString(),
|
|
10022
|
+
rejectedBeforeSubmission: true,
|
|
9439
10023
|
unavailableAgents: unavailableAgentIds,
|
|
9440
10024
|
agentErrors: aggregated.agentErrors
|
|
9441
10025
|
}
|
|
@@ -9452,7 +10036,7 @@ var OwneySDK = class {
|
|
|
9452
10036
|
}
|
|
9453
10037
|
);
|
|
9454
10038
|
}
|
|
9455
|
-
const plannedTarget =
|
|
10039
|
+
const plannedTarget = requested;
|
|
9456
10040
|
const disabledBalances = balances.filter(
|
|
9457
10041
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
9458
10042
|
);
|
|
@@ -9471,13 +10055,23 @@ var OwneySDK = class {
|
|
|
9471
10055
|
}));
|
|
9472
10056
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
9473
10057
|
const orderedPlans = this.orderAgentsForWithdrawal(plans.map((p) => p.agent)).map((agent) => plans.find((plan) => plan.agent === agent));
|
|
10058
|
+
const assertPlan = () => {
|
|
10059
|
+
if (orderedPlans.some((plan) => plan.planned < 0n || plan.planned > plan.balance) || orderedPlans.reduce((sum, plan) => sum + plan.planned, 0n) > plannedTarget)
|
|
10060
|
+
throw new OwneyError(
|
|
10061
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
10062
|
+
"Withdrawal plan failed amount validation.",
|
|
10063
|
+
{ asset, rejectedBeforeSubmission: true }
|
|
10064
|
+
);
|
|
10065
|
+
};
|
|
10066
|
+
assertPlan();
|
|
9474
10067
|
const results = {};
|
|
9475
10068
|
const agentErrors = {
|
|
9476
10069
|
...aggregated.agentErrors ?? {}
|
|
9477
10070
|
};
|
|
9478
10071
|
for (let i = 0; i < orderedPlans.length; i++) {
|
|
9479
10072
|
const p = orderedPlans[i];
|
|
9480
|
-
|
|
10073
|
+
assertPlan();
|
|
10074
|
+
if (p.planned <= 0n) continue;
|
|
9481
10075
|
try {
|
|
9482
10076
|
results[p.agent.id] = await submit(p.agent, p.planned.toString());
|
|
9483
10077
|
notifyAgentResult(p.agent.id, results[p.agent.id]);
|
|
@@ -9501,6 +10095,7 @@ var OwneySDK = class {
|
|
|
9501
10095
|
}
|
|
9502
10096
|
}
|
|
9503
10097
|
if (Object.keys(results).length === 0) {
|
|
10098
|
+
throwUnconfirmed(agentErrors);
|
|
9504
10099
|
throw new OwneyError(
|
|
9505
10100
|
"WITHDRAW_ALL_FAILED",
|
|
9506
10101
|
`Failed to withdraw "${amount}" of "${asset}" from all eligible agents.`,
|
|
@@ -9518,13 +10113,17 @@ var OwneySDK = class {
|
|
|
9518
10113
|
const adjustment = result?.feeAdjustment;
|
|
9519
10114
|
const evidence = result?.evidence;
|
|
9520
10115
|
const submitted = submissionEvidence.get(plan.agent.id);
|
|
9521
|
-
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
|
|
10116
|
+
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;
|
|
9522
10117
|
const fee = BigInt(adjustment.feeAmount);
|
|
9523
10118
|
const net = BigInt(result.amount);
|
|
9524
10119
|
const gross = BigInt(adjustment.grossAmount);
|
|
9525
10120
|
return fee > 0n && net > 0n && gross === plan.planned && net + fee === gross ? total + fee : total;
|
|
9526
10121
|
}, 0n);
|
|
9527
|
-
|
|
10122
|
+
const pendingAmount = orderedPlans.reduce((sum, plan) => {
|
|
10123
|
+
const evidence = results[plan.agent.id]?.evidence;
|
|
10124
|
+
return evidence && (evidence.transfer === "pending" || evidence.transfer === "unknown") && evidence.delivery !== "failed" ? sum + plan.planned : sum;
|
|
10125
|
+
}, 0n);
|
|
10126
|
+
if (withdrawn + verifiedFeeDeductions + pendingAmount < requested) {
|
|
9528
10127
|
throw new OwneyError(
|
|
9529
10128
|
"WITHDRAW_PARTIAL_FAILURE",
|
|
9530
10129
|
`Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
|
|
@@ -9532,6 +10131,7 @@ var OwneySDK = class {
|
|
|
9532
10131
|
asset,
|
|
9533
10132
|
requested: amount,
|
|
9534
10133
|
withdrawn: withdrawn.toString(),
|
|
10134
|
+
pendingAmount: pendingAmount.toString(),
|
|
9535
10135
|
partialResults: results,
|
|
9536
10136
|
agentErrors,
|
|
9537
10137
|
agentFailures,
|
|
@@ -9539,7 +10139,7 @@ var OwneySDK = class {
|
|
|
9539
10139
|
}
|
|
9540
10140
|
);
|
|
9541
10141
|
}
|
|
9542
|
-
return { agentResult: results, totalWithdrawn: withdrawn.toString() };
|
|
10142
|
+
return { agentResult: results, totalWithdrawn: Object.values(results).every((result) => confirmedWithdrawalAmount(result) !== void 0) ? withdrawn.toString() : null };
|
|
9543
10143
|
}
|
|
9544
10144
|
// --- Portfolio reads ---
|
|
9545
10145
|
/**
|
|
@@ -9999,14 +10599,14 @@ var OwneySDK = class {
|
|
|
9999
10599
|
const provider = this.requireConnectedProvider();
|
|
10000
10600
|
const walletChainClient = createPublicClient4({
|
|
10001
10601
|
chain: VIEM_CHAIN2[chainId],
|
|
10002
|
-
transport:
|
|
10602
|
+
transport: custom5(provider)
|
|
10003
10603
|
});
|
|
10004
10604
|
const publicClient = this.getPaidRpcClient(chainId);
|
|
10005
10605
|
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
10006
|
-
const wallet =
|
|
10606
|
+
const wallet = createWalletClient5({
|
|
10007
10607
|
account: state.walletAddress,
|
|
10008
10608
|
chain: VIEM_CHAIN2[chainId],
|
|
10009
|
-
transport:
|
|
10609
|
+
transport: custom5(provider)
|
|
10010
10610
|
});
|
|
10011
10611
|
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
10012
10612
|
const hash = await wallet.writeContract({
|