@owney/sdk 0.7.25-beta.10 → 0.7.25-beta.12
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/README.md +1 -1
- package/dist/index.cjs +918 -244
- package/dist/index.d.cts +317 -225
- package/dist/index.d.ts +317 -225
- package/dist/index.js +857 -183
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,93 @@
|
|
|
1
|
+
// src/lib/funding-evidence.ts
|
|
2
|
+
import { decodeEventLog, decodeFunctionData, erc20Abi, parseAbi } from "viem";
|
|
3
|
+
function notifyFunding(callback, evidence) {
|
|
4
|
+
try {
|
|
5
|
+
void Promise.resolve(callback?.({ ...evidence })).catch(() => {
|
|
6
|
+
});
|
|
7
|
+
} catch {
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
var fullWithdrawalAbi = parseAbi(["function withdrawAllAssetToUser(address recipient, address asset)"]);
|
|
11
|
+
var withdrawalEventAbi = parseAbi(["event WithdrewTokenToUser(address indexed owner, address indexed recipient, address indexed token, uint256 amount)"]);
|
|
12
|
+
function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress) {
|
|
13
|
+
if (evidence.agentId !== "yieldseeker" || transaction.chainId !== evidence.chainId || transaction.hash.toLowerCase() !== evidence.txHash?.toLowerCase() || transaction.to?.toLowerCase() !== evidence.agentWallet?.toLowerCase() || transaction.from.toLowerCase() !== evidence.walletAddress.toLowerCase()) return false;
|
|
14
|
+
try {
|
|
15
|
+
const decoded = decodeFunctionData({ abi: fullWithdrawalAbi, data: transaction.input });
|
|
16
|
+
return decoded.args[0].toLowerCase() === evidence.walletAddress.toLowerCase() && decoded.args[1].toLowerCase() === tokenAddress.toLowerCase();
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
22
|
+
if (receipt.status !== "success") return { ...evidence, transfer: "failed", delivery: "failed" };
|
|
23
|
+
let amount = 0n;
|
|
24
|
+
const seen = /* @__PURE__ */ new Set();
|
|
25
|
+
const attributed = /* @__PURE__ */ new Map();
|
|
26
|
+
const agent = evidence.agentWallet?.toLowerCase();
|
|
27
|
+
const owner = evidence.walletAddress.toLowerCase();
|
|
28
|
+
const logIndexes = [];
|
|
29
|
+
const transferLogs = [];
|
|
30
|
+
const zeroTransferLogs = [];
|
|
31
|
+
for (const log of [...receipt.logs].sort((a, b) => a.logIndex - b.logIndex)) {
|
|
32
|
+
if (log.address.toLowerCase() !== tokenAddress.toLowerCase()) continue;
|
|
33
|
+
if (seen.has(log.logIndex)) continue;
|
|
34
|
+
seen.add(log.logIndex);
|
|
35
|
+
try {
|
|
36
|
+
const event = decodeEventLog({ abi: erc20Abi, data: log.data, topics: log.topics });
|
|
37
|
+
if (event.eventName !== "Transfer") continue;
|
|
38
|
+
const { from, to, value } = event.args;
|
|
39
|
+
if (direction === "withdraw" && value === 0n && from.toLowerCase() === agent && to.toLowerCase() === owner)
|
|
40
|
+
zeroTransferLogs.push(log.logIndex);
|
|
41
|
+
let matched = 0n;
|
|
42
|
+
if (direction === "deposit") {
|
|
43
|
+
if (to.toLowerCase() === agent) matched = value;
|
|
44
|
+
} else {
|
|
45
|
+
const sender = from.toLowerCase();
|
|
46
|
+
const recipient = to.toLowerCase();
|
|
47
|
+
const available = sender === agent ? value : attributed.get(sender) ?? 0n;
|
|
48
|
+
const carried = available < value ? available : value;
|
|
49
|
+
if (sender !== agent) attributed.set(sender, available - carried);
|
|
50
|
+
if (recipient === owner) matched = carried;
|
|
51
|
+
else if (recipient !== agent) attributed.set(recipient, (attributed.get(recipient) ?? 0n) + carried);
|
|
52
|
+
}
|
|
53
|
+
if (matched === 0n) continue;
|
|
54
|
+
amount += matched;
|
|
55
|
+
transferLogs.push({ logIndex: log.logIndex, amountRaw: matched.toString() });
|
|
56
|
+
logIndexes.push(log.logIndex);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const expected = /^\d+$/.test(evidence.intendedAmount) ? BigInt(evidence.intendedAmount) : void 0;
|
|
61
|
+
const emptyPayout = direction === "withdraw" && evidence.agentId === "yieldseeker" && amount === 0n && zeroTransferLogs.length > 0 && receipt.logs.some((log) => {
|
|
62
|
+
if (log.address.toLowerCase() !== agent) return false;
|
|
63
|
+
try {
|
|
64
|
+
const { args } = decodeEventLog({ abi: withdrawalEventAbi, data: log.data, topics: log.topics });
|
|
65
|
+
return args.owner.toLowerCase() === owner && args.recipient.toLowerCase() === owner && args.token.toLowerCase() === tokenAddress.toLowerCase() && args.amount === 0n;
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
if (emptyPayout) return {
|
|
71
|
+
...evidence,
|
|
72
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
73
|
+
confirmedAmount: "0",
|
|
74
|
+
logIndexes: zeroTransferLogs,
|
|
75
|
+
transferLogs: zeroTransferLogs.map((logIndex) => ({ logIndex, amountRaw: "0" })),
|
|
76
|
+
transfer: "failed",
|
|
77
|
+
delivery: "failed"
|
|
78
|
+
};
|
|
79
|
+
const delivered = amount > 0n && (expected === void 0 || amount >= expected);
|
|
80
|
+
return {
|
|
81
|
+
...evidence,
|
|
82
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
83
|
+
logIndexes,
|
|
84
|
+
transferLogs,
|
|
85
|
+
...amount > 0n ? { confirmedAmount: amount.toString() } : {},
|
|
86
|
+
transfer: delivered ? "confirmed" : "unknown",
|
|
87
|
+
delivery: direction === "withdraw" ? delivered ? "confirmed" : "unknown" : "not-applicable"
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
1
91
|
// src/lib/deposit-batch-callback.ts
|
|
2
92
|
var batchCallbacks = /* @__PURE__ */ new WeakMap();
|
|
3
93
|
var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
|
|
@@ -212,9 +302,72 @@ var AgentReads = class {
|
|
|
212
302
|
}
|
|
213
303
|
};
|
|
214
304
|
|
|
305
|
+
// src/agents/zyfai/zyfai.withdraw-submission.ts
|
|
306
|
+
var listenersByClient = /* @__PURE__ */ new WeakMap();
|
|
307
|
+
function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
308
|
+
const client = sdk.httpClient;
|
|
309
|
+
if (!client || typeof client.get !== "function" || typeof client.post !== "function")
|
|
310
|
+
return { attempted: () => void 0, rejected: () => false, request: () => void 0, stop: () => {
|
|
311
|
+
} };
|
|
312
|
+
let listeners = listenersByClient.get(client);
|
|
313
|
+
if (!listeners) {
|
|
314
|
+
listeners = /* @__PURE__ */ new Set();
|
|
315
|
+
listenersByClient.set(client, listeners);
|
|
316
|
+
const observers = listeners;
|
|
317
|
+
for (const method of ["get", "post"]) {
|
|
318
|
+
const original = client[method];
|
|
319
|
+
client[method] = async function(...args) {
|
|
320
|
+
const [url, options] = args;
|
|
321
|
+
const withdrawal = method === "get" && url === "/users/withdraw" || method === "post" && url === "/users/partial-withdraw";
|
|
322
|
+
const payload = method === "get" ? options?.params : options;
|
|
323
|
+
const matching = withdrawal ? [...observers].filter((observer) => observer.chainId === payload?.chainId && observer.token === payload?.tokenSymbol) : [];
|
|
324
|
+
const startedAt = Date.now();
|
|
325
|
+
const request2 = {
|
|
326
|
+
attempted: true,
|
|
327
|
+
method: method === "get" ? "GET" : "POST",
|
|
328
|
+
path: method === "get" ? "/users/withdraw" : "/users/partial-withdraw"
|
|
329
|
+
};
|
|
330
|
+
const notify = (outcome) => {
|
|
331
|
+
if (outcome === "rejected" && matching.length !== 1) return;
|
|
332
|
+
for (const observer of matching) {
|
|
333
|
+
try {
|
|
334
|
+
observer.notify(outcome, { ...request2 });
|
|
335
|
+
} catch {
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
notify("attempted");
|
|
340
|
+
try {
|
|
341
|
+
return await original.apply(this, args);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
const message = error instanceof Error ? error.message : "";
|
|
344
|
+
request2.failure = message === "Unauthorized: Invalid API key" ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : message === "Network error: Unable to reach the server" ? "network" : "other";
|
|
345
|
+
if (["unauthorized", "forbidden", "rate-limited"].includes(request2.failure)) notify("rejected");
|
|
346
|
+
throw error;
|
|
347
|
+
} finally {
|
|
348
|
+
request2.elapsedMs = Math.max(0, Date.now() - startedAt);
|
|
349
|
+
notify("finished");
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
let submitted = false;
|
|
355
|
+
let rejected = false;
|
|
356
|
+
let request = { attempted: false };
|
|
357
|
+
const listener = { chainId, token, notify: (outcome, evidence) => {
|
|
358
|
+
request = evidence;
|
|
359
|
+
if (outcome === "attempted") submitted = true;
|
|
360
|
+
else if (outcome === "rejected") rejected = true;
|
|
361
|
+
} };
|
|
362
|
+
listeners.add(listener);
|
|
363
|
+
return { attempted: () => submitted, rejected: () => rejected, request: () => ({ ...request }), stop: () => {
|
|
364
|
+
listeners.delete(listener);
|
|
365
|
+
} };
|
|
366
|
+
}
|
|
367
|
+
|
|
215
368
|
// src/agents/zyfai/zyfai.agent.ts
|
|
216
369
|
import { SMART_SESSIONS_VALIDATOR, ZyfaiSDK } from "@zyfai/sdk";
|
|
217
|
-
import { parseAbi } from "viem";
|
|
370
|
+
import { formatUnits, parseAbi as parseAbi2 } from "viem";
|
|
218
371
|
import { base, arbitrum, mainnet } from "viem/chains";
|
|
219
372
|
|
|
220
373
|
// src/types/config.ts
|
|
@@ -387,7 +540,11 @@ function mapPendingAllocations(raw2) {
|
|
|
387
540
|
}
|
|
388
541
|
function mapBalances(raw2, _chainId, smartWallet) {
|
|
389
542
|
const portfolio = raw2.portfolio;
|
|
390
|
-
|
|
543
|
+
if (!portfolio?.portfolioByChain || typeof portfolio.portfolioByChain !== "object")
|
|
544
|
+
throw new Error("Zyfai balance snapshot is unavailable.");
|
|
545
|
+
const portfolioByChain = portfolio.portfolioByChain;
|
|
546
|
+
const fetchedAt = Date.now();
|
|
547
|
+
const provenance = portfolio;
|
|
391
548
|
let totalBalance = 0;
|
|
392
549
|
const tokens = [];
|
|
393
550
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
|
|
@@ -396,7 +553,8 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
396
553
|
symbol,
|
|
397
554
|
{ balance, balanceWithFee, decimals }
|
|
398
555
|
] of Object.entries(tokensBySymbol ?? {})) {
|
|
399
|
-
|
|
556
|
+
if (balanceWithFee === void 0 && balance === void 0) throw new Error("Zyfai asset balance is unavailable.");
|
|
557
|
+
const amount = hexToDecimal(balanceWithFee ?? balance, decimals);
|
|
400
558
|
totalBalance += amount;
|
|
401
559
|
tokens.push({
|
|
402
560
|
chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
|
|
@@ -446,6 +604,15 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
446
604
|
totalBalanceAsset: "usdc",
|
|
447
605
|
tokens,
|
|
448
606
|
positions,
|
|
607
|
+
readMetadata: tokens.map((token) => ({
|
|
608
|
+
chainId: token.chainId,
|
|
609
|
+
asset: token.asset,
|
|
610
|
+
fetchedAt,
|
|
611
|
+
providerUpdatedAt: typeof provenance.updatedAt === "string" ? provenance.updatedAt : void 0,
|
|
612
|
+
snapshotId: provenance.snapshotId === void 0 ? void 0 : String(provenance.snapshotId),
|
|
613
|
+
blockNumber: provenance.blockNumber === void 0 ? void 0 : String(provenance.blockNumber),
|
|
614
|
+
positionsStatus: Array.isArray(portfolio.positions) ? "available" : "unavailable"
|
|
615
|
+
})),
|
|
449
616
|
...pendingAllocations ? { pendingAllocations } : {}
|
|
450
617
|
};
|
|
451
618
|
}
|
|
@@ -786,28 +953,6 @@ function mapDailyEarnings(raw2, chainId, tokenSymbol) {
|
|
|
786
953
|
return { walletAddress: raw2.walletAddress, chainId, assets };
|
|
787
954
|
}
|
|
788
955
|
|
|
789
|
-
// src/agents/zyfai/zyfai.withdraw-amount.ts
|
|
790
|
-
var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
791
|
-
function topicToAddress(topic) {
|
|
792
|
-
return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
|
|
793
|
-
}
|
|
794
|
-
function extractWithdrawnAmount(logs, recipient, token, decimals) {
|
|
795
|
-
const wantRecipient = recipient.toLowerCase();
|
|
796
|
-
const wantToken = token.toLowerCase();
|
|
797
|
-
let total = 0n;
|
|
798
|
-
let matched = false;
|
|
799
|
-
for (const log of logs) {
|
|
800
|
-
if (log.address.toLowerCase() !== wantToken) continue;
|
|
801
|
-
if ((log.topics[0] ?? "").toLowerCase() !== TRANSFER_TOPIC) continue;
|
|
802
|
-
if (topicToAddress(log.topics[2]) !== wantRecipient) continue;
|
|
803
|
-
if (!log.data || log.data === "0x") continue;
|
|
804
|
-
total += BigInt(log.data);
|
|
805
|
-
matched = true;
|
|
806
|
-
}
|
|
807
|
-
if (!matched) return void 0;
|
|
808
|
-
return String(hexToDecimal(`0x${total.toString(16)}`, decimals));
|
|
809
|
-
}
|
|
810
|
-
|
|
811
956
|
// src/agents/zyfai/zyfai.history-cursor.ts
|
|
812
957
|
var InvalidHistoryCursorError = class extends Error {
|
|
813
958
|
code = "invalid_cursor";
|
|
@@ -1234,7 +1379,7 @@ async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
|
|
|
1234
1379
|
}
|
|
1235
1380
|
|
|
1236
1381
|
// src/agents/zyfai/zyfai.agent.ts
|
|
1237
|
-
var ERC7579_IS_MODULE_INSTALLED_ABI =
|
|
1382
|
+
var ERC7579_IS_MODULE_INSTALLED_ABI = parseAbi2([
|
|
1238
1383
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
1239
1384
|
]);
|
|
1240
1385
|
var WETH_ADDRESS_BY_CHAIN = {
|
|
@@ -1293,10 +1438,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1293
1438
|
connectAuthPromise = null;
|
|
1294
1439
|
rpcUrls;
|
|
1295
1440
|
publicClients = /* @__PURE__ */ new Map();
|
|
1296
|
-
//
|
|
1297
|
-
//
|
|
1298
|
-
|
|
1299
|
-
withdrawAmountCache = /* @__PURE__ */ new Map();
|
|
1441
|
+
// Receipt-derived amounts and log identities, scoped to the owner and asset.
|
|
1442
|
+
// Failures remain retryable; successful immutable evidence has a bounded cache.
|
|
1443
|
+
fundingHistoryCache = /* @__PURE__ */ new Map();
|
|
1300
1444
|
earningsReads = /* @__PURE__ */ new Map();
|
|
1301
1445
|
earningsRefreshes = /* @__PURE__ */ new Map();
|
|
1302
1446
|
earningsSnapshot = null;
|
|
@@ -1324,8 +1468,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1324
1468
|
return client;
|
|
1325
1469
|
}
|
|
1326
1470
|
/**
|
|
1327
|
-
*
|
|
1328
|
-
*
|
|
1471
|
+
* Resolves funding amounts and log identities from token receipts. Withdrawals
|
|
1472
|
+
* must travel from this agent wallet to its owner; top-ups must reach the agent.
|
|
1473
|
+
* The Zyfai history
|
|
1329
1474
|
* payload only reports the *remaining* position for a partial withdraw (see
|
|
1330
1475
|
* the note in `mapEntries`), so without this the UI shows the leftover
|
|
1331
1476
|
* balance instead of the amount withdrawn.
|
|
@@ -1337,48 +1482,62 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1337
1482
|
* Entries with no symbol (older payload shapes) fall back to USDC to
|
|
1338
1483
|
* preserve prior behavior.
|
|
1339
1484
|
*
|
|
1340
|
-
* Mutates `entries` in place
|
|
1341
|
-
*
|
|
1342
|
-
* `amount: undefined`, which consumers render as a hidden/retryable row
|
|
1343
|
-
* rather than a wrong number.
|
|
1485
|
+
* Mutates `entries` in place, sharing receipt requests within the page. RPC
|
|
1486
|
+
* failures leave provider top-ups unchanged and withdrawal amounts unknown.
|
|
1344
1487
|
*/
|
|
1345
|
-
async
|
|
1346
|
-
const
|
|
1347
|
-
(e) => e.action === "Withdraw" && e.transactions[0]?.txHashes[0]
|
|
1488
|
+
async resolveFundingTransfers(entries, chainId, recipient, smartWallet, tokenSymbol) {
|
|
1489
|
+
const movements = entries.filter(
|
|
1490
|
+
(e) => (e.action === "Withdraw" || e.action === "Top up") && e.transactions[0]?.txHashes[0]
|
|
1348
1491
|
);
|
|
1349
|
-
if (
|
|
1492
|
+
if (movements.length === 0) return;
|
|
1350
1493
|
const client = this.getPublicClient(chainId);
|
|
1494
|
+
const receipts = /* @__PURE__ */ new Map();
|
|
1351
1495
|
await Promise.all(
|
|
1352
|
-
|
|
1496
|
+
movements.map(async (entry) => {
|
|
1353
1497
|
const tx = entry.transactions[0];
|
|
1354
1498
|
const txHash = tx.txHashes[0];
|
|
1355
|
-
const rawSymbol = tx.tokenSymbol;
|
|
1499
|
+
const rawSymbol = tx.tokenSymbol ?? tokenSymbol;
|
|
1356
1500
|
const normalizedSymbol = rawSymbol ? rawSymbol.toLowerCase() === "eth" ? "WETH" : rawSymbol.toUpperCase() : "USDC";
|
|
1357
1501
|
const asset = SupportedAssets.find(
|
|
1358
1502
|
(a) => a.chainId === chainId && a.symbol === normalizedSymbol
|
|
1359
|
-
) ?? SupportedAssets.find(
|
|
1360
|
-
(a) => a.chainId === chainId && a.symbol === "USDC"
|
|
1361
1503
|
);
|
|
1362
1504
|
if (!asset) return;
|
|
1363
|
-
|
|
1364
|
-
|
|
1505
|
+
tx.tokenSymbol = asset.symbol;
|
|
1506
|
+
tx.tokenAddress = asset.address;
|
|
1507
|
+
tx.agentWallet = smartWallet;
|
|
1508
|
+
entry.id = `${chainId}:${txHash.toLowerCase()}:${entry.action}:${asset.address.toLowerCase()}:${smartWallet.toLowerCase()}`;
|
|
1509
|
+
if (!/^0x[0-9a-f]{64}$/i.test(txHash)) return;
|
|
1510
|
+
const cacheKey = `${entry.id}:${recipient.toLowerCase()}`;
|
|
1511
|
+
const cached = this.fundingHistoryCache.get(cacheKey);
|
|
1365
1512
|
if (cached !== void 0) {
|
|
1366
|
-
tx
|
|
1513
|
+
Object.assign(tx, cached);
|
|
1367
1514
|
return;
|
|
1368
1515
|
}
|
|
1369
1516
|
try {
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
asset.
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1517
|
+
if (!receipts.has(txHash)) receipts.set(txHash, client.getTransactionReceipt({ hash: txHash }));
|
|
1518
|
+
const receipt = await receipts.get(txHash);
|
|
1519
|
+
const evidence = inspectFundingReceipt(receipt, {
|
|
1520
|
+
agentId: "zyfai",
|
|
1521
|
+
walletAddress: recipient,
|
|
1522
|
+
chainId,
|
|
1523
|
+
token: asset.symbol,
|
|
1524
|
+
agentWallet: smartWallet,
|
|
1525
|
+
intendedAmount: "MAX",
|
|
1526
|
+
txHash,
|
|
1527
|
+
transfer: "unknown",
|
|
1528
|
+
registration: "not-applicable",
|
|
1529
|
+
delivery: "unknown"
|
|
1530
|
+
}, asset.address, entry.action === "Withdraw" ? "withdraw" : "deposit");
|
|
1531
|
+
if (evidence.transfer === "confirmed" && evidence.confirmedAmount) {
|
|
1532
|
+
Object.assign(tx, {
|
|
1533
|
+
amount: formatUnits(BigInt(evidence.confirmedAmount), asset.decimals),
|
|
1534
|
+
amountRaw: evidence.confirmedAmount,
|
|
1535
|
+
blockNumber: evidence.blockNumber,
|
|
1536
|
+
logIndexes: evidence.logIndexes,
|
|
1537
|
+
transferLogs: evidence.transferLogs
|
|
1538
|
+
});
|
|
1539
|
+
if (this.fundingHistoryCache.size >= 256) this.fundingHistoryCache.delete(this.fundingHistoryCache.keys().next().value);
|
|
1540
|
+
this.fundingHistoryCache.set(cacheKey, { ...tx });
|
|
1382
1541
|
}
|
|
1383
1542
|
} catch {
|
|
1384
1543
|
}
|
|
@@ -1467,7 +1626,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1467
1626
|
* Wraps `sdk.connectAccount` with a one-shot retry on auth errors. If the
|
|
1468
1627
|
* cached JWT is rejected by the backend (revoked, signing-key rotated, etc.)
|
|
1469
1628
|
* we drop the cache and try again — the second attempt has no cache entry,
|
|
1470
|
-
* so it falls through to a real SIWE prompt. Zyfai
|
|
1629
|
+
* so it falls through to a real SIWE prompt. Zyfai labels every HTTP 401 with
|
|
1471
1630
|
* an `"Unauthorized: Invalid API key"` error so we can't distinguish revoked
|
|
1472
1631
|
* JWT from a misconfigured API key; we retry once either way, and if the
|
|
1473
1632
|
* second attempt still fails the original error propagates.
|
|
@@ -1500,6 +1659,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1500
1659
|
await this.connectAuth(state, chainId);
|
|
1501
1660
|
}
|
|
1502
1661
|
}
|
|
1662
|
+
/** A rejected execution session must not be reused by the next foreground attempt. */
|
|
1663
|
+
invalidateRejectedWithdrawalSession(error, state, chainId, authToken) {
|
|
1664
|
+
const message = error instanceof Error ? error.message : "";
|
|
1665
|
+
if (!/(?:^|: )Unauthorized: Invalid API key$/.test(message)) return;
|
|
1666
|
+
const internals = this.sdk;
|
|
1667
|
+
if (typeof authToken !== "string" || internals.httpClient.authToken !== authToken || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== chainId) return;
|
|
1668
|
+
clearSession(state.walletAddress, chainId);
|
|
1669
|
+
internals.httpClient.clearAuthToken();
|
|
1670
|
+
internals.authenticatedUserId = null;
|
|
1671
|
+
this.connectedChainId = null;
|
|
1672
|
+
}
|
|
1503
1673
|
/**
|
|
1504
1674
|
* Sync newly-available protocols onto an existing user's profile at wallet
|
|
1505
1675
|
* connection. Zyfai's own protocol sync (`updateUserProtocols`) runs only when
|
|
@@ -2045,7 +2215,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2045
2215
|
await this.ensureSessionKey(ownerAddress, chainId, redeployed.address);
|
|
2046
2216
|
return redeployed.address;
|
|
2047
2217
|
}
|
|
2048
|
-
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
2218
|
+
async deposit(state, chainId, amount, asset, depositCallback, onProgress) {
|
|
2049
2219
|
try {
|
|
2050
2220
|
const validChainId = isValidChainId(chainId);
|
|
2051
2221
|
await this.ensureConnected(state, validChainId);
|
|
@@ -2054,17 +2224,48 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2054
2224
|
this.getAddress(),
|
|
2055
2225
|
validChainId
|
|
2056
2226
|
);
|
|
2227
|
+
notifyFunding(onProgress, {
|
|
2228
|
+
agentId: this.id,
|
|
2229
|
+
chainId,
|
|
2230
|
+
token: asset,
|
|
2231
|
+
walletAddress: state.walletAddress,
|
|
2232
|
+
agentWallet: smartWallet,
|
|
2233
|
+
intendedAmount: amount,
|
|
2234
|
+
transfer: "pending",
|
|
2235
|
+
registration: "pending",
|
|
2236
|
+
delivery: "not-applicable"
|
|
2237
|
+
});
|
|
2057
2238
|
const txHash = await depositCallback(smartWallet, validChainId, amount);
|
|
2058
|
-
|
|
2239
|
+
let evidence2 = {
|
|
2240
|
+
agentId: this.id,
|
|
2241
|
+
chainId,
|
|
2242
|
+
token: asset,
|
|
2243
|
+
walletAddress: state.walletAddress,
|
|
2244
|
+
agentWallet: smartWallet,
|
|
2245
|
+
intendedAmount: amount,
|
|
2246
|
+
txHash,
|
|
2247
|
+
transfer: "pending",
|
|
2248
|
+
registration: "pending",
|
|
2249
|
+
delivery: "not-applicable"
|
|
2250
|
+
};
|
|
2251
|
+
notifyFunding(onProgress, evidence2);
|
|
2059
2252
|
try {
|
|
2253
|
+
const receipt = await this.getPublicClient(validChainId).getTransactionReceipt({ hash: txHash });
|
|
2254
|
+
const token = SupportedAssets.find((token2) => token2.chainId === chainId && token2.symbol === asset);
|
|
2255
|
+
evidence2 = inspectFundingReceipt(receipt, evidence2, token.address, "deposit");
|
|
2256
|
+
} catch {
|
|
2257
|
+
evidence2 = { ...evidence2, transfer: "unknown" };
|
|
2258
|
+
}
|
|
2259
|
+
notifyFunding(onProgress, evidence2);
|
|
2260
|
+
try {
|
|
2261
|
+
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
2060
2262
|
await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
logError
|
|
2065
|
-
);
|
|
2263
|
+
evidence2 = { ...evidence2, registration: "confirmed" };
|
|
2264
|
+
} catch {
|
|
2265
|
+
evidence2 = { ...evidence2, registration: "unknown" };
|
|
2066
2266
|
}
|
|
2067
|
-
|
|
2267
|
+
notifyFunding(onProgress, evidence2);
|
|
2268
|
+
return { txHash, smartWallet, amount, evidence: evidence2 };
|
|
2068
2269
|
}
|
|
2069
2270
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
2070
2271
|
const raw2 = await this.sdk.depositFunds(
|
|
@@ -2074,22 +2275,88 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2074
2275
|
asset,
|
|
2075
2276
|
"aggressive"
|
|
2076
2277
|
);
|
|
2077
|
-
|
|
2278
|
+
let evidence = {
|
|
2279
|
+
agentId: this.id,
|
|
2280
|
+
chainId,
|
|
2281
|
+
token: asset,
|
|
2282
|
+
walletAddress: state.walletAddress,
|
|
2283
|
+
agentWallet: raw2.smartWallet,
|
|
2284
|
+
intendedAmount: amount,
|
|
2285
|
+
txHash: raw2.txHash,
|
|
2286
|
+
transfer: "unknown",
|
|
2287
|
+
registration: "unknown",
|
|
2288
|
+
delivery: "not-applicable"
|
|
2289
|
+
};
|
|
2290
|
+
notifyFunding(onProgress, evidence);
|
|
2291
|
+
try {
|
|
2292
|
+
const receipt = await this.getPublicClient(validChainId).getTransactionReceipt({ hash: raw2.txHash });
|
|
2293
|
+
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
2294
|
+
evidence = inspectFundingReceipt(receipt, evidence, token.address, "deposit");
|
|
2295
|
+
} catch {
|
|
2296
|
+
}
|
|
2297
|
+
notifyFunding(onProgress, evidence);
|
|
2298
|
+
return { ...mapDeposit(raw2), evidence };
|
|
2078
2299
|
} catch (error) {
|
|
2079
2300
|
throw error;
|
|
2080
2301
|
}
|
|
2081
2302
|
}
|
|
2082
2303
|
// --- IAgent: Fund operations ---
|
|
2083
|
-
async withdraw(state, chainId, token, amount) {
|
|
2304
|
+
async withdraw(state, chainId, token, amount, onProgress) {
|
|
2084
2305
|
const validChainId = isValidChainId(chainId);
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2306
|
+
try {
|
|
2307
|
+
await this.ensureConnected(state, validChainId);
|
|
2308
|
+
} catch (error) {
|
|
2309
|
+
notifyFunding(onProgress, {
|
|
2310
|
+
agentId: this.id,
|
|
2311
|
+
chainId,
|
|
2312
|
+
token,
|
|
2313
|
+
walletAddress: state.walletAddress,
|
|
2314
|
+
intendedAmount: amount ?? "MAX",
|
|
2315
|
+
transfer: "failed",
|
|
2316
|
+
registration: "not-applicable",
|
|
2317
|
+
delivery: "failed"
|
|
2318
|
+
});
|
|
2319
|
+
throw error;
|
|
2320
|
+
}
|
|
2321
|
+
const observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
|
|
2322
|
+
const authToken = this.sdk.httpClient?.authToken;
|
|
2323
|
+
let raw2;
|
|
2324
|
+
try {
|
|
2325
|
+
raw2 = await this.sdk.withdrawFunds(this.getAddress(), validChainId, amount, token);
|
|
2326
|
+
} catch (error) {
|
|
2327
|
+
this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken);
|
|
2328
|
+
if (observation.attempted() !== void 0) {
|
|
2329
|
+
const failed = observation.attempted() === false || observation.rejected();
|
|
2330
|
+
notifyFunding(onProgress, {
|
|
2331
|
+
agentId: this.id,
|
|
2332
|
+
chainId,
|
|
2333
|
+
token,
|
|
2334
|
+
walletAddress: state.walletAddress,
|
|
2335
|
+
intendedAmount: amount ?? "MAX",
|
|
2336
|
+
transfer: failed ? "failed" : "unknown",
|
|
2337
|
+
registration: "not-applicable",
|
|
2338
|
+
delivery: failed ? "failed" : "unknown",
|
|
2339
|
+
withdrawalRequest: observation.request()
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
throw error;
|
|
2343
|
+
} finally {
|
|
2344
|
+
observation.stop();
|
|
2345
|
+
}
|
|
2092
2346
|
if (!raw2.success) {
|
|
2347
|
+
const unresolved = !!raw2.txHash || /already|pending|queued|in.progress/i.test(raw2.message ?? "");
|
|
2348
|
+
notifyFunding(onProgress, {
|
|
2349
|
+
agentId: this.id,
|
|
2350
|
+
chainId,
|
|
2351
|
+
token,
|
|
2352
|
+
walletAddress: state.walletAddress,
|
|
2353
|
+
intendedAmount: amount ?? "MAX",
|
|
2354
|
+
txHash: raw2.txHash,
|
|
2355
|
+
transfer: unresolved ? "unknown" : "failed",
|
|
2356
|
+
registration: "not-applicable",
|
|
2357
|
+
delivery: unresolved ? "unknown" : "failed",
|
|
2358
|
+
withdrawalRequest: observation.request()
|
|
2359
|
+
});
|
|
2093
2360
|
throw new OwneyError(
|
|
2094
2361
|
"WITHDRAW_FAILED",
|
|
2095
2362
|
raw2.message || "Zyfai withdraw failed.",
|
|
@@ -2097,13 +2364,45 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2097
2364
|
this.id
|
|
2098
2365
|
);
|
|
2099
2366
|
}
|
|
2100
|
-
|
|
2367
|
+
const evidence = {
|
|
2368
|
+
agentId: this.id,
|
|
2369
|
+
chainId,
|
|
2370
|
+
token,
|
|
2371
|
+
walletAddress: state.walletAddress,
|
|
2372
|
+
intendedAmount: amount ?? "MAX",
|
|
2373
|
+
txHash: raw2.txHash,
|
|
2374
|
+
transfer: "pending",
|
|
2375
|
+
registration: "not-applicable",
|
|
2376
|
+
delivery: "pending",
|
|
2377
|
+
withdrawalRequest: observation.request()
|
|
2378
|
+
};
|
|
2379
|
+
notifyFunding(onProgress, evidence);
|
|
2380
|
+
return { ...mapWithdraw(raw2), evidence };
|
|
2381
|
+
}
|
|
2382
|
+
/** Registration recovery never signs, transfers, activates, or refreshes authentication. */
|
|
2383
|
+
async retryDepositRegistration(state, evidence) {
|
|
2384
|
+
const internals = this.sdk;
|
|
2385
|
+
if (!internals.httpClient.authToken || internals.authenticatedUserId === null || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== evidence.chainId) {
|
|
2386
|
+
throw new OwneyError("NOT_CONNECTED", "Reconnect the wallet before recovering deposit registration.");
|
|
2387
|
+
}
|
|
2388
|
+
if (evidence.transfer !== "confirmed" || !evidence.txHash) {
|
|
2389
|
+
throw new OwneyError("AGENT_API_ERROR", "Deposit transfer must be verified before registration recovery.");
|
|
2390
|
+
}
|
|
2391
|
+
const cid = isValidChainId(evidence.chainId);
|
|
2392
|
+
const tokenAddress = evidence.token === "WETH" ? WETH_ADDRESS_BY_CHAIN[cid] : void 0;
|
|
2393
|
+
await this.sdk.logDeposit(cid, evidence.txHash, evidence.intendedAmount, tokenAddress);
|
|
2394
|
+
return { ...evidence, registration: "confirmed" };
|
|
2101
2395
|
}
|
|
2102
2396
|
// --- IAgent: Portfolio reads ---
|
|
2397
|
+
async getFundingWallet(state, chainId) {
|
|
2398
|
+
return (await this.sdk.getSmartWalletAddress(state.walletAddress, isValidChainId(chainId))).address;
|
|
2399
|
+
}
|
|
2103
2400
|
async getBalances(state, chainId) {
|
|
2401
|
+
const requestedAt = Date.now();
|
|
2104
2402
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
2105
2403
|
const raw2 = await this.sdk.getPortfolio(this.getAddress());
|
|
2106
|
-
|
|
2404
|
+
const balance = mapBalances(raw2, validChainId, smartWallet);
|
|
2405
|
+
return { ...balance, readMetadata: balance.readMetadata?.map((read2) => ({ ...read2, requestedAt })) };
|
|
2107
2406
|
}
|
|
2108
2407
|
earningsKey(state, chainId, smartWallet) {
|
|
2109
2408
|
return JSON.stringify([
|
|
@@ -2176,7 +2475,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2176
2475
|
/**
|
|
2177
2476
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
2178
2477
|
* takes lowercase `assetType` and denominates WETH as "eth" (the same
|
|
2179
|
-
* spelling `
|
|
2478
|
+
* spelling `resolveFundingTransfers` normalizes on the way in). Anything
|
|
2180
2479
|
* unrecognised returns undefined so the request stays unfiltered rather
|
|
2181
2480
|
* than silently narrowing to nothing.
|
|
2182
2481
|
*/
|
|
@@ -2195,6 +2494,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2195
2494
|
}
|
|
2196
2495
|
async getHistory(state, chainId, options) {
|
|
2197
2496
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
2497
|
+
return this.readHistory(state, smartWallet, validChainId, options);
|
|
2498
|
+
}
|
|
2499
|
+
/** Recovery reads never enter connectAuth or wallet activation. */
|
|
2500
|
+
async getFundingHistory(state, chainId, options) {
|
|
2501
|
+
const smartWallet = await this.getFundingWallet(state, chainId);
|
|
2502
|
+
return this.readHistory(state, smartWallet, isValidChainId(chainId), options);
|
|
2503
|
+
}
|
|
2504
|
+
async readHistory(state, smartWallet, validChainId, options) {
|
|
2198
2505
|
const limit = options?.limit ?? 10;
|
|
2199
2506
|
const assetType = _ZyfaiAgent.toZyfaiAssetType(options?.tokenSymbol);
|
|
2200
2507
|
const cursor = options?.cursor ? decodeHistoryCursor(options.cursor) : null;
|
|
@@ -2236,9 +2543,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2236
2543
|
rawOffset: firstOvershoot ? firstOvershoot.rawIdx : offset
|
|
2237
2544
|
}) : void 0;
|
|
2238
2545
|
const data = mapEntries(pageData, validChainId);
|
|
2239
|
-
await this.
|
|
2546
|
+
await this.resolveFundingTransfers(data, validChainId, state.walletAddress, smartWallet, options?.tokenSymbol);
|
|
2547
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2548
|
+
const unique = data.filter((entry) => {
|
|
2549
|
+
if (!entry.id || !entry.transactions[0]?.transferLogs?.length) return true;
|
|
2550
|
+
if (seen.has(entry.id)) return false;
|
|
2551
|
+
seen.add(entry.id);
|
|
2552
|
+
return true;
|
|
2553
|
+
});
|
|
2240
2554
|
return {
|
|
2241
|
-
data,
|
|
2555
|
+
data: unique,
|
|
2242
2556
|
hasMore,
|
|
2243
2557
|
nextCursor
|
|
2244
2558
|
};
|
|
@@ -2292,7 +2606,7 @@ import {
|
|
|
2292
2606
|
createWalletClient as createWalletClient2,
|
|
2293
2607
|
custom as custom2,
|
|
2294
2608
|
encodeFunctionData,
|
|
2295
|
-
erc20Abi,
|
|
2609
|
+
erc20Abi as erc20Abi2,
|
|
2296
2610
|
getAddress as getAddress2,
|
|
2297
2611
|
isAddress as isAddress2
|
|
2298
2612
|
} from "viem";
|
|
@@ -2913,7 +3227,7 @@ var YieldseekerApiClient = class {
|
|
|
2913
3227
|
};
|
|
2914
3228
|
|
|
2915
3229
|
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2916
|
-
import { formatUnits, isAddress } from "viem";
|
|
3230
|
+
import { formatUnits as formatUnits2, isAddress } from "viem";
|
|
2917
3231
|
|
|
2918
3232
|
// src/lib/helpers/snapshot-apy.ts
|
|
2919
3233
|
var DAY_MS = 864e5;
|
|
@@ -2980,10 +3294,10 @@ function raw(value, endpoint) {
|
|
|
2980
3294
|
return BigInt(value);
|
|
2981
3295
|
}
|
|
2982
3296
|
function decimal(value, decimals, endpoint) {
|
|
2983
|
-
return
|
|
3297
|
+
return formatUnits2(raw(value, endpoint), decimals);
|
|
2984
3298
|
}
|
|
2985
3299
|
function usd(rawAmount, decimals, price) {
|
|
2986
|
-
return Number(
|
|
3300
|
+
return Number(formatUnits2(rawAmount, decimals)) * price;
|
|
2987
3301
|
}
|
|
2988
3302
|
function percent(value) {
|
|
2989
3303
|
const result = Number(value);
|
|
@@ -3079,7 +3393,17 @@ function mapYieldseekerBalances(contexts) {
|
|
|
3079
3393
|
totalBalanceAsset: "usdc",
|
|
3080
3394
|
assetBalances,
|
|
3081
3395
|
tokens,
|
|
3082
|
-
positions
|
|
3396
|
+
positions,
|
|
3397
|
+
readMetadata: contexts.map((context) => ({
|
|
3398
|
+
chainId: context.agent.chainId,
|
|
3399
|
+
asset: context.asset,
|
|
3400
|
+
fetchedAt: context.fetchedAt,
|
|
3401
|
+
readVersion: context.readVersion,
|
|
3402
|
+
providerUpdatedAt: context.snapshot.updatedDate,
|
|
3403
|
+
snapshotId: String(context.snapshot.agentWalletSnapshotId),
|
|
3404
|
+
netDepositsRaw: context.snapshot.netDepositsBase,
|
|
3405
|
+
positionsStatus: context.positionsUnavailable ? "unavailable" : "available"
|
|
3406
|
+
}))
|
|
3083
3407
|
};
|
|
3084
3408
|
}
|
|
3085
3409
|
function mapYieldseekerEarnings(contexts) {
|
|
@@ -3091,7 +3415,7 @@ function mapYieldseekerEarnings(contexts) {
|
|
|
3091
3415
|
chain: "BASE",
|
|
3092
3416
|
chainId: 8453,
|
|
3093
3417
|
asset: context.asset,
|
|
3094
|
-
amount:
|
|
3418
|
+
amount: formatUnits2(amount, context.snapshot.baseAssetDecimals)
|
|
3095
3419
|
});
|
|
3096
3420
|
lifetimeEarnings += usd(
|
|
3097
3421
|
amount,
|
|
@@ -3193,6 +3517,7 @@ function transactionHashes(details) {
|
|
|
3193
3517
|
function actionEntry(action) {
|
|
3194
3518
|
if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
|
|
3195
3519
|
return {
|
|
3520
|
+
id: `yieldseeker:action:${action.agentActionId}`,
|
|
3196
3521
|
agent: "yieldseeker",
|
|
3197
3522
|
action: actionType(action.actionType),
|
|
3198
3523
|
date: action.createdDate,
|
|
@@ -3222,7 +3547,7 @@ function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddres
|
|
|
3222
3547
|
const owner = ownerAddress.toLowerCase();
|
|
3223
3548
|
const agentWallet = wallet.walletAddress.toLowerCase();
|
|
3224
3549
|
const baseAsset = agent.assetAddress.toLowerCase();
|
|
3225
|
-
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
|
|
3550
|
+
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset || raw(movement.assetAmount, "historic position") <= 0n) {
|
|
3226
3551
|
return void 0;
|
|
3227
3552
|
}
|
|
3228
3553
|
let action;
|
|
@@ -3247,6 +3572,12 @@ function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddres
|
|
|
3247
3572
|
transactions: [
|
|
3248
3573
|
{
|
|
3249
3574
|
txHashes: [movement.transactionHash],
|
|
3575
|
+
tokenAddress: movement.assetAddress,
|
|
3576
|
+
agentWallet: wallet.walletAddress,
|
|
3577
|
+
logIndexes: [movement.logIndex],
|
|
3578
|
+
transferLogs: [{ logIndex: movement.logIndex, amountRaw: movement.assetAmount }],
|
|
3579
|
+
amountRaw: movement.assetAmount,
|
|
3580
|
+
blockNumber: String(movement.blockNumber),
|
|
3250
3581
|
chainId: agent.chainId,
|
|
3251
3582
|
tokenSymbol: asset,
|
|
3252
3583
|
amount: decimal(
|
|
@@ -3292,7 +3623,8 @@ function mapYieldseekerHistory(contexts, options) {
|
|
|
3292
3623
|
ungrouped.push(entry);
|
|
3293
3624
|
continue;
|
|
3294
3625
|
}
|
|
3295
|
-
const key2 = `${entry.agent}:${entry.action}:${tx.chainId}:${hash.toLowerCase()}`;
|
|
3626
|
+
const key2 = `${entry.agent}:${entry.action}:${tx.chainId}:${hash.toLowerCase()}:${tx.tokenAddress?.toLowerCase() ?? ""}:${tx.agentWallet?.toLowerCase() ?? ""}`;
|
|
3627
|
+
entry.id = key2;
|
|
3296
3628
|
const previous = grouped.get(key2);
|
|
3297
3629
|
if (!previous) {
|
|
3298
3630
|
grouped.set(key2, entry);
|
|
@@ -3305,14 +3637,52 @@ function mapYieldseekerHistory(contexts, options) {
|
|
|
3305
3637
|
}
|
|
3306
3638
|
previous.positions.push(...entry.positions);
|
|
3307
3639
|
previous.transactions.push(...entry.transactions);
|
|
3640
|
+
} else if ((entry.action === "Top up" || entry.action === "Withdraw") && tx.amountRaw) {
|
|
3641
|
+
const target = previous.transactions[0];
|
|
3642
|
+
const known = new Set(target.logIndexes ?? []);
|
|
3643
|
+
if (tx.logIndexes?.some((index) => !known.has(index))) {
|
|
3644
|
+
target.amountRaw = String(BigInt(target.amountRaw ?? "0") + BigInt(tx.amountRaw));
|
|
3645
|
+
target.amount = formatUnits2(
|
|
3646
|
+
BigInt(target.amountRaw),
|
|
3647
|
+
YIELDSEEKER_ASSET_METADATA[target.tokenSymbol].decimals
|
|
3648
|
+
);
|
|
3649
|
+
target.logIndexes = [...known, ...tx.logIndexes.filter((index) => !known.has(index))];
|
|
3650
|
+
target.transferLogs = [...target.transferLogs ?? [], ...(tx.transferLogs ?? []).filter((log) => !known.has(log.logIndex))];
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
const values = [...grouped.values(), ...ungrouped].filter((entry) => {
|
|
3655
|
+
const tx = entry.transactions[0];
|
|
3656
|
+
return tx?.amountRaw !== void 0 || ![...grouped.values()].some((other) => other !== entry && other.action === entry.action && other.transactions[0]?.amountRaw !== void 0 && other.transactions[0]?.txHashes[0]?.toLowerCase() === tx?.txHashes[0]?.toLowerCase());
|
|
3657
|
+
});
|
|
3658
|
+
let boundary;
|
|
3659
|
+
const scope = JSON.stringify([
|
|
3660
|
+
options.ownerAddress.toLowerCase(),
|
|
3661
|
+
contexts.map((context) => `${context.agent.chainId}:${context.wallet.walletAddress.toLowerCase()}:${context.asset}`).sort(),
|
|
3662
|
+
options.fromDate ?? null
|
|
3663
|
+
]);
|
|
3664
|
+
if (options.cursor) {
|
|
3665
|
+
try {
|
|
3666
|
+
boundary = JSON.parse(decodeURIComponent(atob(options.cursor)));
|
|
3667
|
+
if (!boundary || boundary.scope !== scope || typeof boundary.id !== "string" || !Number.isFinite(Date.parse(boundary.date)) || !Number.isFinite(Date.parse(boundary.toDate))) throw new Error();
|
|
3668
|
+
} catch {
|
|
3669
|
+
throw new InvalidHistoryCursorError("Invalid Yieldseeker history cursor.");
|
|
3308
3670
|
}
|
|
3309
3671
|
}
|
|
3310
|
-
const
|
|
3672
|
+
const toDate = boundary?.toDate ?? options.toDate ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3673
|
+
const filtered = values.filter((entry) => (!options.fromDate || entry.date >= options.fromDate) && entry.date <= toDate && (!boundary || entry.date < boundary.date || entry.date === boundary.date && (entry.id ?? "") > boundary.id)).sort((left, right) => right.date.localeCompare(left.date) || (left.id ?? "").localeCompare(right.id ?? ""));
|
|
3674
|
+
const data = filtered.slice(0, Math.max(1, options.limit));
|
|
3675
|
+
const hasMore = filtered.length > data.length;
|
|
3676
|
+
const last = data.at(-1);
|
|
3311
3677
|
return {
|
|
3312
|
-
data
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3678
|
+
data,
|
|
3679
|
+
hasMore,
|
|
3680
|
+
...hasMore && last ? { nextCursor: btoa(encodeURIComponent(JSON.stringify({
|
|
3681
|
+
date: last.date,
|
|
3682
|
+
id: last.id ?? "",
|
|
3683
|
+
toDate,
|
|
3684
|
+
scope
|
|
3685
|
+
}))) } : {}
|
|
3316
3686
|
};
|
|
3317
3687
|
}
|
|
3318
3688
|
function mapYieldseekerProfile(address, contexts) {
|
|
@@ -3487,7 +3857,7 @@ var YieldseekerAgent = class {
|
|
|
3487
3857
|
this.assertAsset(targetAsset);
|
|
3488
3858
|
await this.ensureAgent(state, chainId, targetAsset);
|
|
3489
3859
|
}
|
|
3490
|
-
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
3860
|
+
async deposit(state, chainId, amount, asset, depositCallback, onProgress) {
|
|
3491
3861
|
this.assertChain(chainId);
|
|
3492
3862
|
this.assertAsset(asset);
|
|
3493
3863
|
if (BigInt(amount) <= 0n) {
|
|
@@ -3501,7 +3871,20 @@ var YieldseekerAgent = class {
|
|
|
3501
3871
|
const context = await this.ensureAgent(state, chainId, asset);
|
|
3502
3872
|
const generation = this.readGeneration;
|
|
3503
3873
|
const movement = this.snapshotMovement(state, chainId, context, BigInt(amount));
|
|
3874
|
+
notifyFunding(onProgress, {
|
|
3875
|
+
agentId: this.id,
|
|
3876
|
+
chainId,
|
|
3877
|
+
token: asset,
|
|
3878
|
+
walletAddress: state.walletAddress,
|
|
3879
|
+
agentWallet: context.wallet.walletAddress,
|
|
3880
|
+
intendedAmount: amount,
|
|
3881
|
+
transfer: "pending",
|
|
3882
|
+
registration: "not-applicable",
|
|
3883
|
+
delivery: "not-applicable"
|
|
3884
|
+
});
|
|
3504
3885
|
let txHash;
|
|
3886
|
+
let receipt;
|
|
3887
|
+
const submitted = (hash) => notifyFunding(onProgress, { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount, txHash: hash, transfer: "pending", registration: "not-applicable", delivery: "not-applicable" });
|
|
3505
3888
|
if (depositCallback) {
|
|
3506
3889
|
provideDepositVerificationContext(depositCallback, {
|
|
3507
3890
|
agentId: "yieldseeker",
|
|
@@ -3514,30 +3897,57 @@ var YieldseekerAgent = class {
|
|
|
3514
3897
|
chainId,
|
|
3515
3898
|
amount
|
|
3516
3899
|
);
|
|
3517
|
-
|
|
3900
|
+
submitted(txHash);
|
|
3901
|
+
receipt = await this.waitForReceipt(state, chainId, txHash);
|
|
3518
3902
|
} else {
|
|
3519
3903
|
txHash = await this.submitTransaction(state, chainId, {
|
|
3520
3904
|
from: getAddress2(state.walletAddress),
|
|
3521
3905
|
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3522
3906
|
data: encodeFunctionData({
|
|
3523
|
-
abi:
|
|
3907
|
+
abi: erc20Abi2,
|
|
3524
3908
|
functionName: "transfer",
|
|
3525
3909
|
args: [getAddress2(context.wallet.walletAddress), BigInt(amount)]
|
|
3526
3910
|
}),
|
|
3527
3911
|
value: "0",
|
|
3528
3912
|
chainId
|
|
3913
|
+
}, submitted, (value) => {
|
|
3914
|
+
receipt = value;
|
|
3529
3915
|
});
|
|
3530
3916
|
}
|
|
3531
3917
|
if (this.readGeneration === generation) {
|
|
3532
3918
|
void this.refreshSnapshotAfterMovement(state, chainId, context, "deposit", movement);
|
|
3533
3919
|
}
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3920
|
+
const base5 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount, confirmedAmount: amount, txHash, transfer: "confirmed", registration: "not-applicable", delivery: "not-applicable" };
|
|
3921
|
+
const evidence = receipt ? inspectFundingReceipt(receipt, base5, YIELDSEEKER_ASSET_METADATA[asset].address, "deposit") : base5;
|
|
3922
|
+
notifyFunding(onProgress, evidence);
|
|
3923
|
+
return { txHash, smartWallet: context.wallet.walletAddress, amount, evidence };
|
|
3924
|
+
}
|
|
3925
|
+
async withdraw(state, chainId, asset, amount, onProgress) {
|
|
3926
|
+
let ownerTransferAttempted = false;
|
|
3927
|
+
let evidence = {
|
|
3928
|
+
agentId: this.id,
|
|
3929
|
+
chainId,
|
|
3930
|
+
token: asset,
|
|
3931
|
+
walletAddress: state.walletAddress,
|
|
3932
|
+
intendedAmount: amount ?? "MAX",
|
|
3933
|
+
transfer: "pending",
|
|
3934
|
+
registration: "not-applicable",
|
|
3935
|
+
delivery: "pending"
|
|
3538
3936
|
};
|
|
3937
|
+
try {
|
|
3938
|
+
return await this.withdrawInternal(state, chainId, asset, amount, (next) => {
|
|
3939
|
+
evidence = { ...evidence, ...next };
|
|
3940
|
+
notifyFunding(onProgress, evidence);
|
|
3941
|
+
}, () => {
|
|
3942
|
+
ownerTransferAttempted = true;
|
|
3943
|
+
});
|
|
3944
|
+
} catch (error) {
|
|
3945
|
+
if (!ownerTransferAttempted || error instanceof OwneyError && error.code === "AGENT_TRANSACTION_REVERTED")
|
|
3946
|
+
notifyFunding(onProgress, { ...evidence, transfer: "failed", delivery: "failed" });
|
|
3947
|
+
throw error;
|
|
3948
|
+
}
|
|
3539
3949
|
}
|
|
3540
|
-
async
|
|
3950
|
+
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted) {
|
|
3541
3951
|
this.assertChain(chainId);
|
|
3542
3952
|
this.assertAsset(asset);
|
|
3543
3953
|
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
@@ -3567,17 +3977,32 @@ var YieldseekerAgent = class {
|
|
|
3567
3977
|
context
|
|
3568
3978
|
);
|
|
3569
3979
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3570
|
-
const
|
|
3571
|
-
(
|
|
3572
|
-
|
|
3573
|
-
|
|
3980
|
+
const readIdle = async () => {
|
|
3981
|
+
const balance = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
3982
|
+
address: metadata.address,
|
|
3983
|
+
abi: erc20Abi2,
|
|
3984
|
+
functionName: "balanceOf",
|
|
3985
|
+
args: [getAddress2(context.wallet.walletAddress)]
|
|
3986
|
+
}), chainId, "eth_call", this.id);
|
|
3987
|
+
this.assertSession(generation, chainId, asset);
|
|
3988
|
+
return balance;
|
|
3989
|
+
};
|
|
3990
|
+
const idle = await readIdle();
|
|
3574
3991
|
const deployed = portfolio.positions.reduce(
|
|
3575
3992
|
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3576
3993
|
0n
|
|
3577
3994
|
);
|
|
3578
3995
|
const totalAvailable = idle + deployed;
|
|
3579
3996
|
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3580
|
-
|
|
3997
|
+
if (requested === 0n) {
|
|
3998
|
+
this.invalidateFundingRead(state, chainId, asset);
|
|
3999
|
+
throw new OwneyError(
|
|
4000
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4001
|
+
"No Yieldseeker funds are available to withdraw.",
|
|
4002
|
+
{ asset, available: "0" },
|
|
4003
|
+
this.id
|
|
4004
|
+
);
|
|
4005
|
+
}
|
|
3581
4006
|
if (requested > totalAvailable) {
|
|
3582
4007
|
throw new OwneyError(
|
|
3583
4008
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
@@ -3622,7 +4047,22 @@ var YieldseekerAgent = class {
|
|
|
3622
4047
|
remaining: remaining.toString()
|
|
3623
4048
|
});
|
|
3624
4049
|
}
|
|
4050
|
+
const availableForOwner = confirmedMovement ? await readIdle() : idle;
|
|
4051
|
+
if (availableForOwner === 0n || amount !== void 0 && availableForOwner < requested) {
|
|
4052
|
+
this.invalidateFundingRead(state, chainId, asset);
|
|
4053
|
+
throw new OwneyError(
|
|
4054
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4055
|
+
"Yieldseeker funds are not available for the owner transfer.",
|
|
4056
|
+
{ asset, requested: amount ?? "MAX", available: availableForOwner.toString() },
|
|
4057
|
+
this.id
|
|
4058
|
+
);
|
|
4059
|
+
}
|
|
4060
|
+
const payout = amount === void 0 ? availableForOwner : requested;
|
|
4061
|
+
const plannedMovement = this.snapshotMovement(state, chainId, context, -payout);
|
|
3625
4062
|
const account = getAddress2(state.walletAddress);
|
|
4063
|
+
let receipt;
|
|
4064
|
+
const base5 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount === void 0 ? "MAX" : requested.toString(), transfer: "pending", registration: "not-applicable", delivery: "pending" };
|
|
4065
|
+
onOwnerTransferAttempted?.();
|
|
3626
4066
|
const txHash = await this.submitTransaction(state, chainId, {
|
|
3627
4067
|
from: account,
|
|
3628
4068
|
to: getAddress2(context.wallet.walletAddress),
|
|
@@ -3637,14 +4077,21 @@ var YieldseekerAgent = class {
|
|
|
3637
4077
|
}),
|
|
3638
4078
|
value: "0",
|
|
3639
4079
|
chainId
|
|
4080
|
+
}, (hash) => notifyFunding(onProgress, { ...base5, txHash: hash }), (value) => {
|
|
4081
|
+
receipt = value;
|
|
3640
4082
|
});
|
|
3641
4083
|
confirmedMovement = true;
|
|
3642
4084
|
settlement = plannedMovement;
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
4085
|
+
const evidence = receipt ? inspectFundingReceipt(receipt, { ...base5, txHash }, metadata.address, "withdraw") : { ...base5, txHash, confirmedAmount: payout.toString(), transfer: "confirmed", delivery: "confirmed" };
|
|
4086
|
+
notifyFunding(onProgress, evidence);
|
|
4087
|
+
if (evidence.transfer === "failed" && evidence.confirmedAmount === "0")
|
|
4088
|
+
throw new OwneyError(
|
|
4089
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4090
|
+
"The Yieldseeker withdrawal completed without transferring funds.",
|
|
4091
|
+
{ asset, available: "0", txHash },
|
|
4092
|
+
this.id
|
|
4093
|
+
);
|
|
4094
|
+
return { txHash, type: amount === void 0 ? "full" : "partial", amount: evidence.confirmedAmount ?? payout.toString(), evidence };
|
|
3648
4095
|
} finally {
|
|
3649
4096
|
if (confirmedMovement && this.readGeneration === generation) {
|
|
3650
4097
|
void this.refreshSnapshotAfterMovement(
|
|
@@ -3657,9 +4104,39 @@ var YieldseekerAgent = class {
|
|
|
3657
4104
|
}
|
|
3658
4105
|
}
|
|
3659
4106
|
}
|
|
3660
|
-
async getBalances(state, chainId) {
|
|
4107
|
+
async getBalances(state, chainId, asset) {
|
|
4108
|
+
this.assertChain(chainId);
|
|
4109
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
4110
|
+
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, { asset, allowMissingPositions: true }));
|
|
4111
|
+
}
|
|
4112
|
+
invalidateFundingRead(state, chainId, asset, refreshUntil = Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS) {
|
|
4113
|
+
this.assertAsset(asset);
|
|
4114
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
4115
|
+
const failure = this.snapshotFailures.get(key2);
|
|
4116
|
+
this.advancePortfolioVersion(key2);
|
|
4117
|
+
if (failure && failure.retryAt > Date.now()) this.snapshotFailures.set(key2, failure);
|
|
4118
|
+
if (!this.snapshotMovements.has(key2)) this.snapshotMovements.set(key2, null);
|
|
4119
|
+
const until = Math.min(refreshUntil, Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS);
|
|
4120
|
+
this.reconcileUntil.set(key2, Math.max(this.reconcileUntil.get(key2) ?? 0, until));
|
|
4121
|
+
this.activityRefreshUntil.set(key2, Math.max(this.activityRefreshUntil.get(key2) ?? 0, until));
|
|
4122
|
+
}
|
|
4123
|
+
endFundingBalanceRefresh(state, chainId, asset, read2) {
|
|
3661
4124
|
this.assertChain(chainId);
|
|
3662
|
-
|
|
4125
|
+
this.assertAsset(asset);
|
|
4126
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
4127
|
+
const cached = this.readCache.get(`snapshot:${key2}`);
|
|
4128
|
+
const snapshot = cached?.value?.agentSnapshot;
|
|
4129
|
+
if (!cached || !snapshot || read2.chainId !== chainId || read2.asset.toUpperCase() !== asset || read2.readVersion !== this.portfolioVersion(key2) || read2.fetchedAt !== cached.fetchedAt || read2.snapshotId !== String(snapshot.agentWalletSnapshotId) || read2.providerUpdatedAt !== snapshot.updatedDate || read2.netDepositsRaw !== snapshot.netDepositsBase) return false;
|
|
4130
|
+
this.snapshotMovements.delete(key2);
|
|
4131
|
+
this.reconcileUntil.delete(key2);
|
|
4132
|
+
cached.expiresAt = cached.fetchedAt + YIELDSEEKER_PORTFOLIO_CACHE_MS;
|
|
4133
|
+
return true;
|
|
4134
|
+
}
|
|
4135
|
+
async getFundingWallet(state, chainId, asset) {
|
|
4136
|
+
this.assertAsset(asset);
|
|
4137
|
+
const context = this.agentContexts.get(this.contextKey(state, chainId, asset));
|
|
4138
|
+
if (!context) throw new OwneyError("NOT_CONNECTED", "Reconnect the agent before recovering funding.");
|
|
4139
|
+
return context.wallet.walletAddress;
|
|
3663
4140
|
}
|
|
3664
4141
|
async getEarnings(state, chainId) {
|
|
3665
4142
|
this.assertChain(chainId);
|
|
@@ -3696,6 +4173,7 @@ var YieldseekerAgent = class {
|
|
|
3696
4173
|
);
|
|
3697
4174
|
return mapYieldseekerHistory(contexts, {
|
|
3698
4175
|
limit: options?.limit ?? 10,
|
|
4176
|
+
cursor: options?.cursor,
|
|
3699
4177
|
ownerAddress: state.walletAddress,
|
|
3700
4178
|
vaultAddresses,
|
|
3701
4179
|
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
@@ -3770,6 +4248,7 @@ var YieldseekerAgent = class {
|
|
|
3770
4248
|
if (this.readGeneration === generation && this.pendingReads.get(key2) === request) {
|
|
3771
4249
|
this.readCache.set(key2, {
|
|
3772
4250
|
expiresAt: Date.now() + ttlMs,
|
|
4251
|
+
fetchedAt: Date.now(),
|
|
3773
4252
|
value
|
|
3774
4253
|
});
|
|
3775
4254
|
}
|
|
@@ -3970,6 +4449,7 @@ var YieldseekerAgent = class {
|
|
|
3970
4449
|
if (agent) {
|
|
3971
4450
|
this.readCache.set(this.agentListKey(state, chainId), {
|
|
3972
4451
|
expiresAt: Date.now() + YIELDSEEKER_AGENT_LIST_CACHE_MS,
|
|
4452
|
+
fetchedAt: Date.now(),
|
|
3973
4453
|
value: {
|
|
3974
4454
|
agents: [...agents, agent]
|
|
3975
4455
|
}
|
|
@@ -4030,7 +4510,6 @@ var YieldseekerAgent = class {
|
|
|
4030
4510
|
...previous,
|
|
4031
4511
|
epoch: this.movementEpochs.get(key2) ?? 0,
|
|
4032
4512
|
delta: previous.delta + delta,
|
|
4033
|
-
expectedBalance: previous.expectedBalance + delta,
|
|
4034
4513
|
expectedNetDeposits: previous.expectedNetDeposits + delta
|
|
4035
4514
|
};
|
|
4036
4515
|
}
|
|
@@ -4043,7 +4522,6 @@ var YieldseekerAgent = class {
|
|
|
4043
4522
|
epoch: this.movementEpochs.get(key2) ?? 0,
|
|
4044
4523
|
delta,
|
|
4045
4524
|
decimals: snapshot.baseAssetDecimals,
|
|
4046
|
-
expectedBalance: BigInt(snapshot.totalValueBase) + delta,
|
|
4047
4525
|
expectedNetDeposits: BigInt(snapshot.netDepositsBase) + delta
|
|
4048
4526
|
};
|
|
4049
4527
|
}
|
|
@@ -4072,9 +4550,8 @@ var YieldseekerAgent = class {
|
|
|
4072
4550
|
const movement = this.snapshotMovements.get(contextKey);
|
|
4073
4551
|
const snapshot = response.agentSnapshot;
|
|
4074
4552
|
if (movement && snapshot.baseAssetDecimals === movement.decimals && /^-?\d+$/.test(snapshot.netDepositsBase) && /^\d+$/.test(snapshot.totalValueBase)) {
|
|
4075
|
-
const balance = BigInt(snapshot.totalValueBase);
|
|
4076
4553
|
const netDeposits = BigInt(snapshot.netDepositsBase);
|
|
4077
|
-
const reconciled = movement.delta > 0n ?
|
|
4554
|
+
const reconciled = movement.delta > 0n ? netDeposits >= movement.expectedNetDeposits : netDeposits <= movement.expectedNetDeposits;
|
|
4078
4555
|
if (reconciled) {
|
|
4079
4556
|
this.snapshotMovements.delete(contextKey);
|
|
4080
4557
|
this.reconcileUntil.delete(contextKey);
|
|
@@ -4128,12 +4605,20 @@ var YieldseekerAgent = class {
|
|
|
4128
4605
|
const { version, read: read2 } = this.portfolioSnapshot(state, chainId, context, contextKey);
|
|
4129
4606
|
const parts = this.loadPortfolioParts(state, chainId, context, options, contextKey);
|
|
4130
4607
|
try {
|
|
4131
|
-
|
|
4608
|
+
let positionsUnavailable = false;
|
|
4609
|
+
const safeParts = options.allowMissingPositions ? parts.catch(() => {
|
|
4610
|
+
positionsUnavailable = true;
|
|
4611
|
+
return { positions: [] };
|
|
4612
|
+
}) : parts;
|
|
4613
|
+
const [snapshot, portfolioParts] = await Promise.all([read2, safeParts]);
|
|
4132
4614
|
assertSession();
|
|
4133
4615
|
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4134
4616
|
return {
|
|
4135
4617
|
...context,
|
|
4136
4618
|
snapshot: snapshot.agentSnapshot,
|
|
4619
|
+
fetchedAt: this.readCache.get(`snapshot:${contextKey}`)?.fetchedAt,
|
|
4620
|
+
readVersion: version,
|
|
4621
|
+
positionsUnavailable,
|
|
4137
4622
|
...portfolioParts
|
|
4138
4623
|
};
|
|
4139
4624
|
} catch (error) {
|
|
@@ -4324,9 +4809,11 @@ var YieldseekerAgent = class {
|
|
|
4324
4809
|
this.id
|
|
4325
4810
|
);
|
|
4326
4811
|
}
|
|
4327
|
-
async submitTransaction(state, chainId, transaction) {
|
|
4812
|
+
async submitTransaction(state, chainId, transaction, onSubmitted, onReceipt) {
|
|
4328
4813
|
if (this.transactionExecutor) {
|
|
4329
|
-
|
|
4814
|
+
const hash2 = await this.transactionExecutor(state, chainId, transaction);
|
|
4815
|
+
onSubmitted?.(hash2);
|
|
4816
|
+
return hash2;
|
|
4330
4817
|
}
|
|
4331
4818
|
this.assertTransaction(transaction, state, chainId);
|
|
4332
4819
|
const account = getAddress2(state.walletAddress);
|
|
@@ -4351,6 +4838,7 @@ var YieldseekerAgent = class {
|
|
|
4351
4838
|
data: transaction.data,
|
|
4352
4839
|
value: BigInt(transaction.value)
|
|
4353
4840
|
});
|
|
4841
|
+
onSubmitted?.(hash);
|
|
4354
4842
|
const receipt = await withPaidRpcDiagnostics(
|
|
4355
4843
|
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
4356
4844
|
hash,
|
|
@@ -4368,6 +4856,7 @@ var YieldseekerAgent = class {
|
|
|
4368
4856
|
this.id
|
|
4369
4857
|
);
|
|
4370
4858
|
}
|
|
4859
|
+
onReceipt?.(receipt);
|
|
4371
4860
|
return hash;
|
|
4372
4861
|
}
|
|
4373
4862
|
async waitForReceipt(state, chainId, transactionHash) {
|
|
@@ -4392,6 +4881,7 @@ var YieldseekerAgent = class {
|
|
|
4392
4881
|
this.id
|
|
4393
4882
|
);
|
|
4394
4883
|
}
|
|
4884
|
+
return receipt;
|
|
4395
4885
|
}
|
|
4396
4886
|
assertTransaction(transaction, state, chainId) {
|
|
4397
4887
|
if (!transaction || typeof transaction.from !== "string" || !isAddress2(transaction.from) || typeof transaction.to !== "string" || !isAddress2(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 || getAddress2(transaction.from) !== getAddress2(state.walletAddress)) {
|
|
@@ -4753,10 +5243,9 @@ import {
|
|
|
4753
5243
|
} from "viem";
|
|
4754
5244
|
|
|
4755
5245
|
// src/lib/permit2-batch.ts
|
|
4756
|
-
import { parseAbi as
|
|
5246
|
+
import { parseAbi as parseAbi3, hashStruct } from "viem";
|
|
4757
5247
|
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
4758
5248
|
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
4759
|
-
var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
4760
5249
|
var ERC2612_PERMIT_TYPES = {
|
|
4761
5250
|
Permit: [
|
|
4762
5251
|
{ name: "owner", type: "address" },
|
|
@@ -4766,7 +5255,7 @@ var ERC2612_PERMIT_TYPES = {
|
|
|
4766
5255
|
{ name: "deadline", type: "uint256" }
|
|
4767
5256
|
]
|
|
4768
5257
|
};
|
|
4769
|
-
var ERC2612_READ_ABI =
|
|
5258
|
+
var ERC2612_READ_ABI = parseAbi3([
|
|
4770
5259
|
"function name() view returns (string)",
|
|
4771
5260
|
"function version() view returns (string)",
|
|
4772
5261
|
"function nonces(address owner) view returns (uint256)"
|
|
@@ -4804,7 +5293,7 @@ var PERMIT_BATCH_TYPES = {
|
|
|
4804
5293
|
{ name: "amount", type: "uint256" }
|
|
4805
5294
|
]
|
|
4806
5295
|
};
|
|
4807
|
-
var PERMIT2_BATCH_ABI =
|
|
5296
|
+
var PERMIT2_BATCH_ABI = parseAbi3([
|
|
4808
5297
|
"struct TokenPermissions { address token; uint256 amount; }",
|
|
4809
5298
|
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
4810
5299
|
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
@@ -4888,6 +5377,22 @@ async function execute(i, key2, plan) {
|
|
|
4888
5377
|
let body = initial;
|
|
4889
5378
|
save(key2, body);
|
|
4890
5379
|
try {
|
|
5380
|
+
if (body.erc2612Permit && !body.serializedPermitTransaction) {
|
|
5381
|
+
const preparedPermit = await postSponsorBatchTransfer({
|
|
5382
|
+
apiKey: i.apiKey,
|
|
5383
|
+
baseUrl: i.baseUrl,
|
|
5384
|
+
body
|
|
5385
|
+
});
|
|
5386
|
+
if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || keccak256(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
|
|
5387
|
+
throw new Error(
|
|
5388
|
+
"Sponsorship API did not return a valid prepared permit transaction."
|
|
5389
|
+
);
|
|
5390
|
+
body = {
|
|
5391
|
+
...body,
|
|
5392
|
+
serializedPermitTransaction: preparedPermit.serializedPermitTransaction
|
|
5393
|
+
};
|
|
5394
|
+
save(key2, body);
|
|
5395
|
+
}
|
|
4891
5396
|
if (!body.serializedTransaction) {
|
|
4892
5397
|
const prepared = await postSponsorBatchTransfer({
|
|
4893
5398
|
apiKey: i.apiKey,
|
|
@@ -4904,6 +5409,10 @@ async function execute(i, key2, plan) {
|
|
|
4904
5409
|
};
|
|
4905
5410
|
save(key2, body);
|
|
4906
5411
|
}
|
|
5412
|
+
try {
|
|
5413
|
+
i.onSubmitted?.(keccak256(body.serializedTransaction));
|
|
5414
|
+
} catch {
|
|
5415
|
+
}
|
|
4907
5416
|
const result = await postSponsorBatchTransfer({
|
|
4908
5417
|
apiKey: i.apiKey,
|
|
4909
5418
|
baseUrl: i.baseUrl,
|
|
@@ -4985,7 +5494,7 @@ async function execute(i, key2, plan) {
|
|
|
4985
5494
|
});
|
|
4986
5495
|
erc2612Permit = { ...unsignedPermit, signature: signature2 };
|
|
4987
5496
|
}
|
|
4988
|
-
const spender =
|
|
5497
|
+
const spender = await getSponsorRelayerAddress({
|
|
4989
5498
|
apiKey: i.apiKey,
|
|
4990
5499
|
baseUrl: i.baseUrl,
|
|
4991
5500
|
chainId: i.chainId
|
|
@@ -5032,7 +5541,8 @@ function makeSponsoredTokenCallback(deps) {
|
|
|
5032
5541
|
transfers,
|
|
5033
5542
|
pub,
|
|
5034
5543
|
wallet,
|
|
5035
|
-
onApproved: deps.onApproved
|
|
5544
|
+
onApproved: deps.onApproved,
|
|
5545
|
+
onSubmitted: (hash) => deps.onSubmitted?.(hash, transfers)
|
|
5036
5546
|
});
|
|
5037
5547
|
};
|
|
5038
5548
|
const callback = makeVerificationAwareDepositCallback(
|
|
@@ -5118,7 +5628,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
|
5118
5628
|
}
|
|
5119
5629
|
|
|
5120
5630
|
// src/lib/sponsored-calls-deposit.ts
|
|
5121
|
-
import { encodeFunctionData as encodeFunctionData2, erc20Abi as
|
|
5631
|
+
import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi3, toHex } from "viem";
|
|
5122
5632
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
5123
5633
|
var DEFAULT_MAX_POLLS = 30;
|
|
5124
5634
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -5164,7 +5674,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
5164
5674
|
to: token,
|
|
5165
5675
|
value: "0x0",
|
|
5166
5676
|
data: encodeFunctionData2({
|
|
5167
|
-
abi:
|
|
5677
|
+
abi: erc20Abi3,
|
|
5168
5678
|
functionName: "transfer",
|
|
5169
5679
|
args: [transfer.to, BigInt(transfer.amount)]
|
|
5170
5680
|
})
|
|
@@ -5226,6 +5736,12 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
5226
5736
|
params: [callsId]
|
|
5227
5737
|
});
|
|
5228
5738
|
const txHash = status?.receipts?.[0]?.transactionHash;
|
|
5739
|
+
if (txHash && !status?.receipts?.some((receipt) => receipt.transactionHash !== txHash)) {
|
|
5740
|
+
try {
|
|
5741
|
+
deps.onSubmitted?.(txHash, transfers);
|
|
5742
|
+
} catch {
|
|
5743
|
+
}
|
|
5744
|
+
}
|
|
5229
5745
|
if (status?.receipts?.some((receipt) => receipt.status === "0x0") || typeof status?.status === "number" && status.status >= 400) {
|
|
5230
5746
|
throw new OwneyError(
|
|
5231
5747
|
"SPONSOR_REQUEST_FAILED",
|
|
@@ -5335,6 +5851,7 @@ var OwneySDK = class {
|
|
|
5335
5851
|
paymasterServiceUrl;
|
|
5336
5852
|
cachedSponsoredCallsCallbacks = /* @__PURE__ */ new Map();
|
|
5337
5853
|
agentReads = new AgentReads();
|
|
5854
|
+
recoveredFundingReads = /* @__PURE__ */ new Set();
|
|
5338
5855
|
activationChecks = /* @__PURE__ */ new Map();
|
|
5339
5856
|
readAgent(agent, method, fetch2, params = null) {
|
|
5340
5857
|
const key2 = JSON.stringify([
|
|
@@ -5448,8 +5965,8 @@ var OwneySDK = class {
|
|
|
5448
5965
|
return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
|
|
5449
5966
|
}
|
|
5450
5967
|
/** Builds the default USDC batch callback for the connected wallet. */
|
|
5451
|
-
getDefaultSponsoredCallback(onApproved) {
|
|
5452
|
-
if (!onApproved && this.cachedSponsoredCallback)
|
|
5968
|
+
getDefaultSponsoredCallback(onApproved, onSubmitted) {
|
|
5969
|
+
if (!onApproved && !onSubmitted && this.cachedSponsoredCallback)
|
|
5453
5970
|
return this.cachedSponsoredCallback;
|
|
5454
5971
|
const provider = this.requireConnectedProvider();
|
|
5455
5972
|
const owner = this.state.walletAddress;
|
|
@@ -5458,6 +5975,7 @@ var OwneySDK = class {
|
|
|
5458
5975
|
baseUrl: this.routingApiBaseUrl,
|
|
5459
5976
|
ownerAddress: owner,
|
|
5460
5977
|
onApproved,
|
|
5978
|
+
onSubmitted,
|
|
5461
5979
|
tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
|
|
5462
5980
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
5463
5981
|
// PublicClient/WalletClient param types — structurally identical at
|
|
@@ -5473,13 +5991,13 @@ var OwneySDK = class {
|
|
|
5473
5991
|
transport: custom3(provider)
|
|
5474
5992
|
})
|
|
5475
5993
|
});
|
|
5476
|
-
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
5994
|
+
if (!onApproved && !onSubmitted) this.cachedSponsoredCallback = callback;
|
|
5477
5995
|
return callback;
|
|
5478
5996
|
}
|
|
5479
5997
|
/** Builds the wallet-native sponsored calls callback for compatible paymasters. */
|
|
5480
|
-
getDefaultSponsoredCallsCallback(asset, onApproved) {
|
|
5998
|
+
getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted) {
|
|
5481
5999
|
const cached = this.cachedSponsoredCallsCallbacks.get(asset);
|
|
5482
|
-
if (!onApproved && cached) return cached;
|
|
6000
|
+
if (!onApproved && !onSubmitted && cached) return cached;
|
|
5483
6001
|
const provider = this.requireConnectedProvider();
|
|
5484
6002
|
const callback = makeSponsoredCallsCallback({
|
|
5485
6003
|
apiKey: this.apiKey,
|
|
@@ -5488,9 +6006,10 @@ var OwneySDK = class {
|
|
|
5488
6006
|
ownerAddress: this.state.walletAddress,
|
|
5489
6007
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
5490
6008
|
onApproved,
|
|
6009
|
+
onSubmitted,
|
|
5491
6010
|
tokenAddressByChain: sponsoredTokensFor(asset)
|
|
5492
6011
|
});
|
|
5493
|
-
if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
6012
|
+
if (!onApproved && !onSubmitted) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
5494
6013
|
return callback;
|
|
5495
6014
|
}
|
|
5496
6015
|
/**
|
|
@@ -5499,8 +6018,8 @@ var OwneySDK = class {
|
|
|
5499
6018
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
5500
6019
|
* single-use batch authorization instead of an EIP-3009 authorization.
|
|
5501
6020
|
*/
|
|
5502
|
-
getDefaultWethSponsoredCallback(onApproved) {
|
|
5503
|
-
if (!onApproved && this.cachedWethSponsoredCallback)
|
|
6021
|
+
getDefaultWethSponsoredCallback(onApproved, onSubmitted) {
|
|
6022
|
+
if (!onApproved && !onSubmitted && this.cachedWethSponsoredCallback)
|
|
5504
6023
|
return this.cachedWethSponsoredCallback;
|
|
5505
6024
|
const provider = this.requireConnectedProvider();
|
|
5506
6025
|
const owner = this.state.walletAddress;
|
|
@@ -5509,6 +6028,7 @@ var OwneySDK = class {
|
|
|
5509
6028
|
baseUrl: this.routingApiBaseUrl,
|
|
5510
6029
|
ownerAddress: owner,
|
|
5511
6030
|
onApproved,
|
|
6031
|
+
onSubmitted,
|
|
5512
6032
|
tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
|
|
5513
6033
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
5514
6034
|
// PublicClient/WalletClient param types — structurally identical at
|
|
@@ -5524,7 +6044,7 @@ var OwneySDK = class {
|
|
|
5524
6044
|
transport: custom3(provider)
|
|
5525
6045
|
})
|
|
5526
6046
|
});
|
|
5527
|
-
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
6047
|
+
if (!onApproved && !onSubmitted) this.cachedWethSponsoredCallback = callback;
|
|
5528
6048
|
return callback;
|
|
5529
6049
|
}
|
|
5530
6050
|
getAgent(agentId) {
|
|
@@ -5534,6 +6054,9 @@ var OwneySDK = class {
|
|
|
5534
6054
|
}
|
|
5535
6055
|
return agent;
|
|
5536
6056
|
}
|
|
6057
|
+
getActiveAgentIds() {
|
|
6058
|
+
return [...this.getActiveAgents().keys()].sort();
|
|
6059
|
+
}
|
|
5537
6060
|
getActiveAgents() {
|
|
5538
6061
|
const active = /* @__PURE__ */ new Map();
|
|
5539
6062
|
for (const id of this.activeAgents) {
|
|
@@ -5828,12 +6351,23 @@ var OwneySDK = class {
|
|
|
5828
6351
|
*/
|
|
5829
6352
|
async deposit(options) {
|
|
5830
6353
|
const { amount, asset, depositCallback, agentId, onApproved } = options;
|
|
6354
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
6355
|
+
const onProgress = options.onProgress ? (evidence) => {
|
|
6356
|
+
if (evidence.agentWallet) prepared.set(evidence.agentWallet.toLowerCase(), evidence);
|
|
6357
|
+
notifyFunding(options.onProgress, evidence);
|
|
6358
|
+
} : void 0;
|
|
6359
|
+
const onSubmitted = onProgress ? (hash, destinations) => {
|
|
6360
|
+
for (const destination of destinations) {
|
|
6361
|
+
const evidence = prepared.get(destination.to.toLowerCase());
|
|
6362
|
+
if (evidence) notifyFunding(onProgress, { ...evidence, txHash: hash, transfer: "pending" });
|
|
6363
|
+
}
|
|
6364
|
+
} : void 0;
|
|
5831
6365
|
if (BigInt(amount) === 0n) {
|
|
5832
6366
|
return { agentResults: {} };
|
|
5833
6367
|
}
|
|
5834
6368
|
const state = this.requireState();
|
|
5835
6369
|
const chainId = this.requireChainId();
|
|
5836
|
-
const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
|
|
6370
|
+
const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved, onSubmitted) : this.getDefaultSponsoredCallback(onApproved, onSubmitted));
|
|
5837
6371
|
if (agentId) {
|
|
5838
6372
|
if (this.isAgentDisabled(agentId)) {
|
|
5839
6373
|
throw new OwneyError(
|
|
@@ -5852,7 +6386,8 @@ var OwneySDK = class {
|
|
|
5852
6386
|
amount,
|
|
5853
6387
|
asset,
|
|
5854
6388
|
effectiveCallback,
|
|
5855
|
-
depositCallback
|
|
6389
|
+
depositCallback,
|
|
6390
|
+
onProgress
|
|
5856
6391
|
);
|
|
5857
6392
|
}
|
|
5858
6393
|
const totalAmount = BigInt(amount);
|
|
@@ -5916,7 +6451,7 @@ var OwneySDK = class {
|
|
|
5916
6451
|
run: (callback) => withFailureReporting(
|
|
5917
6452
|
this.apiKey,
|
|
5918
6453
|
agent.id,
|
|
5919
|
-
() => agent.deposit(state, chainId, amount2, asset, callback),
|
|
6454
|
+
() => onProgress ? agent.deposit(state, chainId, amount2, asset, callback, onProgress) : agent.deposit(state, chainId, amount2, asset, callback),
|
|
5920
6455
|
this.routingApiBaseUrl
|
|
5921
6456
|
)
|
|
5922
6457
|
})),
|
|
@@ -5953,7 +6488,8 @@ var OwneySDK = class {
|
|
|
5953
6488
|
agentAmount,
|
|
5954
6489
|
asset,
|
|
5955
6490
|
effectiveCallback,
|
|
5956
|
-
depositCallback
|
|
6491
|
+
depositCallback,
|
|
6492
|
+
onProgress
|
|
5957
6493
|
);
|
|
5958
6494
|
} catch (error) {
|
|
5959
6495
|
if (Object.keys(agentResults).length === 0) throw error;
|
|
@@ -6000,17 +6536,11 @@ var OwneySDK = class {
|
|
|
6000
6536
|
* propagates unchanged. Scoped per agent invocation so one agent's failure
|
|
6001
6537
|
* doesn't force a retry of sibling agents in a multi-agent split.
|
|
6002
6538
|
*/
|
|
6003
|
-
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback) {
|
|
6539
|
+
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress) {
|
|
6004
6540
|
const attempt = (callback) => withFailureReporting(
|
|
6005
6541
|
this.apiKey,
|
|
6006
6542
|
agent.id,
|
|
6007
|
-
() => agent.deposit(
|
|
6008
|
-
state,
|
|
6009
|
-
chainId,
|
|
6010
|
-
amount,
|
|
6011
|
-
asset,
|
|
6012
|
-
callback
|
|
6013
|
-
),
|
|
6543
|
+
() => onProgress ? agent.deposit(state, chainId, amount, asset, callback, onProgress) : agent.deposit(state, chainId, amount, asset, callback),
|
|
6014
6544
|
this.routingApiBaseUrl
|
|
6015
6545
|
);
|
|
6016
6546
|
let approvalAttempted = false;
|
|
@@ -6190,15 +6720,81 @@ var OwneySDK = class {
|
|
|
6190
6720
|
while (current && typeof current === "object" && !seen.has(current)) {
|
|
6191
6721
|
seen.add(current);
|
|
6192
6722
|
const candidate = current;
|
|
6193
|
-
if (candidate.code === 4001 || candidate.code === "4001") return true;
|
|
6723
|
+
if (candidate.code === 4001 || candidate.code === "4001" || candidate.code === "ACTION_REJECTED") return true;
|
|
6194
6724
|
const message = [candidate.message, candidate.shortMessage].filter((value) => typeof value === "string").join(" ");
|
|
6195
|
-
if (/user (?:rejected|denied)|rejected by user/i.test(message)) {
|
|
6725
|
+
if (/user (?:rejected|denied)|rejected by user|request rejected/i.test(message)) {
|
|
6196
6726
|
return true;
|
|
6197
6727
|
}
|
|
6198
6728
|
current = candidate.cause;
|
|
6199
6729
|
}
|
|
6200
6730
|
return false;
|
|
6201
6731
|
}
|
|
6732
|
+
captureReadScope(requestedChain) {
|
|
6733
|
+
const original = this.requireState();
|
|
6734
|
+
const chainId = this.requireChainId();
|
|
6735
|
+
if (requestedChain !== void 0 && requestedChain !== chainId) {
|
|
6736
|
+
throw new OwneyError("NO_ACTIVE_CHAIN", "Account read scope changed. Please retry.");
|
|
6737
|
+
}
|
|
6738
|
+
const wallet = original.walletAddress;
|
|
6739
|
+
return { state: { ...original }, chainId, assertCurrent: () => {
|
|
6740
|
+
if (this.state !== original || this.state.walletAddress !== wallet || this.state.chainId !== chainId)
|
|
6741
|
+
throw new OwneyError("NOT_CONNECTED", "Account read scope changed. Please retry.");
|
|
6742
|
+
} };
|
|
6743
|
+
}
|
|
6744
|
+
/** Revalidate a saved transfer against the authenticated account and actual receipt. */
|
|
6745
|
+
async inspectFunding(evidence, direction, options = {}) {
|
|
6746
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
6747
|
+
if (state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase() || !evidence.txHash)
|
|
6748
|
+
throw new OwneyError("AGENT_API_ERROR", "Funding evidence does not match the connected account.");
|
|
6749
|
+
const agent = this.getAgent(evidence.agentId);
|
|
6750
|
+
const verifiedWallet = agent.getFundingWallet ? await agent.getFundingWallet(state, chainId, evidence.token) : (await agent.getUserProfile(state, chainId)).smartWallet;
|
|
6751
|
+
const agentWallet = evidence.agentWallet ?? verifiedWallet;
|
|
6752
|
+
if (!agentWallet || agentWallet.toLowerCase() !== verifiedWallet.toLowerCase())
|
|
6753
|
+
throw new OwneyError("AGENT_API_ERROR", "Funding wallet does not match this account.");
|
|
6754
|
+
const token = SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token);
|
|
6755
|
+
if (!token) throw new OwneyError("CHAIN_UNSUPPORTED", "Unsupported funding asset.");
|
|
6756
|
+
const rpc = this.getPaidRpcClient(chainId);
|
|
6757
|
+
const receipt = await rpc.getTransactionReceipt({ hash: evidence.txHash });
|
|
6758
|
+
assertCurrent();
|
|
6759
|
+
let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction);
|
|
6760
|
+
if (direction === "withdraw" && evidence.agentId === "yieldseeker" && verified.transfer === "unknown" && verified.confirmedAmount && BigInt(verified.confirmedAmount) > 0n) {
|
|
6761
|
+
const transaction = await rpc.getTransaction({ hash: evidence.txHash });
|
|
6762
|
+
assertCurrent();
|
|
6763
|
+
if (isFullYieldseekerWithdrawal(transaction, { ...evidence, agentWallet }, token.address))
|
|
6764
|
+
verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet, intendedAmount: "MAX" }, token.address, direction);
|
|
6765
|
+
}
|
|
6766
|
+
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${evidence.agentId}:${evidence.token}:${evidence.txHash}`;
|
|
6767
|
+
if ((verified.transfer === "confirmed" || verified.transfer === "failed" && verified.confirmedAmount === "0") && !this.recoveredFundingReads.has(key2)) {
|
|
6768
|
+
if (options.refreshBalance !== false)
|
|
6769
|
+
agent.invalidateFundingRead?.(state, chainId, evidence.token, options.balanceRefreshUntil);
|
|
6770
|
+
this.recoveredFundingReads.add(key2);
|
|
6771
|
+
if (this.recoveredFundingReads.size > 256) this.recoveredFundingReads.delete(this.recoveredFundingReads.values().next().value);
|
|
6772
|
+
}
|
|
6773
|
+
return verified;
|
|
6774
|
+
}
|
|
6775
|
+
/** A display acknowledgement controls polling only; it never confirms a financial outcome. */
|
|
6776
|
+
endFundingBalanceRefresh(evidence, read2) {
|
|
6777
|
+
const { state, chainId } = this.captureReadScope(evidence.chainId);
|
|
6778
|
+
if (state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase() || read2.chainId !== chainId || read2.asset.toUpperCase() !== evidence.token) return false;
|
|
6779
|
+
return this.getAgent(evidence.agentId).endFundingBalanceRefresh?.(state, chainId, evidence.token, read2) ?? false;
|
|
6780
|
+
}
|
|
6781
|
+
/** History lookup for receipt recovery; adapters may not authenticate or sign here. */
|
|
6782
|
+
async getFundingHistory(evidence, fromDate, options) {
|
|
6783
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
6784
|
+
if (state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase())
|
|
6785
|
+
throw new OwneyError("NOT_CONNECTED", "Funding account changed.");
|
|
6786
|
+
const agent = this.getAgent(evidence.agentId);
|
|
6787
|
+
if (!agent.getFundingHistory) return { data: [], hasMore: false };
|
|
6788
|
+
const filters = { tokenSymbol: evidence.token, fromDate, limit: 50, ...options };
|
|
6789
|
+
const page = await this.readAgent(agent, "history", () => agent.getFundingHistory(state, chainId, filters), filters);
|
|
6790
|
+
assertCurrent();
|
|
6791
|
+
return page;
|
|
6792
|
+
}
|
|
6793
|
+
async recoverDepositRegistration(evidence) {
|
|
6794
|
+
const verified = await this.inspectFunding(evidence, "deposit");
|
|
6795
|
+
if (verified.agentId !== "zyfai") return verified;
|
|
6796
|
+
return this.getAgent("zyfai").retryDepositRegistration(this.requireState(), verified);
|
|
6797
|
+
}
|
|
6202
6798
|
// --- Fund operations ---
|
|
6203
6799
|
/**
|
|
6204
6800
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -6210,17 +6806,66 @@ var OwneySDK = class {
|
|
|
6210
6806
|
* @returns {AgentWithdrawResult} for a single agent, or {OwneyWithdrawResult} with per-agent results
|
|
6211
6807
|
*/
|
|
6212
6808
|
async withdraw(options) {
|
|
6809
|
+
const state = this.requireState();
|
|
6810
|
+
const chainId = this.requireChainId();
|
|
6811
|
+
const ids = options.agentId ? [options.agentId] : this.getActiveAgentIds().filter((id) => !options.excludeAgentIds?.includes(id));
|
|
6812
|
+
const attempted = /* @__PURE__ */ new Set();
|
|
6813
|
+
try {
|
|
6814
|
+
return await this.withdrawFromAgents(options, state, chainId, attempted);
|
|
6815
|
+
} catch (error) {
|
|
6816
|
+
for (const id of ids) if (!attempted.has(id)) {
|
|
6817
|
+
notifyFunding(options.onProgress, {
|
|
6818
|
+
agentId: id,
|
|
6819
|
+
chainId,
|
|
6820
|
+
token: options.asset,
|
|
6821
|
+
walletAddress: state.walletAddress,
|
|
6822
|
+
intendedAmount: options.amount ?? "MAX",
|
|
6823
|
+
transfer: "failed",
|
|
6824
|
+
registration: "not-applicable",
|
|
6825
|
+
delivery: "failed"
|
|
6826
|
+
});
|
|
6827
|
+
}
|
|
6828
|
+
throw error;
|
|
6829
|
+
}
|
|
6830
|
+
}
|
|
6831
|
+
async withdrawFromAgents(options, state, chainId, attempted) {
|
|
6213
6832
|
const { asset, amount, agentId, onAgentResult } = options;
|
|
6214
6833
|
const notifyAgentResult = (id, result) => {
|
|
6215
6834
|
try {
|
|
6835
|
+
if (result.evidence) notifyFunding(options.onProgress, result.evidence);
|
|
6216
6836
|
onAgentResult?.(id, result);
|
|
6217
6837
|
} catch (error) {
|
|
6218
6838
|
console.warn("Withdrawal result observer failed:", error);
|
|
6219
6839
|
}
|
|
6220
6840
|
};
|
|
6221
|
-
const state = this.requireState();
|
|
6222
|
-
const chainId = this.requireChainId();
|
|
6223
6841
|
const token = asset;
|
|
6842
|
+
const submissionEvidence = /* @__PURE__ */ new Map();
|
|
6843
|
+
const submit = async (agent, requested2) => {
|
|
6844
|
+
attempted.add(agent.id);
|
|
6845
|
+
let evidence = {
|
|
6846
|
+
agentId: agent.id,
|
|
6847
|
+
chainId,
|
|
6848
|
+
token,
|
|
6849
|
+
walletAddress: state.walletAddress,
|
|
6850
|
+
intendedAmount: requested2 ?? "MAX",
|
|
6851
|
+
transfer: "pending",
|
|
6852
|
+
registration: "not-applicable",
|
|
6853
|
+
delivery: "pending"
|
|
6854
|
+
};
|
|
6855
|
+
const progress = (next) => {
|
|
6856
|
+
evidence = { ...evidence, ...next };
|
|
6857
|
+
submissionEvidence.set(agent.id, evidence);
|
|
6858
|
+
notifyFunding(options.onProgress, evidence);
|
|
6859
|
+
};
|
|
6860
|
+
progress(evidence);
|
|
6861
|
+
try {
|
|
6862
|
+
return await agent.withdraw(state, chainId, token, requested2, progress);
|
|
6863
|
+
} catch (error) {
|
|
6864
|
+
if (!evidence.txHash && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
6865
|
+
progress({ ...evidence, transfer: "failed", delivery: "failed" });
|
|
6866
|
+
throw error;
|
|
6867
|
+
}
|
|
6868
|
+
};
|
|
6224
6869
|
const assetInfo = SupportedAssets.find(
|
|
6225
6870
|
(a) => a.chainId === chainId && a.symbol === token
|
|
6226
6871
|
);
|
|
@@ -6236,23 +6881,24 @@ var OwneySDK = class {
|
|
|
6236
6881
|
const result = await withFailureReporting(
|
|
6237
6882
|
this.apiKey,
|
|
6238
6883
|
agent.id,
|
|
6239
|
-
() => agent
|
|
6884
|
+
() => submit(agent, amount),
|
|
6240
6885
|
this.routingApiBaseUrl
|
|
6241
6886
|
);
|
|
6242
6887
|
notifyAgentResult(agent.id, result);
|
|
6243
6888
|
return result;
|
|
6244
6889
|
}
|
|
6245
|
-
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
6890
|
+
const eligibleAgents = this.getEligibleAgents(chainId, asset).filter((agent) => !options.excludeAgentIds?.includes(agent.id));
|
|
6246
6891
|
const withdrawalAgents = this.orderAgentsForWithdrawal(eligibleAgents);
|
|
6247
6892
|
if (!amount) {
|
|
6248
6893
|
const results2 = {};
|
|
6249
6894
|
const agentErrors2 = {};
|
|
6250
6895
|
for (const agent of withdrawalAgents) {
|
|
6251
6896
|
try {
|
|
6252
|
-
results2[agent.id] = await agent
|
|
6897
|
+
results2[agent.id] = await submit(agent);
|
|
6253
6898
|
notifyAgentResult(agent.id, results2[agent.id]);
|
|
6254
6899
|
} catch (err) {
|
|
6255
6900
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
6901
|
+
if (err instanceof OwneyError && err.code === "WITHDRAW_INSUFFICIENT_BALANCE" && err.details?.available === "0" && submissionEvidence.get(agent.id)?.transfer === "failed") continue;
|
|
6256
6902
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
6257
6903
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
6258
6904
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
@@ -6271,6 +6917,13 @@ var OwneySDK = class {
|
|
|
6271
6917
|
{ asset, agentErrors: agentErrors2 }
|
|
6272
6918
|
);
|
|
6273
6919
|
}
|
|
6920
|
+
if (Object.keys(agentErrors2).length > 0) {
|
|
6921
|
+
throw new OwneyError(
|
|
6922
|
+
"WITHDRAW_PARTIAL_FAILURE",
|
|
6923
|
+
"Some agents could not complete the withdrawal.",
|
|
6924
|
+
{ asset, partialResults: results2, agentErrors: agentErrors2 }
|
|
6925
|
+
);
|
|
6926
|
+
}
|
|
6274
6927
|
const amountsKnown = Object.values(results2).every(
|
|
6275
6928
|
(result) => typeof result.amount === "string" && /^\d+$/.test(result.amount)
|
|
6276
6929
|
);
|
|
@@ -6280,7 +6933,7 @@ var OwneySDK = class {
|
|
|
6280
6933
|
};
|
|
6281
6934
|
}
|
|
6282
6935
|
const requested = BigInt(amount);
|
|
6283
|
-
const aggregated = await this.getBalances();
|
|
6936
|
+
const aggregated = await this.getBalances(void 0, { chainId, asset: token });
|
|
6284
6937
|
const unavailableAgents = eligibleAgents.filter(
|
|
6285
6938
|
(agent) => !(agent.id in aggregated.agentBalances)
|
|
6286
6939
|
);
|
|
@@ -6342,12 +6995,7 @@ var OwneySDK = class {
|
|
|
6342
6995
|
const p = orderedPlans[i];
|
|
6343
6996
|
if (p.planned === 0n) continue;
|
|
6344
6997
|
try {
|
|
6345
|
-
results[p.agent.id] = await p.agent.
|
|
6346
|
-
state,
|
|
6347
|
-
chainId,
|
|
6348
|
-
token,
|
|
6349
|
-
p.planned.toString()
|
|
6350
|
-
);
|
|
6998
|
+
results[p.agent.id] = await submit(p.agent, p.planned.toString());
|
|
6351
6999
|
notifyAgentResult(p.agent.id, results[p.agent.id]);
|
|
6352
7000
|
} catch (err) {
|
|
6353
7001
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
@@ -6362,7 +7010,8 @@ var OwneySDK = class {
|
|
|
6362
7010
|
);
|
|
6363
7011
|
const failedAmount = p.planned;
|
|
6364
7012
|
p.planned = 0n;
|
|
6365
|
-
|
|
7013
|
+
if (submissionEvidence.get(p.agent.id)?.transfer === "failed")
|
|
7014
|
+
redistributeShare(orderedPlans, i, failedAmount);
|
|
6366
7015
|
}
|
|
6367
7016
|
}
|
|
6368
7017
|
if (Object.keys(results).length === 0) {
|
|
@@ -6399,12 +7048,19 @@ var OwneySDK = class {
|
|
|
6399
7048
|
* @param agentId - Optional. Agent to query. Omit for aggregated balances.
|
|
6400
7049
|
* @returns {AgentBalance} for a single agent, or {OwneyBalances} with totalBalance and per-agent breakdown
|
|
6401
7050
|
*/
|
|
6402
|
-
async getBalances(agentId) {
|
|
6403
|
-
const state = this.
|
|
6404
|
-
const
|
|
7051
|
+
async getBalances(agentId, options) {
|
|
7052
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(options?.chainId);
|
|
7053
|
+
const asset = options?.asset;
|
|
7054
|
+
const readBalance = (agent) => this.readAgent(
|
|
7055
|
+
agent,
|
|
7056
|
+
"balances",
|
|
7057
|
+
() => asset ? agent.getBalances(state, chainId, asset) : agent.getBalances(state, chainId),
|
|
7058
|
+
asset ? { asset } : null
|
|
7059
|
+
);
|
|
6405
7060
|
if (agentId) {
|
|
6406
7061
|
const agent = this.getAgent(agentId);
|
|
6407
|
-
const result = await
|
|
7062
|
+
const result = await readBalance(agent);
|
|
7063
|
+
assertCurrent();
|
|
6408
7064
|
return {
|
|
6409
7065
|
...result,
|
|
6410
7066
|
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
@@ -6415,7 +7071,7 @@ var OwneySDK = class {
|
|
|
6415
7071
|
const entries = [...this.getActiveAgents().entries()];
|
|
6416
7072
|
const balanceResults = await Promise.allSettled(
|
|
6417
7073
|
entries.map(async ([id, agent]) => {
|
|
6418
|
-
const b = await
|
|
7074
|
+
const b = await readBalance(agent);
|
|
6419
7075
|
return [
|
|
6420
7076
|
id,
|
|
6421
7077
|
{
|
|
@@ -6425,6 +7081,7 @@ var OwneySDK = class {
|
|
|
6425
7081
|
];
|
|
6426
7082
|
})
|
|
6427
7083
|
);
|
|
7084
|
+
assertCurrent();
|
|
6428
7085
|
let successCount = 0;
|
|
6429
7086
|
const agentErrors = {};
|
|
6430
7087
|
const agentRetryAt = {};
|
|
@@ -6692,12 +7349,13 @@ var OwneySDK = class {
|
|
|
6692
7349
|
* @returns {OwneyAgentHistory} `data` (up to `limit` entries), `hasMore`, `nextCursor`.
|
|
6693
7350
|
*/
|
|
6694
7351
|
async getHistory(options) {
|
|
6695
|
-
const state = this.
|
|
6696
|
-
const chainId = this.requireChainId();
|
|
7352
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(options?.chainId);
|
|
6697
7353
|
const { agentId, filters } = options ?? {};
|
|
6698
7354
|
if (agentId) {
|
|
6699
7355
|
const agent = this.getAgent(agentId);
|
|
6700
|
-
|
|
7356
|
+
const page = await this.readAgent(agent, "history", () => agent.getHistory(state, chainId, filters), filters);
|
|
7357
|
+
assertCurrent();
|
|
7358
|
+
return page;
|
|
6701
7359
|
}
|
|
6702
7360
|
const activeAgents = [...this.getActiveAgents().values()];
|
|
6703
7361
|
const cursorMap = filters?.cursor ? decodeMultiAgentCursor(filters.cursor) : {};
|
|
@@ -6707,20 +7365,35 @@ var OwneySDK = class {
|
|
|
6707
7365
|
if (filters?.cursor && agentCursor === void 0) {
|
|
6708
7366
|
return { agentId: agent.id, page: null };
|
|
6709
7367
|
}
|
|
6710
|
-
const agentFilters = { ...filters, cursor: agentCursor };
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
7368
|
+
const agentFilters = { ...filters, cursor: agentCursor || void 0 };
|
|
7369
|
+
try {
|
|
7370
|
+
const page = await this.readAgent(
|
|
7371
|
+
agent,
|
|
7372
|
+
"history",
|
|
7373
|
+
() => agent.getHistory(state, chainId, agentFilters),
|
|
7374
|
+
agentFilters
|
|
7375
|
+
);
|
|
7376
|
+
return { agentId: agent.id, page };
|
|
7377
|
+
} catch (error) {
|
|
7378
|
+
return { agentId: agent.id, page: null, error };
|
|
7379
|
+
}
|
|
6718
7380
|
})
|
|
6719
7381
|
);
|
|
7382
|
+
assertCurrent();
|
|
7383
|
+
const agentErrors = {};
|
|
7384
|
+
const agentRetryAt = {};
|
|
6720
7385
|
const merged = [];
|
|
6721
7386
|
const nextCursorMap = {};
|
|
6722
7387
|
let hasMore = false;
|
|
6723
|
-
for (const
|
|
7388
|
+
for (const result of perAgent) {
|
|
7389
|
+
const { agentId: id, page } = result;
|
|
7390
|
+
if ("error" in result) {
|
|
7391
|
+
agentErrors[id] = result.error instanceof Error ? result.error.message : String(result.error);
|
|
7392
|
+
const delay = rateLimitDelay(result.error);
|
|
7393
|
+
if (delay !== void 0) agentRetryAt[id] = Date.now() + delay;
|
|
7394
|
+
nextCursorMap[id] = cursorMap[id] ?? "";
|
|
7395
|
+
hasMore = true;
|
|
7396
|
+
}
|
|
6724
7397
|
if (!page) continue;
|
|
6725
7398
|
merged.push(...page.data);
|
|
6726
7399
|
if (page.hasMore && page.nextCursor) {
|
|
@@ -6740,6 +7413,7 @@ var OwneySDK = class {
|
|
|
6740
7413
|
});
|
|
6741
7414
|
return {
|
|
6742
7415
|
data: merged,
|
|
7416
|
+
...Object.keys(agentErrors).length ? { agentErrors, agentRetryAt } : {},
|
|
6743
7417
|
hasMore,
|
|
6744
7418
|
nextCursor: hasMore ? encodeMultiAgentCursor(nextCursorMap) : void 0
|
|
6745
7419
|
};
|