@owney/sdk 0.7.25-beta.13 → 0.7.25-beta.15
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 +1572 -534
- package/dist/index.d.cts +41 -6
- package/dist/index.d.ts +41 -6
- package/dist/index.js +1504 -463
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
34
34
|
|
|
35
35
|
// src/lib/funding-evidence.ts
|
|
36
36
|
var import_viem = require("viem");
|
|
37
|
+
var import_account_abstraction = require("viem/account-abstraction");
|
|
37
38
|
function notifyFunding(callback, evidence) {
|
|
38
39
|
try {
|
|
39
40
|
void Promise.resolve(callback?.({ ...evidence })).catch(() => {
|
|
@@ -43,11 +44,53 @@ function notifyFunding(callback, evidence) {
|
|
|
43
44
|
}
|
|
44
45
|
var fullWithdrawalAbi = (0, import_viem.parseAbi)(["function withdrawAllAssetToUser(address recipient, address asset)"]);
|
|
45
46
|
var withdrawalEventAbi = (0, import_viem.parseAbi)(["event WithdrewTokenToUser(address indexed owner, address indexed recipient, address indexed token, uint256 amount)"]);
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
var accountExecutionAbi = (0, import_viem.parseAbi)([
|
|
48
|
+
"function execute(address target,uint256 value,bytes data)",
|
|
49
|
+
"function executeBatch((address target,uint256 value,bytes data)[] calls)"
|
|
50
|
+
]);
|
|
51
|
+
var entryPoint06 = "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789";
|
|
52
|
+
var entryPoint07 = "0x0000000071727de22e5e9d8baf0edac6f37da032";
|
|
53
|
+
function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress, receipt) {
|
|
54
|
+
if (evidence.agentId !== "yieldseeker" || transaction.chainId !== evidence.chainId || transaction.hash.toLowerCase() !== evidence.txHash?.toLowerCase() || !evidence.agentWallet) return false;
|
|
55
|
+
const matchesFullCall = (data) => {
|
|
56
|
+
try {
|
|
57
|
+
const decoded = (0, import_viem.decodeFunctionData)({ abi: fullWithdrawalAbi, data });
|
|
58
|
+
return decoded.args[0].toLowerCase() === evidence.walletAddress.toLowerCase() && decoded.args[1].toLowerCase() === tokenAddress.toLowerCase();
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
if (transaction.to?.toLowerCase() === evidence.agentWallet.toLowerCase() && transaction.from.toLowerCase() === evidence.walletAddress.toLowerCase()) return matchesFullCall(transaction.input);
|
|
48
64
|
try {
|
|
49
|
-
const
|
|
50
|
-
|
|
65
|
+
const address = transaction.to?.toLowerCase();
|
|
66
|
+
if (address !== entryPoint06 && address !== entryPoint07) return false;
|
|
67
|
+
const version = address === entryPoint06 ? "0.6" : "0.7";
|
|
68
|
+
const decoded = (0, import_viem.decodeFunctionData)({ abi: version === "0.6" ? import_account_abstraction.entryPoint06Abi : import_account_abstraction.entryPoint07Abi, data: transaction.input });
|
|
69
|
+
if (decoded.functionName !== "handleOps") return false;
|
|
70
|
+
const operations = decoded.args[0].filter((operation2) => [evidence.agentWallet.toLowerCase(), evidence.walletAddress.toLowerCase()].includes(operation2.sender.toLowerCase()));
|
|
71
|
+
if (operations.length !== 1) return false;
|
|
72
|
+
const operation = operations[0];
|
|
73
|
+
const userOperation = (0, import_account_abstraction.toUserOperation)(operation);
|
|
74
|
+
const hash = (0, import_account_abstraction.getUserOperationHash)({
|
|
75
|
+
chainId: evidence.chainId,
|
|
76
|
+
entryPointAddress: transaction.to,
|
|
77
|
+
entryPointVersion: version,
|
|
78
|
+
userOperation
|
|
79
|
+
});
|
|
80
|
+
if (evidence.userOpHash && hash.toLowerCase() !== evidence.userOpHash.toLowerCase()) return false;
|
|
81
|
+
if (!receipt?.logs.some((log) => {
|
|
82
|
+
if (log.address.toLowerCase() !== address) return false;
|
|
83
|
+
try {
|
|
84
|
+
const event = (0, import_viem.decodeEventLog)({ abi: import_account_abstraction.entryPoint06Abi, data: log.data, topics: log.topics });
|
|
85
|
+
return event.eventName === "UserOperationEvent" && event.args.userOpHash.toLowerCase() === hash.toLowerCase() && event.args.sender.toLowerCase() === operation.sender.toLowerCase() && event.args.success;
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
})) return false;
|
|
90
|
+
if (operation.sender.toLowerCase() === evidence.agentWallet.toLowerCase()) return matchesFullCall(operation.callData);
|
|
91
|
+
const execution = (0, import_viem.decodeFunctionData)({ abi: accountExecutionAbi, data: operation.callData });
|
|
92
|
+
const calls = execution.functionName === "execute" ? [{ target: execution.args[0], value: execution.args[1], data: execution.args[2] }] : execution.args[0];
|
|
93
|
+
return calls.length === 1 && calls[0].value === 0n && calls[0].target.toLowerCase() === evidence.agentWallet.toLowerCase() && matchesFullCall(calls[0].data);
|
|
51
94
|
} catch {
|
|
52
95
|
return false;
|
|
53
96
|
}
|
|
@@ -59,6 +102,8 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
|
59
102
|
const attributed = /* @__PURE__ */ new Map();
|
|
60
103
|
const agent = evidence.agentWallet?.toLowerCase();
|
|
61
104
|
const owner = evidence.walletAddress.toLowerCase();
|
|
105
|
+
const source = direction === "deposit" ? owner : agent;
|
|
106
|
+
const destination = direction === "deposit" ? agent : owner;
|
|
62
107
|
const logIndexes = [];
|
|
63
108
|
const transferLogs = [];
|
|
64
109
|
const zeroTransferLogs = [];
|
|
@@ -73,17 +118,13 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
|
73
118
|
if (direction === "withdraw" && value === 0n && from.toLowerCase() === agent && to.toLowerCase() === owner)
|
|
74
119
|
zeroTransferLogs.push(log.logIndex);
|
|
75
120
|
let matched = 0n;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (sender !== agent) attributed.set(sender, available - carried);
|
|
84
|
-
if (recipient === owner) matched = carried;
|
|
85
|
-
else if (recipient !== agent) attributed.set(recipient, (attributed.get(recipient) ?? 0n) + carried);
|
|
86
|
-
}
|
|
121
|
+
const sender = from.toLowerCase();
|
|
122
|
+
const recipient = to.toLowerCase();
|
|
123
|
+
const available = sender === source ? value : attributed.get(sender) ?? 0n;
|
|
124
|
+
const carried = available < value ? available : value;
|
|
125
|
+
if (sender !== source) attributed.set(sender, available - carried);
|
|
126
|
+
if (recipient === destination) matched = carried;
|
|
127
|
+
else if (recipient !== source) attributed.set(recipient, (attributed.get(recipient) ?? 0n) + carried);
|
|
87
128
|
if (matched === 0n) continue;
|
|
88
129
|
amount += matched;
|
|
89
130
|
transferLogs.push({ logIndex: log.logIndex, amountRaw: matched.toString() });
|
|
@@ -375,8 +416,8 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
|
375
416
|
return await original.apply(this, args);
|
|
376
417
|
} catch (error) {
|
|
377
418
|
const message = error instanceof Error ? error.message : "";
|
|
378
|
-
request2.failure = message === "Unauthorized: Invalid API key" ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : message === "Network error: Unable to reach the server" ? "network" : "other";
|
|
379
|
-
if (["unauthorized", "forbidden", "rate-limited"].includes(request2.failure)) notify("rejected");
|
|
419
|
+
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" : message === "Network error: Unable to reach the server" ? "network" : "other";
|
|
420
|
+
if (["unauthorized", "forbidden", "rate-limited", "insufficient-balance", "session-key-missing"].includes(request2.failure)) notify("rejected");
|
|
380
421
|
throw error;
|
|
381
422
|
} finally {
|
|
382
423
|
request2.elapsedMs = Math.max(0, Date.now() - startedAt);
|
|
@@ -399,9 +440,49 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
|
399
440
|
} };
|
|
400
441
|
}
|
|
401
442
|
|
|
443
|
+
// src/agents/zyfai/zyfai.withdraw-empty.ts
|
|
444
|
+
function hasEmptyWithdrawalSnapshot(raw2, chainId, token) {
|
|
445
|
+
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
446
|
+
const response = record(raw2);
|
|
447
|
+
const portfolio = record(response?.portfolio);
|
|
448
|
+
const chains = record(portfolio?.portfolioByChain);
|
|
449
|
+
if (response?.success === false || !chains || !Array.isArray(portfolio?.positions) || !Array.isArray(portfolio.staleBalances)) return false;
|
|
450
|
+
if (portfolio.pendingAsyncWithdrawals !== void 0 && (!Array.isArray(portfolio.pendingAsyncWithdrawals) || portfolio.pendingAsyncWithdrawals.length > 0)) return false;
|
|
451
|
+
const symbol = (value) => typeof value === "string" ? value.toUpperCase() === "ETH" ? "WETH" : value.toUpperCase() : void 0;
|
|
452
|
+
const chain = (value) => {
|
|
453
|
+
if (typeof value === "number") return value;
|
|
454
|
+
if (typeof value !== "string") return void 0;
|
|
455
|
+
return /^\d+$/.test(value) ? Number(value) : { base: 8453, arbitrum: 42161, ethereum: 1, mainnet: 1 }[value.trim().toLowerCase()];
|
|
456
|
+
};
|
|
457
|
+
const zero = (value) => typeof value === "string" && /^(?:0x)?0+$/i.test(value);
|
|
458
|
+
for (const [id, assets] of Object.entries(chains)) {
|
|
459
|
+
const idNumber = chain(id);
|
|
460
|
+
if (idNumber === void 0) return false;
|
|
461
|
+
if (idNumber !== chainId) continue;
|
|
462
|
+
const entries = record(assets);
|
|
463
|
+
if (!entries) return false;
|
|
464
|
+
for (const [asset, value] of Object.entries(entries)) {
|
|
465
|
+
if (symbol(asset) !== token) continue;
|
|
466
|
+
const balance = record(value);
|
|
467
|
+
if (!balance || !zero(balance.balance)) return false;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
for (const value of [...portfolio.positions, ...portfolio.staleBalances]) {
|
|
471
|
+
const entry = record(value);
|
|
472
|
+
if (!entry) return false;
|
|
473
|
+
const id = chain(entry.chainId ?? entry.chain);
|
|
474
|
+
const asset = symbol(entry.tokenSymbol ?? entry.token_symbol);
|
|
475
|
+
if (id === void 0 || asset === void 0) return false;
|
|
476
|
+
if (id !== chainId || asset !== token) continue;
|
|
477
|
+
const amounts = [entry.balance, entry.amount, entry.underlyingAmount].filter((value2) => value2 !== void 0);
|
|
478
|
+
if (!amounts.length || !amounts.every(zero)) return false;
|
|
479
|
+
}
|
|
480
|
+
return true;
|
|
481
|
+
}
|
|
482
|
+
|
|
402
483
|
// src/agents/zyfai/zyfai.agent.ts
|
|
403
484
|
var import_sdk = require("@zyfai/sdk");
|
|
404
|
-
var
|
|
485
|
+
var import_viem4 = require("viem");
|
|
405
486
|
var import_chains = require("viem/chains");
|
|
406
487
|
|
|
407
488
|
// src/types/config.ts
|
|
@@ -488,14 +569,6 @@ function hexToDecimal(hex, decimals = 6) {
|
|
|
488
569
|
const remainder = parsed % divisor;
|
|
489
570
|
return Number(whole) + Number(remainder) / Number(divisor);
|
|
490
571
|
}
|
|
491
|
-
function normalizeDecimalPlace(value, decimals = 6) {
|
|
492
|
-
const parsed = BigInt(value);
|
|
493
|
-
if (decimals <= 0) return Number(parsed);
|
|
494
|
-
const divisor = BigInt(10 ** decimals);
|
|
495
|
-
const whole = parsed / divisor;
|
|
496
|
-
const remainder = parsed % divisor;
|
|
497
|
-
return Number(whole) + Number(remainder) / Number(divisor);
|
|
498
|
-
}
|
|
499
572
|
var DayFilterMapping = {
|
|
500
573
|
"30D": 30,
|
|
501
574
|
"14D": 14,
|
|
@@ -503,6 +576,8 @@ var DayFilterMapping = {
|
|
|
503
576
|
};
|
|
504
577
|
|
|
505
578
|
// src/agents/zyfai/zyfai.mapper.ts
|
|
579
|
+
var import_viem2 = require("viem");
|
|
580
|
+
var exactHexAmount = (raw2, decimals) => (0, import_viem2.formatUnits)(BigInt(/^0x/i.test(raw2) ? raw2 : `0x${raw2}`), decimals);
|
|
506
581
|
var DECIMALS_BY_SYMBOL = {
|
|
507
582
|
usdc: 6,
|
|
508
583
|
weth: 18,
|
|
@@ -557,15 +632,15 @@ function mapPendingAllocations(raw2) {
|
|
|
557
632
|
if (typeof e.balance !== "string") continue;
|
|
558
633
|
let amount;
|
|
559
634
|
try {
|
|
560
|
-
|
|
635
|
+
if (BigInt(/^0x/i.test(e.balance) ? e.balance : `0x${e.balance}`) <= 0n) continue;
|
|
636
|
+
amount = exactHexAmount(e.balance, tokenDecimals(e.tokenSymbol));
|
|
561
637
|
} catch {
|
|
562
638
|
continue;
|
|
563
639
|
}
|
|
564
|
-
if (!Number.isFinite(amount) || amount <= 0) continue;
|
|
565
640
|
pending.push({
|
|
566
641
|
chainId: e.chainId,
|
|
567
642
|
asset: e.tokenSymbol,
|
|
568
|
-
amount
|
|
643
|
+
amount,
|
|
569
644
|
isPending: e.isPending === true,
|
|
570
645
|
...typeof e.updatedAt === "string" && e.updatedAt !== "" ? { since: e.updatedAt } : {}
|
|
571
646
|
});
|
|
@@ -588,13 +663,13 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
588
663
|
{ balance, balanceWithFee, decimals }
|
|
589
664
|
] of Object.entries(tokensBySymbol ?? {})) {
|
|
590
665
|
if (balanceWithFee === void 0 && balance === void 0) throw new Error("Zyfai asset balance is unavailable.");
|
|
591
|
-
const amount =
|
|
592
|
-
totalBalance += amount;
|
|
666
|
+
const amount = exactHexAmount(balanceWithFee ?? balance, tokenDecimals(symbol, decimals));
|
|
667
|
+
totalBalance += Number(amount);
|
|
593
668
|
tokens.push({
|
|
594
669
|
chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
|
|
595
670
|
chainId: positionChainId,
|
|
596
671
|
asset: symbol,
|
|
597
|
-
amount
|
|
672
|
+
amount
|
|
598
673
|
});
|
|
599
674
|
}
|
|
600
675
|
}
|
|
@@ -608,19 +683,17 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
608
683
|
// legacy `amount` fallback is hex. Runtime
|
|
609
684
|
// getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
|
|
610
685
|
// when present, otherwise fall back to the per-symbol lookup.
|
|
611
|
-
amount:
|
|
612
|
-
p.underlyingAmountWithFee ?? p.underlyingAmount
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
p.decimals
|
|
623
|
-
)
|
|
686
|
+
amount: p.underlyingAmountWithFee ?? p.underlyingAmount ? (0, import_viem2.formatUnits)(
|
|
687
|
+
BigInt(p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0"),
|
|
688
|
+
tokenDecimals(
|
|
689
|
+
p.token_symbol,
|
|
690
|
+
p.decimals
|
|
691
|
+
)
|
|
692
|
+
) : exactHexAmount(
|
|
693
|
+
p.amount || "0",
|
|
694
|
+
tokenDecimals(
|
|
695
|
+
p.token_symbol,
|
|
696
|
+
p.decimals
|
|
624
697
|
)
|
|
625
698
|
),
|
|
626
699
|
apy: netApy(p.pool_apy_withFee, p.pool_apy, "pool_apy_withFee"),
|
|
@@ -1275,7 +1348,7 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
1275
1348
|
}
|
|
1276
1349
|
|
|
1277
1350
|
// src/lib/paid-rpc.ts
|
|
1278
|
-
var
|
|
1351
|
+
var import_viem3 = require("viem");
|
|
1279
1352
|
var PAID_RPC_RETRY_COUNT = 3;
|
|
1280
1353
|
var PAID_RPC_RETRY_DELAY_MS = 1e3;
|
|
1281
1354
|
function configuredRpcProxyBaseUrl(baseUrl) {
|
|
@@ -1311,9 +1384,9 @@ function createPaidRpcClient(chain, rpcUrls) {
|
|
|
1311
1384
|
rpcUrls,
|
|
1312
1385
|
chain.id
|
|
1313
1386
|
);
|
|
1314
|
-
return (0,
|
|
1387
|
+
return (0, import_viem3.createPublicClient)({
|
|
1315
1388
|
chain,
|
|
1316
|
-
transport: (0,
|
|
1389
|
+
transport: (0, import_viem3.http)(url, {
|
|
1317
1390
|
retryCount: PAID_RPC_RETRY_COUNT,
|
|
1318
1391
|
retryDelay: PAID_RPC_RETRY_DELAY_MS
|
|
1319
1392
|
})
|
|
@@ -1410,7 +1483,7 @@ async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
|
|
|
1410
1483
|
}
|
|
1411
1484
|
|
|
1412
1485
|
// src/agents/zyfai/zyfai.agent.ts
|
|
1413
|
-
var ERC7579_IS_MODULE_INSTALLED_ABI = (0,
|
|
1486
|
+
var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem4.parseAbi)([
|
|
1414
1487
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
1415
1488
|
]);
|
|
1416
1489
|
var WETH_ADDRESS_BY_CHAIN = {
|
|
@@ -1561,7 +1634,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1561
1634
|
}, asset.address, entry.action === "Withdraw" ? "withdraw" : "deposit");
|
|
1562
1635
|
if (evidence.transfer === "confirmed" && evidence.confirmedAmount) {
|
|
1563
1636
|
Object.assign(tx, {
|
|
1564
|
-
amount: (0,
|
|
1637
|
+
amount: (0, import_viem4.formatUnits)(BigInt(evidence.confirmedAmount), asset.decimals),
|
|
1565
1638
|
amountRaw: evidence.confirmedAmount,
|
|
1566
1639
|
blockNumber: evidence.blockNumber,
|
|
1567
1640
|
logIndexes: evidence.logIndexes,
|
|
@@ -2288,6 +2361,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2288
2361
|
evidence2 = { ...evidence2, transfer: "unknown" };
|
|
2289
2362
|
}
|
|
2290
2363
|
notifyFunding(onProgress, evidence2);
|
|
2364
|
+
if (evidence2.transfer === "failed")
|
|
2365
|
+
throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
|
|
2291
2366
|
try {
|
|
2292
2367
|
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
2293
2368
|
await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
|
|
@@ -2326,12 +2401,77 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2326
2401
|
} catch {
|
|
2327
2402
|
}
|
|
2328
2403
|
notifyFunding(onProgress, evidence);
|
|
2404
|
+
if (evidence.transfer === "failed")
|
|
2405
|
+
throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash: raw2.txHash }, this.id);
|
|
2329
2406
|
return { ...mapDeposit(raw2), evidence };
|
|
2330
2407
|
} catch (error) {
|
|
2331
2408
|
throw error;
|
|
2332
2409
|
}
|
|
2333
2410
|
}
|
|
2334
2411
|
// --- IAgent: Fund operations ---
|
|
2412
|
+
/** Called only after the execution endpoint explicitly rejected a request.
|
|
2413
|
+
* A timeout or an accepted request never authorizes repair and resubmission. */
|
|
2414
|
+
async recoverWithdrawalSession(state, chainId, token, owner) {
|
|
2415
|
+
const internals = this.sdk;
|
|
2416
|
+
const assertScope = () => {
|
|
2417
|
+
const account = internals.walletClient?.account?.address;
|
|
2418
|
+
if (state.walletAddress.toLowerCase() !== owner.toLowerCase() || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
|
|
2419
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai withdrawal.", void 0, this.id);
|
|
2420
|
+
};
|
|
2421
|
+
assertScope();
|
|
2422
|
+
const [wallet, registered, portfolio] = await Promise.all([
|
|
2423
|
+
this.sdk.getSmartWalletAddress(owner, chainId),
|
|
2424
|
+
this.sdk.getSmartWalletByEOA(owner),
|
|
2425
|
+
this.sdk.getPortfolio(owner).catch(() => void 0)
|
|
2426
|
+
]);
|
|
2427
|
+
assertScope();
|
|
2428
|
+
if (!(0, import_viem4.isAddress)(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
|
|
2429
|
+
throw new OwneyError(
|
|
2430
|
+
"AGENT_AUTH_FAILED",
|
|
2431
|
+
"Could not verify Zyfai's withdrawal wallet.",
|
|
2432
|
+
{ reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
|
|
2433
|
+
this.id
|
|
2434
|
+
);
|
|
2435
|
+
if (hasEmptyWithdrawalSnapshot(portfolio, chainId, token)) {
|
|
2436
|
+
const asset = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === token);
|
|
2437
|
+
const idle = asset ? await this.getPublicClient(chainId).readContract({
|
|
2438
|
+
address: asset.address,
|
|
2439
|
+
abi: import_viem4.erc20Abi,
|
|
2440
|
+
functionName: "balanceOf",
|
|
2441
|
+
args: [wallet.address]
|
|
2442
|
+
}).catch(() => void 0) : void 0;
|
|
2443
|
+
assertScope();
|
|
2444
|
+
if (idle === 0n) throw new OwneyError(
|
|
2445
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
2446
|
+
"No funds remain in Zyfai for this asset.",
|
|
2447
|
+
{ available: "0", chainId, token },
|
|
2448
|
+
this.id
|
|
2449
|
+
);
|
|
2450
|
+
}
|
|
2451
|
+
const walletState = await this.readWalletState(owner);
|
|
2452
|
+
assertScope();
|
|
2453
|
+
const unavailable = () => new OwneyError(
|
|
2454
|
+
"AGENT_AUTH_FAILED",
|
|
2455
|
+
"Zyfai has no active withdrawal session. Its withdrawal was not submitted.",
|
|
2456
|
+
{ reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
|
|
2457
|
+
this.id
|
|
2458
|
+
);
|
|
2459
|
+
if (walletState.predeployed !== false || internals.isPredeployed === true) throw unavailable();
|
|
2460
|
+
internals.hasActiveSessionKey = false;
|
|
2461
|
+
const key2 = `${owner.toLowerCase()}:${chainId}`;
|
|
2462
|
+
this.sessionKeyAttempts.delete(key2);
|
|
2463
|
+
try {
|
|
2464
|
+
const result = await this.sdk.createSessionKey(owner, chainId);
|
|
2465
|
+
assertScope();
|
|
2466
|
+
if (result.success === false || result.alreadyActive === true) throw unavailable();
|
|
2467
|
+
this.sessionKeyAttempts.add(key2);
|
|
2468
|
+
} catch (error) {
|
|
2469
|
+
assertScope();
|
|
2470
|
+
const code = error?.code;
|
|
2471
|
+
if (code === 4001 || code === "ACTION_REJECTED" || error instanceof Error && /user (rejected|denied)|request rejected|action rejected/i.test(error.message)) throw error;
|
|
2472
|
+
throw unavailable();
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2335
2475
|
async withdraw(state, chainId, token, amount, onProgress) {
|
|
2336
2476
|
const validChainId = isValidChainId(chainId);
|
|
2337
2477
|
try {
|
|
@@ -2349,11 +2489,35 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2349
2489
|
});
|
|
2350
2490
|
throw error;
|
|
2351
2491
|
}
|
|
2352
|
-
|
|
2492
|
+
let observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
|
|
2353
2493
|
const authToken = this.sdk.httpClient?.authToken;
|
|
2494
|
+
const owner = this.getAddress();
|
|
2354
2495
|
let raw2;
|
|
2355
2496
|
try {
|
|
2356
|
-
|
|
2497
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
2498
|
+
try {
|
|
2499
|
+
raw2 = await this.sdk.withdrawFunds(owner, validChainId, amount, token);
|
|
2500
|
+
break;
|
|
2501
|
+
} catch (error) {
|
|
2502
|
+
if (!observation.rejected() || observation.request()?.failure !== "session-key-missing") throw error;
|
|
2503
|
+
if (attempt === 1)
|
|
2504
|
+
throw new OwneyError(
|
|
2505
|
+
"AGENT_AUTH_FAILED",
|
|
2506
|
+
"Zyfai has no active withdrawal session. Its withdrawal was not submitted.",
|
|
2507
|
+
{ reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
|
|
2508
|
+
this.id
|
|
2509
|
+
);
|
|
2510
|
+
await this.recoverWithdrawalSession(state, validChainId, token, owner);
|
|
2511
|
+
observation.stop();
|
|
2512
|
+
observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
if (!raw2) throw new OwneyError(
|
|
2516
|
+
"AGENT_INVALID_RESPONSE",
|
|
2517
|
+
"Zyfai returned no withdrawal result.",
|
|
2518
|
+
{ chainId, token, phase: "observe" },
|
|
2519
|
+
this.id
|
|
2520
|
+
);
|
|
2357
2521
|
} catch (error) {
|
|
2358
2522
|
this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken);
|
|
2359
2523
|
if (observation.attempted() !== void 0) {
|
|
@@ -2632,7 +2796,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2632
2796
|
};
|
|
2633
2797
|
|
|
2634
2798
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2635
|
-
var
|
|
2799
|
+
var import_viem11 = require("viem");
|
|
2636
2800
|
var import_chains4 = require("viem/chains");
|
|
2637
2801
|
|
|
2638
2802
|
// src/lib/chain-guard.ts
|
|
@@ -2671,7 +2835,7 @@ async function ensureWalletOnChain(pub, wallet, expected) {
|
|
|
2671
2835
|
}
|
|
2672
2836
|
|
|
2673
2837
|
// src/lib/transfer-auth.ts
|
|
2674
|
-
var
|
|
2838
|
+
var import_viem5 = require("viem");
|
|
2675
2839
|
|
|
2676
2840
|
// src/lib/sponsor-client.ts
|
|
2677
2841
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
@@ -2787,7 +2951,7 @@ async function postSponsorBatchTransfer(input) {
|
|
|
2787
2951
|
}
|
|
2788
2952
|
|
|
2789
2953
|
// src/lib/permit2.ts
|
|
2790
|
-
var
|
|
2954
|
+
var import_viem6 = require("viem");
|
|
2791
2955
|
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2792
2956
|
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2793
2957
|
function permit2ApprovalAmount(requiredAmount) {
|
|
@@ -2828,7 +2992,7 @@ var ERC20_ALLOWANCE_ABI = [
|
|
|
2828
2992
|
function randomPermit2Nonce() {
|
|
2829
2993
|
const bytes = new Uint8Array(32);
|
|
2830
2994
|
globalThis.crypto.getRandomValues(bytes);
|
|
2831
|
-
return BigInt((0,
|
|
2995
|
+
return BigInt((0, import_viem6.bytesToHex)(bytes));
|
|
2832
2996
|
}
|
|
2833
2997
|
async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
|
|
2834
2998
|
return publicClient.readContract({
|
|
@@ -2871,7 +3035,7 @@ function makeVerificationAwareDepositCallback(implementation) {
|
|
|
2871
3035
|
|
|
2872
3036
|
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2873
3037
|
var import_siwe = require("siwe");
|
|
2874
|
-
var
|
|
3038
|
+
var import_viem7 = require("viem");
|
|
2875
3039
|
var import_chains2 = require("viem/chains");
|
|
2876
3040
|
|
|
2877
3041
|
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
@@ -2984,7 +3148,7 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
|
2984
3148
|
return new import_siwe.SiweMessage({
|
|
2985
3149
|
scheme: url.protocol.slice(0, -1),
|
|
2986
3150
|
domain: url.host,
|
|
2987
|
-
address: (0,
|
|
3151
|
+
address: (0, import_viem7.getAddress)(address),
|
|
2988
3152
|
uri: url.origin,
|
|
2989
3153
|
version: "1",
|
|
2990
3154
|
chainId,
|
|
@@ -3070,15 +3234,15 @@ var YieldseekerAuth = class {
|
|
|
3070
3234
|
clearYieldseekerSession(state.walletAddress, chainId);
|
|
3071
3235
|
}
|
|
3072
3236
|
async sign(state, chainId) {
|
|
3073
|
-
const account = (0,
|
|
3074
|
-
const publicClient = (0,
|
|
3237
|
+
const account = (0, import_viem7.getAddress)(state.walletAddress);
|
|
3238
|
+
const publicClient = (0, import_viem7.createPublicClient)({
|
|
3075
3239
|
chain: import_chains2.base,
|
|
3076
|
-
transport: (0,
|
|
3240
|
+
transport: (0, import_viem7.custom)(state.provider)
|
|
3077
3241
|
});
|
|
3078
|
-
const walletClient = (0,
|
|
3242
|
+
const walletClient = (0, import_viem7.createWalletClient)({
|
|
3079
3243
|
account,
|
|
3080
3244
|
chain: import_chains2.base,
|
|
3081
|
-
transport: (0,
|
|
3245
|
+
transport: (0, import_viem7.custom)(state.provider)
|
|
3082
3246
|
});
|
|
3083
3247
|
await ensureWalletOnChain(
|
|
3084
3248
|
publicClient,
|
|
@@ -3245,7 +3409,7 @@ var YieldseekerApiClient = class {
|
|
|
3245
3409
|
};
|
|
3246
3410
|
|
|
3247
3411
|
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
3248
|
-
var
|
|
3412
|
+
var import_viem8 = require("viem");
|
|
3249
3413
|
|
|
3250
3414
|
// src/lib/helpers/snapshot-apy.ts
|
|
3251
3415
|
var DAY_MS = 864e5;
|
|
@@ -3312,10 +3476,10 @@ function raw(value, endpoint) {
|
|
|
3312
3476
|
return BigInt(value);
|
|
3313
3477
|
}
|
|
3314
3478
|
function decimal(value, decimals, endpoint) {
|
|
3315
|
-
return (0,
|
|
3479
|
+
return (0, import_viem8.formatUnits)(raw(value, endpoint), decimals);
|
|
3316
3480
|
}
|
|
3317
3481
|
function usd(rawAmount, decimals, price) {
|
|
3318
|
-
return Number((0,
|
|
3482
|
+
return Number((0, import_viem8.formatUnits)(rawAmount, decimals)) * price;
|
|
3319
3483
|
}
|
|
3320
3484
|
function percent(value) {
|
|
3321
3485
|
const result = Number(value);
|
|
@@ -3341,7 +3505,7 @@ function assetAddressValue(record, address) {
|
|
|
3341
3505
|
}
|
|
3342
3506
|
function position(value, asset, baseAssetDecimals) {
|
|
3343
3507
|
const option = value?.yieldOption;
|
|
3344
|
-
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0,
|
|
3508
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem8.isAddress)(option.address)) {
|
|
3345
3509
|
return invalid("yield positions", "missing vault metadata");
|
|
3346
3510
|
}
|
|
3347
3511
|
return {
|
|
@@ -3433,7 +3597,7 @@ function mapYieldseekerEarnings(contexts) {
|
|
|
3433
3597
|
chain: "BASE",
|
|
3434
3598
|
chainId: 8453,
|
|
3435
3599
|
asset: context.asset,
|
|
3436
|
-
amount: (0,
|
|
3600
|
+
amount: (0, import_viem8.formatUnits)(amount, context.snapshot.baseAssetDecimals)
|
|
3437
3601
|
});
|
|
3438
3602
|
lifetimeEarnings += usd(
|
|
3439
3603
|
amount,
|
|
@@ -3550,6 +3714,49 @@ function actionEntry(action) {
|
|
|
3550
3714
|
rebalanceLog: []
|
|
3551
3715
|
};
|
|
3552
3716
|
}
|
|
3717
|
+
function allocationEntry(action, context) {
|
|
3718
|
+
if (action.actionType.toLowerCase() !== "autoseek") return void 0;
|
|
3719
|
+
const hash = action.details?.depositTransactionHash;
|
|
3720
|
+
const deposits = action.details?.deposits;
|
|
3721
|
+
if (typeof hash !== "string" || !/^0x[a-fA-F0-9]{64}$/.test(hash) || !Array.isArray(deposits) || deposits.length === 0) return void 0;
|
|
3722
|
+
const positions = [];
|
|
3723
|
+
let amountRaw = 0n;
|
|
3724
|
+
for (const deposit of deposits) {
|
|
3725
|
+
if (!deposit || typeof deposit !== "object") return void 0;
|
|
3726
|
+
const leg = deposit;
|
|
3727
|
+
const symbol = typeof leg.assetSymbol === "string" ? leg.assetSymbol.toUpperCase() : "";
|
|
3728
|
+
if (symbol !== context.asset && !(context.asset === "WETH" && symbol === "ETH")) return void 0;
|
|
3729
|
+
if (typeof leg.vaultAddress !== "string" || !(0, import_viem8.isAddress)(leg.vaultAddress)) return void 0;
|
|
3730
|
+
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;
|
|
3731
|
+
if (rawAmount === void 0 || rawAmount <= 0n) return void 0;
|
|
3732
|
+
const option = context.positions.find((position2) => position2.yieldOption.address.toLowerCase() === leg.vaultAddress.toLowerCase())?.yieldOption;
|
|
3733
|
+
positions.push({
|
|
3734
|
+
protocol: typeof leg.vaultProvider === "string" && leg.vaultProvider ? leg.vaultProvider : option?.provider || "Vault",
|
|
3735
|
+
pool: typeof leg.vaultName === "string" && leg.vaultName ? leg.vaultName : option?.name,
|
|
3736
|
+
tokenSymbol: context.asset,
|
|
3737
|
+
amount: (0, import_viem8.formatUnits)(rawAmount, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
|
|
3738
|
+
});
|
|
3739
|
+
amountRaw += rawAmount;
|
|
3740
|
+
}
|
|
3741
|
+
return {
|
|
3742
|
+
agent: "yieldseeker",
|
|
3743
|
+
action: "Deposit",
|
|
3744
|
+
date: action.createdDate,
|
|
3745
|
+
oldApy: null,
|
|
3746
|
+
newApy: null,
|
|
3747
|
+
positions,
|
|
3748
|
+
rebalanceLog: [],
|
|
3749
|
+
transactions: [{
|
|
3750
|
+
txHashes: [hash],
|
|
3751
|
+
chainId: context.agent.chainId,
|
|
3752
|
+
tokenAddress: context.agent.assetAddress,
|
|
3753
|
+
tokenSymbol: context.asset,
|
|
3754
|
+
agentWallet: context.wallet.walletAddress,
|
|
3755
|
+
amountRaw: String(amountRaw),
|
|
3756
|
+
amount: (0, import_viem8.formatUnits)(amountRaw, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
|
|
3757
|
+
}]
|
|
3758
|
+
};
|
|
3759
|
+
}
|
|
3553
3760
|
function depositDestination(context, movement) {
|
|
3554
3761
|
const to = movement.toAddress.toLowerCase();
|
|
3555
3762
|
const option = context.positions.find(({ yieldOption }) => yieldOption.chainId === movement.chainId && yieldOption.assetAddress.toLowerCase() === context.agent.assetAddress.toLowerCase() && yieldOption.address.toLowerCase() === to)?.yieldOption;
|
|
@@ -3617,20 +3824,30 @@ function mapYieldseekerHistory(contexts, options) {
|
|
|
3617
3824
|
seenMovements.add(key2);
|
|
3618
3825
|
return true;
|
|
3619
3826
|
});
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3827
|
+
const movementEntries = movements.map(
|
|
3828
|
+
(movement) => movementEntry(
|
|
3829
|
+
movement,
|
|
3830
|
+
context.wallet,
|
|
3831
|
+
context.agent,
|
|
3832
|
+
context.asset,
|
|
3833
|
+
options.ownerAddress,
|
|
3834
|
+
options.vaultAddresses,
|
|
3835
|
+
depositDestination(context, movement)
|
|
3836
|
+
)
|
|
3837
|
+
).filter((entry) => entry !== void 0);
|
|
3838
|
+
const deposits = new Set(movementEntries.filter((entry) => entry.action === "Deposit").map((entry) => entry.transactions[0]?.txHashes[0]?.toLowerCase()));
|
|
3839
|
+
const actions = (context.actions ?? []).flatMap((action) => {
|
|
3840
|
+
const allocation = allocationEntry(action, context);
|
|
3841
|
+
if (allocation) {
|
|
3842
|
+
const hash = allocation.transactions[0].txHashes[0].toLowerCase();
|
|
3843
|
+
if (deposits.has(hash)) return [];
|
|
3844
|
+
deposits.add(hash);
|
|
3845
|
+
return [allocation];
|
|
3846
|
+
}
|
|
3847
|
+
const entry = actionEntry(action);
|
|
3848
|
+
return entry ? [entry] : [];
|
|
3849
|
+
});
|
|
3850
|
+
return [...movementEntries, ...actions];
|
|
3634
3851
|
});
|
|
3635
3852
|
const grouped = /* @__PURE__ */ new Map();
|
|
3636
3853
|
const ungrouped = [];
|
|
@@ -3660,7 +3877,7 @@ function mapYieldseekerHistory(contexts, options) {
|
|
|
3660
3877
|
const known = new Set(target.logIndexes ?? []);
|
|
3661
3878
|
if (tx.logIndexes?.some((index) => !known.has(index))) {
|
|
3662
3879
|
target.amountRaw = String(BigInt(target.amountRaw ?? "0") + BigInt(tx.amountRaw));
|
|
3663
|
-
target.amount = (0,
|
|
3880
|
+
target.amount = (0, import_viem8.formatUnits)(
|
|
3664
3881
|
BigInt(target.amountRaw),
|
|
3665
3882
|
YIELDSEEKER_ASSET_METADATA[target.tokenSymbol].decimals
|
|
3666
3883
|
);
|
|
@@ -3741,51 +3958,53 @@ function mapYieldseekerAgentApy(options, days) {
|
|
|
3741
3958
|
}
|
|
3742
3959
|
|
|
3743
3960
|
// src/agents/yieldseeker/yieldseeker.wallet-version.ts
|
|
3744
|
-
var
|
|
3961
|
+
var import_viem9 = require("viem");
|
|
3745
3962
|
var YIELDSEEKER_FACTORY = "0x9c7410a0faCAC60850C46eE5b58B518daec95130";
|
|
3746
3963
|
var YIELDSEEKER_V1_IMPLEMENTATION = "0x32403455357589523435dB18b414057D7cE05051";
|
|
3747
3964
|
var YIELDSEEKER_V2_IMPLEMENTATION = "0x613Db9c924ADB7A7A90c11b02Cce20c913E3Fe50";
|
|
3748
3965
|
var ERC1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
|
3749
|
-
var walletAbi = (0,
|
|
3966
|
+
var walletAbi = (0, import_viem9.parseAbi)([
|
|
3750
3967
|
"function owner() view returns (address)",
|
|
3751
3968
|
"function ownerAgentIndex() view returns (uint256)",
|
|
3752
3969
|
"function baseAsset() view returns (address)",
|
|
3753
3970
|
"function FACTORY() view returns (address)",
|
|
3754
|
-
"function upgradeToLatest()"
|
|
3971
|
+
"function upgradeToLatest()",
|
|
3972
|
+
"event Upgraded(address indexed implementation)"
|
|
3755
3973
|
]);
|
|
3756
|
-
var factoryAbi = (0,
|
|
3974
|
+
var factoryAbi = (0, import_viem9.parseAbi)([
|
|
3757
3975
|
"function userWallets(address owner, uint256 index) view returns (address)",
|
|
3758
3976
|
"function agentWalletImplementation() view returns (address)"
|
|
3759
3977
|
]);
|
|
3760
3978
|
function yieldseekerOwnerPath(code) {
|
|
3761
3979
|
return !code || code === "0x" || /^0xef0100[0-9a-f]{40}$/i.test(code) ? "eoa" : "smart";
|
|
3762
3980
|
}
|
|
3763
|
-
async function readYieldseekerWalletStatus(client, wallet, owner, asset) {
|
|
3764
|
-
const agentWallet = (0,
|
|
3765
|
-
const expectedOwner = (0,
|
|
3766
|
-
const expectedAsset = (0,
|
|
3981
|
+
async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNumber) {
|
|
3982
|
+
const agentWallet = (0, import_viem9.getAddress)(wallet);
|
|
3983
|
+
const expectedOwner = (0, import_viem9.getAddress)(owner);
|
|
3984
|
+
const expectedAsset = (0, import_viem9.getAddress)(asset);
|
|
3767
3985
|
const [code, ownerCode, slot, actualOwner, index, actualAsset, actualFactory, latest] = await Promise.all([
|
|
3768
|
-
client.getBytecode({ address: agentWallet }),
|
|
3769
|
-
client.getBytecode({ address: expectedOwner }),
|
|
3770
|
-
client.getStorageAt({ address: agentWallet, slot: ERC1967_IMPLEMENTATION_SLOT }),
|
|
3771
|
-
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "owner" }),
|
|
3772
|
-
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "ownerAgentIndex" }),
|
|
3773
|
-
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "baseAsset" }),
|
|
3774
|
-
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "FACTORY" }),
|
|
3775
|
-
client.readContract({ address: YIELDSEEKER_FACTORY, abi: factoryAbi, functionName: "agentWalletImplementation" })
|
|
3986
|
+
client.getBytecode({ address: agentWallet, blockNumber }),
|
|
3987
|
+
client.getBytecode({ address: expectedOwner, blockNumber }),
|
|
3988
|
+
client.getStorageAt({ address: agentWallet, slot: ERC1967_IMPLEMENTATION_SLOT, blockNumber }),
|
|
3989
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "owner", blockNumber }),
|
|
3990
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "ownerAgentIndex", blockNumber }),
|
|
3991
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "baseAsset", blockNumber }),
|
|
3992
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "FACTORY", blockNumber }),
|
|
3993
|
+
client.readContract({ address: YIELDSEEKER_FACTORY, abi: factoryAbi, functionName: "agentWalletImplementation", blockNumber })
|
|
3776
3994
|
]);
|
|
3777
3995
|
if (!code || code === "0x" || !slot || /^0x0+$/.test(slot)) {
|
|
3778
3996
|
throw new Error("Yieldseeker wallet is not a deployed ERC1967 proxy");
|
|
3779
3997
|
}
|
|
3780
|
-
const implementation = (0,
|
|
3998
|
+
const implementation = (0, import_viem9.getAddress)(`0x${slot.slice(-40)}`);
|
|
3781
3999
|
const mapped = await client.readContract({
|
|
3782
4000
|
address: YIELDSEEKER_FACTORY,
|
|
3783
4001
|
abi: factoryAbi,
|
|
3784
4002
|
functionName: "userWallets",
|
|
3785
|
-
args: [expectedOwner, index]
|
|
4003
|
+
args: [expectedOwner, index],
|
|
4004
|
+
blockNumber
|
|
3786
4005
|
});
|
|
3787
|
-
const verified = (0,
|
|
3788
|
-
const version = !verified ? "unknown" : (0,
|
|
4006
|
+
const verified = (0, import_viem9.isAddressEqual)(actualOwner, expectedOwner) && (0, import_viem9.isAddressEqual)(actualAsset, expectedAsset) && (0, import_viem9.isAddressEqual)(actualFactory, YIELDSEEKER_FACTORY) && (0, import_viem9.isAddressEqual)(mapped, agentWallet);
|
|
4007
|
+
const version = !verified ? "unknown" : (0, import_viem9.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem9.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
|
|
3789
4008
|
return {
|
|
3790
4009
|
agentWallet,
|
|
3791
4010
|
owner: actualOwner,
|
|
@@ -3794,18 +4013,20 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset) {
|
|
|
3794
4013
|
implementation,
|
|
3795
4014
|
version,
|
|
3796
4015
|
verified,
|
|
3797
|
-
upgradeAvailable: version === "v1" && (0,
|
|
4016
|
+
upgradeAvailable: version === "v1" && (0, import_viem9.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
|
|
3798
4017
|
};
|
|
3799
4018
|
}
|
|
3800
4019
|
var YIELDSEEKER_UPGRADE_ABI = walletAbi;
|
|
3801
4020
|
|
|
3802
4021
|
// src/agents/yieldseeker/yieldseeker.sponsored-withdraw.ts
|
|
3803
|
-
var
|
|
3804
|
-
var
|
|
4022
|
+
var import_viem10 = require("viem");
|
|
4023
|
+
var import_account_abstraction2 = require("viem/account-abstraction");
|
|
3805
4024
|
var import_accounts = require("viem/accounts");
|
|
3806
4025
|
var import_chains3 = require("viem/chains");
|
|
3807
4026
|
var ENTRY_POINT_V06 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
|
|
3808
4027
|
var USER_OP_FEE_HEADROOM = 2n;
|
|
4028
|
+
var OBSERVATION_WINDOW_MS = 6e4;
|
|
4029
|
+
var OBSERVATION_POLL_MS = 1500;
|
|
3809
4030
|
var ENTRY_POINT_NONCE_ABI = [
|
|
3810
4031
|
{ type: "function", name: "getNonce", stateMutability: "view", inputs: [
|
|
3811
4032
|
{ name: "sender", type: "address" },
|
|
@@ -3815,7 +4036,7 @@ var ENTRY_POINT_NONCE_ABI = [
|
|
|
3815
4036
|
var DEFAULT_ROUTING_API = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3816
4037
|
var SIMULATION_ACCOUNT = (0, import_accounts.privateKeyToAccount)((0, import_accounts.generatePrivateKey)());
|
|
3817
4038
|
function operationHash(operation) {
|
|
3818
|
-
return (0,
|
|
4039
|
+
return (0, import_account_abstraction2.getUserOperationHash)({
|
|
3819
4040
|
chainId: 8453,
|
|
3820
4041
|
entryPointAddress: ENTRY_POINT_V06,
|
|
3821
4042
|
entryPointVersion: "0.6",
|
|
@@ -3838,10 +4059,119 @@ function sponsorError(message, safeToFallback, details = {}) {
|
|
|
3838
4059
|
return new OwneyError(
|
|
3839
4060
|
"SPONSOR_REQUEST_FAILED",
|
|
3840
4061
|
message,
|
|
3841
|
-
{
|
|
4062
|
+
{
|
|
4063
|
+
safeToFallback,
|
|
4064
|
+
phase: safeToFallback ? "prepare" : "observe",
|
|
4065
|
+
failureCode: safeToFallback ? "SPONSOR_PREPARATION_FAILED" : "SPONSOR_OUTCOME_UNRESOLVED",
|
|
4066
|
+
...safeToFallback ? { rejectedBeforeSubmission: true } : {},
|
|
4067
|
+
...details
|
|
4068
|
+
},
|
|
3842
4069
|
"yieldseeker"
|
|
3843
4070
|
);
|
|
3844
4071
|
}
|
|
4072
|
+
function providerReason(value) {
|
|
4073
|
+
if (typeof value !== "string") return void 0;
|
|
4074
|
+
return value.replace(/https?:\/\/\S+/gi, "[url]").replace(/0x[0-9a-fA-F]{40,}/g, "[hex]").replace(/[A-Za-z0-9_+=\/-]{48,}/g, "[token]").slice(0, 300);
|
|
4075
|
+
}
|
|
4076
|
+
function rpcDiagnostics(data) {
|
|
4077
|
+
const details = {};
|
|
4078
|
+
if (typeof data?.failureCode === "string" && [
|
|
4079
|
+
"SPONSOR_RATE_LIMIT",
|
|
4080
|
+
"SPONSOR_UPSTREAM_UNAVAILABLE",
|
|
4081
|
+
"SPONSOR_RPC_UNSUPPORTED",
|
|
4082
|
+
"SPONSOR_POLICY_REJECTED",
|
|
4083
|
+
"SPONSOR_BUDGET_REJECTED",
|
|
4084
|
+
"SPONSOR_EXECUTION_REVERTED"
|
|
4085
|
+
].includes(data.failureCode))
|
|
4086
|
+
details.failureCode = data.failureCode;
|
|
4087
|
+
if (typeof data?.revertSelector === "string" && /^0x[0-9a-fA-F]{8}$/.test(data.revertSelector))
|
|
4088
|
+
details.revertSelector = data.revertSelector;
|
|
4089
|
+
if (typeof data?.revertName === "string" && [
|
|
4090
|
+
"InsufficientBalance",
|
|
4091
|
+
"InvalidAsset",
|
|
4092
|
+
"Unauthorized",
|
|
4093
|
+
"Error",
|
|
4094
|
+
"Panic",
|
|
4095
|
+
"FailedOp",
|
|
4096
|
+
"FailedOpWithRevert"
|
|
4097
|
+
].includes(data.revertName))
|
|
4098
|
+
details.revertName = data.revertName;
|
|
4099
|
+
if (typeof data?.aaCode === "string" && /^AA[0-9]{2}$/.test(data.aaCode)) details.aaCode = data.aaCode;
|
|
4100
|
+
if (typeof data?.panicCode === "string" && /^0x[0-9a-fA-F]{1,2}$/.test(data.panicCode)) {
|
|
4101
|
+
const panicCode = (0, import_viem10.toHex)(BigInt(data.panicCode));
|
|
4102
|
+
if (["0x0", "0x1", "0x11", "0x12", "0x21", "0x22", "0x31", "0x32", "0x41", "0x51"].includes(panicCode))
|
|
4103
|
+
details.panicCode = panicCode;
|
|
4104
|
+
}
|
|
4105
|
+
return details;
|
|
4106
|
+
}
|
|
4107
|
+
function isHash(value) {
|
|
4108
|
+
return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value);
|
|
4109
|
+
}
|
|
4110
|
+
async function assertOwnerSession(state, owner) {
|
|
4111
|
+
const [chain, accounts] = await Promise.all([
|
|
4112
|
+
state.provider.request({ method: "eth_chainId" }),
|
|
4113
|
+
state.provider.request({ method: "eth_accounts" })
|
|
4114
|
+
]);
|
|
4115
|
+
if (Number(chain) !== 8453)
|
|
4116
|
+
throw new OwneyError(
|
|
4117
|
+
"CHAIN_MISMATCH",
|
|
4118
|
+
"Switch back to Base before withdrawing.",
|
|
4119
|
+
{ rejectedBeforeSubmission: true, phase: "prepare" },
|
|
4120
|
+
"yieldseeker"
|
|
4121
|
+
);
|
|
4122
|
+
if (!Array.isArray(accounts) || typeof accounts[0] !== "string" || accounts[0].toLowerCase() !== owner.toLowerCase())
|
|
4123
|
+
throw new OwneyError(
|
|
4124
|
+
"NOT_CONNECTED",
|
|
4125
|
+
"The connected withdrawal account changed.",
|
|
4126
|
+
{ rejectedBeforeSubmission: true, phase: "prepare" },
|
|
4127
|
+
"yieldseeker"
|
|
4128
|
+
);
|
|
4129
|
+
}
|
|
4130
|
+
function savePending(onPending, evidence, beforeSubmission = false) {
|
|
4131
|
+
try {
|
|
4132
|
+
onPending?.(evidence);
|
|
4133
|
+
} catch {
|
|
4134
|
+
if (beforeSubmission)
|
|
4135
|
+
throw sponsorError(
|
|
4136
|
+
"Withdrawal status cannot be saved before submission.",
|
|
4137
|
+
true,
|
|
4138
|
+
{ phase: "prepare", rejectedBeforeSubmission: true, failureCode: "RECOVERY_STORAGE_UNAVAILABLE" }
|
|
4139
|
+
);
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
async function checkpoint(callback, pending) {
|
|
4143
|
+
try {
|
|
4144
|
+
await callback?.(pending);
|
|
4145
|
+
} catch {
|
|
4146
|
+
throw sponsorError(
|
|
4147
|
+
"Withdrawal status cannot be saved before submission.",
|
|
4148
|
+
true,
|
|
4149
|
+
{ phase: "prepare", rejectedBeforeSubmission: true, failureCode: "RECOVERY_STORAGE_UNAVAILABLE" }
|
|
4150
|
+
);
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
function finalPaymasterFields(value) {
|
|
4154
|
+
const result = {};
|
|
4155
|
+
for (const key2 of [
|
|
4156
|
+
"paymasterAndData",
|
|
4157
|
+
"callGasLimit",
|
|
4158
|
+
"verificationGasLimit",
|
|
4159
|
+
"preVerificationGas",
|
|
4160
|
+
"maxFeePerGas",
|
|
4161
|
+
"maxPriorityFeePerGas"
|
|
4162
|
+
]) {
|
|
4163
|
+
const field = value[key2];
|
|
4164
|
+
if (field === void 0) continue;
|
|
4165
|
+
if (typeof field !== "string" || !/^0x[0-9a-fA-F]+$/.test(field))
|
|
4166
|
+
throw sponsorError(
|
|
4167
|
+
"Paymaster returned an invalid final operation.",
|
|
4168
|
+
true,
|
|
4169
|
+
{ phase: "prepare", rejectedBeforeSubmission: true, failureCode: "INVALID_PAYMASTER_QUOTE" }
|
|
4170
|
+
);
|
|
4171
|
+
result[key2] = field;
|
|
4172
|
+
}
|
|
4173
|
+
return result;
|
|
4174
|
+
}
|
|
3845
4175
|
async function requestIntent(input) {
|
|
3846
4176
|
const baseUrl = (input.routingApiBaseUrl ?? DEFAULT_ROUTING_API).replace(/\/$/, "");
|
|
3847
4177
|
let response;
|
|
@@ -3863,18 +4193,35 @@ async function requestIntent(input) {
|
|
|
3863
4193
|
action: input.action,
|
|
3864
4194
|
yieldseekerUserId: input.userId,
|
|
3865
4195
|
yieldseekerAgentId: input.agentId
|
|
3866
|
-
})
|
|
4196
|
+
}),
|
|
4197
|
+
signal: AbortSignal.timeout(15e3)
|
|
3867
4198
|
});
|
|
3868
4199
|
} catch {
|
|
3869
|
-
throw sponsorError(
|
|
4200
|
+
throw sponsorError(
|
|
4201
|
+
"Withdrawal sponsorship could not be requested.",
|
|
4202
|
+
true,
|
|
4203
|
+
{ phase: "prepare", rejectedBeforeSubmission: true, failureCode: "INTENT_NETWORK_ERROR" }
|
|
4204
|
+
);
|
|
3870
4205
|
}
|
|
3871
4206
|
const envelope = await response.json().catch(() => null);
|
|
3872
4207
|
const intent = envelope?.data;
|
|
3873
4208
|
if (!response.ok || !envelope?.success || !intent?.intent || !["eoa", "smart"].includes(intent.ownerPath)) {
|
|
4209
|
+
const rawCode = typeof envelope?.failureCode === "string" ? envelope.failureCode : envelope?.code;
|
|
4210
|
+
const failureCode = typeof rawCode === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(rawCode) ? rawCode : void 0;
|
|
4211
|
+
if (input.refreshYieldseekerSignature && (failureCode === "YIELDSEEKER_AUTH_REJECTED" || failureCode === "AUTH_REJECTED" || envelope?.sponsorshipStage === "provider-auth" && (envelope.providerStatus === 401 || envelope.providerStatus === 403))) {
|
|
4212
|
+
const signature = await input.refreshYieldseekerSignature(input.yieldseekerSignature);
|
|
4213
|
+
return requestIntent({ ...input, yieldseekerSignature: signature, refreshYieldseekerSignature: void 0 });
|
|
4214
|
+
}
|
|
3874
4215
|
throw sponsorError(
|
|
3875
4216
|
"Withdrawal sponsorship is currently unavailable.",
|
|
3876
4217
|
true,
|
|
3877
|
-
{
|
|
4218
|
+
{
|
|
4219
|
+
statusCode: response.status,
|
|
4220
|
+
phase: "prepare",
|
|
4221
|
+
rejectedBeforeSubmission: true,
|
|
4222
|
+
failureCode: failureCode ?? "INTENT_REJECTED",
|
|
4223
|
+
providerReason: providerReason(envelope?.message)
|
|
4224
|
+
}
|
|
3878
4225
|
);
|
|
3879
4226
|
}
|
|
3880
4227
|
const url = new URL(`${baseUrl}/api/v1/sponsor/yieldseeker-withdraw/rpc`);
|
|
@@ -3887,19 +4234,55 @@ async function rpc(url, method, params, phase) {
|
|
|
3887
4234
|
response = await fetch(url, {
|
|
3888
4235
|
method: "POST",
|
|
3889
4236
|
headers: { "content-type": "application/json" },
|
|
3890
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
|
|
4237
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
4238
|
+
signal: AbortSignal.timeout(15e3)
|
|
3891
4239
|
});
|
|
3892
4240
|
} catch {
|
|
3893
|
-
throw sponsorError(
|
|
4241
|
+
throw sponsorError(
|
|
4242
|
+
"Withdrawal sponsorship request status is unknown.",
|
|
4243
|
+
phase === "prepare",
|
|
4244
|
+
{
|
|
4245
|
+
phase,
|
|
4246
|
+
rpcMethod: method,
|
|
4247
|
+
...phase === "prepare" ? { rejectedBeforeSubmission: true } : {},
|
|
4248
|
+
failureCode: "RPC_NETWORK_ERROR"
|
|
4249
|
+
}
|
|
4250
|
+
);
|
|
3894
4251
|
}
|
|
3895
4252
|
const payload = await response.json().catch(() => null);
|
|
3896
4253
|
if (!response.ok || payload?.error || payload?.result === void 0) {
|
|
3897
4254
|
const rejection = payload?.error;
|
|
3898
|
-
const
|
|
4255
|
+
const diagnostics = rpcDiagnostics(rejection?.data);
|
|
4256
|
+
const executionReverted = rejection?.code === -32521 || diagnostics.failureCode === "SPONSOR_EXECUTION_REVERTED";
|
|
4257
|
+
const details = {
|
|
4258
|
+
statusCode: response.status,
|
|
4259
|
+
rpcCode: rejection?.code,
|
|
4260
|
+
rpcMethod: method,
|
|
4261
|
+
phase,
|
|
4262
|
+
providerReason: providerReason(rejection?.data?.providerReason) ?? providerReason(rejection?.message),
|
|
4263
|
+
failureCode: "RPC_REJECTED",
|
|
4264
|
+
...diagnostics
|
|
4265
|
+
};
|
|
4266
|
+
if (phase === "prepare" && executionReverted)
|
|
4267
|
+
throw new OwneyError(
|
|
4268
|
+
"WITHDRAW_FAILED",
|
|
4269
|
+
"Yieldseeker withdrawal simulation reverted before submission.",
|
|
4270
|
+
{
|
|
4271
|
+
...details,
|
|
4272
|
+
failureCode: "SPONSOR_EXECUTION_REVERTED",
|
|
4273
|
+
safeToFallback: false,
|
|
4274
|
+
rejectedBeforeSubmission: true
|
|
4275
|
+
},
|
|
4276
|
+
"yieldseeker"
|
|
4277
|
+
);
|
|
4278
|
+
const definitiveRejection = phase === "send" && !executionReverted && response.status < 500 && !!rejection && (rejection.data?.rejectedBeforeSubmission === true || [-32501, -32502, -32503, -32504, -32505, -32507].includes(rejection.code ?? 0) || /maxFeePerGas.*(?:too low|lower than|less than|must be at least)/i.test(rejection.message ?? ""));
|
|
3899
4279
|
throw sponsorError(
|
|
3900
4280
|
phase !== "prepare" && !definitiveRejection ? "Sponsored withdrawal submission status is unknown." : "Withdrawal sponsorship was declined before submission.",
|
|
3901
4281
|
phase === "prepare" || definitiveRejection,
|
|
3902
|
-
{
|
|
4282
|
+
{
|
|
4283
|
+
...details,
|
|
4284
|
+
...phase === "prepare" || definitiveRejection ? { rejectedBeforeSubmission: true } : {}
|
|
4285
|
+
}
|
|
3903
4286
|
);
|
|
3904
4287
|
}
|
|
3905
4288
|
return payload.result;
|
|
@@ -3919,13 +4302,15 @@ async function submitEoa(input) {
|
|
|
3919
4302
|
let nonce;
|
|
3920
4303
|
let fees;
|
|
3921
4304
|
try {
|
|
3922
|
-
nonce = await
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
4305
|
+
[nonce, fees] = await Promise.all([
|
|
4306
|
+
input.client.readContract({
|
|
4307
|
+
address: ENTRY_POINT_V06,
|
|
4308
|
+
abi: ENTRY_POINT_NONCE_ABI,
|
|
4309
|
+
functionName: "getNonce",
|
|
4310
|
+
args: [input.agentWallet, 0n]
|
|
4311
|
+
}),
|
|
4312
|
+
input.client.estimateFeesPerGas()
|
|
4313
|
+
]);
|
|
3929
4314
|
} catch {
|
|
3930
4315
|
throw sponsorError("Could not prepare the sponsored withdrawal.", true);
|
|
3931
4316
|
}
|
|
@@ -3933,14 +4318,14 @@ async function submitEoa(input) {
|
|
|
3933
4318
|
throw sponsorError("Base did not return EIP-1559 gas fees.", true);
|
|
3934
4319
|
let operation = {
|
|
3935
4320
|
sender: input.agentWallet,
|
|
3936
|
-
nonce: (0,
|
|
4321
|
+
nonce: (0, import_viem10.toHex)(nonce),
|
|
3937
4322
|
initCode: "0x",
|
|
3938
4323
|
callData: input.callData,
|
|
3939
|
-
callGasLimit: (0,
|
|
3940
|
-
verificationGasLimit: (0,
|
|
3941
|
-
preVerificationGas: (0,
|
|
3942
|
-
maxFeePerGas: (0,
|
|
3943
|
-
maxPriorityFeePerGas: (0,
|
|
4324
|
+
callGasLimit: (0, import_viem10.toHex)(3e5),
|
|
4325
|
+
verificationGasLimit: (0, import_viem10.toHex)(3e5),
|
|
4326
|
+
preVerificationGas: (0, import_viem10.toHex)(6e4),
|
|
4327
|
+
maxFeePerGas: (0, import_viem10.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
|
|
4328
|
+
maxPriorityFeePerGas: (0, import_viem10.toHex)(fees.maxPriorityFeePerGas),
|
|
3944
4329
|
paymasterAndData: "0x",
|
|
3945
4330
|
signature: "0x"
|
|
3946
4331
|
};
|
|
@@ -3948,7 +4333,7 @@ async function submitEoa(input) {
|
|
|
3948
4333
|
const stub = await rpc(
|
|
3949
4334
|
input.rpcUrl,
|
|
3950
4335
|
"pm_getPaymasterStubData",
|
|
3951
|
-
[operation, ENTRY_POINT_V06, (0,
|
|
4336
|
+
[operation, ENTRY_POINT_V06, (0, import_viem10.toHex)(8453), {}],
|
|
3952
4337
|
"prepare"
|
|
3953
4338
|
);
|
|
3954
4339
|
if (!stub.paymasterAndData?.startsWith("0x"))
|
|
@@ -3963,39 +4348,63 @@ async function submitEoa(input) {
|
|
|
3963
4348
|
const final = await rpc(
|
|
3964
4349
|
input.rpcUrl,
|
|
3965
4350
|
"pm_getPaymasterData",
|
|
3966
|
-
[operation, ENTRY_POINT_V06, (0,
|
|
4351
|
+
[operation, ENTRY_POINT_V06, (0, import_viem10.toHex)(8453), {}],
|
|
3967
4352
|
"prepare"
|
|
3968
4353
|
);
|
|
3969
4354
|
if (!final.paymasterAndData?.startsWith("0x"))
|
|
3970
4355
|
throw sponsorError("CDP did not return final paymaster data.", true);
|
|
3971
|
-
operation = { ...operation,
|
|
4356
|
+
operation = { ...operation, ...finalPaymasterFields(final) };
|
|
3972
4357
|
}
|
|
3973
4358
|
const userOpHash = operationHash(operation);
|
|
3974
|
-
|
|
4359
|
+
await assertOwnerSession(input.state, input.owner);
|
|
4360
|
+
const wallet = (0, import_viem10.createWalletClient)({
|
|
3975
4361
|
account: input.owner,
|
|
3976
4362
|
chain: import_chains3.base,
|
|
3977
|
-
transport: (0,
|
|
4363
|
+
transport: (0, import_viem10.custom)(input.state.provider)
|
|
3978
4364
|
});
|
|
3979
4365
|
const signature = await wallet.signMessage({ message: { raw: userOpHash } });
|
|
3980
4366
|
operation = { ...operation, signature };
|
|
3981
|
-
input.
|
|
4367
|
+
await assertOwnerSession(input.state, input.owner);
|
|
4368
|
+
const userOperation = {
|
|
4369
|
+
sender: operation.sender,
|
|
4370
|
+
nonce: operation.nonce,
|
|
4371
|
+
initCode: operation.initCode,
|
|
4372
|
+
callData: operation.callData,
|
|
4373
|
+
callGasLimit: operation.callGasLimit,
|
|
4374
|
+
verificationGasLimit: operation.verificationGasLimit,
|
|
4375
|
+
preVerificationGas: operation.preVerificationGas,
|
|
4376
|
+
maxFeePerGas: operation.maxFeePerGas,
|
|
4377
|
+
maxPriorityFeePerGas: operation.maxPriorityFeePerGas,
|
|
4378
|
+
paymasterAndData: operation.paymasterAndData
|
|
4379
|
+
};
|
|
4380
|
+
savePending(input.onPending, { userOpHash, userOperation }, true);
|
|
4381
|
+
await checkpoint(input.onBeforeSubmission, { userOpHash, userOperation });
|
|
3982
4382
|
const submittedHash = await rpc(
|
|
3983
4383
|
input.rpcUrl,
|
|
3984
4384
|
"eth_sendUserOperation",
|
|
3985
4385
|
[operation, ENTRY_POINT_V06],
|
|
3986
4386
|
"send"
|
|
3987
4387
|
);
|
|
3988
|
-
if (submittedHash.toLowerCase() !== userOpHash.toLowerCase())
|
|
4388
|
+
if (!isHash(submittedHash) || submittedHash.toLowerCase() !== userOpHash.toLowerCase())
|
|
3989
4389
|
throw sponsorError(
|
|
3990
4390
|
"Bundler returned a different UserOperation hash.",
|
|
3991
4391
|
false,
|
|
3992
|
-
{ userOpHash, submittedHash }
|
|
4392
|
+
{ userOpHash, submittedHash, phase: "send", failureCode: "BUNDLER_HASH_MISMATCH" }
|
|
3993
4393
|
);
|
|
3994
|
-
|
|
3995
|
-
|
|
4394
|
+
const deadline = Date.now() + OBSERVATION_WINDOW_MS;
|
|
4395
|
+
let lastReadError;
|
|
4396
|
+
for (let poll = 0; poll < 40 && Date.now() < deadline; poll++) {
|
|
4397
|
+
let response;
|
|
4398
|
+
try {
|
|
4399
|
+
response = await rpc(input.rpcUrl, "eth_getUserOperationReceipt", [userOpHash], "observe");
|
|
4400
|
+
} catch (error) {
|
|
4401
|
+
lastReadError = error;
|
|
4402
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(OBSERVATION_POLL_MS, Math.max(0, deadline - Date.now()))));
|
|
4403
|
+
continue;
|
|
4404
|
+
}
|
|
3996
4405
|
const txHash = response?.receipt?.transactionHash;
|
|
3997
|
-
if (txHash) {
|
|
3998
|
-
input.onPending
|
|
4406
|
+
if (isHash(txHash)) {
|
|
4407
|
+
savePending(input.onPending, { userOpHash, txHash });
|
|
3999
4408
|
if (response?.success === false)
|
|
4000
4409
|
throw new OwneyError(
|
|
4001
4410
|
"AGENT_TRANSACTION_REVERTED",
|
|
@@ -4005,12 +4414,17 @@ async function submitEoa(input) {
|
|
|
4005
4414
|
);
|
|
4006
4415
|
return { txHash, receipt: await waitForTransaction(input.client, txHash) };
|
|
4007
4416
|
}
|
|
4008
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
4417
|
+
await new Promise((resolve) => setTimeout(resolve, OBSERVATION_POLL_MS));
|
|
4009
4418
|
}
|
|
4010
4419
|
throw sponsorError(
|
|
4011
4420
|
"Sponsored withdrawal is still pending; check status before retrying.",
|
|
4012
4421
|
false,
|
|
4013
|
-
{
|
|
4422
|
+
{
|
|
4423
|
+
userOpHash,
|
|
4424
|
+
phase: "observe",
|
|
4425
|
+
failureCode: "RECEIPT_UNRESOLVED",
|
|
4426
|
+
...lastReadError instanceof OwneyError ? { lastObservationError: lastReadError.details } : {}
|
|
4427
|
+
}
|
|
4014
4428
|
);
|
|
4015
4429
|
}
|
|
4016
4430
|
async function submitSmart(input) {
|
|
@@ -4023,21 +4437,22 @@ async function submitSmart(input) {
|
|
|
4023
4437
|
} catch {
|
|
4024
4438
|
throw sponsorError("This account cannot check paymaster support on Base.", true);
|
|
4025
4439
|
}
|
|
4026
|
-
|
|
4440
|
+
const supportsPaymaster = capabilities?.[(0, import_viem10.toHex)(8453)]?.paymasterService?.supported ?? capabilities?.["8453"]?.paymasterService?.supported ?? capabilities?.["0x0"]?.paymasterService?.supported;
|
|
4441
|
+
if (supportsPaymaster !== true)
|
|
4027
4442
|
throw sponsorError("This account cannot use a paymaster on Base.", true);
|
|
4443
|
+
await assertOwnerSession(input.state, input.owner);
|
|
4028
4444
|
let sent;
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
throw sponsorError("Upgrade or withdrawal status cannot be saved.", true);
|
|
4033
|
-
}
|
|
4445
|
+
const requestedId = (0, import_viem10.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
|
|
4446
|
+
savePending(input.onPending, { callsId: requestedId }, true);
|
|
4447
|
+
await checkpoint(input.onBeforeSubmission, { callsId: requestedId });
|
|
4034
4448
|
try {
|
|
4035
4449
|
sent = await input.state.provider.request({
|
|
4036
4450
|
method: "wallet_sendCalls",
|
|
4037
4451
|
params: [{
|
|
4038
4452
|
version: "2.0.0",
|
|
4039
4453
|
from: input.owner,
|
|
4040
|
-
chainId: (0,
|
|
4454
|
+
chainId: (0, import_viem10.toHex)(8453),
|
|
4455
|
+
id: requestedId,
|
|
4041
4456
|
atomicRequired: false,
|
|
4042
4457
|
calls: [{ to: input.agentWallet, value: "0x0", data: input.callData }],
|
|
4043
4458
|
capabilities: { paymasterService: { url: input.rpcUrl } }
|
|
@@ -4046,17 +4461,27 @@ async function submitSmart(input) {
|
|
|
4046
4461
|
} catch (error) {
|
|
4047
4462
|
if (error?.code === 4001) throw error;
|
|
4048
4463
|
if ([4200, -32601].includes(Number(error?.code)))
|
|
4049
|
-
throw sponsorError(
|
|
4050
|
-
|
|
4464
|
+
throw sponsorError(
|
|
4465
|
+
"This account does not support sponsored calls.",
|
|
4466
|
+
true,
|
|
4467
|
+
{ phase: "send", failureCode: "WALLET_CALLS_UNSUPPORTED" }
|
|
4468
|
+
);
|
|
4469
|
+
throw sponsorError(
|
|
4470
|
+
"Wallet submission status is unknown. Check activity before retrying.",
|
|
4471
|
+
false,
|
|
4472
|
+
{ callsId: requestedId, phase: "send", failureCode: "WALLET_SEND_UNRESOLVED" }
|
|
4473
|
+
);
|
|
4051
4474
|
}
|
|
4052
4475
|
const callsId = typeof sent === "string" ? sent : sent?.id;
|
|
4053
|
-
if (
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4476
|
+
if (typeof callsId !== "string" || callsId.length === 0 || callsId.length > 8194)
|
|
4477
|
+
throw sponsorError(
|
|
4478
|
+
"Wallet returned no calls ID; submission status is unknown.",
|
|
4479
|
+
false,
|
|
4480
|
+
{ callsId: requestedId, phase: "send", failureCode: "WALLET_SEND_UNRESOLVED" }
|
|
4481
|
+
);
|
|
4482
|
+
savePending(input.onPending, { callsId });
|
|
4483
|
+
const deadline = Date.now() + OBSERVATION_WINDOW_MS;
|
|
4484
|
+
for (let poll = 0; poll < 40 && Date.now() < deadline; poll++) {
|
|
4060
4485
|
let status;
|
|
4061
4486
|
try {
|
|
4062
4487
|
status = await input.state.provider.request({
|
|
@@ -4064,37 +4489,44 @@ async function submitSmart(input) {
|
|
|
4064
4489
|
params: [callsId]
|
|
4065
4490
|
});
|
|
4066
4491
|
} catch {
|
|
4067
|
-
|
|
4492
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(OBSERVATION_POLL_MS, Math.max(0, deadline - Date.now()))));
|
|
4493
|
+
continue;
|
|
4068
4494
|
}
|
|
4069
|
-
if (status?.status
|
|
4495
|
+
if (status?.id !== void 0 && status.id !== callsId || status?.chainId !== void 0 && Number(status.chainId) !== 8453)
|
|
4496
|
+
throw sponsorError(
|
|
4497
|
+
"Wallet returned a different sponsored operation status.",
|
|
4498
|
+
false,
|
|
4499
|
+
{ callsId, phase: "observe", failureCode: "WALLET_STATUS_MISMATCH" }
|
|
4500
|
+
);
|
|
4501
|
+
if (Number(status?.status) === 400 || Number(status?.status) === 500)
|
|
4070
4502
|
throw sponsorError(
|
|
4071
4503
|
"Sponsored withdrawal failed in the wallet.",
|
|
4072
4504
|
false,
|
|
4073
|
-
{ callsId, executionFailed: true }
|
|
4505
|
+
{ callsId, executionFailed: true, phase: "observe", failureCode: "WALLET_EXECUTION_FAILED" }
|
|
4074
4506
|
);
|
|
4075
|
-
const
|
|
4507
|
+
const hashes = [...new Set((status?.receipts ?? []).map((receipt) => receipt.transactionHash).filter(isHash))];
|
|
4508
|
+
const txHash = hashes.length === 1 ? hashes[0] : void 0;
|
|
4076
4509
|
if (txHash) {
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
"
|
|
4082
|
-
|
|
4083
|
-
|
|
4510
|
+
savePending(input.onPending, { callsId, txHash });
|
|
4511
|
+
if (status?.receipts?.some((receipt) => receipt.transactionHash === txHash && /^(?:0x0+|0|failed|reverted)$/i.test(String(receipt.status))))
|
|
4512
|
+
throw new OwneyError(
|
|
4513
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
4514
|
+
"Sponsored withdrawal execution reverted.",
|
|
4515
|
+
{ callsId, transactionHash: txHash },
|
|
4516
|
+
"yieldseeker"
|
|
4084
4517
|
);
|
|
4085
|
-
}
|
|
4086
4518
|
return { txHash, receipt: await waitForTransaction(input.client, txHash) };
|
|
4087
4519
|
}
|
|
4088
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
4520
|
+
await new Promise((resolve) => setTimeout(resolve, OBSERVATION_POLL_MS));
|
|
4089
4521
|
}
|
|
4090
4522
|
throw sponsorError(
|
|
4091
4523
|
"Sponsored withdrawal is still pending; check status before retrying.",
|
|
4092
4524
|
false,
|
|
4093
|
-
{ callsId }
|
|
4525
|
+
{ callsId, phase: "observe", failureCode: "RECEIPT_UNRESOLVED" }
|
|
4094
4526
|
);
|
|
4095
4527
|
}
|
|
4096
4528
|
async function submitYieldseekerSponsoredWithdrawal(input) {
|
|
4097
|
-
const owner = (0,
|
|
4529
|
+
const owner = (0, import_viem10.getAddress)(input.state.walletAddress);
|
|
4098
4530
|
const { intent, rpcUrl } = await requestIntent({
|
|
4099
4531
|
apiKey: input.apiKey,
|
|
4100
4532
|
routingApiBaseUrl: input.routingApiBaseUrl,
|
|
@@ -4105,13 +4537,14 @@ async function submitYieldseekerSponsoredWithdrawal(input) {
|
|
|
4105
4537
|
agentWallet: input.agentWallet,
|
|
4106
4538
|
asset: input.asset,
|
|
4107
4539
|
callData: input.callData,
|
|
4108
|
-
action: "withdraw"
|
|
4540
|
+
action: "withdraw",
|
|
4541
|
+
refreshYieldseekerSignature: input.refreshYieldseekerSignature
|
|
4109
4542
|
});
|
|
4110
4543
|
const args = { ...input, owner, rpcUrl };
|
|
4111
4544
|
return intent.ownerPath === "eoa" ? submitEoa(args) : submitSmart(args);
|
|
4112
4545
|
}
|
|
4113
4546
|
async function submitYieldseekerSponsoredUpgrade(input) {
|
|
4114
|
-
const owner = (0,
|
|
4547
|
+
const owner = (0, import_viem10.getAddress)(input.state.walletAddress);
|
|
4115
4548
|
const { intent, rpcUrl } = await requestIntent({
|
|
4116
4549
|
apiKey: input.apiKey,
|
|
4117
4550
|
routingApiBaseUrl: input.routingApiBaseUrl,
|
|
@@ -4122,7 +4555,8 @@ async function submitYieldseekerSponsoredUpgrade(input) {
|
|
|
4122
4555
|
agentWallet: input.agentWallet,
|
|
4123
4556
|
asset: input.asset,
|
|
4124
4557
|
callData: input.callData,
|
|
4125
|
-
action: "upgrade"
|
|
4558
|
+
action: "upgrade",
|
|
4559
|
+
refreshYieldseekerSignature: input.refreshYieldseekerSignature
|
|
4126
4560
|
});
|
|
4127
4561
|
if (intent.ownerPath !== "smart")
|
|
4128
4562
|
throw sponsorError("V1 upgrade sponsorship requires a Base Account owner.", true);
|
|
@@ -4143,6 +4577,8 @@ var YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS = 15e3;
|
|
|
4143
4577
|
var YIELDSEEKER_READ_FAILURE_COOLDOWN_MS = 6e4;
|
|
4144
4578
|
var YIELDSEEKER_SETTLEMENT_WINDOW_MS = 5 * 6e4;
|
|
4145
4579
|
var YIELDSEEKER_ACTIVITY_CACHE_MS = 6e4;
|
|
4580
|
+
var YIELDSEEKER_MAX_WITHDRAW_SWEEPS = 3;
|
|
4581
|
+
var YIELDSEEKER_MAX_POSITION_VERIFICATION_MS = 2e3;
|
|
4146
4582
|
function generateYieldseekerUsername() {
|
|
4147
4583
|
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
4148
4584
|
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
@@ -4153,6 +4589,13 @@ function isUsernameConflict(error) {
|
|
|
4153
4589
|
return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
|
|
4154
4590
|
}
|
|
4155
4591
|
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
4592
|
+
{
|
|
4593
|
+
type: "function",
|
|
4594
|
+
name: "feeTracker",
|
|
4595
|
+
stateMutability: "view",
|
|
4596
|
+
inputs: [],
|
|
4597
|
+
outputs: [{ name: "", type: "address" }]
|
|
4598
|
+
},
|
|
4156
4599
|
{
|
|
4157
4600
|
type: "function",
|
|
4158
4601
|
name: "withdrawAssetToUser",
|
|
@@ -4175,6 +4618,13 @@ var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
|
4175
4618
|
outputs: []
|
|
4176
4619
|
}
|
|
4177
4620
|
];
|
|
4621
|
+
var YIELDSEEKER_FEE_TRACKER_ABI = [{
|
|
4622
|
+
type: "function",
|
|
4623
|
+
name: "getFeesOwed",
|
|
4624
|
+
stateMutability: "view",
|
|
4625
|
+
inputs: [{ name: "wallet", type: "address" }],
|
|
4626
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
4627
|
+
}];
|
|
4178
4628
|
function query(params) {
|
|
4179
4629
|
const search = new URLSearchParams();
|
|
4180
4630
|
for (const [key2, value] of Object.entries(params)) {
|
|
@@ -4254,21 +4704,26 @@ var YieldseekerAgent = class {
|
|
|
4254
4704
|
if (useCachedV2 && cached && Date.now() - cached.at < 15 * 6e4) return { ...cached.status };
|
|
4255
4705
|
const status = await readYieldseekerWalletStatus(
|
|
4256
4706
|
this.getReceiptClient(),
|
|
4257
|
-
(0,
|
|
4258
|
-
(0,
|
|
4707
|
+
(0, import_viem11.getAddress)(context.wallet.walletAddress),
|
|
4708
|
+
(0, import_viem11.getAddress)(state.walletAddress),
|
|
4259
4709
|
YIELDSEEKER_ASSET_METADATA[asset].address
|
|
4260
4710
|
);
|
|
4711
|
+
this.rememberWalletStatus(key2, status);
|
|
4712
|
+
return status;
|
|
4713
|
+
}
|
|
4714
|
+
rememberWalletStatus(key2, status) {
|
|
4261
4715
|
if (status.verified && status.version === "v2") {
|
|
4262
4716
|
this.withdrawalVersionCache.set(key2, { at: Date.now(), status: { ...status } });
|
|
4263
4717
|
if (this.withdrawalVersionCache.size > 128)
|
|
4264
4718
|
this.withdrawalVersionCache.delete(this.withdrawalVersionCache.keys().next().value);
|
|
4265
4719
|
} else this.withdrawalVersionCache.delete(key2);
|
|
4266
|
-
return status;
|
|
4267
4720
|
}
|
|
4268
4721
|
/** Explicit owner action. This never runs as a side effect of withdrawal. */
|
|
4269
4722
|
async upgradeWallet(state, chainId, asset, gasMode = "paid") {
|
|
4723
|
+
const generation = this.readGeneration;
|
|
4270
4724
|
const before = await this.getWalletStatus(state, chainId, asset);
|
|
4271
|
-
|
|
4725
|
+
this.assertSession(generation, chainId);
|
|
4726
|
+
if (!before?.verified || before.version === "unknown") {
|
|
4272
4727
|
throw new OwneyError(
|
|
4273
4728
|
"WITHDRAW_FAILED",
|
|
4274
4729
|
"This Yieldseeker wallet cannot be upgraded to the reviewed V2 implementation.",
|
|
@@ -4276,48 +4731,77 @@ var YieldseekerAgent = class {
|
|
|
4276
4731
|
this.id
|
|
4277
4732
|
);
|
|
4278
4733
|
}
|
|
4279
|
-
const
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
if (!storage5)
|
|
4734
|
+
const versionKey = `${chainId}:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}:${asset}`;
|
|
4735
|
+
const pendingKey2 = `owney.yieldseeker.upgrade:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}`;
|
|
4736
|
+
const confirmUpgrade = async (hash, receipt2) => {
|
|
4737
|
+
this.withdrawalVersionCache.delete(versionKey);
|
|
4738
|
+
const upgradeTopic = (0, import_viem11.encodeEventTopics)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded" })[0];
|
|
4739
|
+
const lastUpgrade = receipt2?.logs.filter((log) => log.address.toLowerCase() === before.agentWallet.toLowerCase() && log.topics[0] === upgradeTopic).at(-1);
|
|
4740
|
+
const expectedUpgrade = (() => {
|
|
4741
|
+
if (!lastUpgrade || lastUpgrade.removed) return false;
|
|
4742
|
+
try {
|
|
4743
|
+
const event = (0, import_viem11.decodeEventLog)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded", data: lastUpgrade.data, topics: lastUpgrade.topics });
|
|
4744
|
+
return event.args.implementation.toLowerCase() === YIELDSEEKER_V2_IMPLEMENTATION.toLowerCase();
|
|
4745
|
+
} catch {
|
|
4746
|
+
return false;
|
|
4747
|
+
}
|
|
4748
|
+
})();
|
|
4749
|
+
if (!receipt2 || receipt2.status !== "success" || receipt2.transactionHash.toLowerCase() !== hash.toLowerCase() || typeof receipt2.blockNumber !== "bigint" || !expectedUpgrade)
|
|
4296
4750
|
throw new OwneyError(
|
|
4297
|
-
"
|
|
4298
|
-
"
|
|
4299
|
-
{
|
|
4751
|
+
"WITHDRAW_FAILED",
|
|
4752
|
+
"Yieldseeker wallet upgrade receipt could not be verified.",
|
|
4753
|
+
{ txHash: hash, reason: "UPGRADE_RECEIPT_MISMATCH" },
|
|
4300
4754
|
this.id
|
|
4301
4755
|
);
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4756
|
+
this.assertSession(generation, chainId);
|
|
4757
|
+
const status = {
|
|
4758
|
+
...before,
|
|
4759
|
+
implementation: YIELDSEEKER_V2_IMPLEMENTATION,
|
|
4760
|
+
version: "v2",
|
|
4761
|
+
upgradeAvailable: false
|
|
4762
|
+
};
|
|
4763
|
+
this.rememberWalletStatus(versionKey, status);
|
|
4764
|
+
return { txHash: hash, status };
|
|
4765
|
+
};
|
|
4766
|
+
let storage5;
|
|
4767
|
+
try {
|
|
4768
|
+
storage5 = typeof window === "undefined" ? void 0 : window.localStorage;
|
|
4769
|
+
} catch {
|
|
4770
|
+
storage5 = void 0;
|
|
4771
|
+
}
|
|
4772
|
+
const clearConfirmedUpgrade = () => {
|
|
4305
4773
|
try {
|
|
4306
|
-
|
|
4774
|
+
storage5?.removeItem(pendingKey2);
|
|
4307
4775
|
} catch {
|
|
4308
|
-
throw new OwneyError(
|
|
4309
|
-
"SPONSOR_REQUEST_FAILED",
|
|
4310
|
-
"Upgrade status cannot be read in this browser.",
|
|
4311
|
-
{ safeToFallback: true },
|
|
4312
|
-
this.id
|
|
4313
|
-
);
|
|
4314
4776
|
}
|
|
4777
|
+
};
|
|
4778
|
+
if (!storage5 && (gasMode === "sponsored" || before.ownerPath === "smart"))
|
|
4779
|
+
throw new OwneyError(
|
|
4780
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4781
|
+
"Upgrade status cannot be saved in this browser.",
|
|
4782
|
+
{ safeToFallback: false },
|
|
4783
|
+
this.id
|
|
4784
|
+
);
|
|
4785
|
+
let prior = null;
|
|
4786
|
+
try {
|
|
4787
|
+
const saved = storage5?.getItem(pendingKey2);
|
|
4315
4788
|
try {
|
|
4316
4789
|
prior = JSON.parse(saved ?? "null");
|
|
4317
4790
|
} catch {
|
|
4318
4791
|
prior = { callsId: void 0 };
|
|
4319
4792
|
}
|
|
4320
|
-
|
|
4793
|
+
} catch {
|
|
4794
|
+
if (gasMode === "paid" && before.ownerPath === "eoa") storage5 = void 0;
|
|
4795
|
+
else throw new OwneyError(
|
|
4796
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4797
|
+
"Upgrade status cannot be read in this browser.",
|
|
4798
|
+
{ safeToFallback: false },
|
|
4799
|
+
this.id
|
|
4800
|
+
);
|
|
4801
|
+
}
|
|
4802
|
+
if (prior) {
|
|
4803
|
+
let earlierHash = this.isTransactionHash(prior.txHash) ? prior.txHash : void 0;
|
|
4804
|
+
if (!earlierHash) {
|
|
4321
4805
|
if (!prior.callsId)
|
|
4322
4806
|
throw new OwneyError(
|
|
4323
4807
|
"SPONSOR_REQUEST_FAILED",
|
|
@@ -4327,10 +4811,7 @@ var YieldseekerAgent = class {
|
|
|
4327
4811
|
);
|
|
4328
4812
|
let callsStatus;
|
|
4329
4813
|
try {
|
|
4330
|
-
callsStatus = await state.provider.request({
|
|
4331
|
-
method: "wallet_getCallsStatus",
|
|
4332
|
-
params: [prior.callsId]
|
|
4333
|
-
});
|
|
4814
|
+
callsStatus = await state.provider.request({ method: "wallet_getCallsStatus", params: [prior.callsId] });
|
|
4334
4815
|
} catch {
|
|
4335
4816
|
throw new OwneyError(
|
|
4336
4817
|
"SPONSOR_REQUEST_FAILED",
|
|
@@ -4339,8 +4820,15 @@ var YieldseekerAgent = class {
|
|
|
4339
4820
|
this.id
|
|
4340
4821
|
);
|
|
4341
4822
|
}
|
|
4342
|
-
if (callsStatus?.
|
|
4343
|
-
|
|
4823
|
+
if (callsStatus?.id !== void 0 && callsStatus.id !== prior.callsId || callsStatus?.chainId !== void 0 && Number(callsStatus.chainId) !== chainId)
|
|
4824
|
+
throw new OwneyError(
|
|
4825
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4826
|
+
"The earlier upgrade returned a different operation status.",
|
|
4827
|
+
{ safeToFallback: false, callsId: prior.callsId },
|
|
4828
|
+
this.id
|
|
4829
|
+
);
|
|
4830
|
+
if (Number(callsStatus?.status) === 400 || Number(callsStatus?.status) === 500) {
|
|
4831
|
+
storage5.removeItem(pendingKey2);
|
|
4344
4832
|
throw new OwneyError(
|
|
4345
4833
|
"SPONSOR_REQUEST_FAILED",
|
|
4346
4834
|
"The earlier sponsored upgrade failed.",
|
|
@@ -4348,22 +4836,39 @@ var YieldseekerAgent = class {
|
|
|
4348
4836
|
this.id
|
|
4349
4837
|
);
|
|
4350
4838
|
}
|
|
4351
|
-
const
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4839
|
+
const hashes = [...new Set((callsStatus?.receipts ?? []).map((value) => value.transactionHash).filter(this.isTransactionHash))];
|
|
4840
|
+
earlierHash = hashes.length === 1 ? hashes[0] : void 0;
|
|
4841
|
+
}
|
|
4842
|
+
if (earlierHash) {
|
|
4843
|
+
const recovered = await confirmUpgrade(earlierHash, await this.waitForReceipt(state, chainId, earlierHash));
|
|
4844
|
+
clearConfirmedUpgrade();
|
|
4845
|
+
return recovered;
|
|
4846
|
+
}
|
|
4847
|
+
throw new OwneyError(
|
|
4848
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4849
|
+
"An earlier upgrade is still pending.",
|
|
4850
|
+
{ safeToFallback: false, callsId: prior.callsId },
|
|
4851
|
+
this.id
|
|
4852
|
+
);
|
|
4853
|
+
}
|
|
4854
|
+
if (!before.upgradeAvailable)
|
|
4855
|
+
throw new OwneyError(
|
|
4856
|
+
"WITHDRAW_FAILED",
|
|
4857
|
+
"This Yieldseeker wallet cannot be upgraded to the reviewed V2 implementation.",
|
|
4858
|
+
{ version: before.version },
|
|
4859
|
+
this.id
|
|
4860
|
+
);
|
|
4861
|
+
const data = (0, import_viem11.encodeFunctionData)({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
|
|
4862
|
+
let txHash;
|
|
4863
|
+
let receipt;
|
|
4864
|
+
if (gasMode === "sponsored") {
|
|
4865
|
+
if (before.ownerPath !== "smart")
|
|
4360
4866
|
throw new OwneyError(
|
|
4361
4867
|
"SPONSOR_REQUEST_FAILED",
|
|
4362
|
-
"
|
|
4363
|
-
{ safeToFallback:
|
|
4868
|
+
"EOA owners pay Base gas for the V1 upgrade.",
|
|
4869
|
+
{ safeToFallback: true },
|
|
4364
4870
|
this.id
|
|
4365
4871
|
);
|
|
4366
|
-
}
|
|
4367
4872
|
const context = await this.findAgent(state, chainId, asset);
|
|
4368
4873
|
if (!context) throw new OwneyError("NOT_CONNECTED", "Yieldseeker agent is unavailable.");
|
|
4369
4874
|
try {
|
|
@@ -4373,14 +4878,15 @@ var YieldseekerAgent = class {
|
|
|
4373
4878
|
apiKey: this.owneyApiKey,
|
|
4374
4879
|
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
4375
4880
|
yieldseekerSignature: await this.auth.getToken(state, chainId),
|
|
4881
|
+
refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
|
|
4376
4882
|
userId: context.user.userId,
|
|
4377
4883
|
agentId: context.agent.agentId,
|
|
4378
4884
|
agentWallet: before.agentWallet,
|
|
4379
4885
|
asset: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
4380
4886
|
callData: data,
|
|
4381
4887
|
onPending: (pending) => {
|
|
4382
|
-
const current = storage5.getItem(
|
|
4383
|
-
storage5.setItem(
|
|
4888
|
+
const current = storage5.getItem(pendingKey2);
|
|
4889
|
+
storage5.setItem(pendingKey2, JSON.stringify({
|
|
4384
4890
|
startedAt: Date.now(),
|
|
4385
4891
|
...current ? JSON.parse(current) : {},
|
|
4386
4892
|
...pending
|
|
@@ -4388,31 +4894,25 @@ var YieldseekerAgent = class {
|
|
|
4388
4894
|
}
|
|
4389
4895
|
});
|
|
4390
4896
|
txHash = upgraded.txHash;
|
|
4897
|
+
receipt = upgraded.receipt;
|
|
4391
4898
|
} catch (error) {
|
|
4392
|
-
if (error?.code === 4001 || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) storage5.removeItem(
|
|
4899
|
+
if (error?.code === 4001 || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) storage5.removeItem(pendingKey2);
|
|
4393
4900
|
throw error;
|
|
4394
4901
|
}
|
|
4395
4902
|
} else {
|
|
4396
4903
|
txHash = await this.submitTransaction(state, chainId, {
|
|
4397
|
-
from: (0,
|
|
4904
|
+
from: (0, import_viem11.getAddress)(state.walletAddress),
|
|
4398
4905
|
to: before.agentWallet,
|
|
4399
4906
|
data,
|
|
4400
4907
|
value: "0",
|
|
4401
4908
|
chainId
|
|
4909
|
+
}, void 0, (value) => {
|
|
4910
|
+
receipt = value;
|
|
4402
4911
|
});
|
|
4403
4912
|
}
|
|
4404
|
-
const
|
|
4405
|
-
if (
|
|
4406
|
-
|
|
4407
|
-
"WITHDRAW_FAILED",
|
|
4408
|
-
"Yieldseeker wallet upgrade was not confirmed as V2.",
|
|
4409
|
-
{ txHash, version: status?.version ?? "missing" },
|
|
4410
|
-
this.id
|
|
4411
|
-
);
|
|
4412
|
-
}
|
|
4413
|
-
if (gasMode === "sponsored" && typeof window !== "undefined")
|
|
4414
|
-
window.localStorage.removeItem(`owney.yieldseeker.upgrade:${state.walletAddress.toLowerCase()}:${before.agentWallet.toLowerCase()}`);
|
|
4415
|
-
return { txHash, status };
|
|
4913
|
+
const confirmed = await confirmUpgrade(txHash, receipt ?? await this.waitForReceipt(state, chainId, txHash));
|
|
4914
|
+
if (gasMode === "sponsored") clearConfirmedUpgrade();
|
|
4915
|
+
return confirmed;
|
|
4416
4916
|
}
|
|
4417
4917
|
async disconnect() {
|
|
4418
4918
|
this.withdrawalVersionCache.clear();
|
|
@@ -4485,19 +4985,37 @@ var YieldseekerAgent = class {
|
|
|
4485
4985
|
submitted(txHash);
|
|
4486
4986
|
receipt = await this.waitForReceipt(state, chainId, txHash);
|
|
4487
4987
|
} else {
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4988
|
+
try {
|
|
4989
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
4990
|
+
from: (0, import_viem11.getAddress)(state.walletAddress),
|
|
4991
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
4992
|
+
data: (0, import_viem11.encodeFunctionData)({
|
|
4993
|
+
abi: import_viem11.erc20Abi,
|
|
4994
|
+
functionName: "transfer",
|
|
4995
|
+
args: [(0, import_viem11.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
4996
|
+
}),
|
|
4997
|
+
value: "0",
|
|
4998
|
+
chainId
|
|
4999
|
+
}, submitted, (value) => {
|
|
5000
|
+
receipt = value;
|
|
5001
|
+
});
|
|
5002
|
+
} catch (error) {
|
|
5003
|
+
if (error instanceof OwneyError && error.details?.ownerTransferNotExecuted === true && error.details.reason === "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL") {
|
|
5004
|
+
notifyFunding(onProgress, {
|
|
5005
|
+
agentId: this.id,
|
|
5006
|
+
chainId,
|
|
5007
|
+
token: asset,
|
|
5008
|
+
walletAddress: state.walletAddress,
|
|
5009
|
+
agentWallet: context.wallet.walletAddress,
|
|
5010
|
+
intendedAmount: amount,
|
|
5011
|
+
txHash: String(error.details.originalTransactionHash),
|
|
5012
|
+
transfer: "failed",
|
|
5013
|
+
registration: "not-applicable",
|
|
5014
|
+
delivery: "not-applicable"
|
|
5015
|
+
});
|
|
5016
|
+
}
|
|
5017
|
+
throw error;
|
|
5018
|
+
}
|
|
4501
5019
|
}
|
|
4502
5020
|
if (this.readGeneration === generation) {
|
|
4503
5021
|
void this.refreshSnapshotAfterMovement(state, chainId, context, "deposit", movement);
|
|
@@ -4507,7 +5025,7 @@ var YieldseekerAgent = class {
|
|
|
4507
5025
|
notifyFunding(onProgress, evidence);
|
|
4508
5026
|
return { txHash, smartWallet: context.wallet.walletAddress, amount, evidence };
|
|
4509
5027
|
}
|
|
4510
|
-
async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto") {
|
|
5028
|
+
async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto", onBeforeSubmission) {
|
|
4511
5029
|
let ownerTransferAttempted = false;
|
|
4512
5030
|
let evidence = {
|
|
4513
5031
|
agentId: this.id,
|
|
@@ -4525,10 +5043,10 @@ var YieldseekerAgent = class {
|
|
|
4525
5043
|
notifyFunding(onProgress, evidence);
|
|
4526
5044
|
}, () => {
|
|
4527
5045
|
ownerTransferAttempted = true;
|
|
4528
|
-
}, gasMode);
|
|
5046
|
+
}, gasMode, onBeforeSubmission);
|
|
4529
5047
|
} catch (error) {
|
|
4530
5048
|
if (error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED") this.withdrawalVersionCache.clear();
|
|
4531
|
-
if (!ownerTransferAttempted || error?.code === 4001 || error instanceof OwneyError && (error.code === "AGENT_TRANSACTION_REVERTED" || error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)))
|
|
5049
|
+
if (!ownerTransferAttempted || error?.code === 4001 || error instanceof OwneyError && (error.code === "AGENT_TRANSACTION_REVERTED" || error.details?.ownerTransferNotExecuted === true || error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)))
|
|
4532
5050
|
notifyFunding(onProgress, {
|
|
4533
5051
|
...evidence,
|
|
4534
5052
|
...error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true ? { userOpHash: void 0, callsId: void 0 } : {},
|
|
@@ -4538,7 +5056,7 @@ var YieldseekerAgent = class {
|
|
|
4538
5056
|
throw error;
|
|
4539
5057
|
}
|
|
4540
5058
|
}
|
|
4541
|
-
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted, gasMode = "auto") {
|
|
5059
|
+
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted, gasMode = "auto", onBeforeSubmission) {
|
|
4542
5060
|
this.assertChain(chainId);
|
|
4543
5061
|
this.assertAsset(asset);
|
|
4544
5062
|
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
@@ -4583,31 +5101,170 @@ var YieldseekerAgent = class {
|
|
|
4583
5101
|
let confirmedMovement = false;
|
|
4584
5102
|
let settlement = null;
|
|
4585
5103
|
try {
|
|
4586
|
-
const
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
context
|
|
4590
|
-
);
|
|
5104
|
+
const maxSettlementBasis = amount === void 0 ? this.snapshotMovement(state, chainId, context, 0n) : null;
|
|
5105
|
+
this.advancePortfolioVersion(this.contextKey(state, chainId, asset));
|
|
5106
|
+
const portfolioRead = amount === void 0 ? this.loadActivityContext(state, chainId, context, {}).then((value) => ({ positions: value.positions, feeRateBps: 0 })) : this.loadPortfolioContext(state, chainId, context).then((value) => ({ positions: value.positions, feeRateBps: value.snapshot.feeRateBps }));
|
|
4591
5107
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
4592
|
-
const
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
5108
|
+
const agentWallet = (0, import_viem11.getAddress)(context.wallet.walletAddress);
|
|
5109
|
+
const feeTrackerRead = withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5110
|
+
address: agentWallet,
|
|
5111
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5112
|
+
functionName: "feeTracker"
|
|
5113
|
+
}), chainId, "eth_call", this.id);
|
|
5114
|
+
const readOwnerFunds = async () => {
|
|
5115
|
+
const feeTracker = await feeTrackerRead;
|
|
5116
|
+
this.assertSession(generation, chainId, asset);
|
|
5117
|
+
if (!(0, import_viem11.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
|
|
5118
|
+
throw this.invalidResponse("wallet fee tracker");
|
|
5119
|
+
const [idle2, fees] = await Promise.all([
|
|
5120
|
+
withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5121
|
+
address: metadata.address,
|
|
5122
|
+
abi: import_viem11.erc20Abi,
|
|
5123
|
+
functionName: "balanceOf",
|
|
5124
|
+
args: [agentWallet]
|
|
5125
|
+
}), chainId, "eth_call", this.id),
|
|
5126
|
+
withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5127
|
+
address: (0, import_viem11.getAddress)(feeTracker),
|
|
5128
|
+
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
5129
|
+
functionName: "getFeesOwed",
|
|
5130
|
+
args: [agentWallet]
|
|
5131
|
+
}), chainId, "eth_call", this.id)
|
|
5132
|
+
]);
|
|
5133
|
+
this.assertSession(generation, chainId, asset);
|
|
5134
|
+
if (typeof idle2 !== "bigint" || idle2 < 0n || typeof fees !== "bigint" || fees < 0n)
|
|
5135
|
+
throw this.invalidResponse("wallet withdrawal balance");
|
|
5136
|
+
return { idle: idle2, fees, available: idle2 > fees ? idle2 - fees : 0n };
|
|
5137
|
+
};
|
|
5138
|
+
const [portfolio, initialFunds] = await Promise.all([portfolioRead, readOwnerFunds()]);
|
|
5139
|
+
const idle = initialFunds.idle;
|
|
5140
|
+
let positions = portfolio.positions;
|
|
5141
|
+
let positionsStatus = "empty";
|
|
5142
|
+
const validatePositions = (values) => {
|
|
5143
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5144
|
+
for (const value of values) {
|
|
5145
|
+
const address = value?.yieldOption?.address;
|
|
5146
|
+
if (typeof address !== "string" || !(0, import_viem11.isAddress)(address) || seen.has(address.toLowerCase()) || typeof value.assetsRaw !== "string" || !/^\d+$/.test(value.assetsRaw) || typeof value.withdrawableAssetsRaw !== "string" || !/^\d+$/.test(value.withdrawableAssetsRaw))
|
|
5147
|
+
throw this.invalidResponse("yield positions");
|
|
5148
|
+
seen.add(address.toLowerCase());
|
|
5149
|
+
}
|
|
5150
|
+
return values;
|
|
5151
|
+
};
|
|
5152
|
+
const zeroValuePositions = /* @__PURE__ */ new WeakSet();
|
|
5153
|
+
const hasPositions = (values) => values.some((position2) => !zeroValuePositions.has(position2) && (BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n));
|
|
5154
|
+
const knownPositions = /* @__PURE__ */ new Map();
|
|
5155
|
+
const rememberPositions = (values) => {
|
|
5156
|
+
for (const position2 of values) knownPositions.set(position2.yieldOption.address.toLowerCase(), position2);
|
|
5157
|
+
};
|
|
5158
|
+
const boundedVerification = async (read2, timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
|
|
5159
|
+
let timer;
|
|
5160
|
+
try {
|
|
5161
|
+
return await Promise.race([read2, new Promise((resolve) => {
|
|
5162
|
+
timer = setTimeout(() => resolve(void 0), Math.max(0, timeoutMs));
|
|
5163
|
+
})]);
|
|
5164
|
+
} finally {
|
|
5165
|
+
if (timer) clearTimeout(timer);
|
|
5166
|
+
}
|
|
5167
|
+
};
|
|
5168
|
+
const verifyKnownPositions = async (blockNumber, values = [...knownPositions.values()], timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
|
|
5169
|
+
const verifiedPositions = [];
|
|
5170
|
+
if (timeoutMs <= 0) return { verified: false, positions: verifiedPositions };
|
|
5171
|
+
const reads = values.map(async (position2) => {
|
|
5172
|
+
const address = (0, import_viem11.getAddress)(position2.yieldOption.address);
|
|
5173
|
+
try {
|
|
5174
|
+
const [underlying, shares, withdrawable] = await Promise.all([
|
|
5175
|
+
this.getReceiptClient().readContract({ address, abi: import_viem11.erc4626Abi, functionName: "asset", blockNumber }),
|
|
5176
|
+
this.getReceiptClient().readContract({ address, abi: import_viem11.erc20Abi, functionName: "balanceOf", args: [agentWallet], blockNumber }),
|
|
5177
|
+
this.getReceiptClient().readContract({ address, abi: import_viem11.erc4626Abi, functionName: "maxWithdraw", args: [agentWallet], blockNumber })
|
|
5178
|
+
]);
|
|
5179
|
+
if (typeof underlying !== "string" || !(0, import_viem11.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
|
|
5180
|
+
const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: withdrawable.toString() };
|
|
5181
|
+
if (shares > 0n && withdrawable === 0n) {
|
|
5182
|
+
let positiveValue = false;
|
|
5183
|
+
const valuations = await Promise.all(["convertToAssets", "previewRedeem"].map(async (functionName) => {
|
|
5184
|
+
try {
|
|
5185
|
+
const value = await this.getReceiptClient().readContract({
|
|
5186
|
+
address,
|
|
5187
|
+
abi: import_viem11.erc4626Abi,
|
|
5188
|
+
functionName,
|
|
5189
|
+
args: [shares],
|
|
5190
|
+
blockNumber
|
|
5191
|
+
});
|
|
5192
|
+
if (typeof value !== "bigint" || value < 0n) return null;
|
|
5193
|
+
if (value > 0n && !positiveValue) {
|
|
5194
|
+
positiveValue = true;
|
|
5195
|
+
verifiedPositions.push(verified);
|
|
5196
|
+
}
|
|
5197
|
+
return value;
|
|
5198
|
+
} catch {
|
|
5199
|
+
return null;
|
|
5200
|
+
}
|
|
5201
|
+
}));
|
|
5202
|
+
if (positiveValue) return verified;
|
|
5203
|
+
if (blockNumber === void 0 || valuations.some((value) => value !== 0n)) return null;
|
|
5204
|
+
zeroValuePositions.add(verified);
|
|
5205
|
+
}
|
|
5206
|
+
verifiedPositions.push(verified);
|
|
5207
|
+
return verified;
|
|
5208
|
+
} catch {
|
|
5209
|
+
return null;
|
|
5210
|
+
}
|
|
5211
|
+
});
|
|
5212
|
+
const result = await boundedVerification(Promise.all(reads), timeoutMs);
|
|
5213
|
+
this.assertSession(generation, chainId, asset);
|
|
5214
|
+
return {
|
|
5215
|
+
verified: result !== void 0 && result.every((position2) => position2 !== null),
|
|
5216
|
+
positions: [...verifiedPositions]
|
|
5217
|
+
};
|
|
5218
|
+
};
|
|
5219
|
+
const refreshPositions = async () => {
|
|
5220
|
+
this.assertSession(generation, chainId, asset);
|
|
5221
|
+
const response = await this.walletRequest(
|
|
5222
|
+
state,
|
|
5223
|
+
chainId,
|
|
5224
|
+
this.agentPath(context, "yield-positions")
|
|
5225
|
+
);
|
|
4599
5226
|
this.assertSession(generation, chainId, asset);
|
|
4600
|
-
|
|
5227
|
+
if (!Array.isArray(response?.yieldPositions)) throw this.invalidResponse("yield positions");
|
|
5228
|
+
return validatePositions(response.yieldPositions);
|
|
4601
5229
|
};
|
|
4602
|
-
|
|
4603
|
-
|
|
5230
|
+
if (amount === void 0) {
|
|
5231
|
+
validatePositions(positions);
|
|
5232
|
+
rememberPositions(positions);
|
|
5233
|
+
positionsStatus = hasPositions(positions) ? "remaining" : "empty";
|
|
5234
|
+
}
|
|
5235
|
+
const deployed = positions.reduce(
|
|
4604
5236
|
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
4605
5237
|
0n
|
|
4606
5238
|
);
|
|
4607
|
-
const
|
|
5239
|
+
const grossAvailable = idle + deployed;
|
|
5240
|
+
const totalAvailable = grossAvailable > initialFunds.fees ? grossAvailable - initialFunds.fees : 0n;
|
|
4608
5241
|
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
4609
5242
|
if (requested === 0n) {
|
|
4610
5243
|
this.invalidateFundingRead(state, chainId, asset);
|
|
5244
|
+
const zeroPayoutCandidates = positions.filter((position2) => BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n);
|
|
5245
|
+
if (amount === void 0 && zeroPayoutCandidates.length) {
|
|
5246
|
+
const deadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
|
|
5247
|
+
const blockNumber = await boundedVerification(
|
|
5248
|
+
Promise.resolve().then(() => this.getReceiptClient().getBlockNumber()).catch(() => void 0)
|
|
5249
|
+
);
|
|
5250
|
+
const proof = typeof blockNumber === "bigint" ? await verifyKnownPositions(blockNumber, zeroPayoutCandidates, deadline - Date.now()) : { verified: false, positions: [] };
|
|
5251
|
+
positions = proof.positions;
|
|
5252
|
+
positionsStatus = hasPositions(positions) ? "remaining" : proof.verified ? "empty" : "unavailable";
|
|
5253
|
+
if (positionsStatus !== "empty")
|
|
5254
|
+
throw new OwneyError(
|
|
5255
|
+
"WITHDRAW_FAILED",
|
|
5256
|
+
"Yieldseeker vault funds are not currently withdrawable.",
|
|
5257
|
+
{ asset, positionsStatus, remainingPositions: positions.map((position2) => position2.yieldOption.address) },
|
|
5258
|
+
this.id
|
|
5259
|
+
);
|
|
5260
|
+
}
|
|
5261
|
+
if (grossAvailable > 0n && totalAvailable === 0n)
|
|
5262
|
+
throw new OwneyError(
|
|
5263
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
5264
|
+
"No owner-withdrawable Yieldseeker funds remain after reserved fees.",
|
|
5265
|
+
{ asset, available: "0", reason: "YIELDSEEKER_FEES_ONLY", feesReserved: initialFunds.fees.toString() },
|
|
5266
|
+
this.id
|
|
5267
|
+
);
|
|
4611
5268
|
throw new OwneyError(
|
|
4612
5269
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4613
5270
|
"No Yieldseeker funds are available to withdraw.",
|
|
@@ -4627,46 +5284,104 @@ var YieldseekerAgent = class {
|
|
|
4627
5284
|
this.id
|
|
4628
5285
|
);
|
|
4629
5286
|
}
|
|
4630
|
-
|
|
5287
|
+
const requiredIdle = requested + initialFunds.fees;
|
|
5288
|
+
let remaining = requiredIdle > idle ? requiredIdle - idle : 0n;
|
|
4631
5289
|
let grossUnwound = 0n;
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
chainId,
|
|
4645
|
-
vaultAddress: position2.yieldOption.address,
|
|
4646
|
-
assetsRaw: assetsRaw.toString()
|
|
5290
|
+
let latestUnwindBlock;
|
|
5291
|
+
const unwind = async (position2, assetsRaw) => {
|
|
5292
|
+
this.assertSession(generation, chainId, asset);
|
|
5293
|
+
let unwindTransactionHash;
|
|
5294
|
+
try {
|
|
5295
|
+
const response = await this.walletRequest(
|
|
5296
|
+
state,
|
|
5297
|
+
chainId,
|
|
5298
|
+
this.agentPath(context, "withdraw-from-position"),
|
|
5299
|
+
{
|
|
5300
|
+
method: "POST",
|
|
5301
|
+
body: { chainId, vaultAddress: position2.yieldOption.address, assetsRaw: assetsRaw.toString() }
|
|
4647
5302
|
}
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
5303
|
+
);
|
|
5304
|
+
if (!this.isTransactionHash(response?.transactionHash)) throw this.invalidResponse("position withdrawal");
|
|
5305
|
+
unwindTransactionHash = response.transactionHash;
|
|
5306
|
+
const unwindReceipt = await this.waitForReceipt(state, chainId, unwindTransactionHash);
|
|
5307
|
+
if (unwindReceipt) latestUnwindBlock = unwindReceipt.blockNumber;
|
|
5308
|
+
} catch (error) {
|
|
5309
|
+
this.invalidateFundingRead(state, chainId, asset);
|
|
5310
|
+
throw new OwneyError(
|
|
5311
|
+
error instanceof OwneyError ? error.code : "WITHDRAW_FAILED",
|
|
5312
|
+
"Could not confirm the Yieldseeker vault release. No owner transfer was submitted.",
|
|
5313
|
+
{
|
|
5314
|
+
...error instanceof OwneyError ? error.details : {},
|
|
5315
|
+
withdrawalPhase: "unwind",
|
|
5316
|
+
reason: "YIELDSEEKER_UNWIND_UNRESOLVED",
|
|
5317
|
+
vaultAddress: position2.yieldOption.address,
|
|
5318
|
+
unwindTransactionHash,
|
|
5319
|
+
ownerTransferSubmitted: false
|
|
5320
|
+
},
|
|
5321
|
+
this.id
|
|
5322
|
+
);
|
|
4652
5323
|
}
|
|
4653
|
-
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
4654
5324
|
confirmedMovement = true;
|
|
5325
|
+
this.assertSession(generation, chainId, asset);
|
|
4655
5326
|
grossUnwound += assetsRaw;
|
|
4656
|
-
|
|
5327
|
+
};
|
|
5328
|
+
if (amount === void 0) {
|
|
5329
|
+
for (let sweep = 0; sweep < YIELDSEEKER_MAX_WITHDRAW_SWEEPS && hasPositions(positions); sweep++) {
|
|
5330
|
+
const before = new Map(positions.map((position2) => [position2.yieldOption.address.toLowerCase(), BigInt(position2.withdrawableAssetsRaw)]));
|
|
5331
|
+
let unwound = false;
|
|
5332
|
+
for (const position2 of positions) {
|
|
5333
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
5334
|
+
if (available <= 0n) continue;
|
|
5335
|
+
await unwind(position2, available);
|
|
5336
|
+
unwound = true;
|
|
5337
|
+
}
|
|
5338
|
+
if (!unwound) break;
|
|
5339
|
+
const knownBeforeRefresh = new Set(knownPositions.keys());
|
|
5340
|
+
const [catalog, proof] = await Promise.all([
|
|
5341
|
+
boundedVerification(refreshPositions().catch(() => void 0)),
|
|
5342
|
+
verifyKnownPositions(latestUnwindBlock)
|
|
5343
|
+
]);
|
|
5344
|
+
if (catalog) rememberPositions(catalog);
|
|
5345
|
+
this.assertSession(generation, chainId, asset);
|
|
5346
|
+
if (proof.verified) {
|
|
5347
|
+
positions = [...proof.positions, ...(catalog ?? []).filter((position2) => !knownBeforeRefresh.has(position2.yieldOption.address.toLowerCase()))];
|
|
5348
|
+
} else if (catalog) positions = catalog;
|
|
5349
|
+
else {
|
|
5350
|
+
positionsStatus = "unavailable";
|
|
5351
|
+
break;
|
|
5352
|
+
}
|
|
5353
|
+
positionsStatus = hasPositions(positions) ? "remaining" : "empty";
|
|
5354
|
+
if (positions.some((position2) => BigInt(position2.withdrawableAssetsRaw) > 0n && before.has(position2.yieldOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) >= before.get(position2.yieldOption.address.toLowerCase()))) break;
|
|
5355
|
+
}
|
|
5356
|
+
} else {
|
|
5357
|
+
for (const position2 of positions) {
|
|
5358
|
+
if (remaining === 0n) break;
|
|
5359
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
5360
|
+
if (available <= 0n) continue;
|
|
5361
|
+
const assetsRaw = available < remaining ? available : remaining;
|
|
5362
|
+
await unwind(position2, assetsRaw);
|
|
5363
|
+
remaining -= assetsRaw;
|
|
5364
|
+
}
|
|
4657
5365
|
}
|
|
4658
|
-
if (remaining > 0n) {
|
|
5366
|
+
if (amount !== void 0 && remaining > 0n) {
|
|
4659
5367
|
throw this.invalidResponse("yield positions", {
|
|
4660
5368
|
reason: "Withdrawable positions could not cover the request.",
|
|
4661
5369
|
remaining: remaining.toString()
|
|
4662
5370
|
});
|
|
4663
5371
|
}
|
|
4664
|
-
const availableForOwner = confirmedMovement ? await
|
|
4665
|
-
const feeRate = portfolio.
|
|
5372
|
+
const availableForOwner = confirmedMovement ? (await readOwnerFunds()).available : initialFunds.available;
|
|
5373
|
+
const feeRate = portfolio.feeRateBps;
|
|
4666
5374
|
const maxFee = confirmedMovement && Number.isInteger(feeRate) && feeRate >= 0 && feeRate <= 1e3 ? (grossUnwound * BigInt(feeRate) + 9999n) / 10000n : 0n;
|
|
4667
5375
|
const shortfall = availableForOwner < requested ? requested - availableForOwner : 0n;
|
|
4668
5376
|
if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee) {
|
|
4669
5377
|
this.invalidateFundingRead(state, chainId, asset);
|
|
5378
|
+
if (amount === void 0 && positionsStatus !== "empty")
|
|
5379
|
+
throw new OwneyError(
|
|
5380
|
+
"WITHDRAW_FAILED",
|
|
5381
|
+
"Yieldseeker vault funds could not be cleared for the owner transfer.",
|
|
5382
|
+
{ asset, positionsStatus },
|
|
5383
|
+
this.id
|
|
5384
|
+
);
|
|
4670
5385
|
throw new OwneyError(
|
|
4671
5386
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4672
5387
|
"Yieldseeker funds are not available for the owner transfer.",
|
|
@@ -4675,20 +5390,23 @@ var YieldseekerAgent = class {
|
|
|
4675
5390
|
);
|
|
4676
5391
|
}
|
|
4677
5392
|
const payout = amount === void 0 || availableForOwner < requested ? availableForOwner : requested;
|
|
4678
|
-
const plannedMovement =
|
|
4679
|
-
|
|
5393
|
+
const plannedMovement = amount === void 0 && maxSettlementBasis ? {
|
|
5394
|
+
...maxSettlementBasis,
|
|
5395
|
+
delta: maxSettlementBasis.delta - payout,
|
|
5396
|
+
expectedNetDeposits: maxSettlementBasis.expectedNetDeposits - payout
|
|
5397
|
+
} : this.snapshotMovement(state, chainId, context, -payout);
|
|
5398
|
+
const account = (0, import_viem11.getAddress)(state.walletAddress);
|
|
4680
5399
|
let receipt;
|
|
4681
5400
|
const base6 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount === void 0 ? "MAX" : payout.toString(), transfer: "pending", registration: "not-applicable", delivery: "pending" };
|
|
4682
|
-
const data = amount === void 0 ? (0,
|
|
5401
|
+
const data = amount === void 0 ? (0, import_viem11.encodeFunctionData)({
|
|
4683
5402
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
4684
5403
|
functionName: "withdrawAllAssetToUser",
|
|
4685
5404
|
args: [account, metadata.address]
|
|
4686
|
-
}) : (0,
|
|
5405
|
+
}) : (0, import_viem11.encodeFunctionData)({
|
|
4687
5406
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
4688
5407
|
functionName: "withdrawAssetToUser",
|
|
4689
5408
|
args: [account, metadata.address, payout]
|
|
4690
5409
|
});
|
|
4691
|
-
onOwnerTransferAttempted?.();
|
|
4692
5410
|
let txHash;
|
|
4693
5411
|
if (sponsor) {
|
|
4694
5412
|
const submitted = await submitYieldseekerSponsoredWithdrawal({
|
|
@@ -4697,19 +5415,25 @@ var YieldseekerAgent = class {
|
|
|
4697
5415
|
apiKey: this.owneyApiKey,
|
|
4698
5416
|
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
4699
5417
|
yieldseekerSignature: await this.auth.getToken(state, chainId),
|
|
5418
|
+
refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
|
|
4700
5419
|
userId: context.user.userId,
|
|
4701
5420
|
agentId: context.agent.agentId,
|
|
4702
|
-
agentWallet: (0,
|
|
5421
|
+
agentWallet: (0, import_viem11.getAddress)(context.wallet.walletAddress),
|
|
4703
5422
|
asset: metadata.address,
|
|
4704
5423
|
callData: data,
|
|
4705
|
-
onPending: (pending) => notifyFunding(onProgress, { ...base6, ...pending })
|
|
5424
|
+
onPending: (pending) => notifyFunding(onProgress, { ...base6, ...pending }),
|
|
5425
|
+
onBeforeSubmission: async (pending) => {
|
|
5426
|
+
await onBeforeSubmission?.({ ...base6, ...pending });
|
|
5427
|
+
onOwnerTransferAttempted?.();
|
|
5428
|
+
}
|
|
4706
5429
|
});
|
|
4707
5430
|
txHash = submitted.txHash;
|
|
4708
5431
|
receipt = submitted.receipt;
|
|
4709
5432
|
} else {
|
|
5433
|
+
onOwnerTransferAttempted?.();
|
|
4710
5434
|
txHash = await this.submitTransaction(state, chainId, {
|
|
4711
5435
|
from: account,
|
|
4712
|
-
to: (0,
|
|
5436
|
+
to: (0, import_viem11.getAddress)(context.wallet.walletAddress),
|
|
4713
5437
|
data,
|
|
4714
5438
|
value: "0",
|
|
4715
5439
|
chainId
|
|
@@ -4721,6 +5445,46 @@ var YieldseekerAgent = class {
|
|
|
4721
5445
|
settlement = plannedMovement;
|
|
4722
5446
|
const evidence = receipt ? inspectFundingReceipt(receipt, { ...base6, txHash }, metadata.address, "withdraw") : { ...base6, txHash, confirmedAmount: payout.toString(), transfer: "confirmed", delivery: "confirmed" };
|
|
4723
5447
|
notifyFunding(onProgress, evidence);
|
|
5448
|
+
let completion;
|
|
5449
|
+
if (amount === void 0) {
|
|
5450
|
+
const verificationDeadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
|
|
5451
|
+
const knownAtPayout = new Set(knownPositions.keys());
|
|
5452
|
+
const [catalogVerified, knownProof] = await Promise.all([
|
|
5453
|
+
boundedVerification(refreshPositions().then((values) => {
|
|
5454
|
+
rememberPositions(values);
|
|
5455
|
+
return true;
|
|
5456
|
+
}).catch(() => false)),
|
|
5457
|
+
verifyKnownPositions(receipt?.blockNumber)
|
|
5458
|
+
]);
|
|
5459
|
+
const newPositions = [...knownPositions].filter(([address]) => !knownAtPayout.has(address)).map(([, position2]) => position2);
|
|
5460
|
+
const newProof = newPositions.length ? await verifyKnownPositions(receipt?.blockNumber, newPositions, verificationDeadline - Date.now()) : { verified: true, positions: [] };
|
|
5461
|
+
const proof = {
|
|
5462
|
+
verified: catalogVerified === true && knownProof.verified && newProof.verified,
|
|
5463
|
+
positions: [...knownProof.positions, ...newProof.positions]
|
|
5464
|
+
};
|
|
5465
|
+
positions = proof.positions;
|
|
5466
|
+
positionsStatus = hasPositions(positions) ? "remaining" : proof.verified ? "empty" : "unavailable";
|
|
5467
|
+
if (positionsStatus === "remaining") {
|
|
5468
|
+
const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? payout.toString(), evidence };
|
|
5469
|
+
throw new OwneyError(
|
|
5470
|
+
"WITHDRAW_PARTIAL_FAILURE",
|
|
5471
|
+
"Yieldseeker MAX could not confirm that all vault positions were cleared.",
|
|
5472
|
+
{
|
|
5473
|
+
reason: "YIELDSEEKER_MAX_INCOMPLETE",
|
|
5474
|
+
asset,
|
|
5475
|
+
positionsStatus,
|
|
5476
|
+
remainingPositions: positionsStatus === "remaining" ? positions.filter((position2) => !zeroValuePositions.has(position2) && (BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n)).map((position2) => ({
|
|
5477
|
+
vaultAddress: position2.yieldOption.address,
|
|
5478
|
+
sharesRaw: position2.assetsRaw,
|
|
5479
|
+
withdrawableAmountRaw: position2.withdrawableAssetsRaw
|
|
5480
|
+
})) : void 0,
|
|
5481
|
+
partialResults: { [this.id]: result }
|
|
5482
|
+
},
|
|
5483
|
+
this.id
|
|
5484
|
+
);
|
|
5485
|
+
}
|
|
5486
|
+
if (!proof.verified) completion = "unverified";
|
|
5487
|
+
}
|
|
4724
5488
|
if (evidence.transfer === "failed" && evidence.confirmedAmount === "0")
|
|
4725
5489
|
throw new OwneyError(
|
|
4726
5490
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
@@ -4728,7 +5492,13 @@ var YieldseekerAgent = class {
|
|
|
4728
5492
|
{ asset, available: "0", txHash },
|
|
4729
5493
|
this.id
|
|
4730
5494
|
);
|
|
4731
|
-
return {
|
|
5495
|
+
return {
|
|
5496
|
+
txHash,
|
|
5497
|
+
type: amount === void 0 && !completion ? "full" : "partial",
|
|
5498
|
+
...completion ? { completion } : {},
|
|
5499
|
+
amount: evidence.confirmedAmount ?? payout.toString(),
|
|
5500
|
+
evidence
|
|
5501
|
+
};
|
|
4732
5502
|
} finally {
|
|
4733
5503
|
if (confirmedMovement && this.readGeneration === generation) {
|
|
4734
5504
|
void this.refreshSnapshotAfterMovement(
|
|
@@ -4775,6 +5545,23 @@ var YieldseekerAgent = class {
|
|
|
4775
5545
|
if (!context) throw new OwneyError("NOT_CONNECTED", "Reconnect the agent before recovering funding.");
|
|
4776
5546
|
return context.wallet.walletAddress;
|
|
4777
5547
|
}
|
|
5548
|
+
async hasExistingDepositFunds(state, chainId, asset) {
|
|
5549
|
+
this.assertChain(chainId);
|
|
5550
|
+
this.assertAsset(asset);
|
|
5551
|
+
const generation = this.readGeneration;
|
|
5552
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
5553
|
+
this.assertSession(generation, chainId, asset);
|
|
5554
|
+
if (!context) return void 0;
|
|
5555
|
+
const idle = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5556
|
+
address: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
5557
|
+
abi: import_viem11.erc20Abi,
|
|
5558
|
+
functionName: "balanceOf",
|
|
5559
|
+
args: [(0, import_viem11.getAddress)(context.wallet.walletAddress)]
|
|
5560
|
+
}), chainId, "eth_call", this.id);
|
|
5561
|
+
this.assertSession(generation, chainId, asset);
|
|
5562
|
+
if (typeof idle !== "bigint" || idle < 0n) throw this.invalidResponse("wallet deposit balance");
|
|
5563
|
+
return idle > 0n ? true : void 0;
|
|
5564
|
+
}
|
|
4778
5565
|
async getEarnings(state, chainId) {
|
|
4779
5566
|
this.assertChain(chainId);
|
|
4780
5567
|
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
@@ -4805,7 +5592,7 @@ var YieldseekerAgent = class {
|
|
|
4805
5592
|
);
|
|
4806
5593
|
const vaultAddresses = new Set(
|
|
4807
5594
|
catalog.flat().filter(
|
|
4808
|
-
(yieldOption) => yieldOption.chainId === chainId && (0,
|
|
5595
|
+
(yieldOption) => yieldOption.chainId === chainId && (0, import_viem11.isAddress)(yieldOption.address)
|
|
4809
5596
|
).map((yieldOption) => yieldOption.address.toLowerCase())
|
|
4810
5597
|
);
|
|
4811
5598
|
return mapYieldseekerHistory(contexts, {
|
|
@@ -4932,7 +5719,7 @@ var YieldseekerAgent = class {
|
|
|
4932
5719
|
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
4933
5720
|
).then((walletResponse) => {
|
|
4934
5721
|
this.assertSession(generation, chainId, asset);
|
|
4935
|
-
if (!walletResponse?.agentWallet || !(0,
|
|
5722
|
+
if (!walletResponse?.agentWallet || !(0, import_viem11.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
4936
5723
|
throw this.invalidResponse("agent wallet");
|
|
4937
5724
|
}
|
|
4938
5725
|
const context = {
|
|
@@ -4962,7 +5749,7 @@ var YieldseekerAgent = class {
|
|
|
4962
5749
|
this.users.set(key2, persisted);
|
|
4963
5750
|
return persisted;
|
|
4964
5751
|
}
|
|
4965
|
-
const walletAddress = (0,
|
|
5752
|
+
const walletAddress = (0, import_viem11.getAddress)(state.walletAddress);
|
|
4966
5753
|
const generation = this.readGeneration;
|
|
4967
5754
|
let user = null;
|
|
4968
5755
|
try {
|
|
@@ -5345,7 +6132,7 @@ var YieldseekerAgent = class {
|
|
|
5345
6132
|
this.agentPath(context, "deploy"),
|
|
5346
6133
|
{ method: "POST", body: {} }
|
|
5347
6134
|
);
|
|
5348
|
-
if (!deployed?.agentWallet || !(0,
|
|
6135
|
+
if (!deployed?.agentWallet || !(0, import_viem11.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
|
|
5349
6136
|
throw this.invalidResponse("agent deployment", {
|
|
5350
6137
|
reason: "Deploy did not return the expected Agent Wallet."
|
|
5351
6138
|
});
|
|
@@ -5453,15 +6240,15 @@ var YieldseekerAgent = class {
|
|
|
5453
6240
|
return hash2;
|
|
5454
6241
|
}
|
|
5455
6242
|
this.assertTransaction(transaction, state, chainId);
|
|
5456
|
-
const account = (0,
|
|
5457
|
-
const walletClient = (0,
|
|
6243
|
+
const account = (0, import_viem11.getAddress)(state.walletAddress);
|
|
6244
|
+
const walletClient = (0, import_viem11.createWalletClient)({
|
|
5458
6245
|
account,
|
|
5459
6246
|
chain: import_chains4.base,
|
|
5460
|
-
transport: (0,
|
|
6247
|
+
transport: (0, import_viem11.custom)(state.provider)
|
|
5461
6248
|
});
|
|
5462
|
-
const walletChainClient = (0,
|
|
6249
|
+
const walletChainClient = (0, import_viem11.createPublicClient)({
|
|
5463
6250
|
chain: import_chains4.base,
|
|
5464
|
-
transport: (0,
|
|
6251
|
+
transport: (0, import_viem11.custom)(state.provider)
|
|
5465
6252
|
});
|
|
5466
6253
|
await ensureWalletOnChain(
|
|
5467
6254
|
walletChainClient,
|
|
@@ -5471,30 +6258,84 @@ var YieldseekerAgent = class {
|
|
|
5471
6258
|
const hash = await walletClient.sendTransaction({
|
|
5472
6259
|
account,
|
|
5473
6260
|
chain: import_chains4.base,
|
|
5474
|
-
to: (0,
|
|
6261
|
+
to: (0, import_viem11.getAddress)(transaction.to),
|
|
5475
6262
|
data: transaction.data,
|
|
5476
6263
|
value: BigInt(transaction.value)
|
|
5477
6264
|
});
|
|
5478
6265
|
onSubmitted?.(hash);
|
|
6266
|
+
let replacement;
|
|
5479
6267
|
const receipt = await withPaidRpcDiagnostics(
|
|
5480
6268
|
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
5481
6269
|
hash,
|
|
5482
|
-
confirmations: 1
|
|
6270
|
+
confirmations: 1,
|
|
6271
|
+
onReplaced: (value) => {
|
|
6272
|
+
replacement = value;
|
|
6273
|
+
}
|
|
5483
6274
|
}),
|
|
5484
6275
|
8453,
|
|
5485
6276
|
"eth_getTransactionReceipt",
|
|
5486
6277
|
this.id
|
|
5487
6278
|
);
|
|
6279
|
+
const actualHash = receipt.transactionHash;
|
|
6280
|
+
if (!this.isTransactionHash(actualHash))
|
|
6281
|
+
throw new OwneyError(
|
|
6282
|
+
"WITHDRAW_FAILED",
|
|
6283
|
+
"The confirmed transaction hash could not be verified.",
|
|
6284
|
+
{ originalTransactionHash: hash, ownerTransferNotExecuted: false },
|
|
6285
|
+
this.id
|
|
6286
|
+
);
|
|
6287
|
+
if (actualHash.toLowerCase() !== hash.toLowerCase()) {
|
|
6288
|
+
if (!replacement || replacement.transaction.hash.toLowerCase() !== actualHash.toLowerCase()) {
|
|
6289
|
+
try {
|
|
6290
|
+
const [original2, actual2] = await Promise.all([
|
|
6291
|
+
this.getReceiptClient().getTransaction({ hash }),
|
|
6292
|
+
this.getReceiptClient().getTransaction({ hash: actualHash })
|
|
6293
|
+
]);
|
|
6294
|
+
replacement = { replacedTransaction: original2, transaction: actual2 };
|
|
6295
|
+
} catch {
|
|
6296
|
+
replacement = void 0;
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6299
|
+
const original = replacement?.replacedTransaction;
|
|
6300
|
+
const actual = replacement?.transaction;
|
|
6301
|
+
const sameNonce = !!original && !!actual && original.hash.toLowerCase() === hash.toLowerCase() && actual.hash.toLowerCase() === actualHash.toLowerCase() && original.from.toLowerCase() === account.toLowerCase() && actual.from.toLowerCase() === account.toLowerCase() && original.nonce === actual.nonce && (actual.chainId === void 0 || actual.chainId === chainId) && receipt.blockNumber !== null && receipt.blockNumber !== void 0;
|
|
6302
|
+
if (!sameNonce)
|
|
6303
|
+
throw new OwneyError(
|
|
6304
|
+
"WITHDRAW_FAILED",
|
|
6305
|
+
"The replacement transaction could not be verified.",
|
|
6306
|
+
{
|
|
6307
|
+
originalTransactionHash: hash,
|
|
6308
|
+
replacementTransactionHash: actualHash,
|
|
6309
|
+
reason: "TRANSACTION_REPLACEMENT_UNRESOLVED",
|
|
6310
|
+
ownerTransferNotExecuted: false
|
|
6311
|
+
},
|
|
6312
|
+
this.id
|
|
6313
|
+
);
|
|
6314
|
+
const sameCall = actual.to?.toLowerCase() === transaction.to.toLowerCase() && actual.input?.toLowerCase() === transaction.data.toLowerCase() && actual.value === BigInt(transaction.value);
|
|
6315
|
+
if (!sameCall)
|
|
6316
|
+
throw new OwneyError(
|
|
6317
|
+
"WITHDRAW_FAILED",
|
|
6318
|
+
"The transaction was replaced without executing this withdrawal.",
|
|
6319
|
+
{
|
|
6320
|
+
originalTransactionHash: hash,
|
|
6321
|
+
replacementTransactionHash: actualHash,
|
|
6322
|
+
reason: "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL",
|
|
6323
|
+
ownerTransferNotExecuted: true
|
|
6324
|
+
},
|
|
6325
|
+
this.id
|
|
6326
|
+
);
|
|
6327
|
+
onSubmitted?.(actualHash);
|
|
6328
|
+
}
|
|
5488
6329
|
if (receipt.status !== "success") {
|
|
5489
6330
|
throw new OwneyError(
|
|
5490
6331
|
"AGENT_TRANSACTION_REVERTED",
|
|
5491
|
-
`Yieldseeker transaction reverted (${
|
|
5492
|
-
{ transactionHash:
|
|
6332
|
+
`Yieldseeker transaction reverted (${actualHash}).`,
|
|
6333
|
+
{ transactionHash: actualHash },
|
|
5493
6334
|
this.id
|
|
5494
6335
|
);
|
|
5495
6336
|
}
|
|
5496
6337
|
onReceipt?.(receipt);
|
|
5497
|
-
return
|
|
6338
|
+
return actualHash;
|
|
5498
6339
|
}
|
|
5499
6340
|
async waitForReceipt(state, chainId, transactionHash) {
|
|
5500
6341
|
if (this.unwindReceiptWaiter) {
|
|
@@ -5521,12 +6362,12 @@ var YieldseekerAgent = class {
|
|
|
5521
6362
|
return receipt;
|
|
5522
6363
|
}
|
|
5523
6364
|
assertTransaction(transaction, state, chainId) {
|
|
5524
|
-
if (!transaction || typeof transaction.from !== "string" || !(0,
|
|
6365
|
+
if (!transaction || typeof transaction.from !== "string" || !(0, import_viem11.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem11.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem11.getAddress)(transaction.from) !== (0, import_viem11.getAddress)(state.walletAddress)) {
|
|
5525
6366
|
throw this.invalidResponse("transaction");
|
|
5526
6367
|
}
|
|
5527
6368
|
}
|
|
5528
6369
|
assertAgent(agent) {
|
|
5529
|
-
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0,
|
|
6370
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem11.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
|
|
5530
6371
|
throw this.invalidResponse("agent");
|
|
5531
6372
|
}
|
|
5532
6373
|
}
|
|
@@ -5680,7 +6521,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
|
5680
6521
|
}
|
|
5681
6522
|
|
|
5682
6523
|
// src/lib/helpers/withdraw-helper.ts
|
|
5683
|
-
var
|
|
6524
|
+
var import_viem12 = require("viem");
|
|
5684
6525
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
5685
6526
|
const target = asset.toUpperCase();
|
|
5686
6527
|
return agents.map((agent) => {
|
|
@@ -5688,7 +6529,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
5688
6529
|
const tokenBalance = agentBalance?.tokens.find(
|
|
5689
6530
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
5690
6531
|
);
|
|
5691
|
-
let balance = tokenBalance ? (0,
|
|
6532
|
+
let balance = tokenBalance ? (0, import_viem12.parseUnits)(tokenBalance.amount, decimals) : 0n;
|
|
5692
6533
|
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
5693
6534
|
const chainNameById = {
|
|
5694
6535
|
1: "ETHEREUM",
|
|
@@ -5700,14 +6541,14 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
5700
6541
|
const positionChain = position2.chain.trim().toUpperCase();
|
|
5701
6542
|
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
5702
6543
|
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
5703
|
-
balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0,
|
|
6544
|
+
balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0, import_viem12.parseUnits)(position2.amount, decimals);
|
|
5704
6545
|
}
|
|
5705
6546
|
}
|
|
5706
6547
|
const assetTotal = agentBalance?.assetBalances?.find(
|
|
5707
6548
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
5708
6549
|
);
|
|
5709
6550
|
if (assetTotal) {
|
|
5710
|
-
const total = (0,
|
|
6551
|
+
const total = (0, import_viem12.parseUnits)(assetTotal.amount, decimals);
|
|
5711
6552
|
if (total < balance) balance = total > 0n ? total : 0n;
|
|
5712
6553
|
}
|
|
5713
6554
|
return { agent, balance };
|
|
@@ -5866,14 +6707,14 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
5866
6707
|
}
|
|
5867
6708
|
|
|
5868
6709
|
// src/client.ts
|
|
5869
|
-
var
|
|
6710
|
+
var import_viem16 = require("viem");
|
|
5870
6711
|
var import_chains5 = require("viem/chains");
|
|
5871
6712
|
|
|
5872
6713
|
// src/lib/sponsored-token-batch.ts
|
|
5873
|
-
var
|
|
6714
|
+
var import_viem14 = require("viem");
|
|
5874
6715
|
|
|
5875
6716
|
// src/lib/permit2-batch.ts
|
|
5876
|
-
var
|
|
6717
|
+
var import_viem13 = require("viem");
|
|
5877
6718
|
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
5878
6719
|
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
5879
6720
|
var ERC2612_PERMIT_TYPES = {
|
|
@@ -5885,7 +6726,7 @@ var ERC2612_PERMIT_TYPES = {
|
|
|
5885
6726
|
{ name: "deadline", type: "uint256" }
|
|
5886
6727
|
]
|
|
5887
6728
|
};
|
|
5888
|
-
var ERC2612_READ_ABI = (0,
|
|
6729
|
+
var ERC2612_READ_ABI = (0, import_viem13.parseAbi)([
|
|
5889
6730
|
"function name() view returns (string)",
|
|
5890
6731
|
"function version() view returns (string)",
|
|
5891
6732
|
"function nonces(address owner) view returns (uint256)"
|
|
@@ -5923,7 +6764,7 @@ var PERMIT_BATCH_TYPES = {
|
|
|
5923
6764
|
{ name: "amount", type: "uint256" }
|
|
5924
6765
|
]
|
|
5925
6766
|
};
|
|
5926
|
-
var PERMIT2_BATCH_ABI = (0,
|
|
6767
|
+
var PERMIT2_BATCH_ABI = (0, import_viem13.parseAbi)([
|
|
5927
6768
|
"struct TokenPermissions { address token; uint256 amount; }",
|
|
5928
6769
|
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
5929
6770
|
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
@@ -5966,6 +6807,18 @@ var planOf = (transfers) => JSON.stringify(
|
|
|
5966
6807
|
amount: BigInt(t.amount).toString()
|
|
5967
6808
|
}))
|
|
5968
6809
|
);
|
|
6810
|
+
var pendingKey = (i) => `owney.token-batch.v1:${(0, import_viem14.keccak256)((0, import_viem14.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
|
|
6811
|
+
function pendingTokenBatch(i) {
|
|
6812
|
+
try {
|
|
6813
|
+
const saved = read(pendingKey(i));
|
|
6814
|
+
if (!saved) return;
|
|
6815
|
+
const body = JSON.parse(saved);
|
|
6816
|
+
if (!body.serializedTransaction || body.chainId !== i.chainId || !(0, import_viem14.isAddressEqual)(body.from, i.owner) || !(0, import_viem14.isAddressEqual)(body.token, i.token) || !body.transfers?.length) return;
|
|
6817
|
+
return { txHash: (0, import_viem14.keccak256)(body.serializedTransaction), transfers: body.transfers.map(({ to, amount }) => ({ to, amount })) };
|
|
6818
|
+
} catch {
|
|
6819
|
+
return;
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
5969
6822
|
function read(key2) {
|
|
5970
6823
|
return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
|
|
5971
6824
|
}
|
|
@@ -5982,11 +6835,11 @@ function clear(key2) {
|
|
|
5982
6835
|
else window.localStorage.removeItem(key2);
|
|
5983
6836
|
}
|
|
5984
6837
|
function sponsorTokenBatch(i) {
|
|
5985
|
-
const key2 =
|
|
6838
|
+
const key2 = pendingKey(i);
|
|
5986
6839
|
const plan = planOf(i.transfers);
|
|
5987
6840
|
const active = inflight.get(key2);
|
|
5988
6841
|
if (active) {
|
|
5989
|
-
if (active.plan !== plan)
|
|
6842
|
+
if (active.plan !== plan || i.resumeTxHash && pendingTokenBatch(i)?.txHash !== i.resumeTxHash)
|
|
5990
6843
|
return Promise.reject(
|
|
5991
6844
|
new Error(
|
|
5992
6845
|
"A token deposit is already in progress. Wait for its result before depositing again."
|
|
@@ -6013,7 +6866,7 @@ async function execute(i, key2, plan) {
|
|
|
6013
6866
|
baseUrl: i.baseUrl,
|
|
6014
6867
|
body
|
|
6015
6868
|
});
|
|
6016
|
-
if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0,
|
|
6869
|
+
if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0, import_viem14.keccak256)(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
|
|
6017
6870
|
throw new Error(
|
|
6018
6871
|
"Sponsorship API did not return a valid prepared permit transaction."
|
|
6019
6872
|
);
|
|
@@ -6029,7 +6882,7 @@ async function execute(i, key2, plan) {
|
|
|
6029
6882
|
baseUrl: i.baseUrl,
|
|
6030
6883
|
body
|
|
6031
6884
|
});
|
|
6032
|
-
if (!prepared.serializedTransaction || (0,
|
|
6885
|
+
if (!prepared.serializedTransaction || (0, import_viem14.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
|
|
6033
6886
|
throw new Error(
|
|
6034
6887
|
"Sponsorship API did not return a valid prepared transaction."
|
|
6035
6888
|
);
|
|
@@ -6040,7 +6893,7 @@ async function execute(i, key2, plan) {
|
|
|
6040
6893
|
save(key2, body);
|
|
6041
6894
|
}
|
|
6042
6895
|
try {
|
|
6043
|
-
i.onSubmitted?.((0,
|
|
6896
|
+
i.onSubmitted?.((0, import_viem14.keccak256)(body.serializedTransaction));
|
|
6044
6897
|
} catch {
|
|
6045
6898
|
}
|
|
6046
6899
|
const result = await postSponsorBatchTransfer({
|
|
@@ -6048,7 +6901,7 @@ async function execute(i, key2, plan) {
|
|
|
6048
6901
|
baseUrl: i.baseUrl,
|
|
6049
6902
|
body
|
|
6050
6903
|
});
|
|
6051
|
-
if (result.txHash !== (0,
|
|
6904
|
+
if (result.txHash !== (0, import_viem14.keccak256)(body.serializedTransaction))
|
|
6052
6905
|
throw new Error(
|
|
6053
6906
|
"Sponsorship receipt does not match the pending transaction."
|
|
6054
6907
|
);
|
|
@@ -6061,9 +6914,11 @@ async function execute(i, key2, plan) {
|
|
|
6061
6914
|
}
|
|
6062
6915
|
};
|
|
6063
6916
|
const saved = read(key2);
|
|
6917
|
+
if (i.resumeTxHash && pendingTokenBatch(i)?.txHash !== i.resumeTxHash)
|
|
6918
|
+
throw new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit changed. Check its status before depositing again.");
|
|
6064
6919
|
if (saved) {
|
|
6065
6920
|
const previous = JSON.parse(saved);
|
|
6066
|
-
if (previous.chainId !== i.chainId || !(0,
|
|
6921
|
+
if (previous.chainId !== i.chainId || !(0, import_viem14.isAddressEqual)(previous.from, i.owner) || !(0, import_viem14.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
6067
6922
|
throw new Error(
|
|
6068
6923
|
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
6069
6924
|
);
|
|
@@ -6080,7 +6935,7 @@ async function execute(i, key2, plan) {
|
|
|
6080
6935
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
6081
6936
|
"Insufficient token balance for this deposit."
|
|
6082
6937
|
);
|
|
6083
|
-
if (allowance < total && (i.chainId !== 8453 || !(0,
|
|
6938
|
+
if (allowance < total && (i.chainId !== 8453 || !(0, import_viem14.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
|
|
6084
6939
|
throw new OwneyError(
|
|
6085
6940
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
6086
6941
|
"token deposits need a one-time Permit2 approval."
|
|
@@ -6172,7 +7027,8 @@ function makeSponsoredTokenCallback(deps) {
|
|
|
6172
7027
|
pub,
|
|
6173
7028
|
wallet,
|
|
6174
7029
|
onApproved: deps.onApproved,
|
|
6175
|
-
onSubmitted: (hash) => deps.onSubmitted?.(hash, transfers)
|
|
7030
|
+
onSubmitted: (hash) => deps.onSubmitted?.(hash, transfers),
|
|
7031
|
+
resumeTxHash: deps.resumeTxHash
|
|
6176
7032
|
});
|
|
6177
7033
|
};
|
|
6178
7034
|
const callback = makeVerificationAwareDepositCallback(
|
|
@@ -6258,7 +7114,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
|
6258
7114
|
}
|
|
6259
7115
|
|
|
6260
7116
|
// src/lib/sponsored-calls-deposit.ts
|
|
6261
|
-
var
|
|
7117
|
+
var import_viem15 = require("viem");
|
|
6262
7118
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
6263
7119
|
var DEFAULT_MAX_POLLS = 30;
|
|
6264
7120
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -6266,7 +7122,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
6266
7122
|
method: "wallet_getCapabilities",
|
|
6267
7123
|
params: [owner]
|
|
6268
7124
|
});
|
|
6269
|
-
const forChain = caps?.[(0,
|
|
7125
|
+
const forChain = caps?.[(0, import_viem15.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
6270
7126
|
return Boolean(forChain?.paymasterService?.supported);
|
|
6271
7127
|
}
|
|
6272
7128
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -6303,8 +7159,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
6303
7159
|
const calls = transfers.map((transfer) => ({
|
|
6304
7160
|
to: token,
|
|
6305
7161
|
value: "0x0",
|
|
6306
|
-
data: (0,
|
|
6307
|
-
abi:
|
|
7162
|
+
data: (0, import_viem15.encodeFunctionData)({
|
|
7163
|
+
abi: import_viem15.erc20Abi,
|
|
6308
7164
|
functionName: "transfer",
|
|
6309
7165
|
args: [transfer.to, BigInt(transfer.amount)]
|
|
6310
7166
|
})
|
|
@@ -6336,50 +7192,73 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
6336
7192
|
url.searchParams.append("owneyIntent", intent);
|
|
6337
7193
|
paymasterUrl = url.toString();
|
|
6338
7194
|
}
|
|
6339
|
-
const
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
7195
|
+
const requestedId = (0, import_viem15.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
|
|
7196
|
+
const progress = (id, transfer) => {
|
|
7197
|
+
try {
|
|
7198
|
+
deps.onCallsProgress?.(id, transfers, transfer);
|
|
7199
|
+
} catch {
|
|
7200
|
+
}
|
|
7201
|
+
};
|
|
7202
|
+
progress(requestedId, "pending");
|
|
7203
|
+
let sendResult;
|
|
7204
|
+
try {
|
|
7205
|
+
sendResult = await deps.provider.request({
|
|
7206
|
+
method: "wallet_sendCalls",
|
|
7207
|
+
params: [
|
|
7208
|
+
{
|
|
7209
|
+
version: "2.0.0",
|
|
7210
|
+
id: requestedId,
|
|
7211
|
+
from: deps.ownerAddress,
|
|
7212
|
+
chainId: (0, import_viem15.toHex)(chainId),
|
|
7213
|
+
atomicRequired: transfers.length > 1,
|
|
7214
|
+
calls,
|
|
7215
|
+
capabilities: {
|
|
7216
|
+
paymasterService: { url: paymasterUrl }
|
|
7217
|
+
}
|
|
6350
7218
|
}
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
})
|
|
6354
|
-
|
|
7219
|
+
]
|
|
7220
|
+
});
|
|
7221
|
+
} catch (error) {
|
|
7222
|
+
if (error?.code === 4001 || error?.code === "ACTION_REJECTED")
|
|
7223
|
+
progress(requestedId, "failed");
|
|
7224
|
+
throw error;
|
|
7225
|
+
}
|
|
7226
|
+
try {
|
|
7227
|
+
deps.onApproved?.();
|
|
7228
|
+
} catch {
|
|
7229
|
+
}
|
|
6355
7230
|
const callsId = typeof sendResult === "string" ? sendResult : sendResult?.id;
|
|
6356
7231
|
if (!callsId) {
|
|
6357
7232
|
throw new OwneyError(
|
|
6358
7233
|
"SPONSORED_CALLS_NO_ID",
|
|
6359
7234
|
"Wallet accepted wallet_sendCalls but returned no calls id.",
|
|
6360
|
-
{ chainId }
|
|
7235
|
+
{ chainId, callsId: requestedId }
|
|
6361
7236
|
);
|
|
6362
7237
|
}
|
|
7238
|
+
progress(callsId, "pending");
|
|
6363
7239
|
for (let attempt = 0; attempt < maxPolls; attempt += 1) {
|
|
6364
7240
|
const status = await deps.provider.request({
|
|
6365
7241
|
method: "wallet_getCallsStatus",
|
|
6366
7242
|
params: [callsId]
|
|
6367
7243
|
});
|
|
7244
|
+
if (status?.id && status.id !== callsId || status?.chainId && Number(status.chainId) !== chainId)
|
|
7245
|
+
throw new OwneyError("SPONSORED_CALLS_NO_RECEIPT", "Wallet returned status for a different deposit batch.", { chainId, callsId });
|
|
6368
7246
|
const txHash = status?.receipts?.[0]?.transactionHash;
|
|
6369
|
-
if (txHash && !status?.receipts?.some((receipt) => receipt.transactionHash !== txHash)) {
|
|
7247
|
+
if (txHash && /^0x[\da-f]{64}$/i.test(txHash) && !status?.receipts?.some((receipt) => receipt.transactionHash !== txHash)) {
|
|
6370
7248
|
try {
|
|
6371
7249
|
deps.onSubmitted?.(txHash, transfers);
|
|
6372
7250
|
} catch {
|
|
6373
7251
|
}
|
|
6374
7252
|
}
|
|
6375
|
-
if (status?.
|
|
7253
|
+
if (status?.status === 400 || status?.status === 500) {
|
|
7254
|
+
progress(callsId, "failed");
|
|
6376
7255
|
throw new OwneyError(
|
|
6377
7256
|
"SPONSOR_REQUEST_FAILED",
|
|
6378
7257
|
"The sponsored deposit did not complete successfully.",
|
|
6379
7258
|
{ chainId, callsId, safeToFallback: false }
|
|
6380
7259
|
);
|
|
6381
7260
|
}
|
|
6382
|
-
if (txHash && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
|
|
7261
|
+
if (txHash && /^0x[\da-f]{64}$/i.test(txHash) && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
|
|
6383
7262
|
if (status?.receipts?.some(
|
|
6384
7263
|
(receipt) => receipt.transactionHash !== txHash
|
|
6385
7264
|
))
|
|
@@ -6595,8 +7474,8 @@ var OwneySDK = class {
|
|
|
6595
7474
|
return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
|
|
6596
7475
|
}
|
|
6597
7476
|
/** Builds the default USDC batch callback for the connected wallet. */
|
|
6598
|
-
getDefaultSponsoredCallback(onApproved, onSubmitted) {
|
|
6599
|
-
if (!onApproved && !onSubmitted && this.cachedSponsoredCallback)
|
|
7477
|
+
getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash) {
|
|
7478
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && this.cachedSponsoredCallback)
|
|
6600
7479
|
return this.cachedSponsoredCallback;
|
|
6601
7480
|
const provider = this.requireConnectedProvider();
|
|
6602
7481
|
const owner = this.state.walletAddress;
|
|
@@ -6606,28 +7485,29 @@ var OwneySDK = class {
|
|
|
6606
7485
|
ownerAddress: owner,
|
|
6607
7486
|
onApproved,
|
|
6608
7487
|
onSubmitted,
|
|
7488
|
+
resumeTxHash,
|
|
6609
7489
|
tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
|
|
6610
7490
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
6611
7491
|
// PublicClient/WalletClient param types — structurally identical at
|
|
6612
7492
|
// runtime, but the two share a name TS treats as unrelated.
|
|
6613
7493
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
6614
|
-
getWalletChainClient: (cid) => (0,
|
|
7494
|
+
getWalletChainClient: (cid) => (0, import_viem16.createPublicClient)({
|
|
6615
7495
|
chain: VIEM_CHAIN2[cid],
|
|
6616
|
-
transport: (0,
|
|
7496
|
+
transport: (0, import_viem16.custom)(provider)
|
|
6617
7497
|
}),
|
|
6618
|
-
getWalletClient: (cid) => (0,
|
|
7498
|
+
getWalletClient: (cid) => (0, import_viem16.createWalletClient)({
|
|
6619
7499
|
account: owner,
|
|
6620
7500
|
chain: VIEM_CHAIN2[cid],
|
|
6621
|
-
transport: (0,
|
|
7501
|
+
transport: (0, import_viem16.custom)(provider)
|
|
6622
7502
|
})
|
|
6623
7503
|
});
|
|
6624
|
-
if (!onApproved && !onSubmitted) this.cachedSponsoredCallback = callback;
|
|
7504
|
+
if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedSponsoredCallback = callback;
|
|
6625
7505
|
return callback;
|
|
6626
7506
|
}
|
|
6627
7507
|
/** Builds the wallet-native sponsored calls callback for compatible paymasters. */
|
|
6628
|
-
getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted) {
|
|
7508
|
+
getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress) {
|
|
6629
7509
|
const cached = this.cachedSponsoredCallsCallbacks.get(asset);
|
|
6630
|
-
if (!onApproved && !onSubmitted && cached) return cached;
|
|
7510
|
+
if (!onApproved && !onSubmitted && !onCallsProgress && cached) return cached;
|
|
6631
7511
|
const provider = this.requireConnectedProvider();
|
|
6632
7512
|
const callback = makeSponsoredCallsCallback({
|
|
6633
7513
|
apiKey: this.apiKey,
|
|
@@ -6637,9 +7517,10 @@ var OwneySDK = class {
|
|
|
6637
7517
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
6638
7518
|
onApproved,
|
|
6639
7519
|
onSubmitted,
|
|
7520
|
+
onCallsProgress,
|
|
6640
7521
|
tokenAddressByChain: sponsoredTokensFor(asset)
|
|
6641
7522
|
});
|
|
6642
|
-
if (!onApproved && !onSubmitted) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
7523
|
+
if (!onApproved && !onSubmitted && !onCallsProgress) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
6643
7524
|
return callback;
|
|
6644
7525
|
}
|
|
6645
7526
|
/**
|
|
@@ -6648,8 +7529,8 @@ var OwneySDK = class {
|
|
|
6648
7529
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
6649
7530
|
* single-use batch authorization instead of an EIP-3009 authorization.
|
|
6650
7531
|
*/
|
|
6651
|
-
getDefaultWethSponsoredCallback(onApproved, onSubmitted) {
|
|
6652
|
-
if (!onApproved && !onSubmitted && this.cachedWethSponsoredCallback)
|
|
7532
|
+
getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash) {
|
|
7533
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && this.cachedWethSponsoredCallback)
|
|
6653
7534
|
return this.cachedWethSponsoredCallback;
|
|
6654
7535
|
const provider = this.requireConnectedProvider();
|
|
6655
7536
|
const owner = this.state.walletAddress;
|
|
@@ -6659,22 +7540,23 @@ var OwneySDK = class {
|
|
|
6659
7540
|
ownerAddress: owner,
|
|
6660
7541
|
onApproved,
|
|
6661
7542
|
onSubmitted,
|
|
7543
|
+
resumeTxHash,
|
|
6662
7544
|
tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
|
|
6663
7545
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
6664
7546
|
// PublicClient/WalletClient param types — structurally identical at
|
|
6665
7547
|
// runtime, but the two share a name TS treats as unrelated.
|
|
6666
7548
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
6667
|
-
getWalletChainClient: (cid) => (0,
|
|
7549
|
+
getWalletChainClient: (cid) => (0, import_viem16.createPublicClient)({
|
|
6668
7550
|
chain: VIEM_CHAIN2[cid],
|
|
6669
|
-
transport: (0,
|
|
7551
|
+
transport: (0, import_viem16.custom)(provider)
|
|
6670
7552
|
}),
|
|
6671
|
-
getWalletClient: (cid) => (0,
|
|
7553
|
+
getWalletClient: (cid) => (0, import_viem16.createWalletClient)({
|
|
6672
7554
|
account: owner,
|
|
6673
7555
|
chain: VIEM_CHAIN2[cid],
|
|
6674
|
-
transport: (0,
|
|
7556
|
+
transport: (0, import_viem16.custom)(provider)
|
|
6675
7557
|
})
|
|
6676
7558
|
});
|
|
6677
|
-
if (!onApproved && !onSubmitted) this.cachedWethSponsoredCallback = callback;
|
|
7559
|
+
if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedWethSponsoredCallback = callback;
|
|
6678
7560
|
return callback;
|
|
6679
7561
|
}
|
|
6680
7562
|
getAgent(agentId) {
|
|
@@ -6983,6 +7865,7 @@ var OwneySDK = class {
|
|
|
6983
7865
|
async deposit(options) {
|
|
6984
7866
|
const { amount, asset, depositCallback, agentId, onApproved } = options;
|
|
6985
7867
|
const prepared = /* @__PURE__ */ new Map();
|
|
7868
|
+
const paidFallbackAgents = /* @__PURE__ */ new Set();
|
|
6986
7869
|
const onProgress = options.onProgress ? (evidence) => {
|
|
6987
7870
|
if (evidence.agentWallet) prepared.set(evidence.agentWallet.toLowerCase(), evidence);
|
|
6988
7871
|
notifyFunding(options.onProgress, evidence);
|
|
@@ -6993,152 +7876,184 @@ var OwneySDK = class {
|
|
|
6993
7876
|
if (evidence) notifyFunding(onProgress, { ...evidence, txHash: hash, transfer: "pending" });
|
|
6994
7877
|
}
|
|
6995
7878
|
} : void 0;
|
|
7879
|
+
const onCallsProgress = onProgress ? (callsId, destinations, transfer) => {
|
|
7880
|
+
for (const destination of destinations) {
|
|
7881
|
+
const evidence = prepared.get(destination.to.toLowerCase());
|
|
7882
|
+
if (evidence) notifyFunding(onProgress, { ...evidence, callsId, transfer });
|
|
7883
|
+
}
|
|
7884
|
+
} : void 0;
|
|
6996
7885
|
if (BigInt(amount) === 0n) {
|
|
6997
7886
|
return { agentResults: {} };
|
|
6998
7887
|
}
|
|
6999
7888
|
const state = this.requireState();
|
|
7000
7889
|
const chainId = this.requireChainId();
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
|
|
7013
|
-
return this.depositWithFallback(
|
|
7014
|
-
agent,
|
|
7015
|
-
state,
|
|
7016
|
-
chainId,
|
|
7017
|
-
amount,
|
|
7018
|
-
asset,
|
|
7019
|
-
effectiveCallback,
|
|
7020
|
-
depositCallback,
|
|
7021
|
-
onProgress
|
|
7022
|
-
);
|
|
7023
|
-
}
|
|
7024
|
-
const totalAmount = BigInt(amount);
|
|
7025
|
-
const eligibleAgents = this.getEligibleAgents(chainId, asset, {
|
|
7026
|
-
excludeDisabled: true
|
|
7027
|
-
});
|
|
7028
|
-
const exemptFlags = await Promise.all(
|
|
7029
|
-
eligibleAgents.map(
|
|
7030
|
-
(a) => this.hasExistingBalance(a, state, chainId, asset, true)
|
|
7031
|
-
)
|
|
7032
|
-
);
|
|
7033
|
-
const exempt = new Set(
|
|
7034
|
-
eligibleAgents.filter((_, i) => exemptFlags[i]).map((a) => a.id)
|
|
7035
|
-
);
|
|
7036
|
-
const agentAmounts = this.splitDepositAmount(
|
|
7037
|
-
totalAmount,
|
|
7038
|
-
eligibleAgents,
|
|
7039
|
-
chainId,
|
|
7040
|
-
asset,
|
|
7041
|
-
exempt
|
|
7042
|
-
);
|
|
7043
|
-
if (agentAmounts.length === 0) {
|
|
7044
|
-
const agentsRequiringActivation = eligibleAgents.filter(
|
|
7045
|
-
(agent) => !exempt.has(agent.id)
|
|
7046
|
-
);
|
|
7047
|
-
const perAgentMinimum = agentsRequiringActivation.reduce(
|
|
7048
|
-
(highest, agent) => {
|
|
7049
|
-
const minimum = this.getMinDepositAmount(agent, chainId, asset);
|
|
7050
|
-
return minimum > highest ? minimum : highest;
|
|
7051
|
-
},
|
|
7052
|
-
0n
|
|
7053
|
-
);
|
|
7054
|
-
const minimumRequired = agentsRequiringActivation.length > 0 ? (perAgentMinimum > 0n ? perAgentMinimum : 1n) * BigInt(agentsRequiringActivation.length) : BigInt(eligibleAgents.length);
|
|
7055
|
-
throw new OwneyError(
|
|
7056
|
-
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
7057
|
-
`Amount "${amount}" cannot activate every eligible agent. The combined minimum is "${minimumRequired.toString()}" for ${asset}.`,
|
|
7058
|
-
{
|
|
7059
|
-
amount,
|
|
7060
|
-
asset,
|
|
7061
|
-
chainId,
|
|
7062
|
-
minDepositAmount: minimumRequired.toString(),
|
|
7063
|
-
perAgentDepositAmount: perAgentMinimum.toString(),
|
|
7064
|
-
agentMinimums: agentsRequiringActivation.map((agent) => ({
|
|
7065
|
-
agentId: agent.id,
|
|
7066
|
-
minDepositAmount: this.getMinDepositAmount(
|
|
7067
|
-
agent,
|
|
7068
|
-
chainId,
|
|
7069
|
-
asset
|
|
7070
|
-
).toString()
|
|
7071
|
-
}))
|
|
7072
|
-
}
|
|
7073
|
-
);
|
|
7074
|
-
}
|
|
7075
|
-
const batchTransfer = getDepositBatchTransfer(effectiveCallback);
|
|
7076
|
-
if (!depositCallback && batchTransfer) {
|
|
7077
|
-
return runAgentDepositBatch(
|
|
7078
|
-
chainId,
|
|
7079
|
-
agentAmounts.map(({ agent, amount: amount2 }) => ({
|
|
7080
|
-
id: agent.id,
|
|
7081
|
-
amount: amount2,
|
|
7082
|
-
run: (callback) => withFailureReporting(
|
|
7083
|
-
this.apiKey,
|
|
7084
|
-
agent.id,
|
|
7085
|
-
() => onProgress ? agent.deposit(state, chainId, amount2, asset, callback, onProgress) : agent.deposit(state, chainId, amount2, asset, callback),
|
|
7086
|
-
this.routingApiBaseUrl
|
|
7087
|
-
)
|
|
7088
|
-
})),
|
|
7089
|
-
async (cid, transfers) => {
|
|
7090
|
-
try {
|
|
7091
|
-
return await batchTransfer(cid, transfers);
|
|
7092
|
-
} catch (error) {
|
|
7093
|
-
if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
|
|
7094
|
-
throw error;
|
|
7095
|
-
const requiredAmount = transfers.reduce(
|
|
7096
|
-
(sum, transfer) => sum + BigInt(transfer.amount),
|
|
7097
|
-
0n
|
|
7098
|
-
);
|
|
7099
|
-
await this.approvePermit2(
|
|
7100
|
-
asset,
|
|
7101
|
-
requiredAmount,
|
|
7102
|
-
cid
|
|
7103
|
-
);
|
|
7104
|
-
return batchTransfer(cid, transfers);
|
|
7105
|
-
}
|
|
7890
|
+
if (options.resumeTxHash && (depositCallback || this.paymasterServiceUrl || !/^0x[\da-f]{64}$/i.test(options.resumeTxHash)))
|
|
7891
|
+
throw new OwneyError("DEPOSIT_IN_PROGRESS", "This deposit cannot resume the saved relayer transaction.");
|
|
7892
|
+
const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash) : this.getDefaultSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash));
|
|
7893
|
+
try {
|
|
7894
|
+
if (agentId) {
|
|
7895
|
+
if (this.isAgentDisabled(agentId)) {
|
|
7896
|
+
throw new OwneyError(
|
|
7897
|
+
"AGENT_DISABLED",
|
|
7898
|
+
`Cannot deposit into disabled agent "${agentId}". Existing balances remain visible and withdrawable.`,
|
|
7899
|
+
{ agentId }
|
|
7900
|
+
);
|
|
7106
7901
|
}
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
index,
|
|
7112
|
-
{ agent, amount: agentAmount }
|
|
7113
|
-
] of agentAmounts.entries()) {
|
|
7114
|
-
try {
|
|
7115
|
-
agentResults[agent.id] = await this.depositWithFallback(
|
|
7902
|
+
const agent = this.getAgent(agentId);
|
|
7903
|
+
this.validateAssetSupport(agent, chainId, asset);
|
|
7904
|
+
await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
|
|
7905
|
+
return await this.depositWithFallback(
|
|
7116
7906
|
agent,
|
|
7117
7907
|
state,
|
|
7118
7908
|
chainId,
|
|
7119
|
-
|
|
7909
|
+
amount,
|
|
7120
7910
|
asset,
|
|
7121
7911
|
effectiveCallback,
|
|
7122
7912
|
depositCallback,
|
|
7123
|
-
onProgress
|
|
7913
|
+
onProgress,
|
|
7914
|
+
() => paidFallbackAgents.add(agent.id),
|
|
7915
|
+
!!options.resumeTxHash
|
|
7124
7916
|
);
|
|
7125
|
-
}
|
|
7126
|
-
|
|
7917
|
+
}
|
|
7918
|
+
const totalAmount = BigInt(amount);
|
|
7919
|
+
const eligibleAgents = this.getEligibleAgents(chainId, asset, {
|
|
7920
|
+
excludeDisabled: true
|
|
7921
|
+
});
|
|
7922
|
+
const prospectiveShare = totalAmount / BigInt(eligibleAgents.length);
|
|
7923
|
+
const meetsEveryMinimum = eligibleAgents.every((agent) => prospectiveShare >= this.getMinDepositAmount(agent, chainId, asset));
|
|
7924
|
+
const exemptFlags = meetsEveryMinimum ? eligibleAgents.map(() => false) : await Promise.all(eligibleAgents.map((agent) => this.hasExistingBalance(agent, state, chainId, asset, true)));
|
|
7925
|
+
const exempt = new Set(
|
|
7926
|
+
eligibleAgents.filter((_, i) => exemptFlags[i]).map((a) => a.id)
|
|
7927
|
+
);
|
|
7928
|
+
const agentAmounts = this.splitDepositAmount(
|
|
7929
|
+
totalAmount,
|
|
7930
|
+
eligibleAgents,
|
|
7931
|
+
chainId,
|
|
7932
|
+
asset,
|
|
7933
|
+
exempt
|
|
7934
|
+
);
|
|
7935
|
+
if (agentAmounts.length === 0) {
|
|
7936
|
+
const agentsRequiringActivation = eligibleAgents.filter(
|
|
7937
|
+
(agent) => !exempt.has(agent.id)
|
|
7938
|
+
);
|
|
7939
|
+
const perAgentMinimum = agentsRequiringActivation.reduce(
|
|
7940
|
+
(highest, agent) => {
|
|
7941
|
+
const minimum = this.getMinDepositAmount(agent, chainId, asset);
|
|
7942
|
+
return minimum > highest ? minimum : highest;
|
|
7943
|
+
},
|
|
7944
|
+
0n
|
|
7945
|
+
);
|
|
7946
|
+
const minimumRequired = agentsRequiringActivation.length > 0 ? (perAgentMinimum > 0n ? perAgentMinimum : 1n) * BigInt(agentsRequiringActivation.length) : BigInt(eligibleAgents.length);
|
|
7127
7947
|
throw new OwneyError(
|
|
7128
|
-
"
|
|
7129
|
-
"
|
|
7948
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
7949
|
+
`Amount "${amount}" cannot activate every eligible agent. The combined minimum is "${minimumRequired.toString()}" for ${asset}.`,
|
|
7130
7950
|
{
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7951
|
+
amount,
|
|
7952
|
+
asset,
|
|
7953
|
+
chainId,
|
|
7954
|
+
minDepositAmount: minimumRequired.toString(),
|
|
7955
|
+
perAgentDepositAmount: perAgentMinimum.toString(),
|
|
7956
|
+
agentMinimums: agentsRequiringActivation.map((agent) => ({
|
|
7957
|
+
agentId: agent.id,
|
|
7958
|
+
minDepositAmount: this.getMinDepositAmount(
|
|
7959
|
+
agent,
|
|
7960
|
+
chainId,
|
|
7961
|
+
asset
|
|
7962
|
+
).toString()
|
|
7963
|
+
}))
|
|
7964
|
+
}
|
|
7965
|
+
);
|
|
7966
|
+
}
|
|
7967
|
+
const batchTransfer = getDepositBatchTransfer(effectiveCallback);
|
|
7968
|
+
if (!depositCallback && batchTransfer) {
|
|
7969
|
+
return await runAgentDepositBatch(
|
|
7970
|
+
chainId,
|
|
7971
|
+
agentAmounts.map(({ agent, amount: amount2 }) => ({
|
|
7972
|
+
id: agent.id,
|
|
7973
|
+
amount: amount2,
|
|
7974
|
+
run: (callback) => withFailureReporting(
|
|
7975
|
+
this.apiKey,
|
|
7976
|
+
agent.id,
|
|
7977
|
+
() => onProgress ? agent.deposit(state, chainId, amount2, asset, callback, onProgress) : agent.deposit(state, chainId, amount2, asset, callback),
|
|
7978
|
+
this.routingApiBaseUrl
|
|
7979
|
+
)
|
|
7980
|
+
})),
|
|
7981
|
+
async (cid, transfers) => {
|
|
7982
|
+
try {
|
|
7983
|
+
return await batchTransfer(cid, transfers);
|
|
7984
|
+
} catch (error) {
|
|
7985
|
+
if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
|
|
7986
|
+
throw error;
|
|
7987
|
+
const requiredAmount = transfers.reduce(
|
|
7988
|
+
(sum, transfer) => sum + BigInt(transfer.amount),
|
|
7989
|
+
0n
|
|
7990
|
+
);
|
|
7991
|
+
await this.approvePermit2(
|
|
7992
|
+
asset,
|
|
7993
|
+
requiredAmount,
|
|
7994
|
+
cid
|
|
7995
|
+
);
|
|
7996
|
+
return batchTransfer(cid, transfers);
|
|
7997
|
+
}
|
|
7998
|
+
}
|
|
7138
7999
|
);
|
|
7139
8000
|
}
|
|
8001
|
+
const agentResults = {};
|
|
8002
|
+
for (const [
|
|
8003
|
+
index,
|
|
8004
|
+
{ agent, amount: agentAmount }
|
|
8005
|
+
] of agentAmounts.entries()) {
|
|
8006
|
+
try {
|
|
8007
|
+
agentResults[agent.id] = await this.depositWithFallback(
|
|
8008
|
+
agent,
|
|
8009
|
+
state,
|
|
8010
|
+
chainId,
|
|
8011
|
+
agentAmount,
|
|
8012
|
+
asset,
|
|
8013
|
+
effectiveCallback,
|
|
8014
|
+
depositCallback,
|
|
8015
|
+
onProgress,
|
|
8016
|
+
() => paidFallbackAgents.add(agent.id),
|
|
8017
|
+
!!options.resumeTxHash
|
|
8018
|
+
);
|
|
8019
|
+
} catch (error) {
|
|
8020
|
+
if (Object.keys(agentResults).length === 0) throw error;
|
|
8021
|
+
throw new OwneyError(
|
|
8022
|
+
"DEPOSIT_PARTIAL_FAILURE",
|
|
8023
|
+
"Some deposits completed before another agent failed. Check activity and balances before depositing again; the failed transfer may still settle.",
|
|
8024
|
+
{
|
|
8025
|
+
agentResults,
|
|
8026
|
+
failedAgentId: agent.id,
|
|
8027
|
+
failedAmount: agentAmount,
|
|
8028
|
+
unattemptedAgentIds: agentAmounts.slice(index + 1).map(({ agent: agent2 }) => agent2.id),
|
|
8029
|
+
cause: error
|
|
8030
|
+
},
|
|
8031
|
+
agent.id
|
|
8032
|
+
);
|
|
8033
|
+
}
|
|
8034
|
+
}
|
|
8035
|
+
return { agentResults };
|
|
8036
|
+
} catch (error) {
|
|
8037
|
+
if (!depositCallback && onProgress && !options.resumeTxHash) {
|
|
8038
|
+
const ids = agentId ? [agentId] : this.getEligibleAgents(chainId, asset).map((agent) => agent.id);
|
|
8039
|
+
for (const id of ids) {
|
|
8040
|
+
const evidence = [...prepared.values()].find((value) => value.agentId === id);
|
|
8041
|
+
if (paidFallbackAgents.has(id) || evidence?.transfer === "confirmed" || evidence?.transfer === "failed") continue;
|
|
8042
|
+
const rejected = error instanceof OwneyError && error.details?.notSubmitted === true;
|
|
8043
|
+
if (!rejected && (evidence?.txHash || evidence?.userOpHash || evidence?.callsId)) continue;
|
|
8044
|
+
notifyFunding(onProgress, { ...evidence ?? {
|
|
8045
|
+
agentId: id,
|
|
8046
|
+
chainId,
|
|
8047
|
+
token: asset,
|
|
8048
|
+
walletAddress: state.walletAddress,
|
|
8049
|
+
intendedAmount: amount,
|
|
8050
|
+
registration: "unknown",
|
|
8051
|
+
delivery: "not-applicable"
|
|
8052
|
+
}, transfer: "failed" });
|
|
8053
|
+
}
|
|
8054
|
+
}
|
|
8055
|
+
throw error;
|
|
7140
8056
|
}
|
|
7141
|
-
return { agentResults };
|
|
7142
8057
|
}
|
|
7143
8058
|
/**
|
|
7144
8059
|
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
@@ -7167,7 +8082,7 @@ var OwneySDK = class {
|
|
|
7167
8082
|
* propagates unchanged. Scoped per agent invocation so one agent's failure
|
|
7168
8083
|
* doesn't force a retry of sibling agents in a multi-agent split.
|
|
7169
8084
|
*/
|
|
7170
|
-
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress) {
|
|
8085
|
+
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress, onPaidFallback, resumeOnly = false) {
|
|
7171
8086
|
const attempt = (callback) => withFailureReporting(
|
|
7172
8087
|
this.apiKey,
|
|
7173
8088
|
agent.id,
|
|
@@ -7179,6 +8094,7 @@ var OwneySDK = class {
|
|
|
7179
8094
|
try {
|
|
7180
8095
|
return await attempt(effectiveCallback);
|
|
7181
8096
|
} catch (error) {
|
|
8097
|
+
if (resumeOnly) throw error;
|
|
7182
8098
|
if (!approvalAttempted && appCallback === void 0 && (asset === "WETH" || asset === "USDC") && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
7183
8099
|
approvalAttempted = true;
|
|
7184
8100
|
console.warn(
|
|
@@ -7196,6 +8112,7 @@ var OwneySDK = class {
|
|
|
7196
8112
|
`[owney-sdk] ${asset} sponsorship unavailable, falling back to user-paid deposit:`,
|
|
7197
8113
|
error
|
|
7198
8114
|
);
|
|
8115
|
+
onPaidFallback?.();
|
|
7199
8116
|
return attempt(void 0);
|
|
7200
8117
|
}
|
|
7201
8118
|
}
|
|
@@ -7278,7 +8195,12 @@ var OwneySDK = class {
|
|
|
7278
8195
|
*/
|
|
7279
8196
|
async hasExistingBalance(agent, state, chainId, asset, requireReliableRead = false) {
|
|
7280
8197
|
try {
|
|
7281
|
-
|
|
8198
|
+
if (agent.hasExistingDepositFunds && await agent.hasExistingDepositFunds(
|
|
8199
|
+
state,
|
|
8200
|
+
chainId,
|
|
8201
|
+
asset
|
|
8202
|
+
).catch(() => void 0) === true) return true;
|
|
8203
|
+
const balance = await agent.getBalances(state, chainId, asset);
|
|
7282
8204
|
const target = asset.toLowerCase();
|
|
7283
8205
|
const token = balance.tokens.find(
|
|
7284
8206
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
@@ -7391,7 +8313,7 @@ var OwneySDK = class {
|
|
|
7391
8313
|
if (direction === "withdraw" && evidence.agentId === "yieldseeker" && verified.transfer === "unknown" && verified.confirmedAmount && BigInt(verified.confirmedAmount) > 0n) {
|
|
7392
8314
|
const transaction = await rpc2.getTransaction({ hash: evidence.txHash });
|
|
7393
8315
|
assertCurrent();
|
|
7394
|
-
if (isFullYieldseekerWithdrawal(transaction, { ...evidence, agentWallet }, token.address))
|
|
8316
|
+
if (isFullYieldseekerWithdrawal(transaction, { ...evidence, agentWallet }, token.address, receipt))
|
|
7395
8317
|
verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet, intendedAmount: "MAX" }, token.address, direction);
|
|
7396
8318
|
}
|
|
7397
8319
|
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${evidence.agentId}:${evidence.token}:${evidence.txHash}`;
|
|
@@ -7404,7 +8326,8 @@ var OwneySDK = class {
|
|
|
7404
8326
|
return verified;
|
|
7405
8327
|
}
|
|
7406
8328
|
/** Resolve a saved sponsored-withdrawal ID without opening a wallet prompt. */
|
|
7407
|
-
async resolveSponsoredWithdrawalEvidence(evidence) {
|
|
8329
|
+
async resolveSponsoredWithdrawalEvidence(evidence, options = {}) {
|
|
8330
|
+
if (evidence.callsId && !evidence.userOpHash) return this.resolveWalletCallsFundingEvidence(evidence, "withdraw");
|
|
7408
8331
|
const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
7409
8332
|
if (evidence.agentId !== "yieldseeker" || chainId !== 8453 || evidence.txHash || state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase()) return evidence;
|
|
7410
8333
|
const agent = this.getAgent("yieldseeker");
|
|
@@ -7420,18 +8343,26 @@ var OwneySDK = class {
|
|
|
7420
8343
|
owner: state.walletAddress,
|
|
7421
8344
|
agentWallet: wallet,
|
|
7422
8345
|
asset: SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token)?.address,
|
|
7423
|
-
userOpHash: evidence.userOpHash
|
|
8346
|
+
userOpHash: evidence.userOpHash,
|
|
8347
|
+
...evidence.userOperation ? { userOperation: Object.fromEntries([
|
|
8348
|
+
"sender",
|
|
8349
|
+
"nonce",
|
|
8350
|
+
"initCode",
|
|
8351
|
+
"callData",
|
|
8352
|
+
"callGasLimit",
|
|
8353
|
+
"verificationGasLimit",
|
|
8354
|
+
"preVerificationGas",
|
|
8355
|
+
"maxFeePerGas",
|
|
8356
|
+
"maxPriorityFeePerGas",
|
|
8357
|
+
"paymasterAndData"
|
|
8358
|
+
].map((key2) => [key2, evidence.userOperation[key2]])) } : {},
|
|
8359
|
+
...Number.isFinite(options.submittedAt) && options.submittedAt > 0 ? { submittedAt: options.submittedAt } : {}
|
|
7424
8360
|
})
|
|
7425
8361
|
});
|
|
7426
8362
|
if (!response.ok) throw new OwneyError("AGENT_API_ERROR", "Could not resolve sponsored withdrawal status.");
|
|
7427
8363
|
const payload = await response.json();
|
|
7428
8364
|
txHash = payload.data?.txHash ?? void 0;
|
|
7429
8365
|
executionFailed = payload.data?.executionFailed === true;
|
|
7430
|
-
} else if (evidence.callsId) {
|
|
7431
|
-
const status = await state.provider.request({ method: "wallet_getCallsStatus", params: [evidence.callsId] });
|
|
7432
|
-
if (status?.id && status.id !== evidence.callsId || status?.chainId && Number(status.chainId) !== chainId) return evidence;
|
|
7433
|
-
executionFailed = status?.status === 400 || status?.status === 500;
|
|
7434
|
-
txHash = status?.receipts?.[0]?.transactionHash;
|
|
7435
8366
|
}
|
|
7436
8367
|
assertCurrent();
|
|
7437
8368
|
if (executionFailed) return {
|
|
@@ -7442,6 +8373,60 @@ var OwneySDK = class {
|
|
|
7442
8373
|
};
|
|
7443
8374
|
return txHash && /^0x[0-9a-fA-F]{64}$/.test(txHash) ? { ...evidence, txHash } : evidence;
|
|
7444
8375
|
}
|
|
8376
|
+
/** Recover an EIP-5792 deposit or payout using this account's exact batch ID. */
|
|
8377
|
+
async resolveWalletCallsFundingEvidence(evidence, direction) {
|
|
8378
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
8379
|
+
if (!evidence.callsId || evidence.txHash || state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase()) return evidence;
|
|
8380
|
+
const agent = this.getAgent(evidence.agentId);
|
|
8381
|
+
const wallet = agent.getFundingWallet ? await agent.getFundingWallet(state, chainId, evidence.token) : (await agent.getUserProfile(state, chainId)).smartWallet;
|
|
8382
|
+
if (!wallet || wallet.toLowerCase() !== evidence.agentWallet?.toLowerCase()) return evidence;
|
|
8383
|
+
const status = await state.provider.request({ method: "wallet_getCallsStatus", params: [evidence.callsId] });
|
|
8384
|
+
assertCurrent();
|
|
8385
|
+
if (status?.id !== void 0 && status.id !== evidence.callsId || status?.chainId !== void 0 && Number(status.chainId) !== chainId) return evidence;
|
|
8386
|
+
const statusCode2 = Number(status?.status);
|
|
8387
|
+
if (statusCode2 === 400 || statusCode2 === 500) return {
|
|
8388
|
+
...evidence,
|
|
8389
|
+
transfer: "failed",
|
|
8390
|
+
delivery: direction === "withdraw" ? "failed" : "not-applicable"
|
|
8391
|
+
};
|
|
8392
|
+
const hashes = [...new Set((status?.receipts ?? []).map((receipt) => receipt.transactionHash?.toLowerCase()).filter((hash) => !!hash && /^0x[\da-f]{64}$/i.test(hash)))];
|
|
8393
|
+
if (hashes.length === 1) return { ...evidence, txHash: hashes[0] };
|
|
8394
|
+
if (hashes.length > 16) return evidence;
|
|
8395
|
+
const matches = [];
|
|
8396
|
+
for (const txHash of hashes) {
|
|
8397
|
+
const inspected = await this.inspectFunding({ ...evidence, txHash }, direction);
|
|
8398
|
+
assertCurrent();
|
|
8399
|
+
if (inspected.transfer === "confirmed") matches.push(inspected);
|
|
8400
|
+
}
|
|
8401
|
+
return matches.length === 1 ? matches[0] : evidence;
|
|
8402
|
+
}
|
|
8403
|
+
/** Whether a foreground deposit can resume the same signed batch, never a
|
|
8404
|
+
* fresh authorization. The batch rail independently rechecks the exact plan. */
|
|
8405
|
+
async canResumePendingDeposit(evidences, amount) {
|
|
8406
|
+
if (this.paymasterServiceUrl || !evidences.length || !/^\d+$/.test(amount)) return false;
|
|
8407
|
+
const first = evidences[0];
|
|
8408
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(first.chainId);
|
|
8409
|
+
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === first.token);
|
|
8410
|
+
if (!token) return false;
|
|
8411
|
+
const pending = pendingTokenBatch({
|
|
8412
|
+
apiKey: this.apiKey,
|
|
8413
|
+
baseUrl: this.routingApiBaseUrl,
|
|
8414
|
+
owner: state.walletAddress,
|
|
8415
|
+
chainId,
|
|
8416
|
+
token: token.address
|
|
8417
|
+
});
|
|
8418
|
+
if (!pending || pending.transfers.length !== evidences.length || pending.transfers.reduce((sum, transfer) => sum + BigInt(transfer.amount), 0n).toString() !== amount) return false;
|
|
8419
|
+
const destinations = /* @__PURE__ */ new Set();
|
|
8420
|
+
for (const evidence of evidences) {
|
|
8421
|
+
if (evidence.chainId !== chainId || evidence.token !== first.token || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || evidence.callsId || evidence.userOpHash || evidence.txHash?.toLowerCase() !== pending.txHash.toLowerCase()) return false;
|
|
8422
|
+
const agent = this.getAgent(evidence.agentId);
|
|
8423
|
+
const wallet = agent.getFundingWallet ? await agent.getFundingWallet(state, chainId, evidence.token) : (await agent.getUserProfile(state, chainId)).smartWallet;
|
|
8424
|
+
if (!wallet || destinations.has(wallet.toLowerCase()) || !pending.transfers.some((transfer) => transfer.to.toLowerCase() === wallet.toLowerCase() && transfer.amount === evidence.intendedAmount)) return false;
|
|
8425
|
+
destinations.add(wallet.toLowerCase());
|
|
8426
|
+
}
|
|
8427
|
+
assertCurrent();
|
|
8428
|
+
return true;
|
|
8429
|
+
}
|
|
7445
8430
|
/** A display acknowledgement controls polling only; it never confirms a financial outcome. */
|
|
7446
8431
|
endFundingBalanceRefresh(evidence, read2) {
|
|
7447
8432
|
const { state, chainId } = this.captureReadScope(evidence.chainId);
|
|
@@ -7542,6 +8527,30 @@ var OwneySDK = class {
|
|
|
7542
8527
|
}
|
|
7543
8528
|
};
|
|
7544
8529
|
const token = asset;
|
|
8530
|
+
const agentFailures = {};
|
|
8531
|
+
const recordAgentFailure = (id, error) => {
|
|
8532
|
+
if (!(error instanceof OwneyError)) return;
|
|
8533
|
+
const details = error.details ?? {};
|
|
8534
|
+
const codeValue = (value) => typeof value === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(value) ? value : void 0;
|
|
8535
|
+
const revertNames = ["InsufficientBalance", "InvalidAsset", "Unauthorized", "Error", "Panic", "FailedOp", "FailedOpWithRevert"];
|
|
8536
|
+
const panicCodes = [0, 1, 17, 18, 33, 34, 49, 50, 65, 81];
|
|
8537
|
+
agentFailures[id] = {
|
|
8538
|
+
code: error.code,
|
|
8539
|
+
phase: ["prepare", "send", "observe"].includes(String(details.phase)) ? details.phase : void 0,
|
|
8540
|
+
failureCode: codeValue(details.failureCode),
|
|
8541
|
+
reason: codeValue(details.reason),
|
|
8542
|
+
withdrawalPhase: details.withdrawalPhase === "unwind" ? "unwind" : void 0,
|
|
8543
|
+
statusCode: typeof details.statusCode === "number" ? details.statusCode : void 0,
|
|
8544
|
+
rpcCode: typeof details.rpcCode === "number" ? details.rpcCode : void 0,
|
|
8545
|
+
rpcMethod: typeof details.rpcMethod === "string" && /^[A-Za-z_]{1,80}$/.test(details.rpcMethod) ? details.rpcMethod : void 0,
|
|
8546
|
+
revertSelector: typeof details.revertSelector === "string" && /^0x[0-9a-f]{8}$/i.test(details.revertSelector) ? details.revertSelector : void 0,
|
|
8547
|
+
revertName: typeof details.revertName === "string" && revertNames.includes(details.revertName) ? details.revertName : void 0,
|
|
8548
|
+
aaCode: typeof details.aaCode === "string" && /^AA[0-9]{2}$/.test(details.aaCode) ? details.aaCode : void 0,
|
|
8549
|
+
panicCode: typeof details.panicCode === "string" && /^0x[0-9a-f]{1,2}$/i.test(details.panicCode) && panicCodes.includes(Number.parseInt(details.panicCode.slice(2), 16)) ? details.panicCode : void 0,
|
|
8550
|
+
safeToFallback: typeof details.safeToFallback === "boolean" ? details.safeToFallback : void 0,
|
|
8551
|
+
executionFailed: typeof details.executionFailed === "boolean" ? details.executionFailed : void 0
|
|
8552
|
+
};
|
|
8553
|
+
};
|
|
7545
8554
|
const submissionEvidence = /* @__PURE__ */ new Map();
|
|
7546
8555
|
const submit = async (agent, requested2) => {
|
|
7547
8556
|
attempted.add(agent.id);
|
|
@@ -7562,9 +8571,19 @@ var OwneySDK = class {
|
|
|
7562
8571
|
};
|
|
7563
8572
|
progress(evidence);
|
|
7564
8573
|
try {
|
|
8574
|
+
if (options.onBeforeSubmission)
|
|
8575
|
+
return await agent.withdraw(
|
|
8576
|
+
state,
|
|
8577
|
+
chainId,
|
|
8578
|
+
token,
|
|
8579
|
+
requested2,
|
|
8580
|
+
progress,
|
|
8581
|
+
options.gasMode ?? "auto",
|
|
8582
|
+
(next) => options.onBeforeSubmission({ ...evidence, ...next })
|
|
8583
|
+
);
|
|
7565
8584
|
return await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
|
|
7566
8585
|
} catch (error) {
|
|
7567
|
-
if (!evidence.txHash && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
8586
|
+
if (!evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
7568
8587
|
progress({ ...evidence, transfer: "failed", delivery: "failed" });
|
|
7569
8588
|
throw error;
|
|
7570
8589
|
}
|
|
@@ -7595,6 +8614,7 @@ var OwneySDK = class {
|
|
|
7595
8614
|
if (!amount) {
|
|
7596
8615
|
const results2 = {};
|
|
7597
8616
|
const agentErrors2 = {};
|
|
8617
|
+
const incompleteWithdrawals = {};
|
|
7598
8618
|
for (const agent of withdrawalAgents) {
|
|
7599
8619
|
try {
|
|
7600
8620
|
results2[agent.id] = await submit(agent);
|
|
@@ -7603,8 +8623,17 @@ var OwneySDK = class {
|
|
|
7603
8623
|
if (err instanceof OwneyError && err.code === "SPONSOR_REQUEST_FAILED" && err.details?.safeToFallback === true) throw err;
|
|
7604
8624
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
7605
8625
|
if (err instanceof OwneyError && err.code === "WITHDRAW_INSUFFICIENT_BALANCE" && err.details?.available === "0" && submissionEvidence.get(agent.id)?.transfer === "failed") continue;
|
|
8626
|
+
if (agent.id === "yieldseeker" && err instanceof OwneyError && err.code === "WITHDRAW_PARTIAL_FAILURE" && err.details?.reason === "YIELDSEEKER_MAX_INCOMPLETE") {
|
|
8627
|
+
incompleteWithdrawals[agent.id] = { positionsStatus: err.details.positionsStatus };
|
|
8628
|
+
const evidence = submissionEvidence.get(agent.id);
|
|
8629
|
+
if (evidence?.agentId === agent.id && evidence.chainId === chainId && evidence.token === token && evidence.walletAddress.toLowerCase() === state.walletAddress.toLowerCase() && evidence.intendedAmount === "MAX" && evidence.transfer === "confirmed" && evidence.delivery === "confirmed" && evidence.txHash && /^0x[\da-f]{64}$/i.test(evidence.txHash) && evidence.confirmedAmount && /^\d+$/.test(evidence.confirmedAmount) && BigInt(evidence.confirmedAmount) > 0n) {
|
|
8630
|
+
results2[agent.id] = { txHash: evidence.txHash, type: "partial", amount: evidence.confirmedAmount, evidence };
|
|
8631
|
+
notifyAgentResult(agent.id, results2[agent.id]);
|
|
8632
|
+
}
|
|
8633
|
+
}
|
|
7606
8634
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
7607
8635
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
8636
|
+
recordAgentFailure(agent.id, err);
|
|
7608
8637
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
7609
8638
|
void reportAgentFailure(
|
|
7610
8639
|
this.apiKey,
|
|
@@ -7615,17 +8644,23 @@ var OwneySDK = class {
|
|
|
7615
8644
|
}
|
|
7616
8645
|
}
|
|
7617
8646
|
if (Object.keys(results2).length === 0) {
|
|
8647
|
+
if (Object.keys(agentErrors2).length === 0)
|
|
8648
|
+
throw new OwneyError(
|
|
8649
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
8650
|
+
"No funds are currently available to withdraw for this asset.",
|
|
8651
|
+
{ asset, available: "0" }
|
|
8652
|
+
);
|
|
7618
8653
|
throw new OwneyError(
|
|
7619
8654
|
"WITHDRAW_ALL_FAILED",
|
|
7620
8655
|
`Failed to withdraw "${asset}" from all eligible agents.`,
|
|
7621
|
-
{ asset, agentErrors: agentErrors2 }
|
|
8656
|
+
{ asset, agentErrors: agentErrors2, agentFailures }
|
|
7622
8657
|
);
|
|
7623
8658
|
}
|
|
7624
8659
|
if (Object.keys(agentErrors2).length > 0) {
|
|
7625
8660
|
throw new OwneyError(
|
|
7626
8661
|
"WITHDRAW_PARTIAL_FAILURE",
|
|
7627
8662
|
"Some agents could not complete the withdrawal.",
|
|
7628
|
-
{ asset, partialResults: results2, agentErrors: agentErrors2 }
|
|
8663
|
+
{ asset, partialResults: results2, agentErrors: agentErrors2, agentFailures, ...Object.keys(incompleteWithdrawals).length ? { incompleteWithdrawals } : {} }
|
|
7629
8664
|
);
|
|
7630
8665
|
}
|
|
7631
8666
|
const amountsKnown = Object.values(results2).every(
|
|
@@ -7706,6 +8741,7 @@ var OwneySDK = class {
|
|
|
7706
8741
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
7707
8742
|
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
7708
8743
|
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
8744
|
+
recordAgentFailure(p.agent.id, err);
|
|
7709
8745
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
7710
8746
|
void reportAgentFailure(
|
|
7711
8747
|
this.apiKey,
|
|
@@ -7726,7 +8762,8 @@ var OwneySDK = class {
|
|
|
7726
8762
|
{
|
|
7727
8763
|
asset,
|
|
7728
8764
|
amount,
|
|
7729
|
-
agentErrors
|
|
8765
|
+
agentErrors,
|
|
8766
|
+
agentFailures
|
|
7730
8767
|
}
|
|
7731
8768
|
);
|
|
7732
8769
|
}
|
|
@@ -7741,6 +8778,7 @@ var OwneySDK = class {
|
|
|
7741
8778
|
withdrawn: withdrawn.toString(),
|
|
7742
8779
|
partialResults: results,
|
|
7743
8780
|
agentErrors,
|
|
8781
|
+
agentFailures,
|
|
7744
8782
|
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
7745
8783
|
}
|
|
7746
8784
|
);
|
|
@@ -8200,16 +9238,16 @@ var OwneySDK = class {
|
|
|
8200
9238
|
);
|
|
8201
9239
|
}
|
|
8202
9240
|
const provider = this.requireConnectedProvider();
|
|
8203
|
-
const walletChainClient = (0,
|
|
9241
|
+
const walletChainClient = (0, import_viem16.createPublicClient)({
|
|
8204
9242
|
chain: VIEM_CHAIN2[chainId],
|
|
8205
|
-
transport: (0,
|
|
9243
|
+
transport: (0, import_viem16.custom)(provider)
|
|
8206
9244
|
});
|
|
8207
9245
|
const publicClient = this.getPaidRpcClient(chainId);
|
|
8208
9246
|
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
8209
|
-
const wallet = (0,
|
|
9247
|
+
const wallet = (0, import_viem16.createWalletClient)({
|
|
8210
9248
|
account: state.walletAddress,
|
|
8211
9249
|
chain: VIEM_CHAIN2[chainId],
|
|
8212
|
-
transport: (0,
|
|
9250
|
+
transport: (0, import_viem16.custom)(provider)
|
|
8213
9251
|
});
|
|
8214
9252
|
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
8215
9253
|
const hash = await wallet.writeContract({
|
|
@@ -8371,7 +9409,7 @@ var OwneySDK = class {
|
|
|
8371
9409
|
};
|
|
8372
9410
|
|
|
8373
9411
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
8374
|
-
var
|
|
9412
|
+
var import_viem17 = require("viem");
|
|
8375
9413
|
var import_siwe2 = require("siwe");
|
|
8376
9414
|
var import_sdk2 = require("@zyfai/sdk");
|
|
8377
9415
|
|
|
@@ -8498,7 +9536,7 @@ function buildSIWXConfig(deps) {
|
|
|
8498
9536
|
issuedAt,
|
|
8499
9537
|
toString() {
|
|
8500
9538
|
return new import_siwe2.SiweMessage({
|
|
8501
|
-
address: (0,
|
|
9539
|
+
address: (0, import_viem17.getAddress)(accountAddress),
|
|
8502
9540
|
chainId: numericChainId(chainId),
|
|
8503
9541
|
domain,
|
|
8504
9542
|
uri,
|