@owney/sdk 0.7.25-beta.18 → 0.7.25-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +983 -410
- package/dist/index.d.cts +33 -20
- package/dist/index.d.ts +33 -20
- package/dist/index.js +892 -317
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16,6 +16,29 @@ var accountExecutionAbi = parseAbi([
|
|
|
16
16
|
]);
|
|
17
17
|
var entryPoint06 = "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789";
|
|
18
18
|
var entryPoint07 = "0x0000000071727de22e5e9d8baf0edac6f37da032";
|
|
19
|
+
function yieldseekerExecution(receipt, evidence) {
|
|
20
|
+
const operationEvents = receipt.logs.flatMap((log) => {
|
|
21
|
+
if (![entryPoint06, entryPoint07].includes(log.address.toLowerCase())) return [];
|
|
22
|
+
try {
|
|
23
|
+
const event = decodeEventLog({ abi: entryPoint06Abi, data: log.data, topics: log.topics });
|
|
24
|
+
return event.eventName === "UserOperationEvent" ? [{ ...event.args, logIndex: log.logIndex }] : [];
|
|
25
|
+
} catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}).sort((a, b) => a.logIndex - b.logIndex);
|
|
29
|
+
const bundled = !!evidence.userOpHash || !!evidence.callsId || operationEvents.length > 0 || [entryPoint06, entryPoint07].includes(receipt.to?.toLowerCase() ?? "");
|
|
30
|
+
if (!bundled) return { status: "confirmed", logs: receipt.logs };
|
|
31
|
+
const senders = evidence.userOperation?.sender ? [evidence.userOperation.sender.toLowerCase()] : evidence.callsId ? [evidence.walletAddress.toLowerCase()] : [evidence.walletAddress.toLowerCase(), evidence.agentWallet?.toLowerCase()];
|
|
32
|
+
const matches = operationEvents.filter((event) => senders.includes(event.sender.toLowerCase()) && (!evidence.userOpHash || event.userOpHash.toLowerCase() === evidence.userOpHash.toLowerCase()));
|
|
33
|
+
if (matches.length !== 1) return { status: "unknown", logs: [] };
|
|
34
|
+
const operation = matches[0];
|
|
35
|
+
if (!operation.success) return { status: "failed", logs: [] };
|
|
36
|
+
const previous = operationEvents.filter((event) => event.logIndex < operation.logIndex).at(-1)?.logIndex ?? -1;
|
|
37
|
+
return {
|
|
38
|
+
status: "confirmed",
|
|
39
|
+
logs: receipt.logs.filter((log) => log.logIndex > previous && log.logIndex < operation.logIndex)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
19
42
|
function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress, receipt) {
|
|
20
43
|
if (evidence.agentId !== "yieldseeker" || transaction.chainId !== evidence.chainId || transaction.hash.toLowerCase() !== evidence.txHash?.toLowerCase() || !evidence.agentWallet) return false;
|
|
21
44
|
const matchesFullCall = (data) => {
|
|
@@ -61,8 +84,27 @@ function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress, receip
|
|
|
61
84
|
return false;
|
|
62
85
|
}
|
|
63
86
|
}
|
|
64
|
-
function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
87
|
+
function inspectFundingReceipt(receipt, evidence, tokenAddress, direction, options = {}) {
|
|
88
|
+
evidence = {
|
|
89
|
+
...evidence,
|
|
90
|
+
confirmedAmount: void 0,
|
|
91
|
+
amountCompletion: void 0,
|
|
92
|
+
shortfallAmount: void 0,
|
|
93
|
+
blockNumber: void 0,
|
|
94
|
+
logIndexes: void 0,
|
|
95
|
+
transferLogs: void 0
|
|
96
|
+
};
|
|
97
|
+
if (evidence.txHash && receipt.transactionHash && evidence.txHash.toLowerCase() !== receipt.transactionHash.toLowerCase())
|
|
98
|
+
return { ...evidence, transfer: "unknown", delivery: direction === "withdraw" ? "unknown" : "not-applicable" };
|
|
65
99
|
if (receipt.status !== "success") return { ...evidence, transfer: "failed", delivery: "failed" };
|
|
100
|
+
const execution = direction === "withdraw" && evidence.agentId === "yieldseeker" ? yieldseekerExecution(receipt, evidence) : { status: "confirmed", logs: receipt.logs };
|
|
101
|
+
if (execution.status !== "confirmed") return {
|
|
102
|
+
...evidence,
|
|
103
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
104
|
+
...execution.status === "failed" ? { confirmedAmount: "0" } : {},
|
|
105
|
+
transfer: execution.status,
|
|
106
|
+
delivery: execution.status
|
|
107
|
+
};
|
|
66
108
|
let amount = 0n;
|
|
67
109
|
const seen = /* @__PURE__ */ new Set();
|
|
68
110
|
const attributed = /* @__PURE__ */ new Map();
|
|
@@ -73,7 +115,7 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
|
73
115
|
const logIndexes = [];
|
|
74
116
|
const transferLogs = [];
|
|
75
117
|
const zeroTransferLogs = [];
|
|
76
|
-
for (const log of [...
|
|
118
|
+
for (const log of [...execution.logs].sort((a, b) => a.logIndex - b.logIndex)) {
|
|
77
119
|
if (log.address.toLowerCase() !== tokenAddress.toLowerCase()) continue;
|
|
78
120
|
if (seen.has(log.logIndex)) continue;
|
|
79
121
|
seen.add(log.logIndex);
|
|
@@ -99,7 +141,7 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
|
99
141
|
}
|
|
100
142
|
}
|
|
101
143
|
const expected = /^\d+$/.test(evidence.intendedAmount) ? BigInt(evidence.intendedAmount) : void 0;
|
|
102
|
-
const emptyPayout = direction === "withdraw" && evidence.agentId === "yieldseeker" && amount === 0n && zeroTransferLogs.length > 0 &&
|
|
144
|
+
const emptyPayout = direction === "withdraw" && evidence.agentId === "yieldseeker" && amount === 0n && zeroTransferLogs.length > 0 && execution.logs.some((log) => {
|
|
103
145
|
if (log.address.toLowerCase() !== agent) return false;
|
|
104
146
|
try {
|
|
105
147
|
const { args } = decodeEventLog({ abi: withdrawalEventAbi, data: log.data, topics: log.topics });
|
|
@@ -117,18 +159,30 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
|
117
159
|
transfer: "failed",
|
|
118
160
|
delivery: "failed"
|
|
119
161
|
};
|
|
120
|
-
const
|
|
162
|
+
const exactReceipt = !!evidence.txHash && /^0x[\da-f]{64}$/i.test(evidence.txHash) && receipt.transactionHash?.toLowerCase() === evidence.txHash.toLowerCase();
|
|
163
|
+
const delivered = amount > 0n && (options.requireAmountMatch ? expected === void 0 || amount === expected : direction === "withdraw" && exactReceipt || expected === void 0 || amount >= expected);
|
|
121
164
|
return {
|
|
122
165
|
...evidence,
|
|
123
166
|
blockNumber: receipt.blockNumber.toString(),
|
|
124
167
|
logIndexes,
|
|
125
168
|
transferLogs,
|
|
126
169
|
...amount > 0n ? { confirmedAmount: amount.toString() } : {},
|
|
170
|
+
...delivered && direction === "withdraw" && expected !== void 0 ? {
|
|
171
|
+
amountCompletion: amount < expected ? "shortfall" : "complete",
|
|
172
|
+
shortfallAmount: (amount < expected ? expected - amount : 0n).toString()
|
|
173
|
+
} : {},
|
|
127
174
|
transfer: delivered ? "confirmed" : "unknown",
|
|
128
175
|
delivery: direction === "withdraw" ? delivered ? "confirmed" : "unknown" : "not-applicable"
|
|
129
176
|
};
|
|
130
177
|
}
|
|
131
178
|
|
|
179
|
+
// src/lib/helpers/funding-amount.ts
|
|
180
|
+
function isFundingAmount(value, allowZero = false) {
|
|
181
|
+
if (typeof value !== "string" || !/^\d{1,78}$/.test(value)) return false;
|
|
182
|
+
const amount = BigInt(value);
|
|
183
|
+
return (allowZero ? amount >= 0n : amount > 0n) && amount <= (1n << 256n) - 1n;
|
|
184
|
+
}
|
|
185
|
+
|
|
132
186
|
// src/lib/deposit-batch-callback.ts
|
|
133
187
|
var batchCallbacks = /* @__PURE__ */ new WeakMap();
|
|
134
188
|
var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
|
|
@@ -345,7 +399,7 @@ var AgentReads = class {
|
|
|
345
399
|
|
|
346
400
|
// src/agents/zyfai/zyfai.withdraw-submission.ts
|
|
347
401
|
var listenersByClient = /* @__PURE__ */ new WeakMap();
|
|
348
|
-
function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
402
|
+
function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
349
403
|
const client = sdk.httpClient;
|
|
350
404
|
if (!client || typeof client.get !== "function" || typeof client.post !== "function")
|
|
351
405
|
return { attempted: () => void 0, rejected: () => false, request: () => void 0, stop: () => {
|
|
@@ -362,6 +416,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
|
362
416
|
const withdrawal = method === "get" && url === "/users/withdraw" || method === "post" && url === "/users/partial-withdraw";
|
|
363
417
|
const payload = method === "get" ? options?.params : options;
|
|
364
418
|
const matching = withdrawal ? [...observers].filter((observer) => observer.chainId === payload?.chainId && observer.token === payload?.tokenSymbol) : [];
|
|
419
|
+
for (const observer of matching) observer.assertCurrent?.();
|
|
365
420
|
const startedAt = Date.now();
|
|
366
421
|
const request2 = {
|
|
367
422
|
attempted: true,
|
|
@@ -406,7 +461,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
|
406
461
|
const status = error?.response?.status;
|
|
407
462
|
if (request2.httpStatus === void 0 && typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599)
|
|
408
463
|
request2.httpStatus = status;
|
|
409
|
-
request2.failure = message === "Unauthorized: Invalid API key" ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : /^Idle balance \d+(?:\.\d+)? (USDC|WETH|ETH|EURC) is below the minimum withdrawal of \d+(?:\.\d+)? \1\.?$/.test(message) ? "insufficient-balance" : /^User doesn't have any active session key\.?$/.test(message) ? "session-key-missing" : method === "post" && request2.httpStatus === 400 && message === "Partial withdrawal already in progress" ? "withdrawal-in-progress" : message === "Network error: Unable to reach the server" ? "network" : "other";
|
|
464
|
+
request2.failure = message === "Unauthorized: Invalid API key" ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : request2.httpStatus === 400 && message === "No balance to withdraw" ? "insufficient-balance" : /^Idle balance \d+(?:\.\d+)? (USDC|WETH|ETH|EURC) is below the minimum withdrawal of \d+(?:\.\d+)? \1\.?$/.test(message) ? "insufficient-balance" : /^User doesn't have any active session key\.?$/.test(message) ? "session-key-missing" : method === "post" && request2.httpStatus === 400 && message === "Partial withdrawal already in progress" ? "withdrawal-in-progress" : message === "Network error: Unable to reach the server" ? "network" : "other";
|
|
410
465
|
if (["unauthorized", "forbidden", "rate-limited", "insufficient-balance", "session-key-missing", "withdrawal-in-progress"].includes(request2.failure) && (request2.httpStatus === void 0 || request2.httpStatus >= 400 && request2.httpStatus < 500)) notify("rejected");
|
|
411
466
|
throw error;
|
|
412
467
|
} finally {
|
|
@@ -419,7 +474,7 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
|
419
474
|
let submitted = false;
|
|
420
475
|
let rejected = false;
|
|
421
476
|
let request = { attempted: false };
|
|
422
|
-
const listener = { chainId, token, notify: (outcome, evidence) => {
|
|
477
|
+
const listener = { chainId, token, assertCurrent, notify: (outcome, evidence) => {
|
|
423
478
|
request = evidence;
|
|
424
479
|
if (outcome === "attempted") submitted = true;
|
|
425
480
|
else if (outcome === "rejected") rejected = true;
|
|
@@ -470,10 +525,8 @@ function hasEmptyWithdrawalSnapshot(raw2, chainId, token) {
|
|
|
470
525
|
return true;
|
|
471
526
|
}
|
|
472
527
|
|
|
473
|
-
// src/agents/zyfai/zyfai.
|
|
474
|
-
import {
|
|
475
|
-
import { erc20Abi as erc20Abi2, formatUnits as formatUnits2, isAddress, parseAbi as parseAbi2 } from "viem";
|
|
476
|
-
import { base, arbitrum, mainnet } from "viem/chains";
|
|
528
|
+
// src/agents/zyfai/zyfai.deposit.ts
|
|
529
|
+
import { createWalletClient, custom, encodeFunctionData, erc20Abi as erc20Abi2, isAddress } from "viem";
|
|
477
530
|
|
|
478
531
|
// src/types/config.ts
|
|
479
532
|
var P_SUPPORTED_CHAINS = {
|
|
@@ -522,6 +575,43 @@ var SupportedAssets = [
|
|
|
522
575
|
}
|
|
523
576
|
];
|
|
524
577
|
|
|
578
|
+
// src/agents/zyfai/zyfai.deposit.ts
|
|
579
|
+
async function submitZyfaiDeposit({ sdk, state, chain, amount, asset, smartWallet, assertCurrent }) {
|
|
580
|
+
const owner = state.walletAddress;
|
|
581
|
+
const provider = state.provider;
|
|
582
|
+
const token = SupportedAssets.find((value) => value.chainId === chain.id && value.symbol === asset);
|
|
583
|
+
if (!token || !isAddress(owner) || !isAddress(smartWallet) || !/^\d+$/.test(amount) || BigInt(amount) <= 0n)
|
|
584
|
+
throw new OwneyError("AGENT_INVALID_RESPONSE", "Invalid Zyfai deposit transfer.", { chainId: chain.id, asset }, "zyfai");
|
|
585
|
+
assertCurrent();
|
|
586
|
+
const prepared = await sdk.prepareEnterPosition({ userAddress: owner, chainId: chain.id, amount, asset, strategy: "aggressive" });
|
|
587
|
+
assertCurrent();
|
|
588
|
+
const expectedData = encodeFunctionData({ abi: erc20Abi2, functionName: "transfer", args: [smartWallet, BigInt(amount)] });
|
|
589
|
+
const transfer = prepared?.transfer;
|
|
590
|
+
if (!transfer || transfer.safeAddress?.toLowerCase() !== smartWallet.toLowerCase() || transfer.tokenAddress?.toLowerCase() !== token.address.toLowerCase() || transfer.to?.toLowerCase() !== token.address.toLowerCase() || transfer.data?.toLowerCase() !== expectedData.toLowerCase() || transfer.value !== "0")
|
|
591
|
+
throw new OwneyError("AGENT_INVALID_RESPONSE", "Zyfai prepared a different deposit transfer.", { chainId: chain.id, asset }, "zyfai");
|
|
592
|
+
const wallet = createWalletClient({ account: owner, chain, transport: custom({
|
|
593
|
+
request: async (args) => {
|
|
594
|
+
assertCurrent();
|
|
595
|
+
if (args.method === "eth_sendTransaction") {
|
|
596
|
+
const [accounts, chainId] = await Promise.all([
|
|
597
|
+
provider.request({ method: "eth_accounts" }),
|
|
598
|
+
provider.request({ method: "eth_chainId" })
|
|
599
|
+
]);
|
|
600
|
+
assertCurrent();
|
|
601
|
+
if (!Array.isArray(accounts) || typeof accounts[0] !== "string" || accounts[0].toLowerCase() !== owner.toLowerCase() || Number(chainId) !== chain.id)
|
|
602
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai deposit.", void 0, "zyfai");
|
|
603
|
+
}
|
|
604
|
+
return provider.request(args);
|
|
605
|
+
}
|
|
606
|
+
}, { retryCount: 0 }) });
|
|
607
|
+
return wallet.sendTransaction({ account: owner, chain, to: token.address, data: expectedData, value: 0n });
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/agents/zyfai/zyfai.agent.ts
|
|
611
|
+
import { SMART_SESSIONS_VALIDATOR, ZyfaiSDK } from "@zyfai/sdk";
|
|
612
|
+
import { erc20Abi as erc20Abi3, formatUnits as formatUnits2, isAddress as isAddress2, parseAbi as parseAbi2 } from "viem";
|
|
613
|
+
import { base, arbitrum, mainnet } from "viem/chains";
|
|
614
|
+
|
|
525
615
|
// src/lib/debug.ts
|
|
526
616
|
var configuredDebug = false;
|
|
527
617
|
function setOwneyDebug(enabled) {
|
|
@@ -581,13 +671,6 @@ function tokenDecimals(symbol, explicit) {
|
|
|
581
671
|
return explicit;
|
|
582
672
|
return DECIMALS_BY_SYMBOL[(symbol ?? "").toLowerCase()] ?? 6;
|
|
583
673
|
}
|
|
584
|
-
function mapDeposit(raw2) {
|
|
585
|
-
return {
|
|
586
|
-
txHash: raw2.txHash,
|
|
587
|
-
smartWallet: raw2.smartWallet,
|
|
588
|
-
amount: raw2.amount
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
674
|
function mapWithdraw(raw2) {
|
|
592
675
|
return {
|
|
593
676
|
txHash: raw2.txHash,
|
|
@@ -1490,6 +1573,14 @@ var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
|
|
|
1490
1573
|
var EARNINGS_REFRESH_COOLDOWN_MS = 60 * 60 * 1e3;
|
|
1491
1574
|
var WITHDRAW_BALANCE_TIMEOUT_MS = 2e3;
|
|
1492
1575
|
var ZyfaiAgent = class _ZyfaiAgent {
|
|
1576
|
+
constructor(apiKey, rpcUrls, referralSource) {
|
|
1577
|
+
this.apiKey = apiKey;
|
|
1578
|
+
this.referralSource = referralSource;
|
|
1579
|
+
this.rpcUrls = resolveRpcUrls(rpcUrls);
|
|
1580
|
+
this.sdk = this.createSdk();
|
|
1581
|
+
}
|
|
1582
|
+
apiKey;
|
|
1583
|
+
referralSource;
|
|
1493
1584
|
id = "zyfai";
|
|
1494
1585
|
supportedChainIds = [8453, 42161, 1];
|
|
1495
1586
|
supportedAssets = [
|
|
@@ -1534,6 +1625,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1534
1625
|
*/
|
|
1535
1626
|
sessionKeyAttempts = /* @__PURE__ */ new Set();
|
|
1536
1627
|
connectAuthPromise = null;
|
|
1628
|
+
connectAuthScope = null;
|
|
1629
|
+
connectionGeneration = 0;
|
|
1630
|
+
sdkHasConnected = false;
|
|
1537
1631
|
rpcUrls;
|
|
1538
1632
|
publicClients = /* @__PURE__ */ new Map();
|
|
1539
1633
|
// Receipt-derived amounts and log identities, scoped to the owner and asset.
|
|
@@ -1544,17 +1638,20 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1544
1638
|
earningsSnapshot = null;
|
|
1545
1639
|
earningsGeneration = 0;
|
|
1546
1640
|
balanceSnapshots = /* @__PURE__ */ new WeakMap();
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1641
|
+
/** A replaced wallet session gets its own vendor client. In-flight calls
|
|
1642
|
+
* keep their original client; authentication must never change owners under
|
|
1643
|
+
* an operation that is waiting for provider reads or an HTTP response. */
|
|
1644
|
+
createSdk() {
|
|
1645
|
+
const sdk = new ZyfaiSDK({
|
|
1646
|
+
apiKey: this.apiKey,
|
|
1551
1647
|
rpcUrls: this.rpcUrls,
|
|
1552
1648
|
// Forwarded to /auth/login by the SDK's own SIWE handshake, so
|
|
1553
1649
|
// attribution survives without the AppKit SIWX config (which carried it
|
|
1554
1650
|
// via `createOwneySIWX({ referralSource })` when apps used the modal).
|
|
1555
|
-
referralSource
|
|
1651
|
+
referralSource: this.referralSource
|
|
1556
1652
|
});
|
|
1557
|
-
this.installAuthCache();
|
|
1653
|
+
this.installAuthCache(sdk);
|
|
1654
|
+
return sdk;
|
|
1558
1655
|
}
|
|
1559
1656
|
getPublicClient(chainId) {
|
|
1560
1657
|
const cached = this.publicClients.get(chainId);
|
|
@@ -1653,9 +1750,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1653
1750
|
* No-op on the server (no `window`) — the SDK is constructed during SSR
|
|
1654
1751
|
* for some apps and the patch only matters in the browser anyway.
|
|
1655
1752
|
*/
|
|
1656
|
-
installAuthCache() {
|
|
1753
|
+
installAuthCache(sdk = this.sdk) {
|
|
1657
1754
|
if (typeof window === "undefined") return;
|
|
1658
|
-
const internals =
|
|
1755
|
+
const internals = sdk;
|
|
1659
1756
|
const original = internals.authenticateUser.bind(internals);
|
|
1660
1757
|
internals.authenticateUser = async () => {
|
|
1661
1758
|
if (internals.authenticatedUserId !== null) return;
|
|
@@ -1700,25 +1797,41 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1700
1797
|
/** Light connect: SIWE auth only. Re-auths if chain changed. */
|
|
1701
1798
|
async connectAuth(state, chainId) {
|
|
1702
1799
|
const validChainId = isValidChainId(chainId);
|
|
1703
|
-
|
|
1800
|
+
const owner = state.walletAddress.toLowerCase();
|
|
1801
|
+
const scope = `${owner}:${validChainId}`;
|
|
1802
|
+
if (this.connectedAddress?.toLowerCase() === owner && this.connectedChainId === validChainId) {
|
|
1704
1803
|
return;
|
|
1705
1804
|
}
|
|
1706
|
-
if (this.connectAuthPromise) {
|
|
1805
|
+
if (this.connectAuthPromise && this.connectAuthScope === scope) {
|
|
1707
1806
|
await this.connectAuthPromise;
|
|
1708
|
-
return;
|
|
1807
|
+
return this.connectAuth(state, chainId);
|
|
1709
1808
|
}
|
|
1710
|
-
this.
|
|
1809
|
+
if (this.sdkHasConnected || this.connectedAddress) this.sdk = this.createSdk();
|
|
1810
|
+
this.sdkHasConnected = true;
|
|
1811
|
+
const executionSdk = this.sdk;
|
|
1812
|
+
const generation = ++this.connectionGeneration;
|
|
1813
|
+
this.connectedAddress = null;
|
|
1814
|
+
this.connectedChainId = null;
|
|
1815
|
+
const pending = (async () => {
|
|
1711
1816
|
const address = await this.connectAccountWithRetry(
|
|
1712
1817
|
state.provider,
|
|
1713
|
-
validChainId
|
|
1818
|
+
validChainId,
|
|
1819
|
+
executionSdk
|
|
1714
1820
|
);
|
|
1821
|
+
if (generation !== this.connectionGeneration || address.toLowerCase() !== owner)
|
|
1822
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while connecting Zyfai.", void 0, this.id);
|
|
1715
1823
|
this.connectedAddress = address;
|
|
1716
1824
|
this.connectedChainId = validChainId;
|
|
1717
1825
|
})();
|
|
1826
|
+
this.connectAuthPromise = pending;
|
|
1827
|
+
this.connectAuthScope = scope;
|
|
1718
1828
|
try {
|
|
1719
|
-
await
|
|
1829
|
+
await pending;
|
|
1720
1830
|
} finally {
|
|
1721
|
-
this.connectAuthPromise
|
|
1831
|
+
if (this.connectAuthPromise === pending) {
|
|
1832
|
+
this.connectAuthPromise = null;
|
|
1833
|
+
this.connectAuthScope = null;
|
|
1834
|
+
}
|
|
1722
1835
|
}
|
|
1723
1836
|
}
|
|
1724
1837
|
/**
|
|
@@ -1730,16 +1843,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1730
1843
|
* JWT from a misconfigured API key; we retry once either way, and if the
|
|
1731
1844
|
* second attempt still fails the original error propagates.
|
|
1732
1845
|
*/
|
|
1733
|
-
async connectAccountWithRetry(provider, chainId) {
|
|
1846
|
+
async connectAccountWithRetry(provider, chainId, executionSdk = this.sdk) {
|
|
1734
1847
|
try {
|
|
1735
|
-
return await
|
|
1848
|
+
return await executionSdk.connectAccount(provider, chainId);
|
|
1736
1849
|
} catch (error) {
|
|
1737
1850
|
const message = error instanceof Error ? error.message : String(error);
|
|
1738
1851
|
if (!message.toLowerCase().includes("unauthorized")) throw error;
|
|
1739
|
-
const internals =
|
|
1852
|
+
const internals = executionSdk;
|
|
1740
1853
|
const address = internals.walletClient?.account?.address;
|
|
1741
1854
|
if (address) clearSession(address, chainId);
|
|
1742
|
-
return await
|
|
1855
|
+
return await executionSdk.connectAccount(provider, chainId);
|
|
1743
1856
|
}
|
|
1744
1857
|
}
|
|
1745
1858
|
/**
|
|
@@ -1751,11 +1864,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1751
1864
|
* withdrawFunds) fail with "No account connected".
|
|
1752
1865
|
*/
|
|
1753
1866
|
async ensureConnected(state, chainId) {
|
|
1867
|
+
const assertOwner = () => {
|
|
1868
|
+
if (this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== chainId)
|
|
1869
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while connecting Zyfai.", void 0, this.id);
|
|
1870
|
+
};
|
|
1754
1871
|
await this.connectAuth(state, chainId);
|
|
1872
|
+
assertOwner();
|
|
1755
1873
|
const internals = this.sdk;
|
|
1756
1874
|
if (!internals.walletClient || internals.authenticatedUserId === null) {
|
|
1757
1875
|
this.connectedChainId = null;
|
|
1758
1876
|
await this.connectAuth(state, chainId);
|
|
1877
|
+
assertOwner();
|
|
1759
1878
|
}
|
|
1760
1879
|
}
|
|
1761
1880
|
/** A rejected execution session must not be reused by the next foreground attempt. */
|
|
@@ -1816,6 +1935,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1816
1935
|
}
|
|
1817
1936
|
// --- IAgent: Connection lifecycle ---
|
|
1818
1937
|
async disconnect() {
|
|
1938
|
+
const executionSdk = this.sdk;
|
|
1939
|
+
this.connectionGeneration++;
|
|
1819
1940
|
this.earningsGeneration++;
|
|
1820
1941
|
this.earningsReads.clear();
|
|
1821
1942
|
this.earningsRefreshes.clear();
|
|
@@ -1823,10 +1944,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1823
1944
|
if (this.connectedAddress && this.connectedChainId !== null) {
|
|
1824
1945
|
clearSession(this.connectedAddress, this.connectedChainId);
|
|
1825
1946
|
}
|
|
1826
|
-
await this.sdk.disconnectAccount();
|
|
1827
1947
|
this.connectedAddress = null;
|
|
1828
1948
|
this.connectedChainId = null;
|
|
1949
|
+
this.connectAuthPromise = null;
|
|
1950
|
+
this.connectAuthScope = null;
|
|
1829
1951
|
this.smartWalletCache.clear();
|
|
1952
|
+
this.sdk = this.createSdk();
|
|
1953
|
+
this.sdkHasConnected = false;
|
|
1954
|
+
await executionSdk.disconnectAccount();
|
|
1830
1955
|
}
|
|
1831
1956
|
// --- IAgent: Account lifecycle ---
|
|
1832
1957
|
async activateAgent(state, chainId) {
|
|
@@ -2315,93 +2440,79 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2315
2440
|
return redeployed.address;
|
|
2316
2441
|
}
|
|
2317
2442
|
async deposit(state, chainId, amount, asset, depositCallback, onProgress) {
|
|
2443
|
+
const validChainId = isValidChainId(chainId);
|
|
2444
|
+
const owner = state.walletAddress;
|
|
2445
|
+
const provider = state.provider;
|
|
2446
|
+
await this.ensureConnected(state, validChainId);
|
|
2447
|
+
const executionSdk = this.sdk;
|
|
2448
|
+
const generation = this.connectionGeneration;
|
|
2449
|
+
const assertCurrent = () => {
|
|
2450
|
+
const account = executionSdk.walletClient?.account?.address;
|
|
2451
|
+
if (generation !== this.connectionGeneration || executionSdk !== this.sdk || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== validChainId || state.walletAddress.toLowerCase() !== owner.toLowerCase() || state.provider !== provider || state.chainId !== null && state.chainId !== validChainId || account && account.toLowerCase() !== owner.toLowerCase())
|
|
2452
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai deposit.", void 0, this.id);
|
|
2453
|
+
};
|
|
2454
|
+
assertCurrent();
|
|
2455
|
+
const smartWallet = await this.ensureWalletDeployed(owner, validChainId);
|
|
2456
|
+
assertCurrent();
|
|
2457
|
+
notifyFunding(onProgress, {
|
|
2458
|
+
agentId: this.id,
|
|
2459
|
+
chainId,
|
|
2460
|
+
token: asset,
|
|
2461
|
+
walletAddress: owner,
|
|
2462
|
+
agentWallet: smartWallet,
|
|
2463
|
+
intendedAmount: amount,
|
|
2464
|
+
transfer: "pending",
|
|
2465
|
+
registration: "pending",
|
|
2466
|
+
delivery: "not-applicable"
|
|
2467
|
+
});
|
|
2468
|
+
const txHash = depositCallback ? await depositCallback(smartWallet, validChainId, amount) : await submitZyfaiDeposit({
|
|
2469
|
+
sdk: executionSdk,
|
|
2470
|
+
state: { ...state, walletAddress: owner, provider },
|
|
2471
|
+
chain: VIEM_CHAIN[validChainId],
|
|
2472
|
+
amount,
|
|
2473
|
+
asset,
|
|
2474
|
+
smartWallet,
|
|
2475
|
+
assertCurrent
|
|
2476
|
+
});
|
|
2477
|
+
let evidence = {
|
|
2478
|
+
agentId: this.id,
|
|
2479
|
+
chainId,
|
|
2480
|
+
token: asset,
|
|
2481
|
+
walletAddress: owner,
|
|
2482
|
+
agentWallet: smartWallet,
|
|
2483
|
+
intendedAmount: amount,
|
|
2484
|
+
txHash,
|
|
2485
|
+
transfer: "pending",
|
|
2486
|
+
registration: "pending",
|
|
2487
|
+
delivery: "not-applicable"
|
|
2488
|
+
};
|
|
2489
|
+
notifyFunding(onProgress, evidence);
|
|
2318
2490
|
try {
|
|
2319
|
-
const
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
token: asset,
|
|
2342
|
-
walletAddress: state.walletAddress,
|
|
2343
|
-
agentWallet: smartWallet,
|
|
2344
|
-
intendedAmount: amount,
|
|
2345
|
-
txHash,
|
|
2346
|
-
transfer: "pending",
|
|
2347
|
-
registration: "pending",
|
|
2348
|
-
delivery: "not-applicable"
|
|
2349
|
-
};
|
|
2350
|
-
notifyFunding(onProgress, evidence2);
|
|
2351
|
-
try {
|
|
2352
|
-
const receipt = await this.getPublicClient(validChainId).getTransactionReceipt({ hash: txHash });
|
|
2353
|
-
const token = SupportedAssets.find((token2) => token2.chainId === chainId && token2.symbol === asset);
|
|
2354
|
-
evidence2 = inspectFundingReceipt(receipt, evidence2, token.address, "deposit");
|
|
2355
|
-
} catch {
|
|
2356
|
-
evidence2 = { ...evidence2, transfer: "unknown" };
|
|
2357
|
-
}
|
|
2358
|
-
notifyFunding(onProgress, evidence2);
|
|
2359
|
-
if (evidence2.transfer === "failed")
|
|
2360
|
-
throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
|
|
2361
|
-
try {
|
|
2362
|
-
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
2363
|
-
await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
|
|
2364
|
-
evidence2 = { ...evidence2, registration: "confirmed" };
|
|
2365
|
-
} catch {
|
|
2366
|
-
evidence2 = { ...evidence2, registration: "unknown" };
|
|
2367
|
-
}
|
|
2368
|
-
notifyFunding(onProgress, evidence2);
|
|
2369
|
-
return { txHash, smartWallet, amount, evidence: evidence2 };
|
|
2370
|
-
}
|
|
2371
|
-
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
2372
|
-
const raw2 = await this.sdk.sendDeposit(
|
|
2373
|
-
this.getAddress(),
|
|
2374
|
-
validChainId,
|
|
2375
|
-
amount,
|
|
2376
|
-
asset,
|
|
2377
|
-
"aggressive"
|
|
2378
|
-
);
|
|
2379
|
-
let evidence = {
|
|
2380
|
-
agentId: this.id,
|
|
2381
|
-
chainId,
|
|
2382
|
-
token: asset,
|
|
2383
|
-
walletAddress: state.walletAddress,
|
|
2384
|
-
agentWallet: raw2.smartWallet,
|
|
2385
|
-
intendedAmount: amount,
|
|
2386
|
-
txHash: raw2.txHash,
|
|
2387
|
-
transfer: "unknown",
|
|
2388
|
-
registration: raw2.registration ? "confirmed" : "unknown",
|
|
2389
|
-
delivery: "not-applicable"
|
|
2390
|
-
};
|
|
2391
|
-
notifyFunding(onProgress, evidence);
|
|
2392
|
-
try {
|
|
2393
|
-
const receipt = await this.getPublicClient(validChainId).getTransactionReceipt({ hash: raw2.txHash });
|
|
2394
|
-
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
2395
|
-
evidence = inspectFundingReceipt(receipt, evidence, token.address, "deposit");
|
|
2396
|
-
} catch {
|
|
2397
|
-
}
|
|
2398
|
-
notifyFunding(onProgress, evidence);
|
|
2399
|
-
if (evidence.transfer === "failed")
|
|
2400
|
-
throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash: raw2.txHash }, this.id);
|
|
2401
|
-
return { ...mapDeposit(raw2), evidence };
|
|
2402
|
-
} catch (error) {
|
|
2403
|
-
throw error;
|
|
2491
|
+
const receipt = await this.getPublicClient(validChainId).waitForTransactionReceipt({
|
|
2492
|
+
hash: txHash,
|
|
2493
|
+
confirmations: 1,
|
|
2494
|
+
timeout: 9e4,
|
|
2495
|
+
checkReplacement: false
|
|
2496
|
+
});
|
|
2497
|
+
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
2498
|
+
evidence = inspectFundingReceipt(receipt, evidence, token.address, "deposit");
|
|
2499
|
+
} catch {
|
|
2500
|
+
evidence = { ...evidence, transfer: "unknown" };
|
|
2501
|
+
}
|
|
2502
|
+
notifyFunding(onProgress, evidence);
|
|
2503
|
+
if (evidence.transfer === "failed")
|
|
2504
|
+
throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
|
|
2505
|
+
if (evidence.transfer !== "confirmed") return { txHash, smartWallet, amount, evidence };
|
|
2506
|
+
try {
|
|
2507
|
+
assertCurrent();
|
|
2508
|
+
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
2509
|
+
await (tokenAddress ? executionSdk.logDeposit(validChainId, txHash, amount, tokenAddress) : executionSdk.logDeposit(validChainId, txHash, amount));
|
|
2510
|
+
evidence = { ...evidence, registration: "confirmed" };
|
|
2511
|
+
} catch {
|
|
2512
|
+
evidence = { ...evidence, registration: "unknown" };
|
|
2404
2513
|
}
|
|
2514
|
+
notifyFunding(onProgress, evidence);
|
|
2515
|
+
return { txHash, smartWallet, amount, evidence };
|
|
2405
2516
|
}
|
|
2406
2517
|
// --- IAgent: Fund operations ---
|
|
2407
2518
|
/** Only after a definitive endpoint rejection, combine a new provider
|
|
@@ -2431,7 +2542,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2431
2542
|
this.sdk.getPortfolio(owner).catch(() => void 0)
|
|
2432
2543
|
]);
|
|
2433
2544
|
assertScope();
|
|
2434
|
-
if (!
|
|
2545
|
+
if (!isAddress2(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
|
|
2435
2546
|
throw new OwneyError(
|
|
2436
2547
|
"AGENT_AUTH_FAILED",
|
|
2437
2548
|
"Could not verify Zyfai's withdrawal wallet.",
|
|
@@ -2442,7 +2553,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2442
2553
|
const asset = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === token);
|
|
2443
2554
|
const idle = asset ? await this.getPublicClient(chainId).readContract({
|
|
2444
2555
|
address: asset.address,
|
|
2445
|
-
abi:
|
|
2556
|
+
abi: erc20Abi3,
|
|
2446
2557
|
functionName: "balanceOf",
|
|
2447
2558
|
args: [wallet.address]
|
|
2448
2559
|
}).catch(() => void 0) : void 0;
|
|
@@ -2471,11 +2582,12 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2471
2582
|
/** Called only after the execution endpoint explicitly rejected a request.
|
|
2472
2583
|
* A timeout or an accepted request never authorizes repair and resubmission. */
|
|
2473
2584
|
async recoverWithdrawalSession(state, chainId, token, owner) {
|
|
2585
|
+
const generation = this.connectionGeneration;
|
|
2474
2586
|
await this.assertNotEmptyRejectedWithdrawal(state, chainId, token, owner);
|
|
2475
2587
|
const internals = this.sdk;
|
|
2476
2588
|
const assertScope = () => {
|
|
2477
2589
|
const account = internals.walletClient?.account?.address;
|
|
2478
|
-
if (state.walletAddress.toLowerCase() !== owner.toLowerCase() || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
|
|
2590
|
+
if (generation !== this.connectionGeneration || state.walletAddress.toLowerCase() !== owner.toLowerCase() || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
|
|
2479
2591
|
throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai withdrawal.", void 0, this.id);
|
|
2480
2592
|
};
|
|
2481
2593
|
assertScope();
|
|
@@ -2504,6 +2616,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2504
2616
|
}
|
|
2505
2617
|
}
|
|
2506
2618
|
async withdraw(state, chainId, token, amount, onProgress) {
|
|
2619
|
+
if (amount !== void 0 && (typeof amount !== "string" || !/^[0-9]+$/.test(amount) || BigInt(amount) <= 0n))
|
|
2620
|
+
throw new OwneyError("WITHDRAW_INVALID_AMOUNT", "Withdrawal amount must be a positive integer in token base units.", void 0, this.id);
|
|
2507
2621
|
const validChainId = isValidChainId(chainId);
|
|
2508
2622
|
try {
|
|
2509
2623
|
await this.ensureConnected(state, validChainId);
|
|
@@ -2520,16 +2634,40 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2520
2634
|
});
|
|
2521
2635
|
throw error;
|
|
2522
2636
|
}
|
|
2523
|
-
|
|
2524
|
-
const
|
|
2525
|
-
const
|
|
2637
|
+
const executionSdk = this.sdk;
|
|
2638
|
+
const internals = executionSdk;
|
|
2639
|
+
const generation = this.connectionGeneration;
|
|
2640
|
+
const owner = state.walletAddress;
|
|
2641
|
+
let staleSessionError;
|
|
2642
|
+
const assertCurrent = () => {
|
|
2643
|
+
const account = internals.walletClient?.account?.address;
|
|
2644
|
+
if (generation !== this.connectionGeneration || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== validChainId || state.chainId !== null && state.chainId !== validChainId || account && account.toLowerCase() !== owner.toLowerCase()) {
|
|
2645
|
+
staleSessionError = new OwneyError(
|
|
2646
|
+
"NOT_CONNECTED",
|
|
2647
|
+
"Wallet changed before Zyfai withdrawal submission.",
|
|
2648
|
+
{ phase: "prepare", rejectedBeforeSubmission: true },
|
|
2649
|
+
this.id
|
|
2650
|
+
);
|
|
2651
|
+
throw staleSessionError;
|
|
2652
|
+
}
|
|
2653
|
+
};
|
|
2654
|
+
let observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertCurrent);
|
|
2655
|
+
const authToken = internals.httpClient?.authToken;
|
|
2526
2656
|
let raw2;
|
|
2527
2657
|
try {
|
|
2528
2658
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
2529
2659
|
try {
|
|
2530
|
-
|
|
2660
|
+
assertCurrent();
|
|
2661
|
+
raw2 = await executionSdk.withdrawFunds(owner, validChainId, amount, token);
|
|
2531
2662
|
break;
|
|
2532
2663
|
} catch (error) {
|
|
2664
|
+
if (observation.rejected()) {
|
|
2665
|
+
try {
|
|
2666
|
+
assertCurrent();
|
|
2667
|
+
} catch {
|
|
2668
|
+
throw error;
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2533
2671
|
if (!amount && observation.rejected() && observation.request()?.failure === "insufficient-balance") {
|
|
2534
2672
|
try {
|
|
2535
2673
|
await this.assertNotEmptyRejectedWithdrawal(state, validChainId, token, owner);
|
|
@@ -2547,7 +2685,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2547
2685
|
);
|
|
2548
2686
|
await this.recoverWithdrawalSession(state, validChainId, token, owner);
|
|
2549
2687
|
observation.stop();
|
|
2550
|
-
observation = observeWithdrawalSubmission(
|
|
2688
|
+
observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertCurrent);
|
|
2551
2689
|
}
|
|
2552
2690
|
}
|
|
2553
2691
|
if (!raw2) throw new OwneyError(
|
|
@@ -2557,6 +2695,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2557
2695
|
this.id
|
|
2558
2696
|
);
|
|
2559
2697
|
} catch (error) {
|
|
2698
|
+
if (staleSessionError && observation.attempted() === false) error = staleSessionError;
|
|
2560
2699
|
this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken);
|
|
2561
2700
|
if (observation.attempted() !== void 0) {
|
|
2562
2701
|
const failed = observation.attempted() === false || observation.rejected();
|
|
@@ -2577,7 +2716,6 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2577
2716
|
observation.stop();
|
|
2578
2717
|
}
|
|
2579
2718
|
if (!raw2.success) {
|
|
2580
|
-
const unresolved = !!raw2.txHash || /already|pending|queued|in.progress/i.test(raw2.message ?? "");
|
|
2581
2719
|
notifyFunding(onProgress, {
|
|
2582
2720
|
agentId: this.id,
|
|
2583
2721
|
chainId,
|
|
@@ -2585,9 +2723,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2585
2723
|
walletAddress: state.walletAddress,
|
|
2586
2724
|
intendedAmount: amount ?? "MAX",
|
|
2587
2725
|
txHash: raw2.txHash,
|
|
2588
|
-
transfer:
|
|
2726
|
+
transfer: "unknown",
|
|
2589
2727
|
registration: "not-applicable",
|
|
2590
|
-
delivery:
|
|
2728
|
+
delivery: "unknown",
|
|
2591
2729
|
withdrawalRequest: observation.request()
|
|
2592
2730
|
});
|
|
2593
2731
|
throw new OwneyError(
|
|
@@ -2614,8 +2752,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2614
2752
|
}
|
|
2615
2753
|
/** Registration recovery never signs, transfers, activates, or refreshes authentication. */
|
|
2616
2754
|
async retryDepositRegistration(state, evidence) {
|
|
2617
|
-
const
|
|
2618
|
-
|
|
2755
|
+
const executionSdk = this.sdk;
|
|
2756
|
+
const internals = executionSdk;
|
|
2757
|
+
if (evidence.agentId !== this.id || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || evidence.chainId !== state.chainId || !internals.httpClient.authToken || internals.authenticatedUserId === null || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== evidence.chainId) {
|
|
2619
2758
|
throw new OwneyError("NOT_CONNECTED", "Reconnect the wallet before recovering deposit registration.");
|
|
2620
2759
|
}
|
|
2621
2760
|
if (evidence.transfer !== "confirmed" || !evidence.txHash) {
|
|
@@ -2623,16 +2762,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2623
2762
|
}
|
|
2624
2763
|
const cid = isValidChainId(evidence.chainId);
|
|
2625
2764
|
const tokenAddress = evidence.token === "WETH" ? WETH_ADDRESS_BY_CHAIN[cid] : void 0;
|
|
2626
|
-
await
|
|
2765
|
+
await executionSdk.logDeposit(cid, evidence.txHash, evidence.intendedAmount, tokenAddress);
|
|
2627
2766
|
return { ...evidence, registration: "confirmed" };
|
|
2628
2767
|
}
|
|
2629
2768
|
// --- IAgent: Portfolio reads ---
|
|
2630
2769
|
async getFundingWallet(state, chainId) {
|
|
2631
2770
|
return (await this.sdk.getSmartWalletAddress(state.walletAddress, isValidChainId(chainId))).address;
|
|
2632
2771
|
}
|
|
2633
|
-
/** A positive Safe balance is a
|
|
2634
|
-
*
|
|
2635
|
-
*
|
|
2772
|
+
/** A positive deployed Safe balance is a conservative planning floor. Zero
|
|
2773
|
+
* requires both the mapped Safe's exact idle balance and a fresh complete
|
|
2774
|
+
* empty portfolio, including for an undeployed counterfactual Safe. */
|
|
2636
2775
|
async getWithdrawableBalance(state, chainId, token) {
|
|
2637
2776
|
const validChainId = isValidChainId(chainId);
|
|
2638
2777
|
const asset = SupportedAssets.find((value) => value.chainId === validChainId && value.symbol === token);
|
|
@@ -2661,20 +2800,27 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2661
2800
|
this.sdk.getSmartWalletByEOA(owner)
|
|
2662
2801
|
]);
|
|
2663
2802
|
assertScope();
|
|
2664
|
-
if (!
|
|
2803
|
+
if (!isAddress2(wallet.address) || wallet.isDeployed !== false && (wallet.isDeployed !== true || wallet.isOwner !== true) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase()) throw unavailable();
|
|
2665
2804
|
const client = this.getPublicClient(validChainId);
|
|
2666
2805
|
const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
|
|
2667
2806
|
assertScope();
|
|
2668
2807
|
const idle = await client.readContract({
|
|
2669
2808
|
address: asset.address,
|
|
2670
|
-
abi:
|
|
2809
|
+
abi: erc20Abi3,
|
|
2671
2810
|
functionName: "balanceOf",
|
|
2672
2811
|
args: [wallet.address],
|
|
2673
2812
|
blockNumber
|
|
2674
2813
|
});
|
|
2675
2814
|
assertScope();
|
|
2676
|
-
if (typeof idle !== "bigint" || idle
|
|
2677
|
-
|
|
2815
|
+
if (typeof idle !== "bigint" || idle < 0n) throw unavailable();
|
|
2816
|
+
if (idle > 0n) {
|
|
2817
|
+
if (wallet.isDeployed !== true || wallet.isOwner !== true) throw unavailable();
|
|
2818
|
+
return idle;
|
|
2819
|
+
}
|
|
2820
|
+
const portfolio = await this.sdk.getPortfolio(owner);
|
|
2821
|
+
assertScope();
|
|
2822
|
+
if (!hasEmptyWithdrawalSnapshot(portfolio, validChainId, token)) throw unavailable();
|
|
2823
|
+
return 0n;
|
|
2678
2824
|
};
|
|
2679
2825
|
try {
|
|
2680
2826
|
return await Promise.race([read2(), new Promise((_, reject) => {
|
|
@@ -2717,7 +2863,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2717
2863
|
if (blockNumber < BigInt(evidence.blockNumber)) return void 0;
|
|
2718
2864
|
const idle = await client.readContract({
|
|
2719
2865
|
address: asset.address,
|
|
2720
|
-
abi:
|
|
2866
|
+
abi: erc20Abi3,
|
|
2721
2867
|
functionName: "balanceOf",
|
|
2722
2868
|
args: [balance.smartWallet],
|
|
2723
2869
|
blockNumber
|
|
@@ -2924,15 +3070,15 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2924
3070
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2925
3071
|
import {
|
|
2926
3072
|
createPublicClient as createPublicClient3,
|
|
2927
|
-
createWalletClient as
|
|
2928
|
-
custom as
|
|
3073
|
+
createWalletClient as createWalletClient4,
|
|
3074
|
+
custom as custom4,
|
|
2929
3075
|
decodeEventLog as decodeEventLog2,
|
|
2930
3076
|
encodeEventTopics,
|
|
2931
|
-
encodeFunctionData,
|
|
2932
|
-
erc20Abi as
|
|
3077
|
+
encodeFunctionData as encodeFunctionData2,
|
|
3078
|
+
erc20Abi as erc20Abi5,
|
|
2933
3079
|
erc4626Abi as erc4626Abi2,
|
|
2934
3080
|
getAddress as getAddress5,
|
|
2935
|
-
isAddress as
|
|
3081
|
+
isAddress as isAddress4,
|
|
2936
3082
|
parseUnits
|
|
2937
3083
|
} from "viem";
|
|
2938
3084
|
import { base as base4 } from "viem/chains";
|
|
@@ -3175,8 +3321,8 @@ function makeVerificationAwareDepositCallback(implementation) {
|
|
|
3175
3321
|
import { SiweMessage, generateNonce } from "siwe";
|
|
3176
3322
|
import {
|
|
3177
3323
|
createPublicClient as createPublicClient2,
|
|
3178
|
-
createWalletClient,
|
|
3179
|
-
custom,
|
|
3324
|
+
createWalletClient as createWalletClient2,
|
|
3325
|
+
custom as custom2,
|
|
3180
3326
|
getAddress
|
|
3181
3327
|
} from "viem";
|
|
3182
3328
|
import { base as base2 } from "viem/chains";
|
|
@@ -3380,12 +3526,12 @@ var YieldseekerAuth = class {
|
|
|
3380
3526
|
const account = getAddress(state.walletAddress);
|
|
3381
3527
|
const publicClient = createPublicClient2({
|
|
3382
3528
|
chain: base2,
|
|
3383
|
-
transport:
|
|
3529
|
+
transport: custom2(state.provider)
|
|
3384
3530
|
});
|
|
3385
|
-
const walletClient =
|
|
3531
|
+
const walletClient = createWalletClient2({
|
|
3386
3532
|
account,
|
|
3387
3533
|
chain: base2,
|
|
3388
|
-
transport:
|
|
3534
|
+
transport: custom2(state.provider)
|
|
3389
3535
|
});
|
|
3390
3536
|
await ensureWalletOnChain(
|
|
3391
3537
|
publicClient,
|
|
@@ -3552,7 +3698,7 @@ var YieldseekerApiClient = class {
|
|
|
3552
3698
|
};
|
|
3553
3699
|
|
|
3554
3700
|
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
3555
|
-
import { formatUnits as formatUnits3, isAddress as
|
|
3701
|
+
import { formatUnits as formatUnits3, isAddress as isAddress3 } from "viem";
|
|
3556
3702
|
|
|
3557
3703
|
// src/lib/helpers/snapshot-apy.ts
|
|
3558
3704
|
var DAY_MS = 864e5;
|
|
@@ -3621,6 +3767,14 @@ function raw(value, endpoint) {
|
|
|
3621
3767
|
function decimal(value, decimals, endpoint) {
|
|
3622
3768
|
return formatUnits3(raw(value, endpoint), decimals);
|
|
3623
3769
|
}
|
|
3770
|
+
function balanceRaw(value, endpoint) {
|
|
3771
|
+
const amount = raw(value, endpoint);
|
|
3772
|
+
if (amount < 0n) return invalid(endpoint, "expected a non-negative balance");
|
|
3773
|
+
return amount;
|
|
3774
|
+
}
|
|
3775
|
+
function balanceDecimal(value, decimals, endpoint) {
|
|
3776
|
+
return formatUnits3(balanceRaw(value, endpoint), decimals);
|
|
3777
|
+
}
|
|
3624
3778
|
function usd(rawAmount, decimals, price) {
|
|
3625
3779
|
return Number(formatUnits3(rawAmount, decimals)) * price;
|
|
3626
3780
|
}
|
|
@@ -3648,7 +3802,7 @@ function assetAddressValue(record, address) {
|
|
|
3648
3802
|
}
|
|
3649
3803
|
function position(value, asset, baseAssetDecimals) {
|
|
3650
3804
|
const option = value?.yieldOption;
|
|
3651
|
-
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !
|
|
3805
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !isAddress3(option.address)) {
|
|
3652
3806
|
return invalid("yield positions", "missing vault metadata");
|
|
3653
3807
|
}
|
|
3654
3808
|
return {
|
|
@@ -3662,13 +3816,13 @@ function position(value, asset, baseAssetDecimals) {
|
|
|
3662
3816
|
// underlying base-asset units in `assetsBase`; pair that value with the
|
|
3663
3817
|
// snapshot's corresponding `baseAssetDecimals` for display. Keep the raw
|
|
3664
3818
|
// share quantity separate from the withdrawable underlying asset amount.
|
|
3665
|
-
amount:
|
|
3819
|
+
amount: balanceDecimal(
|
|
3666
3820
|
value.assetsBase,
|
|
3667
3821
|
baseAssetDecimals,
|
|
3668
3822
|
"yield positions"
|
|
3669
3823
|
),
|
|
3670
|
-
amountRaw:
|
|
3671
|
-
withdrawableAmountRaw:
|
|
3824
|
+
amountRaw: balanceRaw(value.assetsRaw, "yield positions").toString(),
|
|
3825
|
+
withdrawableAmountRaw: balanceRaw(value.withdrawableAssetsRaw, "yield positions").toString(),
|
|
3672
3826
|
apy: percent(option.riskAdjustedApy),
|
|
3673
3827
|
tvl: Number(option.totalDepositsUsd),
|
|
3674
3828
|
liquidity: Number(option.withdrawableDepositsUsd)
|
|
@@ -3685,7 +3839,7 @@ function mapYieldseekerBalances(contexts) {
|
|
|
3685
3839
|
chain: "BASE",
|
|
3686
3840
|
chainId: 8453,
|
|
3687
3841
|
asset: context.asset,
|
|
3688
|
-
amount:
|
|
3842
|
+
amount: balanceDecimal(context.snapshot.totalValueBase, context.snapshot.baseAssetDecimals, "snapshot")
|
|
3689
3843
|
});
|
|
3690
3844
|
const idle = assetAddressValue(
|
|
3691
3845
|
context.snapshot.tokenBalances,
|
|
@@ -3695,7 +3849,7 @@ function mapYieldseekerBalances(contexts) {
|
|
|
3695
3849
|
chain: "BASE",
|
|
3696
3850
|
chainId: 8453,
|
|
3697
3851
|
asset: context.asset,
|
|
3698
|
-
amount:
|
|
3852
|
+
amount: balanceDecimal(idle, metadata.decimals, "snapshot")
|
|
3699
3853
|
});
|
|
3700
3854
|
positions.push(
|
|
3701
3855
|
...context.positions.map(
|
|
@@ -3707,7 +3861,7 @@ function mapYieldseekerBalances(contexts) {
|
|
|
3707
3861
|
)
|
|
3708
3862
|
);
|
|
3709
3863
|
totalUsd += usd(
|
|
3710
|
-
|
|
3864
|
+
balanceRaw(context.snapshot.totalValueBase, "snapshot"),
|
|
3711
3865
|
context.snapshot.baseAssetDecimals,
|
|
3712
3866
|
context.snapshot.baseAssetPriceUsd
|
|
3713
3867
|
);
|
|
@@ -3755,6 +3909,35 @@ function mapYieldseekerEarnings(contexts) {
|
|
|
3755
3909
|
tokens
|
|
3756
3910
|
};
|
|
3757
3911
|
}
|
|
3912
|
+
function mapYieldseekerDailyEarnings(walletAddress, contexts, days, now = Date.now()) {
|
|
3913
|
+
const today = new Date(now).toISOString().slice(0, 10);
|
|
3914
|
+
const dayCount = { "7D": 7, "14D": 14, "30D": 30 }[days];
|
|
3915
|
+
const cutoff = new Date(Date.parse(today) - dayCount * 864e5).toISOString().slice(0, 10);
|
|
3916
|
+
const assets = contexts.flatMap((context) => {
|
|
3917
|
+
const snapshots = context.historic?.dailyYieldSnapshots;
|
|
3918
|
+
if (!Array.isArray(snapshots)) invalid("historic position", "missing daily yield snapshots");
|
|
3919
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
3920
|
+
for (const snapshot of snapshots) {
|
|
3921
|
+
const time = Date.parse(snapshot.date);
|
|
3922
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(snapshot.date) || !Number.isFinite(time) || new Date(time).toISOString().slice(0, 10) !== snapshot.date) {
|
|
3923
|
+
invalid("historic position", "expected a UTC snapshot date");
|
|
3924
|
+
}
|
|
3925
|
+
if (snapshot.date > today) continue;
|
|
3926
|
+
if (snapshot.baseAssetDecimals !== YIELDSEEKER_ASSET_METADATA[context.asset].decimals) {
|
|
3927
|
+
invalid("historic position", "base asset decimals do not match the selected asset");
|
|
3928
|
+
}
|
|
3929
|
+
const amount = Number(decimal(snapshot.cumulativeYieldBase, snapshot.baseAssetDecimals, "historic position"));
|
|
3930
|
+
if (!Number.isFinite(amount)) invalid("historic position", "expected finite cumulative yield");
|
|
3931
|
+
byDate.set(snapshot.date, { date: snapshot.date, amount });
|
|
3932
|
+
}
|
|
3933
|
+
const sorted = [...byDate.values()].sort((a, b) => a.date.localeCompare(b.date));
|
|
3934
|
+
const window2 = sorted.filter((point) => point.date >= cutoff);
|
|
3935
|
+
if (!window2.length) return [];
|
|
3936
|
+
const opening = sorted.filter((point) => point.date < cutoff).at(-1);
|
|
3937
|
+
return [{ asset: context.asset, points: opening ? [opening, ...window2] : window2 }];
|
|
3938
|
+
}).sort((a, b) => a.asset.localeCompare(b.asset));
|
|
3939
|
+
return { walletAddress, chainId: 8453, assets };
|
|
3940
|
+
}
|
|
3758
3941
|
function apyForDays(context, days, now) {
|
|
3759
3942
|
if (days === "7D") return percent(context.snapshot.apy7d);
|
|
3760
3943
|
if (days === "30D") return percent(context.snapshot.apy30d);
|
|
@@ -3870,7 +4053,7 @@ function allocationEntry(action, context) {
|
|
|
3870
4053
|
const leg = deposit;
|
|
3871
4054
|
const symbol = typeof leg.assetSymbol === "string" ? leg.assetSymbol.toUpperCase() : "";
|
|
3872
4055
|
if (symbol !== context.asset && !(context.asset === "WETH" && symbol === "ETH")) return void 0;
|
|
3873
|
-
if (typeof leg.vaultAddress !== "string" || !
|
|
4056
|
+
if (typeof leg.vaultAddress !== "string" || !isAddress3(leg.vaultAddress)) return void 0;
|
|
3874
4057
|
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;
|
|
3875
4058
|
if (rawAmount === void 0 || rawAmount <= 0n) return void 0;
|
|
3876
4059
|
const option = context.positions.find((position2) => position2.yieldOption.address.toLowerCase() === leg.vaultAddress.toLowerCase())?.yieldOption;
|
|
@@ -4102,7 +4285,7 @@ function mapYieldseekerAgentApy(options, days) {
|
|
|
4102
4285
|
}
|
|
4103
4286
|
|
|
4104
4287
|
// src/lib/remaining-vault-balance.ts
|
|
4105
|
-
import { erc20Abi as
|
|
4288
|
+
import { erc20Abi as erc20Abi4, erc4626Abi, getAddress as getAddress2 } from "viem";
|
|
4106
4289
|
async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber) {
|
|
4107
4290
|
const addresses = [...new Set(vaults.map((value) => value.toLowerCase()))];
|
|
4108
4291
|
if (addresses.length > 64) return void 0;
|
|
@@ -4114,7 +4297,7 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
|
|
|
4114
4297
|
allowFailure: true,
|
|
4115
4298
|
contracts: batch.map((address) => ({
|
|
4116
4299
|
address: getAddress2(address),
|
|
4117
|
-
abi:
|
|
4300
|
+
abi: erc20Abi4,
|
|
4118
4301
|
functionName: "balanceOf",
|
|
4119
4302
|
args: [wallet]
|
|
4120
4303
|
}))
|
|
@@ -4214,8 +4397,8 @@ var YIELDSEEKER_UPGRADE_ABI = walletAbi;
|
|
|
4214
4397
|
|
|
4215
4398
|
// src/agents/yieldseeker/yieldseeker.sponsored-withdraw.ts
|
|
4216
4399
|
import {
|
|
4217
|
-
createWalletClient as
|
|
4218
|
-
custom as
|
|
4400
|
+
createWalletClient as createWalletClient3,
|
|
4401
|
+
custom as custom3,
|
|
4219
4402
|
getAddress as getAddress4,
|
|
4220
4403
|
toHex
|
|
4221
4404
|
} from "viem";
|
|
@@ -4565,10 +4748,10 @@ async function submitEoa(input) {
|
|
|
4565
4748
|
const userOpHash = operationHash(operation);
|
|
4566
4749
|
await assertOwnerSession(input.state, input.owner);
|
|
4567
4750
|
assertIntentFresh(input.expiresAt);
|
|
4568
|
-
const wallet =
|
|
4751
|
+
const wallet = createWalletClient3({
|
|
4569
4752
|
account: input.owner,
|
|
4570
4753
|
chain: base3,
|
|
4571
|
-
transport:
|
|
4754
|
+
transport: custom3(input.state.provider)
|
|
4572
4755
|
});
|
|
4573
4756
|
const signature = await wallet.signMessage({ message: { raw: userOpHash } });
|
|
4574
4757
|
operation = { ...operation, signature };
|
|
@@ -4870,6 +5053,7 @@ var YieldseekerAgent = class {
|
|
|
4870
5053
|
receiptClient;
|
|
4871
5054
|
// A client preparation hint only; the routing API still checks live identity.
|
|
4872
5055
|
withdrawalVersionCache = /* @__PURE__ */ new Map();
|
|
5056
|
+
pendingUpgrades = /* @__PURE__ */ new Map();
|
|
4873
5057
|
transactionExecutor;
|
|
4874
5058
|
unwindReceiptWaiter;
|
|
4875
5059
|
agentContexts = /* @__PURE__ */ new Map();
|
|
@@ -4985,10 +5169,30 @@ var YieldseekerAgent = class {
|
|
|
4985
5169
|
} catch {
|
|
4986
5170
|
storage5 = void 0;
|
|
4987
5171
|
}
|
|
4988
|
-
const
|
|
5172
|
+
const clearRecordedUpgrade = () => {
|
|
5173
|
+
this.pendingUpgrades.delete(pendingKey2);
|
|
4989
5174
|
try {
|
|
4990
5175
|
storage5?.removeItem(pendingKey2);
|
|
4991
5176
|
} catch {
|
|
5177
|
+
this.pendingUpgrades.set(pendingKey2, null);
|
|
5178
|
+
}
|
|
5179
|
+
};
|
|
5180
|
+
const pendingUpgrade = (hash) => new OwneyError(
|
|
5181
|
+
"WITHDRAW_IN_PROGRESS",
|
|
5182
|
+
"Yieldseeker wallet upgrade is awaiting confirmation. Retry to check its status.",
|
|
5183
|
+
{ txHash: hash, reason: "UPGRADE_CONFIRMATION_PENDING", phase: "observe", safeToFallback: false },
|
|
5184
|
+
this.id
|
|
5185
|
+
);
|
|
5186
|
+
const terminalUpgradeFailure = (error) => error instanceof OwneyError && (error.code === "AGENT_TRANSACTION_REVERTED" || error.details?.reason === "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL");
|
|
5187
|
+
const readUpgradeReceipt = async (hash) => {
|
|
5188
|
+
try {
|
|
5189
|
+
return await this.waitForReceipt(state, chainId, hash);
|
|
5190
|
+
} catch (error) {
|
|
5191
|
+
if (terminalUpgradeFailure(error)) {
|
|
5192
|
+
clearRecordedUpgrade();
|
|
5193
|
+
throw error;
|
|
5194
|
+
}
|
|
5195
|
+
throw pendingUpgrade(hash);
|
|
4992
5196
|
}
|
|
4993
5197
|
};
|
|
4994
5198
|
if (!storage5 && (gasMode === "sponsored" || before.ownerPath === "smart"))
|
|
@@ -5000,7 +5204,7 @@ var YieldseekerAgent = class {
|
|
|
5000
5204
|
);
|
|
5001
5205
|
let prior = null;
|
|
5002
5206
|
try {
|
|
5003
|
-
const saved = storage5?.getItem(pendingKey2);
|
|
5207
|
+
const saved = this.pendingUpgrades.has(pendingKey2) ? JSON.stringify(this.pendingUpgrades.get(pendingKey2)) : storage5?.getItem(pendingKey2);
|
|
5004
5208
|
try {
|
|
5005
5209
|
prior = JSON.parse(saved ?? "null");
|
|
5006
5210
|
} catch {
|
|
@@ -5044,7 +5248,7 @@ var YieldseekerAgent = class {
|
|
|
5044
5248
|
this.id
|
|
5045
5249
|
);
|
|
5046
5250
|
if (Number(callsStatus?.status) === 400 || Number(callsStatus?.status) === 500) {
|
|
5047
|
-
|
|
5251
|
+
clearRecordedUpgrade();
|
|
5048
5252
|
throw new OwneyError(
|
|
5049
5253
|
"SPONSOR_REQUEST_FAILED",
|
|
5050
5254
|
"The earlier sponsored upgrade failed.",
|
|
@@ -5056,8 +5260,8 @@ var YieldseekerAgent = class {
|
|
|
5056
5260
|
earlierHash = hashes.length === 1 ? hashes[0] : void 0;
|
|
5057
5261
|
}
|
|
5058
5262
|
if (earlierHash) {
|
|
5059
|
-
const recovered = await confirmUpgrade(earlierHash, await
|
|
5060
|
-
|
|
5263
|
+
const recovered = await confirmUpgrade(earlierHash, await readUpgradeReceipt(earlierHash));
|
|
5264
|
+
clearRecordedUpgrade();
|
|
5061
5265
|
return recovered;
|
|
5062
5266
|
}
|
|
5063
5267
|
throw new OwneyError(
|
|
@@ -5074,7 +5278,7 @@ var YieldseekerAgent = class {
|
|
|
5074
5278
|
{ version: before.version },
|
|
5075
5279
|
this.id
|
|
5076
5280
|
);
|
|
5077
|
-
const data =
|
|
5281
|
+
const data = encodeFunctionData2({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
|
|
5078
5282
|
let txHash;
|
|
5079
5283
|
let receipt;
|
|
5080
5284
|
if (gasMode === "sponsored") {
|
|
@@ -5101,33 +5305,52 @@ var YieldseekerAgent = class {
|
|
|
5101
5305
|
asset: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
5102
5306
|
callData: data,
|
|
5103
5307
|
onPending: (pending) => {
|
|
5104
|
-
const current = storage5.getItem(pendingKey2);
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
...pending
|
|
5109
|
-
}));
|
|
5308
|
+
const current = this.pendingUpgrades.has(pendingKey2) ? this.pendingUpgrades.get(pendingKey2) : JSON.parse(storage5.getItem(pendingKey2) ?? "null");
|
|
5309
|
+
const next = { startedAt: Date.now(), ...current, ...pending };
|
|
5310
|
+
storage5.setItem(pendingKey2, JSON.stringify(next));
|
|
5311
|
+
this.pendingUpgrades.delete(pendingKey2);
|
|
5110
5312
|
}
|
|
5111
5313
|
});
|
|
5112
5314
|
txHash = upgraded.txHash;
|
|
5113
5315
|
receipt = upgraded.receipt;
|
|
5114
5316
|
} catch (error) {
|
|
5115
|
-
if (error?.code === 4001 || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true))
|
|
5317
|
+
if (error?.code === 4001 || terminalUpgradeFailure(error) || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) clearRecordedUpgrade();
|
|
5116
5318
|
throw error;
|
|
5117
5319
|
}
|
|
5118
5320
|
} else {
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5321
|
+
let submittedHash;
|
|
5322
|
+
try {
|
|
5323
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
5324
|
+
from: getAddress5(state.walletAddress),
|
|
5325
|
+
to: before.agentWallet,
|
|
5326
|
+
data,
|
|
5327
|
+
value: "0",
|
|
5328
|
+
chainId
|
|
5329
|
+
}, (hash) => {
|
|
5330
|
+
submittedHash = hash;
|
|
5331
|
+
const pending = { txHash: hash };
|
|
5332
|
+
this.pendingUpgrades.set(pendingKey2, pending);
|
|
5333
|
+
try {
|
|
5334
|
+
if (storage5) {
|
|
5335
|
+
storage5.setItem(pendingKey2, JSON.stringify(pending));
|
|
5336
|
+
this.pendingUpgrades.delete(pendingKey2);
|
|
5337
|
+
}
|
|
5338
|
+
} catch {
|
|
5339
|
+
}
|
|
5340
|
+
}, (value) => {
|
|
5341
|
+
receipt = value;
|
|
5342
|
+
});
|
|
5343
|
+
} catch (error) {
|
|
5344
|
+
if (terminalUpgradeFailure(error)) {
|
|
5345
|
+
clearRecordedUpgrade();
|
|
5346
|
+
throw error;
|
|
5347
|
+
}
|
|
5348
|
+
if (submittedHash) throw pendingUpgrade(submittedHash);
|
|
5349
|
+
throw error;
|
|
5350
|
+
}
|
|
5128
5351
|
}
|
|
5129
|
-
const confirmed = await confirmUpgrade(txHash, receipt ?? await
|
|
5130
|
-
|
|
5352
|
+
const confirmed = await confirmUpgrade(txHash, receipt ?? await readUpgradeReceipt(txHash));
|
|
5353
|
+
clearRecordedUpgrade();
|
|
5131
5354
|
return confirmed;
|
|
5132
5355
|
}
|
|
5133
5356
|
async disconnect() {
|
|
@@ -5206,8 +5429,8 @@ var YieldseekerAgent = class {
|
|
|
5206
5429
|
txHash = await this.submitTransaction(state, chainId, {
|
|
5207
5430
|
from: getAddress5(state.walletAddress),
|
|
5208
5431
|
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
5209
|
-
data:
|
|
5210
|
-
abi:
|
|
5432
|
+
data: encodeFunctionData2({
|
|
5433
|
+
abi: erc20Abi5,
|
|
5211
5434
|
functionName: "transfer",
|
|
5212
5435
|
args: [getAddress5(context.wallet.walletAddress), BigInt(amount)]
|
|
5213
5436
|
}),
|
|
@@ -5352,12 +5575,12 @@ var YieldseekerAgent = class {
|
|
|
5352
5575
|
blockNumber
|
|
5353
5576
|
}), chainId, "eth_call", this.id);
|
|
5354
5577
|
this.assertSession(generation, chainId, asset);
|
|
5355
|
-
if (!
|
|
5578
|
+
if (!isAddress4(feeTracker) || /^0x0{40}$/i.test(feeTracker))
|
|
5356
5579
|
throw this.invalidResponse("wallet fee tracker");
|
|
5357
5580
|
const [idle2, fees] = await Promise.all([
|
|
5358
5581
|
withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5359
5582
|
address: metadata.address,
|
|
5360
|
-
abi:
|
|
5583
|
+
abi: erc20Abi5,
|
|
5361
5584
|
functionName: "balanceOf",
|
|
5362
5585
|
args: [agentWallet],
|
|
5363
5586
|
blockNumber
|
|
@@ -5411,7 +5634,7 @@ var YieldseekerAgent = class {
|
|
|
5411
5634
|
if (!active || group.length !== 3 || group.some((value) => value.status !== "success")) return null;
|
|
5412
5635
|
this.assertSession(generation, chainId, asset);
|
|
5413
5636
|
const [underlying, shares, withdrawable] = group.map((value) => value.result);
|
|
5414
|
-
if (typeof underlying !== "string" || !
|
|
5637
|
+
if (typeof underlying !== "string" || !isAddress4(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
|
|
5415
5638
|
const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: withdrawable.toString() };
|
|
5416
5639
|
if (shares > 0n && withdrawable === 0n) {
|
|
5417
5640
|
let positiveValue = false;
|
|
@@ -5509,6 +5732,18 @@ var YieldseekerAgent = class {
|
|
|
5509
5732
|
);
|
|
5510
5733
|
}
|
|
5511
5734
|
if (requested > totalAvailable) {
|
|
5735
|
+
if (!funds.complete)
|
|
5736
|
+
throw new OwneyError(
|
|
5737
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
5738
|
+
"Yieldseeker withdrawal liquidity could not be verified for the requested amount.",
|
|
5739
|
+
{
|
|
5740
|
+
asset,
|
|
5741
|
+
requested: requested.toString(),
|
|
5742
|
+
verifiedAvailable: totalAvailable.toString(),
|
|
5743
|
+
reason: "YIELDSEEKER_LIQUIDITY_UNVERIFIED"
|
|
5744
|
+
},
|
|
5745
|
+
this.id
|
|
5746
|
+
);
|
|
5512
5747
|
throw new OwneyError(
|
|
5513
5748
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
5514
5749
|
`Requested withdrawal "${requested}" exceeds available Yieldseeker balance "${totalAvailable}" for asset "${asset}".`,
|
|
@@ -5523,6 +5758,7 @@ var YieldseekerAgent = class {
|
|
|
5523
5758
|
const requiredIdle = requested + initialFunds.fees;
|
|
5524
5759
|
let remaining = requiredIdle > idle ? requiredIdle - idle : 0n;
|
|
5525
5760
|
let grossUnwound = 0n;
|
|
5761
|
+
let confirmedUnwinds = 0n;
|
|
5526
5762
|
let latestUnwindBlock;
|
|
5527
5763
|
const unwind = async (position2, assetsRaw) => {
|
|
5528
5764
|
this.assertSession(generation, chainId, asset);
|
|
@@ -5540,7 +5776,10 @@ var YieldseekerAgent = class {
|
|
|
5540
5776
|
if (!this.isTransactionHash(response?.transactionHash)) throw this.invalidResponse("position withdrawal");
|
|
5541
5777
|
unwindTransactionHash = response.transactionHash;
|
|
5542
5778
|
const unwindReceipt = await this.waitForReceipt(state, chainId, unwindTransactionHash);
|
|
5543
|
-
if (unwindReceipt)
|
|
5779
|
+
if (unwindReceipt) {
|
|
5780
|
+
latestUnwindBlock = unwindReceipt.blockNumber;
|
|
5781
|
+
confirmedUnwinds++;
|
|
5782
|
+
}
|
|
5544
5783
|
} catch (error) {
|
|
5545
5784
|
this.invalidateFundingRead(state, chainId, asset);
|
|
5546
5785
|
throw new OwneyError(
|
|
@@ -5610,7 +5849,7 @@ var YieldseekerAgent = class {
|
|
|
5610
5849
|
const feeRate = feeRateBps ?? 0;
|
|
5611
5850
|
const maxFee = confirmedMovement && Number.isInteger(feeRate) && feeRate >= 0 && feeRate <= 1e3 ? (grossUnwound * BigInt(feeRate) + 9999n) / 10000n : 0n;
|
|
5612
5851
|
const shortfall = availableForOwner < requested ? requested - availableForOwner : 0n;
|
|
5613
|
-
if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee) {
|
|
5852
|
+
if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee + confirmedUnwinds) {
|
|
5614
5853
|
this.invalidateFundingRead(state, chainId, asset);
|
|
5615
5854
|
if (amount === void 0 && positionsStatus !== "empty")
|
|
5616
5855
|
throw new OwneyError(
|
|
@@ -5634,12 +5873,13 @@ var YieldseekerAgent = class {
|
|
|
5634
5873
|
} : this.snapshotMovement(state, chainId, context, -payout);
|
|
5635
5874
|
const account = getAddress5(state.walletAddress);
|
|
5636
5875
|
let receipt;
|
|
5637
|
-
const base6 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount
|
|
5638
|
-
|
|
5876
|
+
const base6 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount ?? "MAX", transfer: "pending", registration: "not-applicable", delivery: "pending" };
|
|
5877
|
+
let submissionEvidence = base6;
|
|
5878
|
+
const data = amount === void 0 ? encodeFunctionData2({
|
|
5639
5879
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5640
5880
|
functionName: "withdrawAllAssetToUser",
|
|
5641
5881
|
args: [account, metadata.address]
|
|
5642
|
-
}) :
|
|
5882
|
+
}) : encodeFunctionData2({
|
|
5643
5883
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5644
5884
|
functionName: "withdrawAssetToUser",
|
|
5645
5885
|
args: [account, metadata.address, payout]
|
|
@@ -5658,7 +5898,10 @@ var YieldseekerAgent = class {
|
|
|
5658
5898
|
agentWallet: getAddress5(context.wallet.walletAddress),
|
|
5659
5899
|
asset: metadata.address,
|
|
5660
5900
|
callData: data,
|
|
5661
|
-
onPending: (pending) =>
|
|
5901
|
+
onPending: (pending) => {
|
|
5902
|
+
submissionEvidence = { ...submissionEvidence, ...pending };
|
|
5903
|
+
notifyFunding(onProgress, submissionEvidence);
|
|
5904
|
+
},
|
|
5662
5905
|
onBeforeSubmission: async (pending) => {
|
|
5663
5906
|
await onBeforeSubmission?.({ ...base6, ...pending });
|
|
5664
5907
|
onOwnerTransferAttempted?.();
|
|
@@ -5678,10 +5921,20 @@ var YieldseekerAgent = class {
|
|
|
5678
5921
|
receipt = value;
|
|
5679
5922
|
});
|
|
5680
5923
|
}
|
|
5681
|
-
|
|
5682
|
-
settlement = plannedMovement;
|
|
5683
|
-
const evidence = receipt ? inspectFundingReceipt(receipt, { ...base6, txHash }, metadata.address, "withdraw") : { ...base6, txHash, confirmedAmount: payout.toString(), transfer: "confirmed", delivery: "confirmed" };
|
|
5924
|
+
const evidence = receipt ? inspectFundingReceipt(receipt, { ...submissionEvidence, txHash }, metadata.address, "withdraw") : { ...submissionEvidence, txHash, transfer: "pending", delivery: "pending" };
|
|
5684
5925
|
notifyFunding(onProgress, evidence);
|
|
5926
|
+
const explicitEmptyPayout = evidence.transfer === "failed" && evidence.confirmedAmount === "0" && evidence.transferLogs?.some((log) => log.amountRaw === "0");
|
|
5927
|
+
if (evidence.transfer === "failed" && !explicitEmptyPayout)
|
|
5928
|
+
throw new OwneyError(
|
|
5929
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
5930
|
+
"The Yieldseeker withdrawal completed without transferring funds.",
|
|
5931
|
+
{ asset, available: "0", txHash },
|
|
5932
|
+
this.id
|
|
5933
|
+
);
|
|
5934
|
+
if (!explicitEmptyPayout && (evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || !evidence.confirmedAmount))
|
|
5935
|
+
return { txHash, type: "partial", completion: "unverified", amount: "0", evidence };
|
|
5936
|
+
confirmedMovement = true;
|
|
5937
|
+
settlement = evidence.confirmedAmount === payout.toString() ? plannedMovement : null;
|
|
5685
5938
|
let completion = maxLiquidity && !maxLiquidity.complete ? "unverified" : void 0;
|
|
5686
5939
|
if (amount === void 0) {
|
|
5687
5940
|
const verificationDeadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
|
|
@@ -5702,7 +5955,7 @@ var YieldseekerAgent = class {
|
|
|
5702
5955
|
positions = proof.positions;
|
|
5703
5956
|
positionsStatus = hasPositions(positions) ? "remaining" : proof.verified ? "empty" : "unavailable";
|
|
5704
5957
|
if (positionsStatus === "remaining") {
|
|
5705
|
-
const result = { txHash, type: "partial", amount: evidence.confirmedAmount ??
|
|
5958
|
+
const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? "0", evidence };
|
|
5706
5959
|
throw new OwneyError(
|
|
5707
5960
|
"WITHDRAW_PARTIAL_FAILURE",
|
|
5708
5961
|
"Yieldseeker MAX could not confirm that all vault positions were cleared.",
|
|
@@ -5722,7 +5975,7 @@ var YieldseekerAgent = class {
|
|
|
5722
5975
|
}
|
|
5723
5976
|
if (!proof.verified) completion = "unverified";
|
|
5724
5977
|
}
|
|
5725
|
-
if (
|
|
5978
|
+
if (explicitEmptyPayout)
|
|
5726
5979
|
throw new OwneyError(
|
|
5727
5980
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
5728
5981
|
"The Yieldseeker withdrawal completed without transferring funds.",
|
|
@@ -5737,7 +5990,7 @@ var YieldseekerAgent = class {
|
|
|
5737
5990
|
type: amount === void 0 && !completion ? "full" : "partial",
|
|
5738
5991
|
...completion ? { completion } : {},
|
|
5739
5992
|
...feeAdjustment ? { feeAdjustment } : {},
|
|
5740
|
-
amount: evidence.confirmedAmount ??
|
|
5993
|
+
amount: evidence.confirmedAmount ?? "0",
|
|
5741
5994
|
evidence
|
|
5742
5995
|
};
|
|
5743
5996
|
} finally {
|
|
@@ -5787,7 +6040,7 @@ var YieldseekerAgent = class {
|
|
|
5787
6040
|
const address = getAddress5(option.address);
|
|
5788
6041
|
return [
|
|
5789
6042
|
{ address, abi: erc4626Abi2, functionName: "asset" },
|
|
5790
|
-
{ address, abi:
|
|
6043
|
+
{ address, abi: erc20Abi5, functionName: "balanceOf", args: [owner] },
|
|
5791
6044
|
{ address, abi: erc4626Abi2, functionName: "maxWithdraw", args: [owner] }
|
|
5792
6045
|
];
|
|
5793
6046
|
})
|
|
@@ -5799,7 +6052,7 @@ var YieldseekerAgent = class {
|
|
|
5799
6052
|
const seen = /* @__PURE__ */ new Set();
|
|
5800
6053
|
for (const value of values) {
|
|
5801
6054
|
const option = value?.yieldOption;
|
|
5802
|
-
if (!option || typeof option.address !== "string" || !
|
|
6055
|
+
if (!option || typeof option.address !== "string" || !isAddress4(option.address) || seen.has(option.address.toLowerCase()) || option.chainId !== void 0 && option.chainId !== chainId || option.assetAddress !== void 0 && (typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== underlying) || typeof value.assetsRaw !== "string" || !/^\d+$/.test(value.assetsRaw) || typeof value.withdrawableAssetsRaw !== "string" || !/^\d+$/.test(value.withdrawableAssetsRaw))
|
|
5803
6056
|
throw this.invalidResponse("yield positions");
|
|
5804
6057
|
seen.add(option.address.toLowerCase());
|
|
5805
6058
|
}
|
|
@@ -5812,7 +6065,7 @@ var YieldseekerAgent = class {
|
|
|
5812
6065
|
const historic = this.readCache.get(`historic:${key2}`)?.value?.position;
|
|
5813
6066
|
const receivedAssets = new Set((historic?.movements ?? []).filter((movement) => movement.chainId === chainId && movement.agentWalletId === context.wallet.agentWalletId && movement.toAddress?.toLowerCase() === context.wallet.walletAddress.toLowerCase()).filter((movement) => typeof movement.assetAddress === "string").map((movement) => movement.assetAddress.toLowerCase()));
|
|
5814
6067
|
for (const option of this.yieldOptions.get(context.asset)?.value ?? []) {
|
|
5815
|
-
if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !
|
|
6068
|
+
if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !isAddress4(option.address) || !receivedAssets.has(option.address.toLowerCase()) || hints.has(option.address.toLowerCase())) continue;
|
|
5816
6069
|
hints.set(option.address.toLowerCase(), {
|
|
5817
6070
|
yieldOption: option,
|
|
5818
6071
|
assetsRaw: "0",
|
|
@@ -5874,11 +6127,11 @@ var YieldseekerAgent = class {
|
|
|
5874
6127
|
for (const position2 of indexedPositions) candidates.set(position2.yieldOption.address.toLowerCase(), position2.yieldOption);
|
|
5875
6128
|
const options = [...candidates.values()];
|
|
5876
6129
|
if (options.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
|
|
5877
|
-
const
|
|
5878
|
-
|
|
6130
|
+
const checksRead = this.readVaultLiquidity(options, wallet, blockNumber).then((value) => ({ value }), (error) => ({ error }));
|
|
6131
|
+
const [idle, feeTracker] = await Promise.all([
|
|
5879
6132
|
client.readContract({
|
|
5880
6133
|
address: metadata.address,
|
|
5881
|
-
abi:
|
|
6134
|
+
abi: erc20Abi5,
|
|
5882
6135
|
functionName: "balanceOf",
|
|
5883
6136
|
args: [wallet],
|
|
5884
6137
|
blockNumber
|
|
@@ -5890,7 +6143,7 @@ var YieldseekerAgent = class {
|
|
|
5890
6143
|
blockNumber
|
|
5891
6144
|
})
|
|
5892
6145
|
]);
|
|
5893
|
-
if (typeof idle !== "bigint" || idle < 0n || !
|
|
6146
|
+
if (typeof idle !== "bigint" || idle < 0n || !isAddress4(feeTracker) || /^0x0{40}$/i.test(feeTracker)) throw unavailable();
|
|
5894
6147
|
assertReadCurrent();
|
|
5895
6148
|
const fees = await client.readContract({
|
|
5896
6149
|
address: getAddress5(feeTracker),
|
|
@@ -5903,6 +6156,18 @@ var YieldseekerAgent = class {
|
|
|
5903
6156
|
const verified = [];
|
|
5904
6157
|
const verifiedVaults = [];
|
|
5905
6158
|
let deployed = 0n;
|
|
6159
|
+
const proof = (complete) => ({
|
|
6160
|
+
blockNumber,
|
|
6161
|
+
idle,
|
|
6162
|
+
fees,
|
|
6163
|
+
available: idle + deployed > fees ? idle + deployed - fees : 0n,
|
|
6164
|
+
positions: [...verified],
|
|
6165
|
+
verifiedVaults: [...verifiedVaults],
|
|
6166
|
+
indexedPositions,
|
|
6167
|
+
complete
|
|
6168
|
+
});
|
|
6169
|
+
assertReadCurrent();
|
|
6170
|
+
knownProof = proof(false);
|
|
5906
6171
|
const collect = (options2, results) => {
|
|
5907
6172
|
assertReadCurrent();
|
|
5908
6173
|
const known = this.balanceVaults.get(contextKey) ?? /* @__PURE__ */ new Set();
|
|
@@ -5918,7 +6183,7 @@ var YieldseekerAgent = class {
|
|
|
5918
6183
|
return;
|
|
5919
6184
|
}
|
|
5920
6185
|
const [underlying, shares, withdrawable] = group.map((value) => value.result);
|
|
5921
|
-
if (typeof underlying !== "string" || !
|
|
6186
|
+
if (typeof underlying !== "string" || !isAddress4(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n || shares === 0n && withdrawable > 0n) {
|
|
5922
6187
|
valid2 = false;
|
|
5923
6188
|
return;
|
|
5924
6189
|
}
|
|
@@ -5935,17 +6200,10 @@ var YieldseekerAgent = class {
|
|
|
5935
6200
|
});
|
|
5936
6201
|
return valid2;
|
|
5937
6202
|
};
|
|
5938
|
-
const
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
fees,
|
|
5943
|
-
available: idle + deployed > fees ? idle + deployed - fees : 0n,
|
|
5944
|
-
positions: [...verified],
|
|
5945
|
-
verifiedVaults: [...verifiedVaults],
|
|
5946
|
-
indexedPositions,
|
|
5947
|
-
complete
|
|
5948
|
-
});
|
|
6203
|
+
const checks = await checksRead;
|
|
6204
|
+
assertReadCurrent();
|
|
6205
|
+
if ("error" in checks) throw checks.error;
|
|
6206
|
+
const allKnownVerified = collect(options, checks.value);
|
|
5949
6207
|
knownProof = proof(false);
|
|
5950
6208
|
const indexed = new Map(indexedPositions.map((position2) => [position2.yieldOption.address.toLowerCase(), position2]));
|
|
5951
6209
|
const discrepancy = verified.some((position2) => {
|
|
@@ -5962,7 +6220,7 @@ var YieldseekerAgent = class {
|
|
|
5962
6220
|
assertReadCurrent();
|
|
5963
6221
|
const eligible = /* @__PURE__ */ new Map();
|
|
5964
6222
|
for (const option of catalog ?? []) {
|
|
5965
|
-
if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() &&
|
|
6223
|
+
if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() && isAddress4(option.address))
|
|
5966
6224
|
eligible.set(option.address.toLowerCase(), option);
|
|
5967
6225
|
}
|
|
5968
6226
|
const extra = [...eligible].filter(([address]) => !candidates.has(address)).map(([, option]) => option);
|
|
@@ -6021,10 +6279,10 @@ var YieldseekerAgent = class {
|
|
|
6021
6279
|
const vaults = [...this.balanceVaults.get(key2) ?? []];
|
|
6022
6280
|
const [invested, idle, tracker] = await Promise.all([
|
|
6023
6281
|
remainingVaultBalance(client, wallet, asset.address, vaults, blockNumber),
|
|
6024
|
-
client.readContract({ address: asset.address, abi:
|
|
6282
|
+
client.readContract({ address: asset.address, abi: erc20Abi5, functionName: "balanceOf", args: [wallet], blockNumber }),
|
|
6025
6283
|
client.readContract({ address: wallet, abi: YIELDSEEKER_AGENT_WALLET_ABI, functionName: "feeTracker", blockNumber })
|
|
6026
6284
|
]);
|
|
6027
|
-
if (invested === void 0 || typeof idle !== "bigint" || idle < 0n || !
|
|
6285
|
+
if (invested === void 0 || typeof idle !== "bigint" || idle < 0n || !isAddress4(tracker) || /^0x0{40}$/i.test(tracker)) return void 0;
|
|
6028
6286
|
const fees = await client.readContract({
|
|
6029
6287
|
address: tracker,
|
|
6030
6288
|
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
@@ -6083,7 +6341,7 @@ var YieldseekerAgent = class {
|
|
|
6083
6341
|
if (!context) return void 0;
|
|
6084
6342
|
const idle = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
6085
6343
|
address: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
6086
|
-
abi:
|
|
6344
|
+
abi: erc20Abi5,
|
|
6087
6345
|
functionName: "balanceOf",
|
|
6088
6346
|
args: [getAddress5(context.wallet.walletAddress)]
|
|
6089
6347
|
}), chainId, "eth_call", this.id);
|
|
@@ -6093,7 +6351,7 @@ var YieldseekerAgent = class {
|
|
|
6093
6351
|
}
|
|
6094
6352
|
async getEarnings(state, chainId) {
|
|
6095
6353
|
this.assertChain(chainId);
|
|
6096
|
-
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
6354
|
+
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, { includePositions: false }));
|
|
6097
6355
|
}
|
|
6098
6356
|
async getAccountApy(state, chainId, days, tokenSymbol) {
|
|
6099
6357
|
this.assertChain(chainId);
|
|
@@ -6105,6 +6363,17 @@ var YieldseekerAgent = class {
|
|
|
6105
6363
|
});
|
|
6106
6364
|
return mapYieldseekerApy(state.walletAddress, contexts, days);
|
|
6107
6365
|
}
|
|
6366
|
+
async getDailyEarnings(state, chainId, days, tokenSymbol) {
|
|
6367
|
+
this.assertChain(chainId);
|
|
6368
|
+
const asset = tokenSymbol?.toUpperCase();
|
|
6369
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
6370
|
+
const contexts = await this.loadActivityPortfolio(state, chainId, {
|
|
6371
|
+
...asset ? { asset } : {},
|
|
6372
|
+
historic: true,
|
|
6373
|
+
includePositions: false
|
|
6374
|
+
});
|
|
6375
|
+
return mapYieldseekerDailyEarnings(state.walletAddress, contexts, days);
|
|
6376
|
+
}
|
|
6108
6377
|
async getHistory(state, chainId, options) {
|
|
6109
6378
|
this.assertChain(chainId);
|
|
6110
6379
|
const asset = options?.tokenSymbol?.toUpperCase();
|
|
@@ -6121,7 +6390,7 @@ var YieldseekerAgent = class {
|
|
|
6121
6390
|
);
|
|
6122
6391
|
const vaultAddresses = new Set(
|
|
6123
6392
|
catalog.flat().filter(
|
|
6124
|
-
(yieldOption) => yieldOption.chainId === chainId &&
|
|
6393
|
+
(yieldOption) => yieldOption.chainId === chainId && isAddress4(yieldOption.address)
|
|
6125
6394
|
).map((yieldOption) => yieldOption.address.toLowerCase())
|
|
6126
6395
|
);
|
|
6127
6396
|
return mapYieldseekerHistory(contexts, {
|
|
@@ -6250,7 +6519,7 @@ var YieldseekerAgent = class {
|
|
|
6250
6519
|
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
6251
6520
|
).then((walletResponse) => {
|
|
6252
6521
|
this.assertSession(generation, chainId, asset);
|
|
6253
|
-
if (!walletResponse?.agentWallet || !
|
|
6522
|
+
if (!walletResponse?.agentWallet || !isAddress4(walletResponse.agentWallet.walletAddress)) {
|
|
6254
6523
|
throw this.invalidResponse("agent wallet");
|
|
6255
6524
|
}
|
|
6256
6525
|
const context = {
|
|
@@ -6615,7 +6884,7 @@ var YieldseekerAgent = class {
|
|
|
6615
6884
|
}
|
|
6616
6885
|
async loadPortfolioParts(state, chainId, context, options, contextKey) {
|
|
6617
6886
|
const [positions, historic, actions] = await Promise.all([
|
|
6618
|
-
this.cachedRead(
|
|
6887
|
+
options.includePositions === false ? Promise.resolve({ yieldPositions: [] }) : this.cachedRead(
|
|
6619
6888
|
`positions:${contextKey}`,
|
|
6620
6889
|
this.portfolioCacheMs(contextKey),
|
|
6621
6890
|
async () => {
|
|
@@ -6664,7 +6933,7 @@ var YieldseekerAgent = class {
|
|
|
6664
6933
|
this.agentPath(context, "deploy"),
|
|
6665
6934
|
{ method: "POST", body: {} }
|
|
6666
6935
|
);
|
|
6667
|
-
if (!deployed?.agentWallet || !
|
|
6936
|
+
if (!deployed?.agentWallet || !isAddress4(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
|
|
6668
6937
|
throw this.invalidResponse("agent deployment", {
|
|
6669
6938
|
reason: "Deploy did not return the expected Agent Wallet."
|
|
6670
6939
|
});
|
|
@@ -6767,20 +7036,20 @@ var YieldseekerAgent = class {
|
|
|
6767
7036
|
}
|
|
6768
7037
|
async submitTransaction(state, chainId, transaction, onSubmitted, onReceipt) {
|
|
6769
7038
|
if (this.transactionExecutor) {
|
|
6770
|
-
const hash2 = await this.transactionExecutor(state, chainId, transaction);
|
|
7039
|
+
const hash2 = await this.transactionExecutor(state, chainId, transaction, onReceipt);
|
|
6771
7040
|
onSubmitted?.(hash2);
|
|
6772
7041
|
return hash2;
|
|
6773
7042
|
}
|
|
6774
7043
|
this.assertTransaction(transaction, state, chainId);
|
|
6775
7044
|
const account = getAddress5(state.walletAddress);
|
|
6776
|
-
const walletClient =
|
|
7045
|
+
const walletClient = createWalletClient4({
|
|
6777
7046
|
account,
|
|
6778
7047
|
chain: base4,
|
|
6779
|
-
transport:
|
|
7048
|
+
transport: custom4(state.provider)
|
|
6780
7049
|
});
|
|
6781
7050
|
const walletChainClient = createPublicClient3({
|
|
6782
7051
|
chain: base4,
|
|
6783
|
-
transport:
|
|
7052
|
+
transport: custom4(state.provider)
|
|
6784
7053
|
});
|
|
6785
7054
|
await ensureWalletOnChain(
|
|
6786
7055
|
walletChainClient,
|
|
@@ -6894,12 +7163,12 @@ var YieldseekerAgent = class {
|
|
|
6894
7163
|
return receipt;
|
|
6895
7164
|
}
|
|
6896
7165
|
assertTransaction(transaction, state, chainId) {
|
|
6897
|
-
if (!transaction || typeof transaction.from !== "string" || !
|
|
7166
|
+
if (!transaction || typeof transaction.from !== "string" || !isAddress4(transaction.from) || typeof transaction.to !== "string" || !isAddress4(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || getAddress5(transaction.from) !== getAddress5(state.walletAddress)) {
|
|
6898
7167
|
throw this.invalidResponse("transaction");
|
|
6899
7168
|
}
|
|
6900
7169
|
}
|
|
6901
7170
|
assertAgent(agent) {
|
|
6902
|
-
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !
|
|
7171
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !isAddress4(agent.assetAddress) || agent.chainId !== 8453) {
|
|
6903
7172
|
throw this.invalidResponse("agent");
|
|
6904
7173
|
}
|
|
6905
7174
|
}
|
|
@@ -7057,11 +7326,20 @@ import { parseUnits as parseUnits2 } from "viem";
|
|
|
7057
7326
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
7058
7327
|
const target = asset.toUpperCase();
|
|
7059
7328
|
return agents.map((agent) => {
|
|
7329
|
+
let invalidBalance = false;
|
|
7330
|
+
const readAmount = (value, raw2 = false) => {
|
|
7331
|
+
if (typeof value !== "string" || !(raw2 ? /^\d+$/ : /^\d+(?:\.\d+)?$/).test(value)) {
|
|
7332
|
+
invalidBalance = true;
|
|
7333
|
+
return 0n;
|
|
7334
|
+
}
|
|
7335
|
+
const [whole, fraction] = value.split(".");
|
|
7336
|
+
return raw2 ? BigInt(value) : parseUnits2(fraction === void 0 ? whole : `${whole}.${fraction.slice(0, decimals)}`, decimals);
|
|
7337
|
+
};
|
|
7060
7338
|
const agentBalance = aggregated[agent.id];
|
|
7061
7339
|
const tokenBalance = agentBalance?.tokens.find(
|
|
7062
7340
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
7063
7341
|
);
|
|
7064
|
-
let balance = tokenBalance ?
|
|
7342
|
+
let balance = tokenBalance ? readAmount(tokenBalance.amount) : 0n;
|
|
7065
7343
|
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
7066
7344
|
const chainNameById = {
|
|
7067
7345
|
1: "ETHEREUM",
|
|
@@ -7073,20 +7351,22 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
7073
7351
|
const positionChain = position2.chain.trim().toUpperCase();
|
|
7074
7352
|
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
7075
7353
|
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
7076
|
-
balance += position2.withdrawableAmountRaw !== void 0 ?
|
|
7354
|
+
balance += position2.withdrawableAmountRaw !== void 0 ? readAmount(position2.withdrawableAmountRaw, true) : readAmount(position2.amount);
|
|
7077
7355
|
}
|
|
7078
7356
|
}
|
|
7079
7357
|
const assetTotal = agentBalance?.assetBalances?.find(
|
|
7080
7358
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
7081
7359
|
);
|
|
7082
7360
|
if (assetTotal) {
|
|
7083
|
-
const total =
|
|
7361
|
+
const total = readAmount(assetTotal.amount);
|
|
7084
7362
|
if (total < balance) balance = total > 0n ? total : 0n;
|
|
7085
7363
|
}
|
|
7086
|
-
return { agent, balance };
|
|
7364
|
+
return invalidBalance ? { agent, balance: 0n, invalidBalance: true } : { agent, balance };
|
|
7087
7365
|
});
|
|
7088
7366
|
}
|
|
7089
7367
|
function planProportionalShares(balances, requested, totalAvailable) {
|
|
7368
|
+
if (requested < 0n || totalAvailable < 0n || balances.some((entry) => entry.balance < 0n) || balances.reduce((sum, entry) => sum + entry.balance, 0n) !== totalAvailable || requested > totalAvailable)
|
|
7369
|
+
throw new Error("Invalid withdrawal planning amounts.");
|
|
7090
7370
|
const plans = balances.map(({ agent, balance }) => ({
|
|
7091
7371
|
agent,
|
|
7092
7372
|
balance,
|
|
@@ -7109,6 +7389,8 @@ function planProportionalShares(balances, requested, totalAvailable) {
|
|
|
7109
7389
|
return plans;
|
|
7110
7390
|
}
|
|
7111
7391
|
function planDisabledDrain(disabled, requested) {
|
|
7392
|
+
if (requested < 0n || disabled.some((entry) => entry.balance < 0n))
|
|
7393
|
+
throw new Error("Invalid withdrawal planning amounts.");
|
|
7112
7394
|
const sorted = [...disabled].filter((d) => d.balance > 0n).sort(
|
|
7113
7395
|
(a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0
|
|
7114
7396
|
);
|
|
@@ -7126,6 +7408,7 @@ function planDisabledDrain(disabled, requested) {
|
|
|
7126
7408
|
return { plans, remaining };
|
|
7127
7409
|
}
|
|
7128
7410
|
function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
7411
|
+
if (amount <= 0n) return;
|
|
7129
7412
|
const pool = candidatePool ?? plans.slice(fromIndex + 1);
|
|
7130
7413
|
const candidates = pool.filter((c) => c.balance - c.planned > 0n);
|
|
7131
7414
|
const totalHeadroom = candidates.reduce(
|
|
@@ -7152,7 +7435,13 @@ function redistributeShare(plans, fromIndex, amount, candidatePool) {
|
|
|
7152
7435
|
}
|
|
7153
7436
|
}
|
|
7154
7437
|
function sumWithdrawnAmount(results) {
|
|
7155
|
-
return Object.values(results).reduce((sum,
|
|
7438
|
+
return Object.values(results).reduce((sum, result) => sum + (confirmedWithdrawalAmount(result) ?? 0n), 0n);
|
|
7439
|
+
}
|
|
7440
|
+
function confirmedWithdrawalAmount(result) {
|
|
7441
|
+
const evidence = result.evidence;
|
|
7442
|
+
if (evidence && (evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed")) return void 0;
|
|
7443
|
+
const value = evidence ? evidence.confirmedAmount : result.amount;
|
|
7444
|
+
return typeof value === "string" && /^\d+$/.test(value) ? BigInt(value) : void 0;
|
|
7156
7445
|
}
|
|
7157
7446
|
|
|
7158
7447
|
// src/lib/helpers/account-apy-helper.ts
|
|
@@ -7238,19 +7527,58 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
7238
7527
|
return out;
|
|
7239
7528
|
}
|
|
7240
7529
|
|
|
7530
|
+
// src/lib/helpers/daily-earnings-helper.ts
|
|
7531
|
+
function aggregateDailyEarnings(series, tokenSymbol) {
|
|
7532
|
+
const wanted = tokenSymbol?.toUpperCase();
|
|
7533
|
+
const byAsset = /* @__PURE__ */ new Map();
|
|
7534
|
+
for (const account of series) {
|
|
7535
|
+
for (const { asset, points } of account.assets) {
|
|
7536
|
+
const symbol = asset.toUpperCase();
|
|
7537
|
+
if (!points.length || wanted && symbol !== wanted) continue;
|
|
7538
|
+
const components = byAsset.get(symbol) ?? [];
|
|
7539
|
+
components.push([...points].sort((a, b) => a.date.localeCompare(b.date)));
|
|
7540
|
+
byAsset.set(symbol, components);
|
|
7541
|
+
}
|
|
7542
|
+
}
|
|
7543
|
+
return [...byAsset.entries()].map(([asset, components]) => {
|
|
7544
|
+
const dates = [...new Set(components.flatMap((points2) => points2.map((point) => point.date)))].sort();
|
|
7545
|
+
const cursors = components.map((points2) => ({
|
|
7546
|
+
index: 0,
|
|
7547
|
+
// Providers can have different lifetime/window offsets and start dates.
|
|
7548
|
+
// Remove each opening reading before combining their earned amounts.
|
|
7549
|
+
baseline: points2[0].amount,
|
|
7550
|
+
current: points2[0].amount
|
|
7551
|
+
}));
|
|
7552
|
+
const points = dates.map((date) => {
|
|
7553
|
+
let amount = 0;
|
|
7554
|
+
components.forEach((component, index) => {
|
|
7555
|
+
const cursor = cursors[index];
|
|
7556
|
+
while (cursor.index < component.length && component[cursor.index].date <= date) {
|
|
7557
|
+
cursor.current = component[cursor.index++].amount;
|
|
7558
|
+
}
|
|
7559
|
+
amount += cursor.current - cursor.baseline;
|
|
7560
|
+
});
|
|
7561
|
+
return { date, amount };
|
|
7562
|
+
});
|
|
7563
|
+
return { asset, points };
|
|
7564
|
+
}).sort((a, b) => a.asset.localeCompare(b.asset));
|
|
7565
|
+
}
|
|
7566
|
+
|
|
7241
7567
|
// src/client.ts
|
|
7242
7568
|
import {
|
|
7243
7569
|
parseUnits as parseUnits3,
|
|
7244
7570
|
createPublicClient as createPublicClient4,
|
|
7245
|
-
createWalletClient as
|
|
7246
|
-
custom as
|
|
7571
|
+
createWalletClient as createWalletClient5,
|
|
7572
|
+
custom as custom5
|
|
7247
7573
|
} from "viem";
|
|
7248
7574
|
import { base as base5, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
7249
7575
|
|
|
7250
7576
|
// src/lib/sponsored-token-batch.ts
|
|
7251
7577
|
import {
|
|
7252
7578
|
isAddressEqual as isAddressEqual2,
|
|
7579
|
+
encodeFunctionData as encodeFunctionData3,
|
|
7253
7580
|
keccak256,
|
|
7581
|
+
parseTransaction,
|
|
7254
7582
|
toBytes
|
|
7255
7583
|
} from "viem";
|
|
7256
7584
|
|
|
@@ -7338,9 +7666,29 @@ function batchTypedData(b, spender) {
|
|
|
7338
7666
|
}
|
|
7339
7667
|
};
|
|
7340
7668
|
}
|
|
7669
|
+
var BATCH_WITNESS_TYPE = "Deposit witness)Deposit(address[] recipients)TokenPermissions(address token,uint256 amount)";
|
|
7670
|
+
function batchWitness(b) {
|
|
7671
|
+
return hashStruct({
|
|
7672
|
+
types: PERMIT_BATCH_TYPES,
|
|
7673
|
+
primaryType: "Deposit",
|
|
7674
|
+
data: { recipients: b.transfers.map((t) => t.to) }
|
|
7675
|
+
});
|
|
7676
|
+
}
|
|
7677
|
+
function batchArgs(b) {
|
|
7678
|
+
return [
|
|
7679
|
+
batchPermit(b),
|
|
7680
|
+
b.transfers.map((t) => ({ to: t.to, requestedAmount: BigInt(t.amount) })),
|
|
7681
|
+
b.from,
|
|
7682
|
+
batchWitness(b),
|
|
7683
|
+
BATCH_WITNESS_TYPE,
|
|
7684
|
+
b.signature
|
|
7685
|
+
];
|
|
7686
|
+
}
|
|
7341
7687
|
|
|
7342
7688
|
// src/lib/sponsored-token-batch.ts
|
|
7343
7689
|
var memory = /* @__PURE__ */ new Map();
|
|
7690
|
+
var settledRecords = /* @__PURE__ */ new Map();
|
|
7691
|
+
var expiredRecords = /* @__PURE__ */ new Map();
|
|
7344
7692
|
var inflight = /* @__PURE__ */ new Map();
|
|
7345
7693
|
var planOf = (transfers) => JSON.stringify(
|
|
7346
7694
|
transfers.map((t) => ({
|
|
@@ -7360,20 +7708,97 @@ function pendingTokenBatch(i) {
|
|
|
7360
7708
|
return;
|
|
7361
7709
|
}
|
|
7362
7710
|
}
|
|
7711
|
+
async function releaseExpiredTokenBatch(i) {
|
|
7712
|
+
const key2 = pendingKey(i);
|
|
7713
|
+
let timer;
|
|
7714
|
+
let expired = false;
|
|
7715
|
+
const check = async () => {
|
|
7716
|
+
let active = read(key2);
|
|
7717
|
+
if (active && i.expectedTxHash) {
|
|
7718
|
+
const current = JSON.parse(active);
|
|
7719
|
+
if (!current.serializedTransaction || keccak256(current.serializedTransaction).toLowerCase() !== i.expectedTxHash.toLowerCase())
|
|
7720
|
+
active = void 0;
|
|
7721
|
+
}
|
|
7722
|
+
const archiveKey = i.expectedTxHash ? `${key2}:expired:${i.expectedTxHash.toLowerCase()}` : void 0;
|
|
7723
|
+
const saved = active ?? (archiveKey && (expiredRecords.get(archiveKey) ?? (typeof window === "undefined" ? void 0 : window.localStorage.getItem(archiveKey))));
|
|
7724
|
+
if (!saved) return;
|
|
7725
|
+
const body = JSON.parse(saved);
|
|
7726
|
+
if (!body.serializedTransaction || body.chainId !== i.chainId || !isAddressEqual2(body.from, i.owner) || !isAddressEqual2(body.token, i.token) || !body.transfers?.length) return;
|
|
7727
|
+
const txHash = keccak256(body.serializedTransaction);
|
|
7728
|
+
if (i.expectedTxHash && txHash.toLowerCase() !== i.expectedTxHash.toLowerCase()) return;
|
|
7729
|
+
const transaction = parseTransaction(body.serializedTransaction);
|
|
7730
|
+
if (transaction.chainId !== i.chainId || !transaction.to || !isAddressEqual2(transaction.to, BATCH_PERMIT2_ADDRESS) || (transaction.value ?? 0n) !== 0n || transaction.data !== encodeFunctionData3({
|
|
7731
|
+
abi: PERMIT2_BATCH_ABI,
|
|
7732
|
+
functionName: "permitWitnessTransferFrom",
|
|
7733
|
+
args: batchArgs(body)
|
|
7734
|
+
})) return;
|
|
7735
|
+
const nonce = BigInt(body.nonce);
|
|
7736
|
+
const deadline = BigInt(body.deadline);
|
|
7737
|
+
if (nonce < 0n || nonce > MAX_UINT256 || deadline < 0n) return;
|
|
7738
|
+
const block = await i.pub.getBlock({ blockTag: "finalized" });
|
|
7739
|
+
if (expired || typeof block.number !== "bigint" || typeof block.timestamp !== "bigint" || block.timestamp <= deadline) return;
|
|
7740
|
+
const bitmap = await i.pub.readContract({
|
|
7741
|
+
address: BATCH_PERMIT2_ADDRESS,
|
|
7742
|
+
abi: PERMIT2_BATCH_ABI,
|
|
7743
|
+
functionName: "nonceBitmap",
|
|
7744
|
+
args: [i.owner, nonce >> 8n],
|
|
7745
|
+
blockNumber: block.number
|
|
7746
|
+
});
|
|
7747
|
+
if (expired || typeof bitmap !== "bigint" || bitmap < 0n || bitmap > MAX_UINT256 || (bitmap & 1n << (nonce & 255n)) !== 0n || active && read(key2) !== active) return;
|
|
7748
|
+
if (active) {
|
|
7749
|
+
const terminalKey = `${key2}:expired:${txHash.toLowerCase()}`;
|
|
7750
|
+
expiredRecords.set(terminalKey, saved);
|
|
7751
|
+
let persisted = typeof window === "undefined";
|
|
7752
|
+
try {
|
|
7753
|
+
if (typeof window !== "undefined") {
|
|
7754
|
+
window.localStorage.setItem(terminalKey, saved);
|
|
7755
|
+
persisted = window.localStorage.getItem(terminalKey) === saved;
|
|
7756
|
+
}
|
|
7757
|
+
} catch {
|
|
7758
|
+
}
|
|
7759
|
+
if (!persisted) settledRecords.set(key2, saved);
|
|
7760
|
+
else clear(key2, body);
|
|
7761
|
+
}
|
|
7762
|
+
return { txHash, transfers: body.transfers.map(({ to, amount }) => ({ to, amount })), blockNumber: block.number };
|
|
7763
|
+
};
|
|
7764
|
+
try {
|
|
7765
|
+
return await Promise.race([check(), new Promise((resolve) => {
|
|
7766
|
+
timer = setTimeout(() => {
|
|
7767
|
+
expired = true;
|
|
7768
|
+
resolve(void 0);
|
|
7769
|
+
}, 8e3);
|
|
7770
|
+
})]);
|
|
7771
|
+
} catch {
|
|
7772
|
+
return;
|
|
7773
|
+
} finally {
|
|
7774
|
+
expired = true;
|
|
7775
|
+
if (timer) clearTimeout(timer);
|
|
7776
|
+
}
|
|
7777
|
+
}
|
|
7363
7778
|
function read(key2) {
|
|
7364
|
-
|
|
7779
|
+
const value = typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
|
|
7780
|
+
return value && settledRecords.get(key2) === value ? void 0 : value;
|
|
7365
7781
|
}
|
|
7366
|
-
function
|
|
7367
|
-
|
|
7782
|
+
function storedBody(body) {
|
|
7783
|
+
return JSON.stringify({
|
|
7368
7784
|
...body,
|
|
7369
7785
|
transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
|
|
7370
7786
|
});
|
|
7787
|
+
}
|
|
7788
|
+
function save(key2, body) {
|
|
7789
|
+
const value = storedBody(body);
|
|
7371
7790
|
if (typeof window === "undefined") memory.set(key2, value);
|
|
7372
7791
|
else window.localStorage.setItem(key2, value);
|
|
7792
|
+
settledRecords.delete(key2);
|
|
7373
7793
|
}
|
|
7374
|
-
function clear(key2) {
|
|
7375
|
-
|
|
7376
|
-
|
|
7794
|
+
function clear(key2, body) {
|
|
7795
|
+
try {
|
|
7796
|
+
if (typeof window === "undefined") memory.delete(key2);
|
|
7797
|
+
else window.localStorage.removeItem(key2);
|
|
7798
|
+
settledRecords.delete(key2);
|
|
7799
|
+
} catch {
|
|
7800
|
+
settledRecords.set(key2, storedBody(body));
|
|
7801
|
+
}
|
|
7377
7802
|
}
|
|
7378
7803
|
function sponsorTokenBatch(i) {
|
|
7379
7804
|
const key2 = pendingKey(i);
|
|
@@ -7481,7 +7906,7 @@ async function execute(i, key2, plan) {
|
|
|
7481
7906
|
if (existing) {
|
|
7482
7907
|
assertReceipt(existing, "deposit");
|
|
7483
7908
|
void post().catch(() => void 0);
|
|
7484
|
-
clear(key2);
|
|
7909
|
+
clear(key2, body);
|
|
7485
7910
|
return hash;
|
|
7486
7911
|
}
|
|
7487
7912
|
let result = await postSigned("deposit", hash);
|
|
@@ -7496,11 +7921,11 @@ async function execute(i, key2, plan) {
|
|
|
7496
7921
|
if (result.status === "pending") await waitForConfirmation("deposit", hash);
|
|
7497
7922
|
}
|
|
7498
7923
|
if (!result || result.status === "pending") void post().catch(() => void 0);
|
|
7499
|
-
clear(key2);
|
|
7924
|
+
clear(key2, body);
|
|
7500
7925
|
return hash;
|
|
7501
7926
|
} catch (error) {
|
|
7502
7927
|
if (!body.serializedTransaction && !body.serializedPermitTransaction || rejectedPreparation(error) || error instanceof OwneyError && error.details?.notSubmitted === true)
|
|
7503
|
-
clear(key2);
|
|
7928
|
+
clear(key2, body);
|
|
7504
7929
|
throw error;
|
|
7505
7930
|
}
|
|
7506
7931
|
};
|
|
@@ -7513,6 +7938,12 @@ async function execute(i, key2, plan) {
|
|
|
7513
7938
|
throw new Error(
|
|
7514
7939
|
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
7515
7940
|
);
|
|
7941
|
+
if (await releaseExpiredTokenBatch({ ...i, expectedTxHash: previous.serializedTransaction ? keccak256(previous.serializedTransaction) : void 0 }))
|
|
7942
|
+
throw new OwneyError(
|
|
7943
|
+
"SPONSOR_REQUEST_FAILED",
|
|
7944
|
+
"The deposit authorization expired without transferring funds. Start a new deposit.",
|
|
7945
|
+
{ notSubmitted: true, safeToFallback: false }
|
|
7946
|
+
);
|
|
7516
7947
|
i.onApproved?.();
|
|
7517
7948
|
return send({ ...previous, transfers: i.transfers });
|
|
7518
7949
|
}
|
|
@@ -7705,7 +8136,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
|
7705
8136
|
}
|
|
7706
8137
|
|
|
7707
8138
|
// src/lib/sponsored-calls-deposit.ts
|
|
7708
|
-
import { encodeFunctionData as
|
|
8139
|
+
import { encodeFunctionData as encodeFunctionData4, erc20Abi as erc20Abi6, toHex as toHex2 } from "viem";
|
|
7709
8140
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
7710
8141
|
var DEFAULT_MAX_POLLS = 30;
|
|
7711
8142
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -7750,8 +8181,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
7750
8181
|
const calls = transfers.map((transfer) => ({
|
|
7751
8182
|
to: token,
|
|
7752
8183
|
value: "0x0",
|
|
7753
|
-
data:
|
|
7754
|
-
abi:
|
|
8184
|
+
data: encodeFunctionData4({
|
|
8185
|
+
abi: erc20Abi6,
|
|
7755
8186
|
functionName: "transfer",
|
|
7756
8187
|
args: [transfer.to, BigInt(transfer.amount)]
|
|
7757
8188
|
})
|
|
@@ -7933,6 +8364,8 @@ var OwneySDK = class {
|
|
|
7933
8364
|
*/
|
|
7934
8365
|
disabledAgents = /* @__PURE__ */ new Set();
|
|
7935
8366
|
state = null;
|
|
8367
|
+
connectionRevision = 0;
|
|
8368
|
+
disconnectingAgents = null;
|
|
7936
8369
|
apiKey;
|
|
7937
8370
|
// The org's agent policy, fetched once per client. Applied to each agent on
|
|
7938
8371
|
// activation (ROUT-229). `undefined` means "not fetched yet", `null` means
|
|
@@ -7954,6 +8387,9 @@ var OwneySDK = class {
|
|
|
7954
8387
|
recoveredFundingReads = /* @__PURE__ */ new Set();
|
|
7955
8388
|
fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
|
|
7956
8389
|
activationChecks = /* @__PURE__ */ new Map();
|
|
8390
|
+
// Protect the SDK entry point as well as the mini-app hook. This owns only
|
|
8391
|
+
// a running call, never a saved/pending provider outcome or a later retry.
|
|
8392
|
+
withdrawalSubmissions = /* @__PURE__ */ new Set();
|
|
7957
8393
|
readAgent(agent, method, fetch2, params = null) {
|
|
7958
8394
|
const key2 = JSON.stringify([
|
|
7959
8395
|
this.state?.walletAddress.toLowerCase() ?? null,
|
|
@@ -7986,9 +8422,10 @@ var OwneySDK = class {
|
|
|
7986
8422
|
*/
|
|
7987
8423
|
// TODO: replace with a proper EIP-1193 Provider type
|
|
7988
8424
|
async connect(provider) {
|
|
7989
|
-
const
|
|
7990
|
-
|
|
7991
|
-
|
|
8425
|
+
const revision = ++this.connectionRevision;
|
|
8426
|
+
const accounts = await provider.request({ method: "eth_accounts" });
|
|
8427
|
+
if (revision !== this.connectionRevision)
|
|
8428
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed while connecting.");
|
|
7992
8429
|
const walletAddress = accounts[0];
|
|
7993
8430
|
if (!walletAddress) {
|
|
7994
8431
|
throw new OwneyError(
|
|
@@ -7996,33 +8433,46 @@ var OwneySDK = class {
|
|
|
7996
8433
|
"No accounts found. Ensure the wallet is unlocked and connected."
|
|
7997
8434
|
);
|
|
7998
8435
|
}
|
|
7999
|
-
this.
|
|
8000
|
-
this.
|
|
8001
|
-
|
|
8436
|
+
const replacingOwner = this.state !== null && this.state.walletAddress.toLowerCase() !== walletAddress.toLowerCase();
|
|
8437
|
+
this.clearConnectionState();
|
|
8438
|
+
await (replacingOwner ? this.disconnectAgents() : this.disconnectingAgents);
|
|
8439
|
+
if (revision !== this.connectionRevision)
|
|
8440
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed while connecting.");
|
|
8002
8441
|
this.state = { provider, walletAddress, chainId: null };
|
|
8003
|
-
this.cachedSponsoredCallback = null;
|
|
8004
|
-
this.cachedWethSponsoredCallback = null;
|
|
8005
|
-
this.cachedSponsoredCallsCallbacks.clear();
|
|
8006
8442
|
}
|
|
8007
8443
|
/**
|
|
8008
8444
|
* Disconnect from all agents and clear connection state.
|
|
8009
8445
|
* After calling this, `connect()` must be called again before using agent methods.
|
|
8010
8446
|
*/
|
|
8011
8447
|
async disconnect() {
|
|
8448
|
+
++this.connectionRevision;
|
|
8449
|
+
this.clearConnectionState();
|
|
8450
|
+
await this.disconnectAgents();
|
|
8451
|
+
}
|
|
8452
|
+
clearConnectionState() {
|
|
8012
8453
|
this.state = null;
|
|
8013
8454
|
this.fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
|
|
8014
|
-
for (const agent of this.agents.values()) {
|
|
8015
|
-
await agent.disconnect();
|
|
8016
|
-
}
|
|
8017
8455
|
this.activeAgents.clear();
|
|
8018
|
-
this.disabledAgents.clear();
|
|
8019
8456
|
this.agentReads.clearInFlight();
|
|
8020
8457
|
this.activationChecks.clear();
|
|
8021
|
-
this.state = null;
|
|
8022
8458
|
this.cachedSponsoredCallback = null;
|
|
8023
8459
|
this.cachedWethSponsoredCallback = null;
|
|
8024
8460
|
this.cachedSponsoredCallsCallbacks.clear();
|
|
8025
8461
|
}
|
|
8462
|
+
disconnectAgents() {
|
|
8463
|
+
if (this.disconnectingAgents) return this.disconnectingAgents;
|
|
8464
|
+
const pending = Promise.allSettled([...this.agents.values()].map(async (agent) => agent.disconnect())).then((results) => {
|
|
8465
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
8466
|
+
if (failure?.status === "rejected") throw failure.reason;
|
|
8467
|
+
});
|
|
8468
|
+
this.disconnectingAgents = pending;
|
|
8469
|
+
void pending.then(() => {
|
|
8470
|
+
if (this.disconnectingAgents === pending) this.disconnectingAgents = null;
|
|
8471
|
+
}, () => {
|
|
8472
|
+
if (this.disconnectingAgents === pending) this.disconnectingAgents = null;
|
|
8473
|
+
});
|
|
8474
|
+
return pending;
|
|
8475
|
+
}
|
|
8026
8476
|
isAgentDisabled(agentId) {
|
|
8027
8477
|
return this.disabledAgents.has(agentId);
|
|
8028
8478
|
}
|
|
@@ -8087,12 +8537,12 @@ var OwneySDK = class {
|
|
|
8087
8537
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
8088
8538
|
getWalletChainClient: (cid) => createPublicClient4({
|
|
8089
8539
|
chain: VIEM_CHAIN2[cid],
|
|
8090
|
-
transport:
|
|
8540
|
+
transport: custom5(provider)
|
|
8091
8541
|
}),
|
|
8092
|
-
getWalletClient: (cid) =>
|
|
8542
|
+
getWalletClient: (cid) => createWalletClient5({
|
|
8093
8543
|
account: owner,
|
|
8094
8544
|
chain: VIEM_CHAIN2[cid],
|
|
8095
|
-
transport:
|
|
8545
|
+
transport: custom5(provider)
|
|
8096
8546
|
})
|
|
8097
8547
|
});
|
|
8098
8548
|
if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedSponsoredCallback = callback;
|
|
@@ -8142,12 +8592,12 @@ var OwneySDK = class {
|
|
|
8142
8592
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
8143
8593
|
getWalletChainClient: (cid) => createPublicClient4({
|
|
8144
8594
|
chain: VIEM_CHAIN2[cid],
|
|
8145
|
-
transport:
|
|
8595
|
+
transport: custom5(provider)
|
|
8146
8596
|
}),
|
|
8147
|
-
getWalletClient: (cid) =>
|
|
8597
|
+
getWalletClient: (cid) => createWalletClient5({
|
|
8148
8598
|
account: owner,
|
|
8149
8599
|
chain: VIEM_CHAIN2[cid],
|
|
8150
|
-
transport:
|
|
8600
|
+
transport: custom5(provider)
|
|
8151
8601
|
})
|
|
8152
8602
|
});
|
|
8153
8603
|
if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedWethSponsoredCallback = callback;
|
|
@@ -8458,6 +8908,12 @@ var OwneySDK = class {
|
|
|
8458
8908
|
*/
|
|
8459
8909
|
async deposit(options) {
|
|
8460
8910
|
const { amount, asset, depositCallback, agentId, onApproved } = options;
|
|
8911
|
+
if (!isFundingAmount(amount, true))
|
|
8912
|
+
throw new OwneyError(
|
|
8913
|
+
"DEPOSIT_INVALID_AMOUNT",
|
|
8914
|
+
"Deposit amount must be an unsigned base-unit integer.",
|
|
8915
|
+
{ phase: "prepare", rejectedBeforeSubmission: true }
|
|
8916
|
+
);
|
|
8461
8917
|
const prepared = /* @__PURE__ */ new Map();
|
|
8462
8918
|
const paidFallbackAgents = /* @__PURE__ */ new Set();
|
|
8463
8919
|
const onProgress = options.onProgress ? (evidence) => {
|
|
@@ -8485,6 +8941,47 @@ var OwneySDK = class {
|
|
|
8485
8941
|
throw new OwneyError("DEPOSIT_IN_PROGRESS", "This deposit cannot resume the saved relayer transaction.");
|
|
8486
8942
|
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));
|
|
8487
8943
|
try {
|
|
8944
|
+
if (options.resumeTxHash) {
|
|
8945
|
+
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
8946
|
+
const pending = token && pendingTokenBatch({
|
|
8947
|
+
apiKey: this.apiKey,
|
|
8948
|
+
baseUrl: this.routingApiBaseUrl,
|
|
8949
|
+
owner: state.walletAddress,
|
|
8950
|
+
chainId,
|
|
8951
|
+
token: token.address
|
|
8952
|
+
});
|
|
8953
|
+
const invalid2 = () => new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit does not match this request. Check its status before depositing again.");
|
|
8954
|
+
if (!pending || pending.txHash.toLowerCase() !== options.resumeTxHash.toLowerCase() || pending.transfers.some((transfer) => !/^\d+$/.test(transfer.amount) || BigInt(transfer.amount) <= 0n) || pending.transfers.reduce((sum, transfer) => sum + BigInt(transfer.amount), 0n) !== BigInt(amount)) throw invalid2();
|
|
8955
|
+
const candidates = agentId ? [this.getAgent(agentId)] : this.getEligibleAgents(chainId, asset);
|
|
8956
|
+
const wallets = await Promise.all(candidates.map(async (agent) => {
|
|
8957
|
+
this.validateAssetSupport(agent, chainId, asset);
|
|
8958
|
+
const wallet = await (agent.getFundingWallet ? agent.getFundingWallet(state, chainId, asset) : agent.getUserProfile(state, chainId).then((profile) => profile.smartWallet)).catch(() => void 0);
|
|
8959
|
+
return { agent, wallet };
|
|
8960
|
+
}));
|
|
8961
|
+
if (this.state !== state || this.state.chainId !== chainId)
|
|
8962
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while recovering the deposit.");
|
|
8963
|
+
const usedAgents = /* @__PURE__ */ new Set();
|
|
8964
|
+
const legs = pending.transfers.map((transfer) => {
|
|
8965
|
+
const matches = wallets.filter((value) => value.wallet?.toLowerCase() === transfer.to.toLowerCase());
|
|
8966
|
+
if (matches.length !== 1 || usedAgents.has(matches[0].agent.id)) throw invalid2();
|
|
8967
|
+
const { agent } = matches[0];
|
|
8968
|
+
usedAgents.add(agent.id);
|
|
8969
|
+
return {
|
|
8970
|
+
id: agent.id,
|
|
8971
|
+
amount: transfer.amount,
|
|
8972
|
+
run: (callback) => withFailureReporting(
|
|
8973
|
+
this.apiKey,
|
|
8974
|
+
agent.id,
|
|
8975
|
+
() => agent.deposit(state, chainId, transfer.amount, asset, callback, onProgress),
|
|
8976
|
+
this.routingApiBaseUrl
|
|
8977
|
+
)
|
|
8978
|
+
};
|
|
8979
|
+
});
|
|
8980
|
+
const batchTransfer2 = getDepositBatchTransfer(effectiveCallback);
|
|
8981
|
+
if (!batchTransfer2) throw invalid2();
|
|
8982
|
+
const result = await runAgentDepositBatch(chainId, legs, batchTransfer2);
|
|
8983
|
+
return agentId ? result.agentResults[agentId] : result;
|
|
8984
|
+
}
|
|
8488
8985
|
if (agentId) {
|
|
8489
8986
|
if (this.isAgentDisabled(agentId)) {
|
|
8490
8987
|
throw new OwneyError(
|
|
@@ -8901,9 +9398,36 @@ var OwneySDK = class {
|
|
|
8901
9398
|
const token = SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token);
|
|
8902
9399
|
if (!token) throw new OwneyError("CHAIN_UNSUPPORTED", "Unsupported funding asset.");
|
|
8903
9400
|
const rpc2 = this.getPaidRpcClient(chainId);
|
|
8904
|
-
|
|
9401
|
+
let receipt;
|
|
9402
|
+
try {
|
|
9403
|
+
receipt = await rpc2.getTransactionReceipt({ hash: evidence.txHash });
|
|
9404
|
+
} catch (error) {
|
|
9405
|
+
if (direction === "deposit") {
|
|
9406
|
+
const expired = await releaseExpiredTokenBatch({
|
|
9407
|
+
apiKey: this.apiKey,
|
|
9408
|
+
baseUrl: this.routingApiBaseUrl,
|
|
9409
|
+
owner: state.walletAddress,
|
|
9410
|
+
chainId,
|
|
9411
|
+
token: token.address,
|
|
9412
|
+
pub: rpc2,
|
|
9413
|
+
expectedTxHash: evidence.txHash
|
|
9414
|
+
});
|
|
9415
|
+
assertCurrent();
|
|
9416
|
+
if (expired?.transfers.some((transfer) => transfer.to.toLowerCase() === agentWallet.toLowerCase() && transfer.amount === evidence.intendedAmount))
|
|
9417
|
+
return {
|
|
9418
|
+
...evidence,
|
|
9419
|
+
agentWallet,
|
|
9420
|
+
transfer: "failed",
|
|
9421
|
+
delivery: "not-applicable",
|
|
9422
|
+
registration: "not-applicable",
|
|
9423
|
+
confirmedAmount: "0",
|
|
9424
|
+
blockNumber: expired.blockNumber.toString()
|
|
9425
|
+
};
|
|
9426
|
+
}
|
|
9427
|
+
throw error;
|
|
9428
|
+
}
|
|
8905
9429
|
assertCurrent();
|
|
8906
|
-
let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction);
|
|
9430
|
+
let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction, options);
|
|
8907
9431
|
if (direction === "withdraw" && evidence.agentId === "yieldseeker" && verified.transfer === "unknown" && verified.confirmedAmount && BigInt(verified.confirmedAmount) > 0n) {
|
|
8908
9432
|
const transaction = await rpc2.getTransaction({ hash: evidence.txHash });
|
|
8909
9433
|
assertCurrent();
|
|
@@ -9077,9 +9601,11 @@ var OwneySDK = class {
|
|
|
9077
9601
|
return page;
|
|
9078
9602
|
}
|
|
9079
9603
|
async recoverDepositRegistration(evidence) {
|
|
9604
|
+
const { state, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
9080
9605
|
const verified = await this.inspectFunding(evidence, "deposit");
|
|
9606
|
+
assertCurrent();
|
|
9081
9607
|
if (verified.agentId !== "zyfai") return verified;
|
|
9082
|
-
return this.getAgent("zyfai").retryDepositRegistration(
|
|
9608
|
+
return this.getAgent("zyfai").retryDepositRegistration(state, verified);
|
|
9083
9609
|
}
|
|
9084
9610
|
// --- Fund operations ---
|
|
9085
9611
|
/** On-chain version and ownership status for this owner's Yieldseeker agent. */
|
|
@@ -9125,10 +9651,24 @@ var OwneySDK = class {
|
|
|
9125
9651
|
* @returns {AgentWithdrawResult} for a single agent, or {OwneyWithdrawResult} with per-agent results
|
|
9126
9652
|
*/
|
|
9127
9653
|
async withdraw(options) {
|
|
9654
|
+
if (options.amount !== void 0 && !isFundingAmount(options.amount))
|
|
9655
|
+
throw new OwneyError(
|
|
9656
|
+
"WITHDRAW_INVALID_AMOUNT",
|
|
9657
|
+
"Withdrawal amount must be a positive base-unit integer.",
|
|
9658
|
+
{ phase: "prepare", rejectedBeforeSubmission: true }
|
|
9659
|
+
);
|
|
9128
9660
|
const state = this.requireState();
|
|
9129
9661
|
const chainId = this.requireChainId();
|
|
9662
|
+
const submissionKey = `${state.walletAddress.toLowerCase()}:${chainId}:${options.asset.toUpperCase()}`;
|
|
9663
|
+
if (this.withdrawalSubmissions.has(submissionKey))
|
|
9664
|
+
throw new OwneyError(
|
|
9665
|
+
"WITHDRAW_IN_PROGRESS",
|
|
9666
|
+
"A withdrawal request is already running.",
|
|
9667
|
+
{ chainId, asset: options.asset, phase: "prepare", rejectedBeforeSubmission: true }
|
|
9668
|
+
);
|
|
9130
9669
|
const ids = options.agentId ? [options.agentId] : this.getActiveAgentIds().filter((id) => !options.excludeAgentIds?.includes(id));
|
|
9131
9670
|
const attempted = /* @__PURE__ */ new Set();
|
|
9671
|
+
this.withdrawalSubmissions.add(submissionKey);
|
|
9132
9672
|
try {
|
|
9133
9673
|
return await this.withdrawFromAgents(options, state, chainId, attempted);
|
|
9134
9674
|
} catch (error) {
|
|
@@ -9145,6 +9685,8 @@ var OwneySDK = class {
|
|
|
9145
9685
|
});
|
|
9146
9686
|
}
|
|
9147
9687
|
throw error;
|
|
9688
|
+
} finally {
|
|
9689
|
+
this.withdrawalSubmissions.delete(submissionKey);
|
|
9148
9690
|
}
|
|
9149
9691
|
}
|
|
9150
9692
|
async withdrawFromAgents(options, state, chainId, attempted) {
|
|
@@ -9183,7 +9725,19 @@ var OwneySDK = class {
|
|
|
9183
9725
|
};
|
|
9184
9726
|
};
|
|
9185
9727
|
const submissionEvidence = /* @__PURE__ */ new Map();
|
|
9728
|
+
const pendingSubmissions = () => Object.fromEntries([...submissionEvidence].filter(([id, evidence]) => evidence.agentId === id && evidence.chainId === chainId && evidence.token === token && evidence.walletAddress.toLowerCase() === state.walletAddress.toLowerCase() && evidence.transfer !== "failed" && evidence.delivery !== "failed" && (evidence.transfer === "unknown" || evidence.txHash || evidence.userOpHash || evidence.callsId)));
|
|
9729
|
+
const throwUnconfirmed = (agentErrors2) => {
|
|
9730
|
+
const pending = pendingSubmissions();
|
|
9731
|
+
if (Object.keys(pending).length)
|
|
9732
|
+
throw new OwneyError(
|
|
9733
|
+
"WITHDRAW_UNCONFIRMED",
|
|
9734
|
+
"The withdrawal was submitted, but its outcome could not be confirmed yet.",
|
|
9735
|
+
{ asset, amount, agentErrors: agentErrors2, agentFailures, pendingSubmissions: pending }
|
|
9736
|
+
);
|
|
9737
|
+
};
|
|
9186
9738
|
const submit = async (agent, requested2) => {
|
|
9739
|
+
if (this.state !== state || this.state.chainId !== chainId)
|
|
9740
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed before withdrawal submission.");
|
|
9187
9741
|
attempted.add(agent.id);
|
|
9188
9742
|
let evidence = {
|
|
9189
9743
|
agentId: agent.id,
|
|
@@ -9195,12 +9749,16 @@ var OwneySDK = class {
|
|
|
9195
9749
|
registration: "not-applicable",
|
|
9196
9750
|
delivery: "pending"
|
|
9197
9751
|
};
|
|
9752
|
+
let adapterReported = false;
|
|
9198
9753
|
const progress = (next) => {
|
|
9754
|
+
if (next.agentId !== agent.id || next.chainId !== chainId || next.token !== token || next.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase()) return;
|
|
9755
|
+
adapterReported = true;
|
|
9199
9756
|
evidence = { ...evidence, ...next };
|
|
9200
9757
|
submissionEvidence.set(agent.id, evidence);
|
|
9201
9758
|
notifyFunding(options.onProgress, evidence);
|
|
9202
9759
|
};
|
|
9203
9760
|
progress(evidence);
|
|
9761
|
+
adapterReported = false;
|
|
9204
9762
|
try {
|
|
9205
9763
|
if (options.onBeforeSubmission)
|
|
9206
9764
|
return await agent.withdraw(
|
|
@@ -9214,7 +9772,7 @@ var OwneySDK = class {
|
|
|
9214
9772
|
);
|
|
9215
9773
|
return await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
|
|
9216
9774
|
} catch (error) {
|
|
9217
|
-
if (!evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
9775
|
+
if (!adapterReported && !evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
9218
9776
|
progress({ ...evidence, transfer: "failed", delivery: "failed" });
|
|
9219
9777
|
throw error;
|
|
9220
9778
|
}
|
|
@@ -9275,6 +9833,7 @@ var OwneySDK = class {
|
|
|
9275
9833
|
}
|
|
9276
9834
|
}
|
|
9277
9835
|
if (Object.keys(results2).length === 0) {
|
|
9836
|
+
throwUnconfirmed(agentErrors2);
|
|
9278
9837
|
if (Object.keys(agentErrors2).length === 0)
|
|
9279
9838
|
throw new OwneyError(
|
|
9280
9839
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
@@ -9295,7 +9854,7 @@ var OwneySDK = class {
|
|
|
9295
9854
|
);
|
|
9296
9855
|
}
|
|
9297
9856
|
const amountsKnown = Object.values(results2).every(
|
|
9298
|
-
(result) =>
|
|
9857
|
+
(result) => confirmedWithdrawalAmount(result) !== void 0
|
|
9299
9858
|
);
|
|
9300
9859
|
return {
|
|
9301
9860
|
agentResult: results2,
|
|
@@ -9314,6 +9873,13 @@ var OwneySDK = class {
|
|
|
9314
9873
|
asset,
|
|
9315
9874
|
assetInfo.decimals
|
|
9316
9875
|
);
|
|
9876
|
+
for (const entry of balances) if (entry.invalidBalance) {
|
|
9877
|
+
if (!unavailableAgents.includes(entry.agent)) unavailableAgents.push(entry.agent);
|
|
9878
|
+
aggregated.agentErrors = {
|
|
9879
|
+
...aggregated.agentErrors,
|
|
9880
|
+
[entry.agent.id]: "Provider returned an invalid withdrawal balance."
|
|
9881
|
+
};
|
|
9882
|
+
}
|
|
9317
9883
|
const indexedAvailable = balances.reduce((sum, entry) => sum + entry.balance, 0n);
|
|
9318
9884
|
await Promise.all(balances.map(async (entry) => {
|
|
9319
9885
|
if (entry.balance !== 0n && indexedAvailable >= requested || !entry.agent.getWithdrawableBalance) return;
|
|
@@ -9353,12 +9919,15 @@ var OwneySDK = class {
|
|
|
9353
9919
|
throw new OwneyError("NOT_CONNECTED", "Account changed while planning withdrawal.");
|
|
9354
9920
|
const unavailableAgentIds = unavailableAgents.map((agent) => agent.id);
|
|
9355
9921
|
const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
|
|
9356
|
-
if (totalAvailable
|
|
9922
|
+
if (totalAvailable < requested && unavailableAgents.length > 0) {
|
|
9357
9923
|
throw new OwneyError(
|
|
9358
9924
|
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
9359
|
-
`Cannot
|
|
9925
|
+
`Cannot cover the requested withdrawal with verified available balances: ${unavailableAgentIds.join(", ")}.`,
|
|
9360
9926
|
{
|
|
9361
9927
|
asset,
|
|
9928
|
+
requested: amount,
|
|
9929
|
+
available: totalAvailable.toString(),
|
|
9930
|
+
rejectedBeforeSubmission: true,
|
|
9362
9931
|
unavailableAgents: unavailableAgentIds,
|
|
9363
9932
|
agentErrors: aggregated.agentErrors
|
|
9364
9933
|
}
|
|
@@ -9375,7 +9944,7 @@ var OwneySDK = class {
|
|
|
9375
9944
|
}
|
|
9376
9945
|
);
|
|
9377
9946
|
}
|
|
9378
|
-
const plannedTarget =
|
|
9947
|
+
const plannedTarget = requested;
|
|
9379
9948
|
const disabledBalances = balances.filter(
|
|
9380
9949
|
(b) => this.isAgentDisabled(b.agent.id)
|
|
9381
9950
|
);
|
|
@@ -9394,13 +9963,23 @@ var OwneySDK = class {
|
|
|
9394
9963
|
}));
|
|
9395
9964
|
const plans = [...disabledPlans, ...enabledPlans];
|
|
9396
9965
|
const orderedPlans = this.orderAgentsForWithdrawal(plans.map((p) => p.agent)).map((agent) => plans.find((plan) => plan.agent === agent));
|
|
9966
|
+
const assertPlan = () => {
|
|
9967
|
+
if (orderedPlans.some((plan) => plan.planned < 0n || plan.planned > plan.balance) || orderedPlans.reduce((sum, plan) => sum + plan.planned, 0n) > plannedTarget)
|
|
9968
|
+
throw new OwneyError(
|
|
9969
|
+
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
9970
|
+
"Withdrawal plan failed amount validation.",
|
|
9971
|
+
{ asset, rejectedBeforeSubmission: true }
|
|
9972
|
+
);
|
|
9973
|
+
};
|
|
9974
|
+
assertPlan();
|
|
9397
9975
|
const results = {};
|
|
9398
9976
|
const agentErrors = {
|
|
9399
9977
|
...aggregated.agentErrors ?? {}
|
|
9400
9978
|
};
|
|
9401
9979
|
for (let i = 0; i < orderedPlans.length; i++) {
|
|
9402
9980
|
const p = orderedPlans[i];
|
|
9403
|
-
|
|
9981
|
+
assertPlan();
|
|
9982
|
+
if (p.planned <= 0n) continue;
|
|
9404
9983
|
try {
|
|
9405
9984
|
results[p.agent.id] = await submit(p.agent, p.planned.toString());
|
|
9406
9985
|
notifyAgentResult(p.agent.id, results[p.agent.id]);
|
|
@@ -9424,6 +10003,7 @@ var OwneySDK = class {
|
|
|
9424
10003
|
}
|
|
9425
10004
|
}
|
|
9426
10005
|
if (Object.keys(results).length === 0) {
|
|
10006
|
+
throwUnconfirmed(agentErrors);
|
|
9427
10007
|
throw new OwneyError(
|
|
9428
10008
|
"WITHDRAW_ALL_FAILED",
|
|
9429
10009
|
`Failed to withdraw "${amount}" of "${asset}" from all eligible agents.`,
|
|
@@ -9441,13 +10021,17 @@ var OwneySDK = class {
|
|
|
9441
10021
|
const adjustment = result?.feeAdjustment;
|
|
9442
10022
|
const evidence = result?.evidence;
|
|
9443
10023
|
const submitted = submissionEvidence.get(plan.agent.id);
|
|
9444
|
-
if (plan.agent.id !== "yieldseeker" || !adjustment || !evidence || !submitted || typeof result.amount !== "string" || !/^\d+$/.test(result.amount) || typeof adjustment.grossAmount !== "string" || !/^\d+$/.test(adjustment.grossAmount) || typeof adjustment.feeAmount !== "string" || !/^\d+$/.test(adjustment.feeAmount) || evidence.agentId !== plan.agent.id || evidence.chainId !== chainId || evidence.token !== token || typeof evidence.walletAddress !== "string" || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || typeof evidence.agentWallet !== "string" || !/^0x[\da-f]{40}$/i.test(evidence.agentWallet) || typeof result.txHash !== "string" || !/^0x[\da-f]{64}$/i.test(result.txHash) || typeof evidence.txHash !== "string" || typeof submitted.txHash !== "string" || evidence.txHash?.toLowerCase() !== result.txHash.toLowerCase() || submitted.txHash?.toLowerCase() !== result.txHash.toLowerCase() || submitted.agentId !== evidence.agentId || submitted.chainId !== chainId || submitted.token !== token || typeof submitted.walletAddress !== "string" || typeof submitted.agentWallet !== "string" || submitted.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || submitted.agentWallet?.toLowerCase() !== evidence.agentWallet.toLowerCase() || evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || submitted.transfer !== "confirmed" || submitted.delivery !== "confirmed" || evidence.confirmedAmount !== result.amount || submitted.confirmedAmount !== result.amount || evidence.intendedAmount
|
|
10024
|
+
if (plan.agent.id !== "yieldseeker" || !adjustment || !evidence || !submitted || typeof result.amount !== "string" || !/^\d+$/.test(result.amount) || typeof adjustment.grossAmount !== "string" || !/^\d+$/.test(adjustment.grossAmount) || typeof adjustment.feeAmount !== "string" || !/^\d+$/.test(adjustment.feeAmount) || evidence.agentId !== plan.agent.id || evidence.chainId !== chainId || evidence.token !== token || typeof evidence.walletAddress !== "string" || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || typeof evidence.agentWallet !== "string" || !/^0x[\da-f]{40}$/i.test(evidence.agentWallet) || typeof result.txHash !== "string" || !/^0x[\da-f]{64}$/i.test(result.txHash) || typeof evidence.txHash !== "string" || typeof submitted.txHash !== "string" || evidence.txHash?.toLowerCase() !== result.txHash.toLowerCase() || submitted.txHash?.toLowerCase() !== result.txHash.toLowerCase() || submitted.agentId !== evidence.agentId || submitted.chainId !== chainId || submitted.token !== token || typeof submitted.walletAddress !== "string" || typeof submitted.agentWallet !== "string" || submitted.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || submitted.agentWallet?.toLowerCase() !== evidence.agentWallet.toLowerCase() || evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || submitted.transfer !== "confirmed" || submitted.delivery !== "confirmed" || evidence.confirmedAmount !== result.amount || submitted.confirmedAmount !== result.amount || ![result.amount, adjustment.grossAmount].includes(evidence.intendedAmount) || ![result.amount, adjustment.grossAmount].includes(submitted.intendedAmount)) return total;
|
|
9445
10025
|
const fee = BigInt(adjustment.feeAmount);
|
|
9446
10026
|
const net = BigInt(result.amount);
|
|
9447
10027
|
const gross = BigInt(adjustment.grossAmount);
|
|
9448
10028
|
return fee > 0n && net > 0n && gross === plan.planned && net + fee === gross ? total + fee : total;
|
|
9449
10029
|
}, 0n);
|
|
9450
|
-
|
|
10030
|
+
const pendingAmount = orderedPlans.reduce((sum, plan) => {
|
|
10031
|
+
const evidence = results[plan.agent.id]?.evidence;
|
|
10032
|
+
return evidence && (evidence.transfer === "pending" || evidence.transfer === "unknown") && evidence.delivery !== "failed" ? sum + plan.planned : sum;
|
|
10033
|
+
}, 0n);
|
|
10034
|
+
if (withdrawn + verifiedFeeDeductions + pendingAmount < requested) {
|
|
9451
10035
|
throw new OwneyError(
|
|
9452
10036
|
"WITHDRAW_PARTIAL_FAILURE",
|
|
9453
10037
|
`Withdrew "${withdrawn.toString()}" of requested "${amount}" of "${asset}". One or more agents failed; remaining balance could not cover the shortfall.`,
|
|
@@ -9455,6 +10039,7 @@ var OwneySDK = class {
|
|
|
9455
10039
|
asset,
|
|
9456
10040
|
requested: amount,
|
|
9457
10041
|
withdrawn: withdrawn.toString(),
|
|
10042
|
+
pendingAmount: pendingAmount.toString(),
|
|
9458
10043
|
partialResults: results,
|
|
9459
10044
|
agentErrors,
|
|
9460
10045
|
agentFailures,
|
|
@@ -9462,7 +10047,7 @@ var OwneySDK = class {
|
|
|
9462
10047
|
}
|
|
9463
10048
|
);
|
|
9464
10049
|
}
|
|
9465
|
-
return { agentResult: results, totalWithdrawn: withdrawn.toString() };
|
|
10050
|
+
return { agentResult: results, totalWithdrawn: Object.values(results).every((result) => confirmedWithdrawalAmount(result) !== void 0) ? withdrawn.toString() : null };
|
|
9466
10051
|
}
|
|
9467
10052
|
// --- Portfolio reads ---
|
|
9468
10053
|
/**
|
|
@@ -9629,10 +10214,8 @@ var OwneySDK = class {
|
|
|
9629
10214
|
* @returns {AccountAgentApy} for a single agent, or {OwneyAccountApy} with totalApy and per-agent breakdown
|
|
9630
10215
|
*/
|
|
9631
10216
|
/**
|
|
9632
|
-
* Daily cumulative
|
|
9633
|
-
* "recent earnings" subline.
|
|
9634
|
-
* `lifetime + unrealized + current x 0.9`, so the figure reconciles with the
|
|
9635
|
-
* balance headline rather than reading ~11% high. (ROUT-452)
|
|
10217
|
+
* Daily cumulative earnings in the selected asset's units, backing the
|
|
10218
|
+
* "recent earnings" subline. Each adapter uses its provider's earnings basis.
|
|
9636
10219
|
*
|
|
9637
10220
|
* Unlike getAccountApy this does NOT blend across agents: earnings are
|
|
9638
10221
|
* summed, not weighted, and an agent that fails to report must not silently
|
|
@@ -9665,7 +10248,7 @@ var OwneySDK = class {
|
|
|
9665
10248
|
const entries = [...this.getActiveAgents().entries()].filter(
|
|
9666
10249
|
([, agent]) => agent.getDailyEarnings
|
|
9667
10250
|
);
|
|
9668
|
-
const
|
|
10251
|
+
const settled = await Promise.allSettled(
|
|
9669
10252
|
entries.map(
|
|
9670
10253
|
([, agent]) => this.readAgent(
|
|
9671
10254
|
agent,
|
|
@@ -9675,23 +10258,15 @@ var OwneySDK = class {
|
|
|
9675
10258
|
)
|
|
9676
10259
|
)
|
|
9677
10260
|
);
|
|
9678
|
-
const
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
for (const point of points) {
|
|
9683
|
-
byDate.set(point.date, (byDate.get(point.date) ?? 0) + point.amount);
|
|
9684
|
-
}
|
|
9685
|
-
byAsset.set(asset, byDate);
|
|
9686
|
-
}
|
|
10261
|
+
const series = settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
10262
|
+
if (entries.length && !series.length) {
|
|
10263
|
+
const failure = settled.find((result) => result.status === "rejected");
|
|
10264
|
+
if (failure?.status === "rejected") throw failure.reason;
|
|
9687
10265
|
}
|
|
9688
10266
|
return {
|
|
9689
10267
|
walletAddress: series[0]?.walletAddress ?? state.walletAddress ?? "",
|
|
9690
10268
|
chainId,
|
|
9691
|
-
assets:
|
|
9692
|
-
asset,
|
|
9693
|
-
points: [...byDate.entries()].map(([date, amount]) => ({ date, amount })).sort((a, b) => a.date.localeCompare(b.date))
|
|
9694
|
-
})).sort((a, b) => a.asset.localeCompare(b.asset))
|
|
10269
|
+
assets: aggregateDailyEarnings(series, tokenSymbol)
|
|
9695
10270
|
};
|
|
9696
10271
|
}
|
|
9697
10272
|
async getAccountApy({
|
|
@@ -9932,14 +10507,14 @@ var OwneySDK = class {
|
|
|
9932
10507
|
const provider = this.requireConnectedProvider();
|
|
9933
10508
|
const walletChainClient = createPublicClient4({
|
|
9934
10509
|
chain: VIEM_CHAIN2[chainId],
|
|
9935
|
-
transport:
|
|
10510
|
+
transport: custom5(provider)
|
|
9936
10511
|
});
|
|
9937
10512
|
const publicClient = this.getPaidRpcClient(chainId);
|
|
9938
10513
|
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
9939
|
-
const wallet =
|
|
10514
|
+
const wallet = createWalletClient5({
|
|
9940
10515
|
account: state.walletAddress,
|
|
9941
10516
|
chain: VIEM_CHAIN2[chainId],
|
|
9942
|
-
transport:
|
|
10517
|
+
transport: custom5(provider)
|
|
9943
10518
|
});
|
|
9944
10519
|
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
9945
10520
|
const hash = await wallet.writeContract({
|