@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.cjs
CHANGED
|
@@ -32,6 +32,96 @@ __export(index_exports, {
|
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(index_exports);
|
|
34
34
|
|
|
35
|
+
// src/lib/funding-evidence.ts
|
|
36
|
+
var import_viem = require("viem");
|
|
37
|
+
function notifyFunding(callback, evidence) {
|
|
38
|
+
try {
|
|
39
|
+
void Promise.resolve(callback?.({ ...evidence })).catch(() => {
|
|
40
|
+
});
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
var fullWithdrawalAbi = (0, import_viem.parseAbi)(["function withdrawAllAssetToUser(address recipient, address asset)"]);
|
|
45
|
+
var withdrawalEventAbi = (0, import_viem.parseAbi)(["event WithdrewTokenToUser(address indexed owner, address indexed recipient, address indexed token, uint256 amount)"]);
|
|
46
|
+
function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress) {
|
|
47
|
+
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;
|
|
48
|
+
try {
|
|
49
|
+
const decoded = (0, import_viem.decodeFunctionData)({ abi: fullWithdrawalAbi, data: transaction.input });
|
|
50
|
+
return decoded.args[0].toLowerCase() === evidence.walletAddress.toLowerCase() && decoded.args[1].toLowerCase() === tokenAddress.toLowerCase();
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
56
|
+
if (receipt.status !== "success") return { ...evidence, transfer: "failed", delivery: "failed" };
|
|
57
|
+
let amount = 0n;
|
|
58
|
+
const seen = /* @__PURE__ */ new Set();
|
|
59
|
+
const attributed = /* @__PURE__ */ new Map();
|
|
60
|
+
const agent = evidence.agentWallet?.toLowerCase();
|
|
61
|
+
const owner = evidence.walletAddress.toLowerCase();
|
|
62
|
+
const logIndexes = [];
|
|
63
|
+
const transferLogs = [];
|
|
64
|
+
const zeroTransferLogs = [];
|
|
65
|
+
for (const log of [...receipt.logs].sort((a, b) => a.logIndex - b.logIndex)) {
|
|
66
|
+
if (log.address.toLowerCase() !== tokenAddress.toLowerCase()) continue;
|
|
67
|
+
if (seen.has(log.logIndex)) continue;
|
|
68
|
+
seen.add(log.logIndex);
|
|
69
|
+
try {
|
|
70
|
+
const event = (0, import_viem.decodeEventLog)({ abi: import_viem.erc20Abi, data: log.data, topics: log.topics });
|
|
71
|
+
if (event.eventName !== "Transfer") continue;
|
|
72
|
+
const { from, to, value } = event.args;
|
|
73
|
+
if (direction === "withdraw" && value === 0n && from.toLowerCase() === agent && to.toLowerCase() === owner)
|
|
74
|
+
zeroTransferLogs.push(log.logIndex);
|
|
75
|
+
let matched = 0n;
|
|
76
|
+
if (direction === "deposit") {
|
|
77
|
+
if (to.toLowerCase() === agent) matched = value;
|
|
78
|
+
} else {
|
|
79
|
+
const sender = from.toLowerCase();
|
|
80
|
+
const recipient = to.toLowerCase();
|
|
81
|
+
const available = sender === agent ? value : attributed.get(sender) ?? 0n;
|
|
82
|
+
const carried = available < value ? available : value;
|
|
83
|
+
if (sender !== agent) attributed.set(sender, available - carried);
|
|
84
|
+
if (recipient === owner) matched = carried;
|
|
85
|
+
else if (recipient !== agent) attributed.set(recipient, (attributed.get(recipient) ?? 0n) + carried);
|
|
86
|
+
}
|
|
87
|
+
if (matched === 0n) continue;
|
|
88
|
+
amount += matched;
|
|
89
|
+
transferLogs.push({ logIndex: log.logIndex, amountRaw: matched.toString() });
|
|
90
|
+
logIndexes.push(log.logIndex);
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const expected = /^\d+$/.test(evidence.intendedAmount) ? BigInt(evidence.intendedAmount) : void 0;
|
|
95
|
+
const emptyPayout = direction === "withdraw" && evidence.agentId === "yieldseeker" && amount === 0n && zeroTransferLogs.length > 0 && receipt.logs.some((log) => {
|
|
96
|
+
if (log.address.toLowerCase() !== agent) return false;
|
|
97
|
+
try {
|
|
98
|
+
const { args } = (0, import_viem.decodeEventLog)({ abi: withdrawalEventAbi, data: log.data, topics: log.topics });
|
|
99
|
+
return args.owner.toLowerCase() === owner && args.recipient.toLowerCase() === owner && args.token.toLowerCase() === tokenAddress.toLowerCase() && args.amount === 0n;
|
|
100
|
+
} catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
if (emptyPayout) return {
|
|
105
|
+
...evidence,
|
|
106
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
107
|
+
confirmedAmount: "0",
|
|
108
|
+
logIndexes: zeroTransferLogs,
|
|
109
|
+
transferLogs: zeroTransferLogs.map((logIndex) => ({ logIndex, amountRaw: "0" })),
|
|
110
|
+
transfer: "failed",
|
|
111
|
+
delivery: "failed"
|
|
112
|
+
};
|
|
113
|
+
const delivered = amount > 0n && (expected === void 0 || amount >= expected);
|
|
114
|
+
return {
|
|
115
|
+
...evidence,
|
|
116
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
117
|
+
logIndexes,
|
|
118
|
+
transferLogs,
|
|
119
|
+
...amount > 0n ? { confirmedAmount: amount.toString() } : {},
|
|
120
|
+
transfer: delivered ? "confirmed" : "unknown",
|
|
121
|
+
delivery: direction === "withdraw" ? delivered ? "confirmed" : "unknown" : "not-applicable"
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
35
125
|
// src/lib/deposit-batch-callback.ts
|
|
36
126
|
var batchCallbacks = /* @__PURE__ */ new WeakMap();
|
|
37
127
|
var getDepositBatchTransfer = (callback) => callback ? batchCallbacks.get(callback) : void 0;
|
|
@@ -246,9 +336,72 @@ var AgentReads = class {
|
|
|
246
336
|
}
|
|
247
337
|
};
|
|
248
338
|
|
|
339
|
+
// src/agents/zyfai/zyfai.withdraw-submission.ts
|
|
340
|
+
var listenersByClient = /* @__PURE__ */ new WeakMap();
|
|
341
|
+
function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
342
|
+
const client = sdk.httpClient;
|
|
343
|
+
if (!client || typeof client.get !== "function" || typeof client.post !== "function")
|
|
344
|
+
return { attempted: () => void 0, rejected: () => false, request: () => void 0, stop: () => {
|
|
345
|
+
} };
|
|
346
|
+
let listeners = listenersByClient.get(client);
|
|
347
|
+
if (!listeners) {
|
|
348
|
+
listeners = /* @__PURE__ */ new Set();
|
|
349
|
+
listenersByClient.set(client, listeners);
|
|
350
|
+
const observers = listeners;
|
|
351
|
+
for (const method of ["get", "post"]) {
|
|
352
|
+
const original = client[method];
|
|
353
|
+
client[method] = async function(...args) {
|
|
354
|
+
const [url, options] = args;
|
|
355
|
+
const withdrawal = method === "get" && url === "/users/withdraw" || method === "post" && url === "/users/partial-withdraw";
|
|
356
|
+
const payload = method === "get" ? options?.params : options;
|
|
357
|
+
const matching = withdrawal ? [...observers].filter((observer) => observer.chainId === payload?.chainId && observer.token === payload?.tokenSymbol) : [];
|
|
358
|
+
const startedAt = Date.now();
|
|
359
|
+
const request2 = {
|
|
360
|
+
attempted: true,
|
|
361
|
+
method: method === "get" ? "GET" : "POST",
|
|
362
|
+
path: method === "get" ? "/users/withdraw" : "/users/partial-withdraw"
|
|
363
|
+
};
|
|
364
|
+
const notify = (outcome) => {
|
|
365
|
+
if (outcome === "rejected" && matching.length !== 1) return;
|
|
366
|
+
for (const observer of matching) {
|
|
367
|
+
try {
|
|
368
|
+
observer.notify(outcome, { ...request2 });
|
|
369
|
+
} catch {
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
notify("attempted");
|
|
374
|
+
try {
|
|
375
|
+
return await original.apply(this, args);
|
|
376
|
+
} catch (error) {
|
|
377
|
+
const message = error instanceof Error ? error.message : "";
|
|
378
|
+
request2.failure = message === "Unauthorized: Invalid API key" ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : message === "Network error: Unable to reach the server" ? "network" : "other";
|
|
379
|
+
if (["unauthorized", "forbidden", "rate-limited"].includes(request2.failure)) notify("rejected");
|
|
380
|
+
throw error;
|
|
381
|
+
} finally {
|
|
382
|
+
request2.elapsedMs = Math.max(0, Date.now() - startedAt);
|
|
383
|
+
notify("finished");
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
let submitted = false;
|
|
389
|
+
let rejected = false;
|
|
390
|
+
let request = { attempted: false };
|
|
391
|
+
const listener = { chainId, token, notify: (outcome, evidence) => {
|
|
392
|
+
request = evidence;
|
|
393
|
+
if (outcome === "attempted") submitted = true;
|
|
394
|
+
else if (outcome === "rejected") rejected = true;
|
|
395
|
+
} };
|
|
396
|
+
listeners.add(listener);
|
|
397
|
+
return { attempted: () => submitted, rejected: () => rejected, request: () => ({ ...request }), stop: () => {
|
|
398
|
+
listeners.delete(listener);
|
|
399
|
+
} };
|
|
400
|
+
}
|
|
401
|
+
|
|
249
402
|
// src/agents/zyfai/zyfai.agent.ts
|
|
250
403
|
var import_sdk = require("@zyfai/sdk");
|
|
251
|
-
var
|
|
404
|
+
var import_viem3 = require("viem");
|
|
252
405
|
var import_chains = require("viem/chains");
|
|
253
406
|
|
|
254
407
|
// src/types/config.ts
|
|
@@ -421,7 +574,11 @@ function mapPendingAllocations(raw2) {
|
|
|
421
574
|
}
|
|
422
575
|
function mapBalances(raw2, _chainId, smartWallet) {
|
|
423
576
|
const portfolio = raw2.portfolio;
|
|
424
|
-
|
|
577
|
+
if (!portfolio?.portfolioByChain || typeof portfolio.portfolioByChain !== "object")
|
|
578
|
+
throw new Error("Zyfai balance snapshot is unavailable.");
|
|
579
|
+
const portfolioByChain = portfolio.portfolioByChain;
|
|
580
|
+
const fetchedAt = Date.now();
|
|
581
|
+
const provenance = portfolio;
|
|
425
582
|
let totalBalance = 0;
|
|
426
583
|
const tokens = [];
|
|
427
584
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
|
|
@@ -430,7 +587,8 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
430
587
|
symbol,
|
|
431
588
|
{ balance, balanceWithFee, decimals }
|
|
432
589
|
] of Object.entries(tokensBySymbol ?? {})) {
|
|
433
|
-
|
|
590
|
+
if (balanceWithFee === void 0 && balance === void 0) throw new Error("Zyfai asset balance is unavailable.");
|
|
591
|
+
const amount = hexToDecimal(balanceWithFee ?? balance, decimals);
|
|
434
592
|
totalBalance += amount;
|
|
435
593
|
tokens.push({
|
|
436
594
|
chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
|
|
@@ -480,6 +638,15 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
480
638
|
totalBalanceAsset: "usdc",
|
|
481
639
|
tokens,
|
|
482
640
|
positions,
|
|
641
|
+
readMetadata: tokens.map((token) => ({
|
|
642
|
+
chainId: token.chainId,
|
|
643
|
+
asset: token.asset,
|
|
644
|
+
fetchedAt,
|
|
645
|
+
providerUpdatedAt: typeof provenance.updatedAt === "string" ? provenance.updatedAt : void 0,
|
|
646
|
+
snapshotId: provenance.snapshotId === void 0 ? void 0 : String(provenance.snapshotId),
|
|
647
|
+
blockNumber: provenance.blockNumber === void 0 ? void 0 : String(provenance.blockNumber),
|
|
648
|
+
positionsStatus: Array.isArray(portfolio.positions) ? "available" : "unavailable"
|
|
649
|
+
})),
|
|
483
650
|
...pendingAllocations ? { pendingAllocations } : {}
|
|
484
651
|
};
|
|
485
652
|
}
|
|
@@ -820,28 +987,6 @@ function mapDailyEarnings(raw2, chainId, tokenSymbol) {
|
|
|
820
987
|
return { walletAddress: raw2.walletAddress, chainId, assets };
|
|
821
988
|
}
|
|
822
989
|
|
|
823
|
-
// src/agents/zyfai/zyfai.withdraw-amount.ts
|
|
824
|
-
var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
825
|
-
function topicToAddress(topic) {
|
|
826
|
-
return `0x${(topic ?? "").slice(-40)}`.toLowerCase();
|
|
827
|
-
}
|
|
828
|
-
function extractWithdrawnAmount(logs, recipient, token, decimals) {
|
|
829
|
-
const wantRecipient = recipient.toLowerCase();
|
|
830
|
-
const wantToken = token.toLowerCase();
|
|
831
|
-
let total = 0n;
|
|
832
|
-
let matched = false;
|
|
833
|
-
for (const log of logs) {
|
|
834
|
-
if (log.address.toLowerCase() !== wantToken) continue;
|
|
835
|
-
if ((log.topics[0] ?? "").toLowerCase() !== TRANSFER_TOPIC) continue;
|
|
836
|
-
if (topicToAddress(log.topics[2]) !== wantRecipient) continue;
|
|
837
|
-
if (!log.data || log.data === "0x") continue;
|
|
838
|
-
total += BigInt(log.data);
|
|
839
|
-
matched = true;
|
|
840
|
-
}
|
|
841
|
-
if (!matched) return void 0;
|
|
842
|
-
return String(hexToDecimal(`0x${total.toString(16)}`, decimals));
|
|
843
|
-
}
|
|
844
|
-
|
|
845
990
|
// src/agents/zyfai/zyfai.history-cursor.ts
|
|
846
991
|
var InvalidHistoryCursorError = class extends Error {
|
|
847
992
|
code = "invalid_cursor";
|
|
@@ -1130,7 +1275,7 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
1130
1275
|
}
|
|
1131
1276
|
|
|
1132
1277
|
// src/lib/paid-rpc.ts
|
|
1133
|
-
var
|
|
1278
|
+
var import_viem2 = require("viem");
|
|
1134
1279
|
var PAID_RPC_RETRY_COUNT = 3;
|
|
1135
1280
|
var PAID_RPC_RETRY_DELAY_MS = 1e3;
|
|
1136
1281
|
function configuredRpcProxyBaseUrl(baseUrl) {
|
|
@@ -1166,9 +1311,9 @@ function createPaidRpcClient(chain, rpcUrls) {
|
|
|
1166
1311
|
rpcUrls,
|
|
1167
1312
|
chain.id
|
|
1168
1313
|
);
|
|
1169
|
-
return (0,
|
|
1314
|
+
return (0, import_viem2.createPublicClient)({
|
|
1170
1315
|
chain,
|
|
1171
|
-
transport: (0,
|
|
1316
|
+
transport: (0, import_viem2.http)(url, {
|
|
1172
1317
|
retryCount: PAID_RPC_RETRY_COUNT,
|
|
1173
1318
|
retryDelay: PAID_RPC_RETRY_DELAY_MS
|
|
1174
1319
|
})
|
|
@@ -1265,7 +1410,7 @@ async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
|
|
|
1265
1410
|
}
|
|
1266
1411
|
|
|
1267
1412
|
// src/agents/zyfai/zyfai.agent.ts
|
|
1268
|
-
var ERC7579_IS_MODULE_INSTALLED_ABI = (0,
|
|
1413
|
+
var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem3.parseAbi)([
|
|
1269
1414
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
1270
1415
|
]);
|
|
1271
1416
|
var WETH_ADDRESS_BY_CHAIN = {
|
|
@@ -1324,10 +1469,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1324
1469
|
connectAuthPromise = null;
|
|
1325
1470
|
rpcUrls;
|
|
1326
1471
|
publicClients = /* @__PURE__ */ new Map();
|
|
1327
|
-
//
|
|
1328
|
-
//
|
|
1329
|
-
|
|
1330
|
-
withdrawAmountCache = /* @__PURE__ */ new Map();
|
|
1472
|
+
// Receipt-derived amounts and log identities, scoped to the owner and asset.
|
|
1473
|
+
// Failures remain retryable; successful immutable evidence has a bounded cache.
|
|
1474
|
+
fundingHistoryCache = /* @__PURE__ */ new Map();
|
|
1331
1475
|
earningsReads = /* @__PURE__ */ new Map();
|
|
1332
1476
|
earningsRefreshes = /* @__PURE__ */ new Map();
|
|
1333
1477
|
earningsSnapshot = null;
|
|
@@ -1355,8 +1499,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1355
1499
|
return client;
|
|
1356
1500
|
}
|
|
1357
1501
|
/**
|
|
1358
|
-
*
|
|
1359
|
-
*
|
|
1502
|
+
* Resolves funding amounts and log identities from token receipts. Withdrawals
|
|
1503
|
+
* must travel from this agent wallet to its owner; top-ups must reach the agent.
|
|
1504
|
+
* The Zyfai history
|
|
1360
1505
|
* payload only reports the *remaining* position for a partial withdraw (see
|
|
1361
1506
|
* the note in `mapEntries`), so without this the UI shows the leftover
|
|
1362
1507
|
* balance instead of the amount withdrawn.
|
|
@@ -1368,48 +1513,62 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1368
1513
|
* Entries with no symbol (older payload shapes) fall back to USDC to
|
|
1369
1514
|
* preserve prior behavior.
|
|
1370
1515
|
*
|
|
1371
|
-
* Mutates `entries` in place
|
|
1372
|
-
*
|
|
1373
|
-
* `amount: undefined`, which consumers render as a hidden/retryable row
|
|
1374
|
-
* rather than a wrong number.
|
|
1516
|
+
* Mutates `entries` in place, sharing receipt requests within the page. RPC
|
|
1517
|
+
* failures leave provider top-ups unchanged and withdrawal amounts unknown.
|
|
1375
1518
|
*/
|
|
1376
|
-
async
|
|
1377
|
-
const
|
|
1378
|
-
(e) => e.action === "Withdraw" && e.transactions[0]?.txHashes[0]
|
|
1519
|
+
async resolveFundingTransfers(entries, chainId, recipient, smartWallet, tokenSymbol) {
|
|
1520
|
+
const movements = entries.filter(
|
|
1521
|
+
(e) => (e.action === "Withdraw" || e.action === "Top up") && e.transactions[0]?.txHashes[0]
|
|
1379
1522
|
);
|
|
1380
|
-
if (
|
|
1523
|
+
if (movements.length === 0) return;
|
|
1381
1524
|
const client = this.getPublicClient(chainId);
|
|
1525
|
+
const receipts = /* @__PURE__ */ new Map();
|
|
1382
1526
|
await Promise.all(
|
|
1383
|
-
|
|
1527
|
+
movements.map(async (entry) => {
|
|
1384
1528
|
const tx = entry.transactions[0];
|
|
1385
1529
|
const txHash = tx.txHashes[0];
|
|
1386
|
-
const rawSymbol = tx.tokenSymbol;
|
|
1530
|
+
const rawSymbol = tx.tokenSymbol ?? tokenSymbol;
|
|
1387
1531
|
const normalizedSymbol = rawSymbol ? rawSymbol.toLowerCase() === "eth" ? "WETH" : rawSymbol.toUpperCase() : "USDC";
|
|
1388
1532
|
const asset = SupportedAssets.find(
|
|
1389
1533
|
(a) => a.chainId === chainId && a.symbol === normalizedSymbol
|
|
1390
|
-
) ?? SupportedAssets.find(
|
|
1391
|
-
(a) => a.chainId === chainId && a.symbol === "USDC"
|
|
1392
1534
|
);
|
|
1393
1535
|
if (!asset) return;
|
|
1394
|
-
|
|
1395
|
-
|
|
1536
|
+
tx.tokenSymbol = asset.symbol;
|
|
1537
|
+
tx.tokenAddress = asset.address;
|
|
1538
|
+
tx.agentWallet = smartWallet;
|
|
1539
|
+
entry.id = `${chainId}:${txHash.toLowerCase()}:${entry.action}:${asset.address.toLowerCase()}:${smartWallet.toLowerCase()}`;
|
|
1540
|
+
if (!/^0x[0-9a-f]{64}$/i.test(txHash)) return;
|
|
1541
|
+
const cacheKey = `${entry.id}:${recipient.toLowerCase()}`;
|
|
1542
|
+
const cached = this.fundingHistoryCache.get(cacheKey);
|
|
1396
1543
|
if (cached !== void 0) {
|
|
1397
|
-
tx
|
|
1544
|
+
Object.assign(tx, cached);
|
|
1398
1545
|
return;
|
|
1399
1546
|
}
|
|
1400
1547
|
try {
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
asset.
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1548
|
+
if (!receipts.has(txHash)) receipts.set(txHash, client.getTransactionReceipt({ hash: txHash }));
|
|
1549
|
+
const receipt = await receipts.get(txHash);
|
|
1550
|
+
const evidence = inspectFundingReceipt(receipt, {
|
|
1551
|
+
agentId: "zyfai",
|
|
1552
|
+
walletAddress: recipient,
|
|
1553
|
+
chainId,
|
|
1554
|
+
token: asset.symbol,
|
|
1555
|
+
agentWallet: smartWallet,
|
|
1556
|
+
intendedAmount: "MAX",
|
|
1557
|
+
txHash,
|
|
1558
|
+
transfer: "unknown",
|
|
1559
|
+
registration: "not-applicable",
|
|
1560
|
+
delivery: "unknown"
|
|
1561
|
+
}, asset.address, entry.action === "Withdraw" ? "withdraw" : "deposit");
|
|
1562
|
+
if (evidence.transfer === "confirmed" && evidence.confirmedAmount) {
|
|
1563
|
+
Object.assign(tx, {
|
|
1564
|
+
amount: (0, import_viem3.formatUnits)(BigInt(evidence.confirmedAmount), asset.decimals),
|
|
1565
|
+
amountRaw: evidence.confirmedAmount,
|
|
1566
|
+
blockNumber: evidence.blockNumber,
|
|
1567
|
+
logIndexes: evidence.logIndexes,
|
|
1568
|
+
transferLogs: evidence.transferLogs
|
|
1569
|
+
});
|
|
1570
|
+
if (this.fundingHistoryCache.size >= 256) this.fundingHistoryCache.delete(this.fundingHistoryCache.keys().next().value);
|
|
1571
|
+
this.fundingHistoryCache.set(cacheKey, { ...tx });
|
|
1413
1572
|
}
|
|
1414
1573
|
} catch {
|
|
1415
1574
|
}
|
|
@@ -1498,7 +1657,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1498
1657
|
* Wraps `sdk.connectAccount` with a one-shot retry on auth errors. If the
|
|
1499
1658
|
* cached JWT is rejected by the backend (revoked, signing-key rotated, etc.)
|
|
1500
1659
|
* we drop the cache and try again — the second attempt has no cache entry,
|
|
1501
|
-
* so it falls through to a real SIWE prompt. Zyfai
|
|
1660
|
+
* so it falls through to a real SIWE prompt. Zyfai labels every HTTP 401 with
|
|
1502
1661
|
* an `"Unauthorized: Invalid API key"` error so we can't distinguish revoked
|
|
1503
1662
|
* JWT from a misconfigured API key; we retry once either way, and if the
|
|
1504
1663
|
* second attempt still fails the original error propagates.
|
|
@@ -1531,6 +1690,17 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1531
1690
|
await this.connectAuth(state, chainId);
|
|
1532
1691
|
}
|
|
1533
1692
|
}
|
|
1693
|
+
/** A rejected execution session must not be reused by the next foreground attempt. */
|
|
1694
|
+
invalidateRejectedWithdrawalSession(error, state, chainId, authToken) {
|
|
1695
|
+
const message = error instanceof Error ? error.message : "";
|
|
1696
|
+
if (!/(?:^|: )Unauthorized: Invalid API key$/.test(message)) return;
|
|
1697
|
+
const internals = this.sdk;
|
|
1698
|
+
if (typeof authToken !== "string" || internals.httpClient.authToken !== authToken || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== chainId) return;
|
|
1699
|
+
clearSession(state.walletAddress, chainId);
|
|
1700
|
+
internals.httpClient.clearAuthToken();
|
|
1701
|
+
internals.authenticatedUserId = null;
|
|
1702
|
+
this.connectedChainId = null;
|
|
1703
|
+
}
|
|
1534
1704
|
/**
|
|
1535
1705
|
* Sync newly-available protocols onto an existing user's profile at wallet
|
|
1536
1706
|
* connection. Zyfai's own protocol sync (`updateUserProtocols`) runs only when
|
|
@@ -2076,7 +2246,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2076
2246
|
await this.ensureSessionKey(ownerAddress, chainId, redeployed.address);
|
|
2077
2247
|
return redeployed.address;
|
|
2078
2248
|
}
|
|
2079
|
-
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
2249
|
+
async deposit(state, chainId, amount, asset, depositCallback, onProgress) {
|
|
2080
2250
|
try {
|
|
2081
2251
|
const validChainId = isValidChainId(chainId);
|
|
2082
2252
|
await this.ensureConnected(state, validChainId);
|
|
@@ -2085,17 +2255,48 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2085
2255
|
this.getAddress(),
|
|
2086
2256
|
validChainId
|
|
2087
2257
|
);
|
|
2258
|
+
notifyFunding(onProgress, {
|
|
2259
|
+
agentId: this.id,
|
|
2260
|
+
chainId,
|
|
2261
|
+
token: asset,
|
|
2262
|
+
walletAddress: state.walletAddress,
|
|
2263
|
+
agentWallet: smartWallet,
|
|
2264
|
+
intendedAmount: amount,
|
|
2265
|
+
transfer: "pending",
|
|
2266
|
+
registration: "pending",
|
|
2267
|
+
delivery: "not-applicable"
|
|
2268
|
+
});
|
|
2088
2269
|
const txHash = await depositCallback(smartWallet, validChainId, amount);
|
|
2089
|
-
|
|
2270
|
+
let evidence2 = {
|
|
2271
|
+
agentId: this.id,
|
|
2272
|
+
chainId,
|
|
2273
|
+
token: asset,
|
|
2274
|
+
walletAddress: state.walletAddress,
|
|
2275
|
+
agentWallet: smartWallet,
|
|
2276
|
+
intendedAmount: amount,
|
|
2277
|
+
txHash,
|
|
2278
|
+
transfer: "pending",
|
|
2279
|
+
registration: "pending",
|
|
2280
|
+
delivery: "not-applicable"
|
|
2281
|
+
};
|
|
2282
|
+
notifyFunding(onProgress, evidence2);
|
|
2090
2283
|
try {
|
|
2284
|
+
const receipt = await this.getPublicClient(validChainId).getTransactionReceipt({ hash: txHash });
|
|
2285
|
+
const token = SupportedAssets.find((token2) => token2.chainId === chainId && token2.symbol === asset);
|
|
2286
|
+
evidence2 = inspectFundingReceipt(receipt, evidence2, token.address, "deposit");
|
|
2287
|
+
} catch {
|
|
2288
|
+
evidence2 = { ...evidence2, transfer: "unknown" };
|
|
2289
|
+
}
|
|
2290
|
+
notifyFunding(onProgress, evidence2);
|
|
2291
|
+
try {
|
|
2292
|
+
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
2091
2293
|
await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
logError
|
|
2096
|
-
);
|
|
2294
|
+
evidence2 = { ...evidence2, registration: "confirmed" };
|
|
2295
|
+
} catch {
|
|
2296
|
+
evidence2 = { ...evidence2, registration: "unknown" };
|
|
2097
2297
|
}
|
|
2098
|
-
|
|
2298
|
+
notifyFunding(onProgress, evidence2);
|
|
2299
|
+
return { txHash, smartWallet, amount, evidence: evidence2 };
|
|
2099
2300
|
}
|
|
2100
2301
|
await this.ensureWalletDeployed(this.getAddress(), validChainId);
|
|
2101
2302
|
const raw2 = await this.sdk.depositFunds(
|
|
@@ -2105,22 +2306,88 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2105
2306
|
asset,
|
|
2106
2307
|
"aggressive"
|
|
2107
2308
|
);
|
|
2108
|
-
|
|
2309
|
+
let evidence = {
|
|
2310
|
+
agentId: this.id,
|
|
2311
|
+
chainId,
|
|
2312
|
+
token: asset,
|
|
2313
|
+
walletAddress: state.walletAddress,
|
|
2314
|
+
agentWallet: raw2.smartWallet,
|
|
2315
|
+
intendedAmount: amount,
|
|
2316
|
+
txHash: raw2.txHash,
|
|
2317
|
+
transfer: "unknown",
|
|
2318
|
+
registration: "unknown",
|
|
2319
|
+
delivery: "not-applicable"
|
|
2320
|
+
};
|
|
2321
|
+
notifyFunding(onProgress, evidence);
|
|
2322
|
+
try {
|
|
2323
|
+
const receipt = await this.getPublicClient(validChainId).getTransactionReceipt({ hash: raw2.txHash });
|
|
2324
|
+
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
2325
|
+
evidence = inspectFundingReceipt(receipt, evidence, token.address, "deposit");
|
|
2326
|
+
} catch {
|
|
2327
|
+
}
|
|
2328
|
+
notifyFunding(onProgress, evidence);
|
|
2329
|
+
return { ...mapDeposit(raw2), evidence };
|
|
2109
2330
|
} catch (error) {
|
|
2110
2331
|
throw error;
|
|
2111
2332
|
}
|
|
2112
2333
|
}
|
|
2113
2334
|
// --- IAgent: Fund operations ---
|
|
2114
|
-
async withdraw(state, chainId, token, amount) {
|
|
2335
|
+
async withdraw(state, chainId, token, amount, onProgress) {
|
|
2115
2336
|
const validChainId = isValidChainId(chainId);
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2337
|
+
try {
|
|
2338
|
+
await this.ensureConnected(state, validChainId);
|
|
2339
|
+
} catch (error) {
|
|
2340
|
+
notifyFunding(onProgress, {
|
|
2341
|
+
agentId: this.id,
|
|
2342
|
+
chainId,
|
|
2343
|
+
token,
|
|
2344
|
+
walletAddress: state.walletAddress,
|
|
2345
|
+
intendedAmount: amount ?? "MAX",
|
|
2346
|
+
transfer: "failed",
|
|
2347
|
+
registration: "not-applicable",
|
|
2348
|
+
delivery: "failed"
|
|
2349
|
+
});
|
|
2350
|
+
throw error;
|
|
2351
|
+
}
|
|
2352
|
+
const observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
|
|
2353
|
+
const authToken = this.sdk.httpClient?.authToken;
|
|
2354
|
+
let raw2;
|
|
2355
|
+
try {
|
|
2356
|
+
raw2 = await this.sdk.withdrawFunds(this.getAddress(), validChainId, amount, token);
|
|
2357
|
+
} catch (error) {
|
|
2358
|
+
this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken);
|
|
2359
|
+
if (observation.attempted() !== void 0) {
|
|
2360
|
+
const failed = observation.attempted() === false || observation.rejected();
|
|
2361
|
+
notifyFunding(onProgress, {
|
|
2362
|
+
agentId: this.id,
|
|
2363
|
+
chainId,
|
|
2364
|
+
token,
|
|
2365
|
+
walletAddress: state.walletAddress,
|
|
2366
|
+
intendedAmount: amount ?? "MAX",
|
|
2367
|
+
transfer: failed ? "failed" : "unknown",
|
|
2368
|
+
registration: "not-applicable",
|
|
2369
|
+
delivery: failed ? "failed" : "unknown",
|
|
2370
|
+
withdrawalRequest: observation.request()
|
|
2371
|
+
});
|
|
2372
|
+
}
|
|
2373
|
+
throw error;
|
|
2374
|
+
} finally {
|
|
2375
|
+
observation.stop();
|
|
2376
|
+
}
|
|
2123
2377
|
if (!raw2.success) {
|
|
2378
|
+
const unresolved = !!raw2.txHash || /already|pending|queued|in.progress/i.test(raw2.message ?? "");
|
|
2379
|
+
notifyFunding(onProgress, {
|
|
2380
|
+
agentId: this.id,
|
|
2381
|
+
chainId,
|
|
2382
|
+
token,
|
|
2383
|
+
walletAddress: state.walletAddress,
|
|
2384
|
+
intendedAmount: amount ?? "MAX",
|
|
2385
|
+
txHash: raw2.txHash,
|
|
2386
|
+
transfer: unresolved ? "unknown" : "failed",
|
|
2387
|
+
registration: "not-applicable",
|
|
2388
|
+
delivery: unresolved ? "unknown" : "failed",
|
|
2389
|
+
withdrawalRequest: observation.request()
|
|
2390
|
+
});
|
|
2124
2391
|
throw new OwneyError(
|
|
2125
2392
|
"WITHDRAW_FAILED",
|
|
2126
2393
|
raw2.message || "Zyfai withdraw failed.",
|
|
@@ -2128,13 +2395,45 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2128
2395
|
this.id
|
|
2129
2396
|
);
|
|
2130
2397
|
}
|
|
2131
|
-
|
|
2398
|
+
const evidence = {
|
|
2399
|
+
agentId: this.id,
|
|
2400
|
+
chainId,
|
|
2401
|
+
token,
|
|
2402
|
+
walletAddress: state.walletAddress,
|
|
2403
|
+
intendedAmount: amount ?? "MAX",
|
|
2404
|
+
txHash: raw2.txHash,
|
|
2405
|
+
transfer: "pending",
|
|
2406
|
+
registration: "not-applicable",
|
|
2407
|
+
delivery: "pending",
|
|
2408
|
+
withdrawalRequest: observation.request()
|
|
2409
|
+
};
|
|
2410
|
+
notifyFunding(onProgress, evidence);
|
|
2411
|
+
return { ...mapWithdraw(raw2), evidence };
|
|
2412
|
+
}
|
|
2413
|
+
/** Registration recovery never signs, transfers, activates, or refreshes authentication. */
|
|
2414
|
+
async retryDepositRegistration(state, evidence) {
|
|
2415
|
+
const internals = this.sdk;
|
|
2416
|
+
if (!internals.httpClient.authToken || internals.authenticatedUserId === null || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== evidence.chainId) {
|
|
2417
|
+
throw new OwneyError("NOT_CONNECTED", "Reconnect the wallet before recovering deposit registration.");
|
|
2418
|
+
}
|
|
2419
|
+
if (evidence.transfer !== "confirmed" || !evidence.txHash) {
|
|
2420
|
+
throw new OwneyError("AGENT_API_ERROR", "Deposit transfer must be verified before registration recovery.");
|
|
2421
|
+
}
|
|
2422
|
+
const cid = isValidChainId(evidence.chainId);
|
|
2423
|
+
const tokenAddress = evidence.token === "WETH" ? WETH_ADDRESS_BY_CHAIN[cid] : void 0;
|
|
2424
|
+
await this.sdk.logDeposit(cid, evidence.txHash, evidence.intendedAmount, tokenAddress);
|
|
2425
|
+
return { ...evidence, registration: "confirmed" };
|
|
2132
2426
|
}
|
|
2133
2427
|
// --- IAgent: Portfolio reads ---
|
|
2428
|
+
async getFundingWallet(state, chainId) {
|
|
2429
|
+
return (await this.sdk.getSmartWalletAddress(state.walletAddress, isValidChainId(chainId))).address;
|
|
2430
|
+
}
|
|
2134
2431
|
async getBalances(state, chainId) {
|
|
2432
|
+
const requestedAt = Date.now();
|
|
2135
2433
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
2136
2434
|
const raw2 = await this.sdk.getPortfolio(this.getAddress());
|
|
2137
|
-
|
|
2435
|
+
const balance = mapBalances(raw2, validChainId, smartWallet);
|
|
2436
|
+
return { ...balance, readMetadata: balance.readMetadata?.map((read2) => ({ ...read2, requestedAt })) };
|
|
2138
2437
|
}
|
|
2139
2438
|
earningsKey(state, chainId, smartWallet) {
|
|
2140
2439
|
return JSON.stringify([
|
|
@@ -2207,7 +2506,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2207
2506
|
/**
|
|
2208
2507
|
* Owney speaks asset symbols ("USDC" / "WETH"); Zyfai's history endpoint
|
|
2209
2508
|
* takes lowercase `assetType` and denominates WETH as "eth" (the same
|
|
2210
|
-
* spelling `
|
|
2509
|
+
* spelling `resolveFundingTransfers` normalizes on the way in). Anything
|
|
2211
2510
|
* unrecognised returns undefined so the request stays unfiltered rather
|
|
2212
2511
|
* than silently narrowing to nothing.
|
|
2213
2512
|
*/
|
|
@@ -2226,6 +2525,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2226
2525
|
}
|
|
2227
2526
|
async getHistory(state, chainId, options) {
|
|
2228
2527
|
const { smartWallet, chainId: validChainId } = await this.resolveSmartWallet(state, chainId);
|
|
2528
|
+
return this.readHistory(state, smartWallet, validChainId, options);
|
|
2529
|
+
}
|
|
2530
|
+
/** Recovery reads never enter connectAuth or wallet activation. */
|
|
2531
|
+
async getFundingHistory(state, chainId, options) {
|
|
2532
|
+
const smartWallet = await this.getFundingWallet(state, chainId);
|
|
2533
|
+
return this.readHistory(state, smartWallet, isValidChainId(chainId), options);
|
|
2534
|
+
}
|
|
2535
|
+
async readHistory(state, smartWallet, validChainId, options) {
|
|
2229
2536
|
const limit = options?.limit ?? 10;
|
|
2230
2537
|
const assetType = _ZyfaiAgent.toZyfaiAssetType(options?.tokenSymbol);
|
|
2231
2538
|
const cursor = options?.cursor ? decodeHistoryCursor(options.cursor) : null;
|
|
@@ -2267,9 +2574,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2267
2574
|
rawOffset: firstOvershoot ? firstOvershoot.rawIdx : offset
|
|
2268
2575
|
}) : void 0;
|
|
2269
2576
|
const data = mapEntries(pageData, validChainId);
|
|
2270
|
-
await this.
|
|
2577
|
+
await this.resolveFundingTransfers(data, validChainId, state.walletAddress, smartWallet, options?.tokenSymbol);
|
|
2578
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2579
|
+
const unique = data.filter((entry) => {
|
|
2580
|
+
if (!entry.id || !entry.transactions[0]?.transferLogs?.length) return true;
|
|
2581
|
+
if (seen.has(entry.id)) return false;
|
|
2582
|
+
seen.add(entry.id);
|
|
2583
|
+
return true;
|
|
2584
|
+
});
|
|
2271
2585
|
return {
|
|
2272
|
-
data,
|
|
2586
|
+
data: unique,
|
|
2273
2587
|
hasMore,
|
|
2274
2588
|
nextCursor
|
|
2275
2589
|
};
|
|
@@ -2318,7 +2632,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2318
2632
|
};
|
|
2319
2633
|
|
|
2320
2634
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2321
|
-
var
|
|
2635
|
+
var import_viem8 = require("viem");
|
|
2322
2636
|
var import_chains3 = require("viem/chains");
|
|
2323
2637
|
|
|
2324
2638
|
// src/lib/chain-guard.ts
|
|
@@ -2357,7 +2671,7 @@ async function ensureWalletOnChain(pub, wallet, expected) {
|
|
|
2357
2671
|
}
|
|
2358
2672
|
|
|
2359
2673
|
// src/lib/transfer-auth.ts
|
|
2360
|
-
var
|
|
2674
|
+
var import_viem4 = require("viem");
|
|
2361
2675
|
|
|
2362
2676
|
// src/lib/sponsor-client.ts
|
|
2363
2677
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
@@ -2473,7 +2787,7 @@ async function postSponsorBatchTransfer(input) {
|
|
|
2473
2787
|
}
|
|
2474
2788
|
|
|
2475
2789
|
// src/lib/permit2.ts
|
|
2476
|
-
var
|
|
2790
|
+
var import_viem5 = require("viem");
|
|
2477
2791
|
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2478
2792
|
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2479
2793
|
function permit2ApprovalAmount(requiredAmount) {
|
|
@@ -2514,7 +2828,7 @@ var ERC20_ALLOWANCE_ABI = [
|
|
|
2514
2828
|
function randomPermit2Nonce() {
|
|
2515
2829
|
const bytes = new Uint8Array(32);
|
|
2516
2830
|
globalThis.crypto.getRandomValues(bytes);
|
|
2517
|
-
return BigInt((0,
|
|
2831
|
+
return BigInt((0, import_viem5.bytesToHex)(bytes));
|
|
2518
2832
|
}
|
|
2519
2833
|
async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
|
|
2520
2834
|
return publicClient.readContract({
|
|
@@ -2557,7 +2871,7 @@ function makeVerificationAwareDepositCallback(implementation) {
|
|
|
2557
2871
|
|
|
2558
2872
|
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2559
2873
|
var import_siwe = require("siwe");
|
|
2560
|
-
var
|
|
2874
|
+
var import_viem6 = require("viem");
|
|
2561
2875
|
var import_chains2 = require("viem/chains");
|
|
2562
2876
|
|
|
2563
2877
|
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
@@ -2670,7 +2984,7 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
|
2670
2984
|
return new import_siwe.SiweMessage({
|
|
2671
2985
|
scheme: url.protocol.slice(0, -1),
|
|
2672
2986
|
domain: url.host,
|
|
2673
|
-
address: (0,
|
|
2987
|
+
address: (0, import_viem6.getAddress)(address),
|
|
2674
2988
|
uri: url.origin,
|
|
2675
2989
|
version: "1",
|
|
2676
2990
|
chainId,
|
|
@@ -2756,15 +3070,15 @@ var YieldseekerAuth = class {
|
|
|
2756
3070
|
clearYieldseekerSession(state.walletAddress, chainId);
|
|
2757
3071
|
}
|
|
2758
3072
|
async sign(state, chainId) {
|
|
2759
|
-
const account = (0,
|
|
2760
|
-
const publicClient = (0,
|
|
3073
|
+
const account = (0, import_viem6.getAddress)(state.walletAddress);
|
|
3074
|
+
const publicClient = (0, import_viem6.createPublicClient)({
|
|
2761
3075
|
chain: import_chains2.base,
|
|
2762
|
-
transport: (0,
|
|
3076
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
2763
3077
|
});
|
|
2764
|
-
const walletClient = (0,
|
|
3078
|
+
const walletClient = (0, import_viem6.createWalletClient)({
|
|
2765
3079
|
account,
|
|
2766
3080
|
chain: import_chains2.base,
|
|
2767
|
-
transport: (0,
|
|
3081
|
+
transport: (0, import_viem6.custom)(state.provider)
|
|
2768
3082
|
});
|
|
2769
3083
|
await ensureWalletOnChain(
|
|
2770
3084
|
publicClient,
|
|
@@ -2931,7 +3245,7 @@ var YieldseekerApiClient = class {
|
|
|
2931
3245
|
};
|
|
2932
3246
|
|
|
2933
3247
|
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
2934
|
-
var
|
|
3248
|
+
var import_viem7 = require("viem");
|
|
2935
3249
|
|
|
2936
3250
|
// src/lib/helpers/snapshot-apy.ts
|
|
2937
3251
|
var DAY_MS = 864e5;
|
|
@@ -2998,10 +3312,10 @@ function raw(value, endpoint) {
|
|
|
2998
3312
|
return BigInt(value);
|
|
2999
3313
|
}
|
|
3000
3314
|
function decimal(value, decimals, endpoint) {
|
|
3001
|
-
return (0,
|
|
3315
|
+
return (0, import_viem7.formatUnits)(raw(value, endpoint), decimals);
|
|
3002
3316
|
}
|
|
3003
3317
|
function usd(rawAmount, decimals, price) {
|
|
3004
|
-
return Number((0,
|
|
3318
|
+
return Number((0, import_viem7.formatUnits)(rawAmount, decimals)) * price;
|
|
3005
3319
|
}
|
|
3006
3320
|
function percent(value) {
|
|
3007
3321
|
const result = Number(value);
|
|
@@ -3027,7 +3341,7 @@ function assetAddressValue(record, address) {
|
|
|
3027
3341
|
}
|
|
3028
3342
|
function position(value, asset, baseAssetDecimals) {
|
|
3029
3343
|
const option = value?.yieldOption;
|
|
3030
|
-
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0,
|
|
3344
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem7.isAddress)(option.address)) {
|
|
3031
3345
|
return invalid("yield positions", "missing vault metadata");
|
|
3032
3346
|
}
|
|
3033
3347
|
return {
|
|
@@ -3097,7 +3411,17 @@ function mapYieldseekerBalances(contexts) {
|
|
|
3097
3411
|
totalBalanceAsset: "usdc",
|
|
3098
3412
|
assetBalances,
|
|
3099
3413
|
tokens,
|
|
3100
|
-
positions
|
|
3414
|
+
positions,
|
|
3415
|
+
readMetadata: contexts.map((context) => ({
|
|
3416
|
+
chainId: context.agent.chainId,
|
|
3417
|
+
asset: context.asset,
|
|
3418
|
+
fetchedAt: context.fetchedAt,
|
|
3419
|
+
readVersion: context.readVersion,
|
|
3420
|
+
providerUpdatedAt: context.snapshot.updatedDate,
|
|
3421
|
+
snapshotId: String(context.snapshot.agentWalletSnapshotId),
|
|
3422
|
+
netDepositsRaw: context.snapshot.netDepositsBase,
|
|
3423
|
+
positionsStatus: context.positionsUnavailable ? "unavailable" : "available"
|
|
3424
|
+
}))
|
|
3101
3425
|
};
|
|
3102
3426
|
}
|
|
3103
3427
|
function mapYieldseekerEarnings(contexts) {
|
|
@@ -3109,7 +3433,7 @@ function mapYieldseekerEarnings(contexts) {
|
|
|
3109
3433
|
chain: "BASE",
|
|
3110
3434
|
chainId: 8453,
|
|
3111
3435
|
asset: context.asset,
|
|
3112
|
-
amount: (0,
|
|
3436
|
+
amount: (0, import_viem7.formatUnits)(amount, context.snapshot.baseAssetDecimals)
|
|
3113
3437
|
});
|
|
3114
3438
|
lifetimeEarnings += usd(
|
|
3115
3439
|
amount,
|
|
@@ -3211,6 +3535,7 @@ function transactionHashes(details) {
|
|
|
3211
3535
|
function actionEntry(action) {
|
|
3212
3536
|
if (action.actionType.toLowerCase().includes("withdraw")) return void 0;
|
|
3213
3537
|
return {
|
|
3538
|
+
id: `yieldseeker:action:${action.agentActionId}`,
|
|
3214
3539
|
agent: "yieldseeker",
|
|
3215
3540
|
action: actionType(action.actionType),
|
|
3216
3541
|
date: action.createdDate,
|
|
@@ -3240,7 +3565,7 @@ function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddres
|
|
|
3240
3565
|
const owner = ownerAddress.toLowerCase();
|
|
3241
3566
|
const agentWallet = wallet.walletAddress.toLowerCase();
|
|
3242
3567
|
const baseAsset = agent.assetAddress.toLowerCase();
|
|
3243
|
-
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset) {
|
|
3568
|
+
if (movement.chainId !== agent.chainId || movement.assetAddress.toLowerCase() !== baseAsset || raw(movement.assetAmount, "historic position") <= 0n) {
|
|
3244
3569
|
return void 0;
|
|
3245
3570
|
}
|
|
3246
3571
|
let action;
|
|
@@ -3265,6 +3590,12 @@ function movementEntry(movement, wallet, agent, asset, ownerAddress, vaultAddres
|
|
|
3265
3590
|
transactions: [
|
|
3266
3591
|
{
|
|
3267
3592
|
txHashes: [movement.transactionHash],
|
|
3593
|
+
tokenAddress: movement.assetAddress,
|
|
3594
|
+
agentWallet: wallet.walletAddress,
|
|
3595
|
+
logIndexes: [movement.logIndex],
|
|
3596
|
+
transferLogs: [{ logIndex: movement.logIndex, amountRaw: movement.assetAmount }],
|
|
3597
|
+
amountRaw: movement.assetAmount,
|
|
3598
|
+
blockNumber: String(movement.blockNumber),
|
|
3268
3599
|
chainId: agent.chainId,
|
|
3269
3600
|
tokenSymbol: asset,
|
|
3270
3601
|
amount: decimal(
|
|
@@ -3310,7 +3641,8 @@ function mapYieldseekerHistory(contexts, options) {
|
|
|
3310
3641
|
ungrouped.push(entry);
|
|
3311
3642
|
continue;
|
|
3312
3643
|
}
|
|
3313
|
-
const key2 = `${entry.agent}:${entry.action}:${tx.chainId}:${hash.toLowerCase()}`;
|
|
3644
|
+
const key2 = `${entry.agent}:${entry.action}:${tx.chainId}:${hash.toLowerCase()}:${tx.tokenAddress?.toLowerCase() ?? ""}:${tx.agentWallet?.toLowerCase() ?? ""}`;
|
|
3645
|
+
entry.id = key2;
|
|
3314
3646
|
const previous = grouped.get(key2);
|
|
3315
3647
|
if (!previous) {
|
|
3316
3648
|
grouped.set(key2, entry);
|
|
@@ -3323,14 +3655,52 @@ function mapYieldseekerHistory(contexts, options) {
|
|
|
3323
3655
|
}
|
|
3324
3656
|
previous.positions.push(...entry.positions);
|
|
3325
3657
|
previous.transactions.push(...entry.transactions);
|
|
3658
|
+
} else if ((entry.action === "Top up" || entry.action === "Withdraw") && tx.amountRaw) {
|
|
3659
|
+
const target = previous.transactions[0];
|
|
3660
|
+
const known = new Set(target.logIndexes ?? []);
|
|
3661
|
+
if (tx.logIndexes?.some((index) => !known.has(index))) {
|
|
3662
|
+
target.amountRaw = String(BigInt(target.amountRaw ?? "0") + BigInt(tx.amountRaw));
|
|
3663
|
+
target.amount = (0, import_viem7.formatUnits)(
|
|
3664
|
+
BigInt(target.amountRaw),
|
|
3665
|
+
YIELDSEEKER_ASSET_METADATA[target.tokenSymbol].decimals
|
|
3666
|
+
);
|
|
3667
|
+
target.logIndexes = [...known, ...tx.logIndexes.filter((index) => !known.has(index))];
|
|
3668
|
+
target.transferLogs = [...target.transferLogs ?? [], ...(tx.transferLogs ?? []).filter((log) => !known.has(log.logIndex))];
|
|
3669
|
+
}
|
|
3326
3670
|
}
|
|
3327
3671
|
}
|
|
3328
|
-
const
|
|
3672
|
+
const values = [...grouped.values(), ...ungrouped].filter((entry) => {
|
|
3673
|
+
const tx = entry.transactions[0];
|
|
3674
|
+
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());
|
|
3675
|
+
});
|
|
3676
|
+
let boundary;
|
|
3677
|
+
const scope = JSON.stringify([
|
|
3678
|
+
options.ownerAddress.toLowerCase(),
|
|
3679
|
+
contexts.map((context) => `${context.agent.chainId}:${context.wallet.walletAddress.toLowerCase()}:${context.asset}`).sort(),
|
|
3680
|
+
options.fromDate ?? null
|
|
3681
|
+
]);
|
|
3682
|
+
if (options.cursor) {
|
|
3683
|
+
try {
|
|
3684
|
+
boundary = JSON.parse(decodeURIComponent(atob(options.cursor)));
|
|
3685
|
+
if (!boundary || boundary.scope !== scope || typeof boundary.id !== "string" || !Number.isFinite(Date.parse(boundary.date)) || !Number.isFinite(Date.parse(boundary.toDate))) throw new Error();
|
|
3686
|
+
} catch {
|
|
3687
|
+
throw new InvalidHistoryCursorError("Invalid Yieldseeker history cursor.");
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
const toDate = boundary?.toDate ?? options.toDate ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3691
|
+
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 ?? ""));
|
|
3692
|
+
const data = filtered.slice(0, Math.max(1, options.limit));
|
|
3693
|
+
const hasMore = filtered.length > data.length;
|
|
3694
|
+
const last = data.at(-1);
|
|
3329
3695
|
return {
|
|
3330
|
-
data
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3696
|
+
data,
|
|
3697
|
+
hasMore,
|
|
3698
|
+
...hasMore && last ? { nextCursor: btoa(encodeURIComponent(JSON.stringify({
|
|
3699
|
+
date: last.date,
|
|
3700
|
+
id: last.id ?? "",
|
|
3701
|
+
toDate,
|
|
3702
|
+
scope
|
|
3703
|
+
}))) } : {}
|
|
3334
3704
|
};
|
|
3335
3705
|
}
|
|
3336
3706
|
function mapYieldseekerProfile(address, contexts) {
|
|
@@ -3505,7 +3875,7 @@ var YieldseekerAgent = class {
|
|
|
3505
3875
|
this.assertAsset(targetAsset);
|
|
3506
3876
|
await this.ensureAgent(state, chainId, targetAsset);
|
|
3507
3877
|
}
|
|
3508
|
-
async deposit(state, chainId, amount, asset, depositCallback) {
|
|
3878
|
+
async deposit(state, chainId, amount, asset, depositCallback, onProgress) {
|
|
3509
3879
|
this.assertChain(chainId);
|
|
3510
3880
|
this.assertAsset(asset);
|
|
3511
3881
|
if (BigInt(amount) <= 0n) {
|
|
@@ -3519,7 +3889,20 @@ var YieldseekerAgent = class {
|
|
|
3519
3889
|
const context = await this.ensureAgent(state, chainId, asset);
|
|
3520
3890
|
const generation = this.readGeneration;
|
|
3521
3891
|
const movement = this.snapshotMovement(state, chainId, context, BigInt(amount));
|
|
3892
|
+
notifyFunding(onProgress, {
|
|
3893
|
+
agentId: this.id,
|
|
3894
|
+
chainId,
|
|
3895
|
+
token: asset,
|
|
3896
|
+
walletAddress: state.walletAddress,
|
|
3897
|
+
agentWallet: context.wallet.walletAddress,
|
|
3898
|
+
intendedAmount: amount,
|
|
3899
|
+
transfer: "pending",
|
|
3900
|
+
registration: "not-applicable",
|
|
3901
|
+
delivery: "not-applicable"
|
|
3902
|
+
});
|
|
3522
3903
|
let txHash;
|
|
3904
|
+
let receipt;
|
|
3905
|
+
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" });
|
|
3523
3906
|
if (depositCallback) {
|
|
3524
3907
|
provideDepositVerificationContext(depositCallback, {
|
|
3525
3908
|
agentId: "yieldseeker",
|
|
@@ -3532,30 +3915,57 @@ var YieldseekerAgent = class {
|
|
|
3532
3915
|
chainId,
|
|
3533
3916
|
amount
|
|
3534
3917
|
);
|
|
3535
|
-
|
|
3918
|
+
submitted(txHash);
|
|
3919
|
+
receipt = await this.waitForReceipt(state, chainId, txHash);
|
|
3536
3920
|
} else {
|
|
3537
3921
|
txHash = await this.submitTransaction(state, chainId, {
|
|
3538
|
-
from: (0,
|
|
3922
|
+
from: (0, import_viem8.getAddress)(state.walletAddress),
|
|
3539
3923
|
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3540
|
-
data: (0,
|
|
3541
|
-
abi:
|
|
3924
|
+
data: (0, import_viem8.encodeFunctionData)({
|
|
3925
|
+
abi: import_viem8.erc20Abi,
|
|
3542
3926
|
functionName: "transfer",
|
|
3543
|
-
args: [(0,
|
|
3927
|
+
args: [(0, import_viem8.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
3544
3928
|
}),
|
|
3545
3929
|
value: "0",
|
|
3546
3930
|
chainId
|
|
3931
|
+
}, submitted, (value) => {
|
|
3932
|
+
receipt = value;
|
|
3547
3933
|
});
|
|
3548
3934
|
}
|
|
3549
3935
|
if (this.readGeneration === generation) {
|
|
3550
3936
|
void this.refreshSnapshotAfterMovement(state, chainId, context, "deposit", movement);
|
|
3551
3937
|
}
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3938
|
+
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" };
|
|
3939
|
+
const evidence = receipt ? inspectFundingReceipt(receipt, base5, YIELDSEEKER_ASSET_METADATA[asset].address, "deposit") : base5;
|
|
3940
|
+
notifyFunding(onProgress, evidence);
|
|
3941
|
+
return { txHash, smartWallet: context.wallet.walletAddress, amount, evidence };
|
|
3942
|
+
}
|
|
3943
|
+
async withdraw(state, chainId, asset, amount, onProgress) {
|
|
3944
|
+
let ownerTransferAttempted = false;
|
|
3945
|
+
let evidence = {
|
|
3946
|
+
agentId: this.id,
|
|
3947
|
+
chainId,
|
|
3948
|
+
token: asset,
|
|
3949
|
+
walletAddress: state.walletAddress,
|
|
3950
|
+
intendedAmount: amount ?? "MAX",
|
|
3951
|
+
transfer: "pending",
|
|
3952
|
+
registration: "not-applicable",
|
|
3953
|
+
delivery: "pending"
|
|
3556
3954
|
};
|
|
3955
|
+
try {
|
|
3956
|
+
return await this.withdrawInternal(state, chainId, asset, amount, (next) => {
|
|
3957
|
+
evidence = { ...evidence, ...next };
|
|
3958
|
+
notifyFunding(onProgress, evidence);
|
|
3959
|
+
}, () => {
|
|
3960
|
+
ownerTransferAttempted = true;
|
|
3961
|
+
});
|
|
3962
|
+
} catch (error) {
|
|
3963
|
+
if (!ownerTransferAttempted || error instanceof OwneyError && error.code === "AGENT_TRANSACTION_REVERTED")
|
|
3964
|
+
notifyFunding(onProgress, { ...evidence, transfer: "failed", delivery: "failed" });
|
|
3965
|
+
throw error;
|
|
3966
|
+
}
|
|
3557
3967
|
}
|
|
3558
|
-
async
|
|
3968
|
+
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted) {
|
|
3559
3969
|
this.assertChain(chainId);
|
|
3560
3970
|
this.assertAsset(asset);
|
|
3561
3971
|
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
@@ -3585,17 +3995,32 @@ var YieldseekerAgent = class {
|
|
|
3585
3995
|
context
|
|
3586
3996
|
);
|
|
3587
3997
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
3588
|
-
const
|
|
3589
|
-
(
|
|
3590
|
-
|
|
3591
|
-
|
|
3998
|
+
const readIdle = async () => {
|
|
3999
|
+
const balance = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
4000
|
+
address: metadata.address,
|
|
4001
|
+
abi: import_viem8.erc20Abi,
|
|
4002
|
+
functionName: "balanceOf",
|
|
4003
|
+
args: [(0, import_viem8.getAddress)(context.wallet.walletAddress)]
|
|
4004
|
+
}), chainId, "eth_call", this.id);
|
|
4005
|
+
this.assertSession(generation, chainId, asset);
|
|
4006
|
+
return balance;
|
|
4007
|
+
};
|
|
4008
|
+
const idle = await readIdle();
|
|
3592
4009
|
const deployed = portfolio.positions.reduce(
|
|
3593
4010
|
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3594
4011
|
0n
|
|
3595
4012
|
);
|
|
3596
4013
|
const totalAvailable = idle + deployed;
|
|
3597
4014
|
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3598
|
-
|
|
4015
|
+
if (requested === 0n) {
|
|
4016
|
+
this.invalidateFundingRead(state, chainId, asset);
|
|
4017
|
+
throw new OwneyError(
|
|
4018
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4019
|
+
"No Yieldseeker funds are available to withdraw.",
|
|
4020
|
+
{ asset, available: "0" },
|
|
4021
|
+
this.id
|
|
4022
|
+
);
|
|
4023
|
+
}
|
|
3599
4024
|
if (requested > totalAvailable) {
|
|
3600
4025
|
throw new OwneyError(
|
|
3601
4026
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
@@ -3640,29 +4065,51 @@ var YieldseekerAgent = class {
|
|
|
3640
4065
|
remaining: remaining.toString()
|
|
3641
4066
|
});
|
|
3642
4067
|
}
|
|
3643
|
-
const
|
|
4068
|
+
const availableForOwner = confirmedMovement ? await readIdle() : idle;
|
|
4069
|
+
if (availableForOwner === 0n || amount !== void 0 && availableForOwner < requested) {
|
|
4070
|
+
this.invalidateFundingRead(state, chainId, asset);
|
|
4071
|
+
throw new OwneyError(
|
|
4072
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4073
|
+
"Yieldseeker funds are not available for the owner transfer.",
|
|
4074
|
+
{ asset, requested: amount ?? "MAX", available: availableForOwner.toString() },
|
|
4075
|
+
this.id
|
|
4076
|
+
);
|
|
4077
|
+
}
|
|
4078
|
+
const payout = amount === void 0 ? availableForOwner : requested;
|
|
4079
|
+
const plannedMovement = this.snapshotMovement(state, chainId, context, -payout);
|
|
4080
|
+
const account = (0, import_viem8.getAddress)(state.walletAddress);
|
|
4081
|
+
let receipt;
|
|
4082
|
+
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" };
|
|
4083
|
+
onOwnerTransferAttempted?.();
|
|
3644
4084
|
const txHash = await this.submitTransaction(state, chainId, {
|
|
3645
4085
|
from: account,
|
|
3646
|
-
to: (0,
|
|
3647
|
-
data: amount === void 0 ? (0,
|
|
4086
|
+
to: (0, import_viem8.getAddress)(context.wallet.walletAddress),
|
|
4087
|
+
data: amount === void 0 ? (0, import_viem8.encodeFunctionData)({
|
|
3648
4088
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3649
4089
|
functionName: "withdrawAllAssetToUser",
|
|
3650
4090
|
args: [account, metadata.address]
|
|
3651
|
-
}) : (0,
|
|
4091
|
+
}) : (0, import_viem8.encodeFunctionData)({
|
|
3652
4092
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
3653
4093
|
functionName: "withdrawAssetToUser",
|
|
3654
4094
|
args: [account, metadata.address, requested]
|
|
3655
4095
|
}),
|
|
3656
4096
|
value: "0",
|
|
3657
4097
|
chainId
|
|
4098
|
+
}, (hash) => notifyFunding(onProgress, { ...base5, txHash: hash }), (value) => {
|
|
4099
|
+
receipt = value;
|
|
3658
4100
|
});
|
|
3659
4101
|
confirmedMovement = true;
|
|
3660
4102
|
settlement = plannedMovement;
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
4103
|
+
const evidence = receipt ? inspectFundingReceipt(receipt, { ...base5, txHash }, metadata.address, "withdraw") : { ...base5, txHash, confirmedAmount: payout.toString(), transfer: "confirmed", delivery: "confirmed" };
|
|
4104
|
+
notifyFunding(onProgress, evidence);
|
|
4105
|
+
if (evidence.transfer === "failed" && evidence.confirmedAmount === "0")
|
|
4106
|
+
throw new OwneyError(
|
|
4107
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4108
|
+
"The Yieldseeker withdrawal completed without transferring funds.",
|
|
4109
|
+
{ asset, available: "0", txHash },
|
|
4110
|
+
this.id
|
|
4111
|
+
);
|
|
4112
|
+
return { txHash, type: amount === void 0 ? "full" : "partial", amount: evidence.confirmedAmount ?? payout.toString(), evidence };
|
|
3666
4113
|
} finally {
|
|
3667
4114
|
if (confirmedMovement && this.readGeneration === generation) {
|
|
3668
4115
|
void this.refreshSnapshotAfterMovement(
|
|
@@ -3675,9 +4122,39 @@ var YieldseekerAgent = class {
|
|
|
3675
4122
|
}
|
|
3676
4123
|
}
|
|
3677
4124
|
}
|
|
3678
|
-
async getBalances(state, chainId) {
|
|
4125
|
+
async getBalances(state, chainId, asset) {
|
|
4126
|
+
this.assertChain(chainId);
|
|
4127
|
+
if (asset !== void 0) this.assertAsset(asset);
|
|
4128
|
+
return mapYieldseekerBalances(await this.loadPortfolio(state, chainId, { asset, allowMissingPositions: true }));
|
|
4129
|
+
}
|
|
4130
|
+
invalidateFundingRead(state, chainId, asset, refreshUntil = Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS) {
|
|
4131
|
+
this.assertAsset(asset);
|
|
4132
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
4133
|
+
const failure = this.snapshotFailures.get(key2);
|
|
4134
|
+
this.advancePortfolioVersion(key2);
|
|
4135
|
+
if (failure && failure.retryAt > Date.now()) this.snapshotFailures.set(key2, failure);
|
|
4136
|
+
if (!this.snapshotMovements.has(key2)) this.snapshotMovements.set(key2, null);
|
|
4137
|
+
const until = Math.min(refreshUntil, Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS);
|
|
4138
|
+
this.reconcileUntil.set(key2, Math.max(this.reconcileUntil.get(key2) ?? 0, until));
|
|
4139
|
+
this.activityRefreshUntil.set(key2, Math.max(this.activityRefreshUntil.get(key2) ?? 0, until));
|
|
4140
|
+
}
|
|
4141
|
+
endFundingBalanceRefresh(state, chainId, asset, read2) {
|
|
3679
4142
|
this.assertChain(chainId);
|
|
3680
|
-
|
|
4143
|
+
this.assertAsset(asset);
|
|
4144
|
+
const key2 = this.contextKey(state, chainId, asset);
|
|
4145
|
+
const cached = this.readCache.get(`snapshot:${key2}`);
|
|
4146
|
+
const snapshot = cached?.value?.agentSnapshot;
|
|
4147
|
+
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;
|
|
4148
|
+
this.snapshotMovements.delete(key2);
|
|
4149
|
+
this.reconcileUntil.delete(key2);
|
|
4150
|
+
cached.expiresAt = cached.fetchedAt + YIELDSEEKER_PORTFOLIO_CACHE_MS;
|
|
4151
|
+
return true;
|
|
4152
|
+
}
|
|
4153
|
+
async getFundingWallet(state, chainId, asset) {
|
|
4154
|
+
this.assertAsset(asset);
|
|
4155
|
+
const context = this.agentContexts.get(this.contextKey(state, chainId, asset));
|
|
4156
|
+
if (!context) throw new OwneyError("NOT_CONNECTED", "Reconnect the agent before recovering funding.");
|
|
4157
|
+
return context.wallet.walletAddress;
|
|
3681
4158
|
}
|
|
3682
4159
|
async getEarnings(state, chainId) {
|
|
3683
4160
|
this.assertChain(chainId);
|
|
@@ -3709,11 +4186,12 @@ var YieldseekerAgent = class {
|
|
|
3709
4186
|
);
|
|
3710
4187
|
const vaultAddresses = new Set(
|
|
3711
4188
|
catalog.flat().filter(
|
|
3712
|
-
(yieldOption) => yieldOption.chainId === chainId && (0,
|
|
4189
|
+
(yieldOption) => yieldOption.chainId === chainId && (0, import_viem8.isAddress)(yieldOption.address)
|
|
3713
4190
|
).map((yieldOption) => yieldOption.address.toLowerCase())
|
|
3714
4191
|
);
|
|
3715
4192
|
return mapYieldseekerHistory(contexts, {
|
|
3716
4193
|
limit: options?.limit ?? 10,
|
|
4194
|
+
cursor: options?.cursor,
|
|
3717
4195
|
ownerAddress: state.walletAddress,
|
|
3718
4196
|
vaultAddresses,
|
|
3719
4197
|
...options?.fromDate ? { fromDate: options.fromDate } : {},
|
|
@@ -3788,6 +4266,7 @@ var YieldseekerAgent = class {
|
|
|
3788
4266
|
if (this.readGeneration === generation && this.pendingReads.get(key2) === request) {
|
|
3789
4267
|
this.readCache.set(key2, {
|
|
3790
4268
|
expiresAt: Date.now() + ttlMs,
|
|
4269
|
+
fetchedAt: Date.now(),
|
|
3791
4270
|
value
|
|
3792
4271
|
});
|
|
3793
4272
|
}
|
|
@@ -3834,7 +4313,7 @@ var YieldseekerAgent = class {
|
|
|
3834
4313
|
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
3835
4314
|
).then((walletResponse) => {
|
|
3836
4315
|
this.assertSession(generation, chainId, asset);
|
|
3837
|
-
if (!walletResponse?.agentWallet || !(0,
|
|
4316
|
+
if (!walletResponse?.agentWallet || !(0, import_viem8.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
3838
4317
|
throw this.invalidResponse("agent wallet");
|
|
3839
4318
|
}
|
|
3840
4319
|
const context = {
|
|
@@ -3864,7 +4343,7 @@ var YieldseekerAgent = class {
|
|
|
3864
4343
|
this.users.set(key2, persisted);
|
|
3865
4344
|
return persisted;
|
|
3866
4345
|
}
|
|
3867
|
-
const walletAddress = (0,
|
|
4346
|
+
const walletAddress = (0, import_viem8.getAddress)(state.walletAddress);
|
|
3868
4347
|
const generation = this.readGeneration;
|
|
3869
4348
|
let user = null;
|
|
3870
4349
|
try {
|
|
@@ -3988,6 +4467,7 @@ var YieldseekerAgent = class {
|
|
|
3988
4467
|
if (agent) {
|
|
3989
4468
|
this.readCache.set(this.agentListKey(state, chainId), {
|
|
3990
4469
|
expiresAt: Date.now() + YIELDSEEKER_AGENT_LIST_CACHE_MS,
|
|
4470
|
+
fetchedAt: Date.now(),
|
|
3991
4471
|
value: {
|
|
3992
4472
|
agents: [...agents, agent]
|
|
3993
4473
|
}
|
|
@@ -4048,7 +4528,6 @@ var YieldseekerAgent = class {
|
|
|
4048
4528
|
...previous,
|
|
4049
4529
|
epoch: this.movementEpochs.get(key2) ?? 0,
|
|
4050
4530
|
delta: previous.delta + delta,
|
|
4051
|
-
expectedBalance: previous.expectedBalance + delta,
|
|
4052
4531
|
expectedNetDeposits: previous.expectedNetDeposits + delta
|
|
4053
4532
|
};
|
|
4054
4533
|
}
|
|
@@ -4061,7 +4540,6 @@ var YieldseekerAgent = class {
|
|
|
4061
4540
|
epoch: this.movementEpochs.get(key2) ?? 0,
|
|
4062
4541
|
delta,
|
|
4063
4542
|
decimals: snapshot.baseAssetDecimals,
|
|
4064
|
-
expectedBalance: BigInt(snapshot.totalValueBase) + delta,
|
|
4065
4543
|
expectedNetDeposits: BigInt(snapshot.netDepositsBase) + delta
|
|
4066
4544
|
};
|
|
4067
4545
|
}
|
|
@@ -4090,9 +4568,8 @@ var YieldseekerAgent = class {
|
|
|
4090
4568
|
const movement = this.snapshotMovements.get(contextKey);
|
|
4091
4569
|
const snapshot = response.agentSnapshot;
|
|
4092
4570
|
if (movement && snapshot.baseAssetDecimals === movement.decimals && /^-?\d+$/.test(snapshot.netDepositsBase) && /^\d+$/.test(snapshot.totalValueBase)) {
|
|
4093
|
-
const balance = BigInt(snapshot.totalValueBase);
|
|
4094
4571
|
const netDeposits = BigInt(snapshot.netDepositsBase);
|
|
4095
|
-
const reconciled = movement.delta > 0n ?
|
|
4572
|
+
const reconciled = movement.delta > 0n ? netDeposits >= movement.expectedNetDeposits : netDeposits <= movement.expectedNetDeposits;
|
|
4096
4573
|
if (reconciled) {
|
|
4097
4574
|
this.snapshotMovements.delete(contextKey);
|
|
4098
4575
|
this.reconcileUntil.delete(contextKey);
|
|
@@ -4146,12 +4623,20 @@ var YieldseekerAgent = class {
|
|
|
4146
4623
|
const { version, read: read2 } = this.portfolioSnapshot(state, chainId, context, contextKey);
|
|
4147
4624
|
const parts = this.loadPortfolioParts(state, chainId, context, options, contextKey);
|
|
4148
4625
|
try {
|
|
4149
|
-
|
|
4626
|
+
let positionsUnavailable = false;
|
|
4627
|
+
const safeParts = options.allowMissingPositions ? parts.catch(() => {
|
|
4628
|
+
positionsUnavailable = true;
|
|
4629
|
+
return { positions: [] };
|
|
4630
|
+
}) : parts;
|
|
4631
|
+
const [snapshot, portfolioParts] = await Promise.all([read2, safeParts]);
|
|
4150
4632
|
assertSession();
|
|
4151
4633
|
if (this.portfolioVersion(contextKey) !== version) continue;
|
|
4152
4634
|
return {
|
|
4153
4635
|
...context,
|
|
4154
4636
|
snapshot: snapshot.agentSnapshot,
|
|
4637
|
+
fetchedAt: this.readCache.get(`snapshot:${contextKey}`)?.fetchedAt,
|
|
4638
|
+
readVersion: version,
|
|
4639
|
+
positionsUnavailable,
|
|
4155
4640
|
...portfolioParts
|
|
4156
4641
|
};
|
|
4157
4642
|
} catch (error) {
|
|
@@ -4241,7 +4726,7 @@ var YieldseekerAgent = class {
|
|
|
4241
4726
|
this.agentPath(context, "deploy"),
|
|
4242
4727
|
{ method: "POST", body: {} }
|
|
4243
4728
|
);
|
|
4244
|
-
if (!deployed?.agentWallet || !(0,
|
|
4729
|
+
if (!deployed?.agentWallet || !(0, import_viem8.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
|
|
4245
4730
|
throw this.invalidResponse("agent deployment", {
|
|
4246
4731
|
reason: "Deploy did not return the expected Agent Wallet."
|
|
4247
4732
|
});
|
|
@@ -4342,20 +4827,22 @@ var YieldseekerAgent = class {
|
|
|
4342
4827
|
this.id
|
|
4343
4828
|
);
|
|
4344
4829
|
}
|
|
4345
|
-
async submitTransaction(state, chainId, transaction) {
|
|
4830
|
+
async submitTransaction(state, chainId, transaction, onSubmitted, onReceipt) {
|
|
4346
4831
|
if (this.transactionExecutor) {
|
|
4347
|
-
|
|
4832
|
+
const hash2 = await this.transactionExecutor(state, chainId, transaction);
|
|
4833
|
+
onSubmitted?.(hash2);
|
|
4834
|
+
return hash2;
|
|
4348
4835
|
}
|
|
4349
4836
|
this.assertTransaction(transaction, state, chainId);
|
|
4350
|
-
const account = (0,
|
|
4351
|
-
const walletClient = (0,
|
|
4837
|
+
const account = (0, import_viem8.getAddress)(state.walletAddress);
|
|
4838
|
+
const walletClient = (0, import_viem8.createWalletClient)({
|
|
4352
4839
|
account,
|
|
4353
4840
|
chain: import_chains3.base,
|
|
4354
|
-
transport: (0,
|
|
4841
|
+
transport: (0, import_viem8.custom)(state.provider)
|
|
4355
4842
|
});
|
|
4356
|
-
const walletChainClient = (0,
|
|
4843
|
+
const walletChainClient = (0, import_viem8.createPublicClient)({
|
|
4357
4844
|
chain: import_chains3.base,
|
|
4358
|
-
transport: (0,
|
|
4845
|
+
transport: (0, import_viem8.custom)(state.provider)
|
|
4359
4846
|
});
|
|
4360
4847
|
await ensureWalletOnChain(
|
|
4361
4848
|
walletChainClient,
|
|
@@ -4365,10 +4852,11 @@ var YieldseekerAgent = class {
|
|
|
4365
4852
|
const hash = await walletClient.sendTransaction({
|
|
4366
4853
|
account,
|
|
4367
4854
|
chain: import_chains3.base,
|
|
4368
|
-
to: (0,
|
|
4855
|
+
to: (0, import_viem8.getAddress)(transaction.to),
|
|
4369
4856
|
data: transaction.data,
|
|
4370
4857
|
value: BigInt(transaction.value)
|
|
4371
4858
|
});
|
|
4859
|
+
onSubmitted?.(hash);
|
|
4372
4860
|
const receipt = await withPaidRpcDiagnostics(
|
|
4373
4861
|
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
4374
4862
|
hash,
|
|
@@ -4386,6 +4874,7 @@ var YieldseekerAgent = class {
|
|
|
4386
4874
|
this.id
|
|
4387
4875
|
);
|
|
4388
4876
|
}
|
|
4877
|
+
onReceipt?.(receipt);
|
|
4389
4878
|
return hash;
|
|
4390
4879
|
}
|
|
4391
4880
|
async waitForReceipt(state, chainId, transactionHash) {
|
|
@@ -4410,14 +4899,15 @@ var YieldseekerAgent = class {
|
|
|
4410
4899
|
this.id
|
|
4411
4900
|
);
|
|
4412
4901
|
}
|
|
4902
|
+
return receipt;
|
|
4413
4903
|
}
|
|
4414
4904
|
assertTransaction(transaction, state, chainId) {
|
|
4415
|
-
if (!transaction || typeof transaction.from !== "string" || !(0,
|
|
4905
|
+
if (!transaction || typeof transaction.from !== "string" || !(0, import_viem8.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem8.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem8.getAddress)(transaction.from) !== (0, import_viem8.getAddress)(state.walletAddress)) {
|
|
4416
4906
|
throw this.invalidResponse("transaction");
|
|
4417
4907
|
}
|
|
4418
4908
|
}
|
|
4419
4909
|
assertAgent(agent) {
|
|
4420
|
-
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0,
|
|
4910
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem8.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
|
|
4421
4911
|
throw this.invalidResponse("agent");
|
|
4422
4912
|
}
|
|
4423
4913
|
}
|
|
@@ -4570,7 +5060,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
|
4570
5060
|
}
|
|
4571
5061
|
|
|
4572
5062
|
// src/lib/helpers/withdraw-helper.ts
|
|
4573
|
-
var
|
|
5063
|
+
var import_viem9 = require("viem");
|
|
4574
5064
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
4575
5065
|
const target = asset.toUpperCase();
|
|
4576
5066
|
return agents.map((agent) => {
|
|
@@ -4578,7 +5068,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
4578
5068
|
const tokenBalance = agentBalance?.tokens.find(
|
|
4579
5069
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
4580
5070
|
);
|
|
4581
|
-
let balance = tokenBalance ? (0,
|
|
5071
|
+
let balance = tokenBalance ? (0, import_viem9.parseUnits)(tokenBalance.amount, decimals) : 0n;
|
|
4582
5072
|
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
4583
5073
|
const chainNameById = {
|
|
4584
5074
|
1: "ETHEREUM",
|
|
@@ -4590,14 +5080,14 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
4590
5080
|
const positionChain = position2.chain.trim().toUpperCase();
|
|
4591
5081
|
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
4592
5082
|
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
4593
|
-
balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0,
|
|
5083
|
+
balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0, import_viem9.parseUnits)(position2.amount, decimals);
|
|
4594
5084
|
}
|
|
4595
5085
|
}
|
|
4596
5086
|
const assetTotal = agentBalance?.assetBalances?.find(
|
|
4597
5087
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
4598
5088
|
);
|
|
4599
5089
|
if (assetTotal) {
|
|
4600
|
-
const total = (0,
|
|
5090
|
+
const total = (0, import_viem9.parseUnits)(assetTotal.amount, decimals);
|
|
4601
5091
|
if (total < balance) balance = total > 0n ? total : 0n;
|
|
4602
5092
|
}
|
|
4603
5093
|
return { agent, balance };
|
|
@@ -4756,17 +5246,16 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
4756
5246
|
}
|
|
4757
5247
|
|
|
4758
5248
|
// src/client.ts
|
|
4759
|
-
var
|
|
5249
|
+
var import_viem13 = require("viem");
|
|
4760
5250
|
var import_chains4 = require("viem/chains");
|
|
4761
5251
|
|
|
4762
5252
|
// src/lib/sponsored-token-batch.ts
|
|
4763
|
-
var
|
|
5253
|
+
var import_viem11 = require("viem");
|
|
4764
5254
|
|
|
4765
5255
|
// src/lib/permit2-batch.ts
|
|
4766
|
-
var
|
|
5256
|
+
var import_viem10 = require("viem");
|
|
4767
5257
|
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
4768
5258
|
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
4769
|
-
var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
4770
5259
|
var ERC2612_PERMIT_TYPES = {
|
|
4771
5260
|
Permit: [
|
|
4772
5261
|
{ name: "owner", type: "address" },
|
|
@@ -4776,7 +5265,7 @@ var ERC2612_PERMIT_TYPES = {
|
|
|
4776
5265
|
{ name: "deadline", type: "uint256" }
|
|
4777
5266
|
]
|
|
4778
5267
|
};
|
|
4779
|
-
var ERC2612_READ_ABI = (0,
|
|
5268
|
+
var ERC2612_READ_ABI = (0, import_viem10.parseAbi)([
|
|
4780
5269
|
"function name() view returns (string)",
|
|
4781
5270
|
"function version() view returns (string)",
|
|
4782
5271
|
"function nonces(address owner) view returns (uint256)"
|
|
@@ -4814,7 +5303,7 @@ var PERMIT_BATCH_TYPES = {
|
|
|
4814
5303
|
{ name: "amount", type: "uint256" }
|
|
4815
5304
|
]
|
|
4816
5305
|
};
|
|
4817
|
-
var PERMIT2_BATCH_ABI = (0,
|
|
5306
|
+
var PERMIT2_BATCH_ABI = (0, import_viem10.parseAbi)([
|
|
4818
5307
|
"struct TokenPermissions { address token; uint256 amount; }",
|
|
4819
5308
|
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
4820
5309
|
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
@@ -4873,7 +5362,7 @@ function clear(key2) {
|
|
|
4873
5362
|
else window.localStorage.removeItem(key2);
|
|
4874
5363
|
}
|
|
4875
5364
|
function sponsorTokenBatch(i) {
|
|
4876
|
-
const key2 = `owney.token-batch.v1:${(0,
|
|
5365
|
+
const key2 = `owney.token-batch.v1:${(0, import_viem11.keccak256)((0, import_viem11.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
|
|
4877
5366
|
const plan = planOf(i.transfers);
|
|
4878
5367
|
const active = inflight.get(key2);
|
|
4879
5368
|
if (active) {
|
|
@@ -4898,13 +5387,29 @@ async function execute(i, key2, plan) {
|
|
|
4898
5387
|
let body = initial;
|
|
4899
5388
|
save(key2, body);
|
|
4900
5389
|
try {
|
|
5390
|
+
if (body.erc2612Permit && !body.serializedPermitTransaction) {
|
|
5391
|
+
const preparedPermit = await postSponsorBatchTransfer({
|
|
5392
|
+
apiKey: i.apiKey,
|
|
5393
|
+
baseUrl: i.baseUrl,
|
|
5394
|
+
body
|
|
5395
|
+
});
|
|
5396
|
+
if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0, import_viem11.keccak256)(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
|
|
5397
|
+
throw new Error(
|
|
5398
|
+
"Sponsorship API did not return a valid prepared permit transaction."
|
|
5399
|
+
);
|
|
5400
|
+
body = {
|
|
5401
|
+
...body,
|
|
5402
|
+
serializedPermitTransaction: preparedPermit.serializedPermitTransaction
|
|
5403
|
+
};
|
|
5404
|
+
save(key2, body);
|
|
5405
|
+
}
|
|
4901
5406
|
if (!body.serializedTransaction) {
|
|
4902
5407
|
const prepared = await postSponsorBatchTransfer({
|
|
4903
5408
|
apiKey: i.apiKey,
|
|
4904
5409
|
baseUrl: i.baseUrl,
|
|
4905
5410
|
body
|
|
4906
5411
|
});
|
|
4907
|
-
if (!prepared.serializedTransaction || (0,
|
|
5412
|
+
if (!prepared.serializedTransaction || (0, import_viem11.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
|
|
4908
5413
|
throw new Error(
|
|
4909
5414
|
"Sponsorship API did not return a valid prepared transaction."
|
|
4910
5415
|
);
|
|
@@ -4914,12 +5419,16 @@ async function execute(i, key2, plan) {
|
|
|
4914
5419
|
};
|
|
4915
5420
|
save(key2, body);
|
|
4916
5421
|
}
|
|
5422
|
+
try {
|
|
5423
|
+
i.onSubmitted?.((0, import_viem11.keccak256)(body.serializedTransaction));
|
|
5424
|
+
} catch {
|
|
5425
|
+
}
|
|
4917
5426
|
const result = await postSponsorBatchTransfer({
|
|
4918
5427
|
apiKey: i.apiKey,
|
|
4919
5428
|
baseUrl: i.baseUrl,
|
|
4920
5429
|
body
|
|
4921
5430
|
});
|
|
4922
|
-
if (result.txHash !== (0,
|
|
5431
|
+
if (result.txHash !== (0, import_viem11.keccak256)(body.serializedTransaction))
|
|
4923
5432
|
throw new Error(
|
|
4924
5433
|
"Sponsorship receipt does not match the pending transaction."
|
|
4925
5434
|
);
|
|
@@ -4934,7 +5443,7 @@ async function execute(i, key2, plan) {
|
|
|
4934
5443
|
const saved = read(key2);
|
|
4935
5444
|
if (saved) {
|
|
4936
5445
|
const previous = JSON.parse(saved);
|
|
4937
|
-
if (previous.chainId !== i.chainId || !(0,
|
|
5446
|
+
if (previous.chainId !== i.chainId || !(0, import_viem11.isAddressEqual)(previous.from, i.owner) || !(0, import_viem11.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
4938
5447
|
throw new Error(
|
|
4939
5448
|
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
4940
5449
|
);
|
|
@@ -4951,7 +5460,7 @@ async function execute(i, key2, plan) {
|
|
|
4951
5460
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
4952
5461
|
"Insufficient token balance for this deposit."
|
|
4953
5462
|
);
|
|
4954
|
-
if (allowance < total && (i.chainId !== 8453 || !(0,
|
|
5463
|
+
if (allowance < total && (i.chainId !== 8453 || !(0, import_viem11.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
|
|
4955
5464
|
throw new OwneyError(
|
|
4956
5465
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
4957
5466
|
"token deposits need a one-time Permit2 approval."
|
|
@@ -4995,7 +5504,7 @@ async function execute(i, key2, plan) {
|
|
|
4995
5504
|
});
|
|
4996
5505
|
erc2612Permit = { ...unsignedPermit, signature: signature2 };
|
|
4997
5506
|
}
|
|
4998
|
-
const spender =
|
|
5507
|
+
const spender = await getSponsorRelayerAddress({
|
|
4999
5508
|
apiKey: i.apiKey,
|
|
5000
5509
|
baseUrl: i.baseUrl,
|
|
5001
5510
|
chainId: i.chainId
|
|
@@ -5042,7 +5551,8 @@ function makeSponsoredTokenCallback(deps) {
|
|
|
5042
5551
|
transfers,
|
|
5043
5552
|
pub,
|
|
5044
5553
|
wallet,
|
|
5045
|
-
onApproved: deps.onApproved
|
|
5554
|
+
onApproved: deps.onApproved,
|
|
5555
|
+
onSubmitted: (hash) => deps.onSubmitted?.(hash, transfers)
|
|
5046
5556
|
});
|
|
5047
5557
|
};
|
|
5048
5558
|
const callback = makeVerificationAwareDepositCallback(
|
|
@@ -5128,7 +5638,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
|
5128
5638
|
}
|
|
5129
5639
|
|
|
5130
5640
|
// src/lib/sponsored-calls-deposit.ts
|
|
5131
|
-
var
|
|
5641
|
+
var import_viem12 = require("viem");
|
|
5132
5642
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
5133
5643
|
var DEFAULT_MAX_POLLS = 30;
|
|
5134
5644
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -5136,7 +5646,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
5136
5646
|
method: "wallet_getCapabilities",
|
|
5137
5647
|
params: [owner]
|
|
5138
5648
|
});
|
|
5139
|
-
const forChain = caps?.[(0,
|
|
5649
|
+
const forChain = caps?.[(0, import_viem12.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
5140
5650
|
return Boolean(forChain?.paymasterService?.supported);
|
|
5141
5651
|
}
|
|
5142
5652
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -5173,8 +5683,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
5173
5683
|
const calls = transfers.map((transfer) => ({
|
|
5174
5684
|
to: token,
|
|
5175
5685
|
value: "0x0",
|
|
5176
|
-
data: (0,
|
|
5177
|
-
abi:
|
|
5686
|
+
data: (0, import_viem12.encodeFunctionData)({
|
|
5687
|
+
abi: import_viem12.erc20Abi,
|
|
5178
5688
|
functionName: "transfer",
|
|
5179
5689
|
args: [transfer.to, BigInt(transfer.amount)]
|
|
5180
5690
|
})
|
|
@@ -5212,7 +5722,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
5212
5722
|
{
|
|
5213
5723
|
version: "2.0.0",
|
|
5214
5724
|
from: deps.ownerAddress,
|
|
5215
|
-
chainId: (0,
|
|
5725
|
+
chainId: (0, import_viem12.toHex)(chainId),
|
|
5216
5726
|
atomicRequired: transfers.length > 1,
|
|
5217
5727
|
calls,
|
|
5218
5728
|
capabilities: {
|
|
@@ -5236,6 +5746,12 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
5236
5746
|
params: [callsId]
|
|
5237
5747
|
});
|
|
5238
5748
|
const txHash = status?.receipts?.[0]?.transactionHash;
|
|
5749
|
+
if (txHash && !status?.receipts?.some((receipt) => receipt.transactionHash !== txHash)) {
|
|
5750
|
+
try {
|
|
5751
|
+
deps.onSubmitted?.(txHash, transfers);
|
|
5752
|
+
} catch {
|
|
5753
|
+
}
|
|
5754
|
+
}
|
|
5239
5755
|
if (status?.receipts?.some((receipt) => receipt.status === "0x0") || typeof status?.status === "number" && status.status >= 400) {
|
|
5240
5756
|
throw new OwneyError(
|
|
5241
5757
|
"SPONSOR_REQUEST_FAILED",
|
|
@@ -5345,6 +5861,7 @@ var OwneySDK = class {
|
|
|
5345
5861
|
paymasterServiceUrl;
|
|
5346
5862
|
cachedSponsoredCallsCallbacks = /* @__PURE__ */ new Map();
|
|
5347
5863
|
agentReads = new AgentReads();
|
|
5864
|
+
recoveredFundingReads = /* @__PURE__ */ new Set();
|
|
5348
5865
|
activationChecks = /* @__PURE__ */ new Map();
|
|
5349
5866
|
readAgent(agent, method, fetch2, params = null) {
|
|
5350
5867
|
const key2 = JSON.stringify([
|
|
@@ -5458,8 +5975,8 @@ var OwneySDK = class {
|
|
|
5458
5975
|
return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
|
|
5459
5976
|
}
|
|
5460
5977
|
/** Builds the default USDC batch callback for the connected wallet. */
|
|
5461
|
-
getDefaultSponsoredCallback(onApproved) {
|
|
5462
|
-
if (!onApproved && this.cachedSponsoredCallback)
|
|
5978
|
+
getDefaultSponsoredCallback(onApproved, onSubmitted) {
|
|
5979
|
+
if (!onApproved && !onSubmitted && this.cachedSponsoredCallback)
|
|
5463
5980
|
return this.cachedSponsoredCallback;
|
|
5464
5981
|
const provider = this.requireConnectedProvider();
|
|
5465
5982
|
const owner = this.state.walletAddress;
|
|
@@ -5468,28 +5985,29 @@ var OwneySDK = class {
|
|
|
5468
5985
|
baseUrl: this.routingApiBaseUrl,
|
|
5469
5986
|
ownerAddress: owner,
|
|
5470
5987
|
onApproved,
|
|
5988
|
+
onSubmitted,
|
|
5471
5989
|
tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
|
|
5472
5990
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
5473
5991
|
// PublicClient/WalletClient param types — structurally identical at
|
|
5474
5992
|
// runtime, but the two share a name TS treats as unrelated.
|
|
5475
5993
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
5476
|
-
getWalletChainClient: (cid) => (0,
|
|
5994
|
+
getWalletChainClient: (cid) => (0, import_viem13.createPublicClient)({
|
|
5477
5995
|
chain: VIEM_CHAIN2[cid],
|
|
5478
|
-
transport: (0,
|
|
5996
|
+
transport: (0, import_viem13.custom)(provider)
|
|
5479
5997
|
}),
|
|
5480
|
-
getWalletClient: (cid) => (0,
|
|
5998
|
+
getWalletClient: (cid) => (0, import_viem13.createWalletClient)({
|
|
5481
5999
|
account: owner,
|
|
5482
6000
|
chain: VIEM_CHAIN2[cid],
|
|
5483
|
-
transport: (0,
|
|
6001
|
+
transport: (0, import_viem13.custom)(provider)
|
|
5484
6002
|
})
|
|
5485
6003
|
});
|
|
5486
|
-
if (!onApproved) this.cachedSponsoredCallback = callback;
|
|
6004
|
+
if (!onApproved && !onSubmitted) this.cachedSponsoredCallback = callback;
|
|
5487
6005
|
return callback;
|
|
5488
6006
|
}
|
|
5489
6007
|
/** Builds the wallet-native sponsored calls callback for compatible paymasters. */
|
|
5490
|
-
getDefaultSponsoredCallsCallback(asset, onApproved) {
|
|
6008
|
+
getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted) {
|
|
5491
6009
|
const cached = this.cachedSponsoredCallsCallbacks.get(asset);
|
|
5492
|
-
if (!onApproved && cached) return cached;
|
|
6010
|
+
if (!onApproved && !onSubmitted && cached) return cached;
|
|
5493
6011
|
const provider = this.requireConnectedProvider();
|
|
5494
6012
|
const callback = makeSponsoredCallsCallback({
|
|
5495
6013
|
apiKey: this.apiKey,
|
|
@@ -5498,9 +6016,10 @@ var OwneySDK = class {
|
|
|
5498
6016
|
ownerAddress: this.state.walletAddress,
|
|
5499
6017
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
5500
6018
|
onApproved,
|
|
6019
|
+
onSubmitted,
|
|
5501
6020
|
tokenAddressByChain: sponsoredTokensFor(asset)
|
|
5502
6021
|
});
|
|
5503
|
-
if (!onApproved) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
6022
|
+
if (!onApproved && !onSubmitted) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
5504
6023
|
return callback;
|
|
5505
6024
|
}
|
|
5506
6025
|
/**
|
|
@@ -5509,8 +6028,8 @@ var OwneySDK = class {
|
|
|
5509
6028
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
5510
6029
|
* single-use batch authorization instead of an EIP-3009 authorization.
|
|
5511
6030
|
*/
|
|
5512
|
-
getDefaultWethSponsoredCallback(onApproved) {
|
|
5513
|
-
if (!onApproved && this.cachedWethSponsoredCallback)
|
|
6031
|
+
getDefaultWethSponsoredCallback(onApproved, onSubmitted) {
|
|
6032
|
+
if (!onApproved && !onSubmitted && this.cachedWethSponsoredCallback)
|
|
5514
6033
|
return this.cachedWethSponsoredCallback;
|
|
5515
6034
|
const provider = this.requireConnectedProvider();
|
|
5516
6035
|
const owner = this.state.walletAddress;
|
|
@@ -5519,22 +6038,23 @@ var OwneySDK = class {
|
|
|
5519
6038
|
baseUrl: this.routingApiBaseUrl,
|
|
5520
6039
|
ownerAddress: owner,
|
|
5521
6040
|
onApproved,
|
|
6041
|
+
onSubmitted,
|
|
5522
6042
|
tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
|
|
5523
6043
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
5524
6044
|
// PublicClient/WalletClient param types — structurally identical at
|
|
5525
6045
|
// runtime, but the two share a name TS treats as unrelated.
|
|
5526
6046
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
5527
|
-
getWalletChainClient: (cid) => (0,
|
|
6047
|
+
getWalletChainClient: (cid) => (0, import_viem13.createPublicClient)({
|
|
5528
6048
|
chain: VIEM_CHAIN2[cid],
|
|
5529
|
-
transport: (0,
|
|
6049
|
+
transport: (0, import_viem13.custom)(provider)
|
|
5530
6050
|
}),
|
|
5531
|
-
getWalletClient: (cid) => (0,
|
|
6051
|
+
getWalletClient: (cid) => (0, import_viem13.createWalletClient)({
|
|
5532
6052
|
account: owner,
|
|
5533
6053
|
chain: VIEM_CHAIN2[cid],
|
|
5534
|
-
transport: (0,
|
|
6054
|
+
transport: (0, import_viem13.custom)(provider)
|
|
5535
6055
|
})
|
|
5536
6056
|
});
|
|
5537
|
-
if (!onApproved) this.cachedWethSponsoredCallback = callback;
|
|
6057
|
+
if (!onApproved && !onSubmitted) this.cachedWethSponsoredCallback = callback;
|
|
5538
6058
|
return callback;
|
|
5539
6059
|
}
|
|
5540
6060
|
getAgent(agentId) {
|
|
@@ -5544,6 +6064,9 @@ var OwneySDK = class {
|
|
|
5544
6064
|
}
|
|
5545
6065
|
return agent;
|
|
5546
6066
|
}
|
|
6067
|
+
getActiveAgentIds() {
|
|
6068
|
+
return [...this.getActiveAgents().keys()].sort();
|
|
6069
|
+
}
|
|
5547
6070
|
getActiveAgents() {
|
|
5548
6071
|
const active = /* @__PURE__ */ new Map();
|
|
5549
6072
|
for (const id of this.activeAgents) {
|
|
@@ -5838,12 +6361,23 @@ var OwneySDK = class {
|
|
|
5838
6361
|
*/
|
|
5839
6362
|
async deposit(options) {
|
|
5840
6363
|
const { amount, asset, depositCallback, agentId, onApproved } = options;
|
|
6364
|
+
const prepared = /* @__PURE__ */ new Map();
|
|
6365
|
+
const onProgress = options.onProgress ? (evidence) => {
|
|
6366
|
+
if (evidence.agentWallet) prepared.set(evidence.agentWallet.toLowerCase(), evidence);
|
|
6367
|
+
notifyFunding(options.onProgress, evidence);
|
|
6368
|
+
} : void 0;
|
|
6369
|
+
const onSubmitted = onProgress ? (hash, destinations) => {
|
|
6370
|
+
for (const destination of destinations) {
|
|
6371
|
+
const evidence = prepared.get(destination.to.toLowerCase());
|
|
6372
|
+
if (evidence) notifyFunding(onProgress, { ...evidence, txHash: hash, transfer: "pending" });
|
|
6373
|
+
}
|
|
6374
|
+
} : void 0;
|
|
5841
6375
|
if (BigInt(amount) === 0n) {
|
|
5842
6376
|
return { agentResults: {} };
|
|
5843
6377
|
}
|
|
5844
6378
|
const state = this.requireState();
|
|
5845
6379
|
const chainId = this.requireChainId();
|
|
5846
|
-
const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved) : this.getDefaultSponsoredCallback(onApproved));
|
|
6380
|
+
const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved, onSubmitted) : this.getDefaultSponsoredCallback(onApproved, onSubmitted));
|
|
5847
6381
|
if (agentId) {
|
|
5848
6382
|
if (this.isAgentDisabled(agentId)) {
|
|
5849
6383
|
throw new OwneyError(
|
|
@@ -5862,7 +6396,8 @@ var OwneySDK = class {
|
|
|
5862
6396
|
amount,
|
|
5863
6397
|
asset,
|
|
5864
6398
|
effectiveCallback,
|
|
5865
|
-
depositCallback
|
|
6399
|
+
depositCallback,
|
|
6400
|
+
onProgress
|
|
5866
6401
|
);
|
|
5867
6402
|
}
|
|
5868
6403
|
const totalAmount = BigInt(amount);
|
|
@@ -5926,7 +6461,7 @@ var OwneySDK = class {
|
|
|
5926
6461
|
run: (callback) => withFailureReporting(
|
|
5927
6462
|
this.apiKey,
|
|
5928
6463
|
agent.id,
|
|
5929
|
-
() => agent.deposit(state, chainId, amount2, asset, callback),
|
|
6464
|
+
() => onProgress ? agent.deposit(state, chainId, amount2, asset, callback, onProgress) : agent.deposit(state, chainId, amount2, asset, callback),
|
|
5930
6465
|
this.routingApiBaseUrl
|
|
5931
6466
|
)
|
|
5932
6467
|
})),
|
|
@@ -5963,7 +6498,8 @@ var OwneySDK = class {
|
|
|
5963
6498
|
agentAmount,
|
|
5964
6499
|
asset,
|
|
5965
6500
|
effectiveCallback,
|
|
5966
|
-
depositCallback
|
|
6501
|
+
depositCallback,
|
|
6502
|
+
onProgress
|
|
5967
6503
|
);
|
|
5968
6504
|
} catch (error) {
|
|
5969
6505
|
if (Object.keys(agentResults).length === 0) throw error;
|
|
@@ -6010,17 +6546,11 @@ var OwneySDK = class {
|
|
|
6010
6546
|
* propagates unchanged. Scoped per agent invocation so one agent's failure
|
|
6011
6547
|
* doesn't force a retry of sibling agents in a multi-agent split.
|
|
6012
6548
|
*/
|
|
6013
|
-
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback) {
|
|
6549
|
+
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress) {
|
|
6014
6550
|
const attempt = (callback) => withFailureReporting(
|
|
6015
6551
|
this.apiKey,
|
|
6016
6552
|
agent.id,
|
|
6017
|
-
() => agent.deposit(
|
|
6018
|
-
state,
|
|
6019
|
-
chainId,
|
|
6020
|
-
amount,
|
|
6021
|
-
asset,
|
|
6022
|
-
callback
|
|
6023
|
-
),
|
|
6553
|
+
() => onProgress ? agent.deposit(state, chainId, amount, asset, callback, onProgress) : agent.deposit(state, chainId, amount, asset, callback),
|
|
6024
6554
|
this.routingApiBaseUrl
|
|
6025
6555
|
);
|
|
6026
6556
|
let approvalAttempted = false;
|
|
@@ -6200,15 +6730,81 @@ var OwneySDK = class {
|
|
|
6200
6730
|
while (current && typeof current === "object" && !seen.has(current)) {
|
|
6201
6731
|
seen.add(current);
|
|
6202
6732
|
const candidate = current;
|
|
6203
|
-
if (candidate.code === 4001 || candidate.code === "4001") return true;
|
|
6733
|
+
if (candidate.code === 4001 || candidate.code === "4001" || candidate.code === "ACTION_REJECTED") return true;
|
|
6204
6734
|
const message = [candidate.message, candidate.shortMessage].filter((value) => typeof value === "string").join(" ");
|
|
6205
|
-
if (/user (?:rejected|denied)|rejected by user/i.test(message)) {
|
|
6735
|
+
if (/user (?:rejected|denied)|rejected by user|request rejected/i.test(message)) {
|
|
6206
6736
|
return true;
|
|
6207
6737
|
}
|
|
6208
6738
|
current = candidate.cause;
|
|
6209
6739
|
}
|
|
6210
6740
|
return false;
|
|
6211
6741
|
}
|
|
6742
|
+
captureReadScope(requestedChain) {
|
|
6743
|
+
const original = this.requireState();
|
|
6744
|
+
const chainId = this.requireChainId();
|
|
6745
|
+
if (requestedChain !== void 0 && requestedChain !== chainId) {
|
|
6746
|
+
throw new OwneyError("NO_ACTIVE_CHAIN", "Account read scope changed. Please retry.");
|
|
6747
|
+
}
|
|
6748
|
+
const wallet = original.walletAddress;
|
|
6749
|
+
return { state: { ...original }, chainId, assertCurrent: () => {
|
|
6750
|
+
if (this.state !== original || this.state.walletAddress !== wallet || this.state.chainId !== chainId)
|
|
6751
|
+
throw new OwneyError("NOT_CONNECTED", "Account read scope changed. Please retry.");
|
|
6752
|
+
} };
|
|
6753
|
+
}
|
|
6754
|
+
/** Revalidate a saved transfer against the authenticated account and actual receipt. */
|
|
6755
|
+
async inspectFunding(evidence, direction, options = {}) {
|
|
6756
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
6757
|
+
if (state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase() || !evidence.txHash)
|
|
6758
|
+
throw new OwneyError("AGENT_API_ERROR", "Funding evidence does not match the connected account.");
|
|
6759
|
+
const agent = this.getAgent(evidence.agentId);
|
|
6760
|
+
const verifiedWallet = agent.getFundingWallet ? await agent.getFundingWallet(state, chainId, evidence.token) : (await agent.getUserProfile(state, chainId)).smartWallet;
|
|
6761
|
+
const agentWallet = evidence.agentWallet ?? verifiedWallet;
|
|
6762
|
+
if (!agentWallet || agentWallet.toLowerCase() !== verifiedWallet.toLowerCase())
|
|
6763
|
+
throw new OwneyError("AGENT_API_ERROR", "Funding wallet does not match this account.");
|
|
6764
|
+
const token = SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token);
|
|
6765
|
+
if (!token) throw new OwneyError("CHAIN_UNSUPPORTED", "Unsupported funding asset.");
|
|
6766
|
+
const rpc = this.getPaidRpcClient(chainId);
|
|
6767
|
+
const receipt = await rpc.getTransactionReceipt({ hash: evidence.txHash });
|
|
6768
|
+
assertCurrent();
|
|
6769
|
+
let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction);
|
|
6770
|
+
if (direction === "withdraw" && evidence.agentId === "yieldseeker" && verified.transfer === "unknown" && verified.confirmedAmount && BigInt(verified.confirmedAmount) > 0n) {
|
|
6771
|
+
const transaction = await rpc.getTransaction({ hash: evidence.txHash });
|
|
6772
|
+
assertCurrent();
|
|
6773
|
+
if (isFullYieldseekerWithdrawal(transaction, { ...evidence, agentWallet }, token.address))
|
|
6774
|
+
verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet, intendedAmount: "MAX" }, token.address, direction);
|
|
6775
|
+
}
|
|
6776
|
+
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${evidence.agentId}:${evidence.token}:${evidence.txHash}`;
|
|
6777
|
+
if ((verified.transfer === "confirmed" || verified.transfer === "failed" && verified.confirmedAmount === "0") && !this.recoveredFundingReads.has(key2)) {
|
|
6778
|
+
if (options.refreshBalance !== false)
|
|
6779
|
+
agent.invalidateFundingRead?.(state, chainId, evidence.token, options.balanceRefreshUntil);
|
|
6780
|
+
this.recoveredFundingReads.add(key2);
|
|
6781
|
+
if (this.recoveredFundingReads.size > 256) this.recoveredFundingReads.delete(this.recoveredFundingReads.values().next().value);
|
|
6782
|
+
}
|
|
6783
|
+
return verified;
|
|
6784
|
+
}
|
|
6785
|
+
/** A display acknowledgement controls polling only; it never confirms a financial outcome. */
|
|
6786
|
+
endFundingBalanceRefresh(evidence, read2) {
|
|
6787
|
+
const { state, chainId } = this.captureReadScope(evidence.chainId);
|
|
6788
|
+
if (state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase() || read2.chainId !== chainId || read2.asset.toUpperCase() !== evidence.token) return false;
|
|
6789
|
+
return this.getAgent(evidence.agentId).endFundingBalanceRefresh?.(state, chainId, evidence.token, read2) ?? false;
|
|
6790
|
+
}
|
|
6791
|
+
/** History lookup for receipt recovery; adapters may not authenticate or sign here. */
|
|
6792
|
+
async getFundingHistory(evidence, fromDate, options) {
|
|
6793
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
6794
|
+
if (state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase())
|
|
6795
|
+
throw new OwneyError("NOT_CONNECTED", "Funding account changed.");
|
|
6796
|
+
const agent = this.getAgent(evidence.agentId);
|
|
6797
|
+
if (!agent.getFundingHistory) return { data: [], hasMore: false };
|
|
6798
|
+
const filters = { tokenSymbol: evidence.token, fromDate, limit: 50, ...options };
|
|
6799
|
+
const page = await this.readAgent(agent, "history", () => agent.getFundingHistory(state, chainId, filters), filters);
|
|
6800
|
+
assertCurrent();
|
|
6801
|
+
return page;
|
|
6802
|
+
}
|
|
6803
|
+
async recoverDepositRegistration(evidence) {
|
|
6804
|
+
const verified = await this.inspectFunding(evidence, "deposit");
|
|
6805
|
+
if (verified.agentId !== "zyfai") return verified;
|
|
6806
|
+
return this.getAgent("zyfai").retryDepositRegistration(this.requireState(), verified);
|
|
6807
|
+
}
|
|
6212
6808
|
// --- Fund operations ---
|
|
6213
6809
|
/**
|
|
6214
6810
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
@@ -6220,17 +6816,66 @@ var OwneySDK = class {
|
|
|
6220
6816
|
* @returns {AgentWithdrawResult} for a single agent, or {OwneyWithdrawResult} with per-agent results
|
|
6221
6817
|
*/
|
|
6222
6818
|
async withdraw(options) {
|
|
6819
|
+
const state = this.requireState();
|
|
6820
|
+
const chainId = this.requireChainId();
|
|
6821
|
+
const ids = options.agentId ? [options.agentId] : this.getActiveAgentIds().filter((id) => !options.excludeAgentIds?.includes(id));
|
|
6822
|
+
const attempted = /* @__PURE__ */ new Set();
|
|
6823
|
+
try {
|
|
6824
|
+
return await this.withdrawFromAgents(options, state, chainId, attempted);
|
|
6825
|
+
} catch (error) {
|
|
6826
|
+
for (const id of ids) if (!attempted.has(id)) {
|
|
6827
|
+
notifyFunding(options.onProgress, {
|
|
6828
|
+
agentId: id,
|
|
6829
|
+
chainId,
|
|
6830
|
+
token: options.asset,
|
|
6831
|
+
walletAddress: state.walletAddress,
|
|
6832
|
+
intendedAmount: options.amount ?? "MAX",
|
|
6833
|
+
transfer: "failed",
|
|
6834
|
+
registration: "not-applicable",
|
|
6835
|
+
delivery: "failed"
|
|
6836
|
+
});
|
|
6837
|
+
}
|
|
6838
|
+
throw error;
|
|
6839
|
+
}
|
|
6840
|
+
}
|
|
6841
|
+
async withdrawFromAgents(options, state, chainId, attempted) {
|
|
6223
6842
|
const { asset, amount, agentId, onAgentResult } = options;
|
|
6224
6843
|
const notifyAgentResult = (id, result) => {
|
|
6225
6844
|
try {
|
|
6845
|
+
if (result.evidence) notifyFunding(options.onProgress, result.evidence);
|
|
6226
6846
|
onAgentResult?.(id, result);
|
|
6227
6847
|
} catch (error) {
|
|
6228
6848
|
console.warn("Withdrawal result observer failed:", error);
|
|
6229
6849
|
}
|
|
6230
6850
|
};
|
|
6231
|
-
const state = this.requireState();
|
|
6232
|
-
const chainId = this.requireChainId();
|
|
6233
6851
|
const token = asset;
|
|
6852
|
+
const submissionEvidence = /* @__PURE__ */ new Map();
|
|
6853
|
+
const submit = async (agent, requested2) => {
|
|
6854
|
+
attempted.add(agent.id);
|
|
6855
|
+
let evidence = {
|
|
6856
|
+
agentId: agent.id,
|
|
6857
|
+
chainId,
|
|
6858
|
+
token,
|
|
6859
|
+
walletAddress: state.walletAddress,
|
|
6860
|
+
intendedAmount: requested2 ?? "MAX",
|
|
6861
|
+
transfer: "pending",
|
|
6862
|
+
registration: "not-applicable",
|
|
6863
|
+
delivery: "pending"
|
|
6864
|
+
};
|
|
6865
|
+
const progress = (next) => {
|
|
6866
|
+
evidence = { ...evidence, ...next };
|
|
6867
|
+
submissionEvidence.set(agent.id, evidence);
|
|
6868
|
+
notifyFunding(options.onProgress, evidence);
|
|
6869
|
+
};
|
|
6870
|
+
progress(evidence);
|
|
6871
|
+
try {
|
|
6872
|
+
return await agent.withdraw(state, chainId, token, requested2, progress);
|
|
6873
|
+
} catch (error) {
|
|
6874
|
+
if (!evidence.txHash && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
6875
|
+
progress({ ...evidence, transfer: "failed", delivery: "failed" });
|
|
6876
|
+
throw error;
|
|
6877
|
+
}
|
|
6878
|
+
};
|
|
6234
6879
|
const assetInfo = SupportedAssets.find(
|
|
6235
6880
|
(a) => a.chainId === chainId && a.symbol === token
|
|
6236
6881
|
);
|
|
@@ -6246,23 +6891,24 @@ var OwneySDK = class {
|
|
|
6246
6891
|
const result = await withFailureReporting(
|
|
6247
6892
|
this.apiKey,
|
|
6248
6893
|
agent.id,
|
|
6249
|
-
() => agent
|
|
6894
|
+
() => submit(agent, amount),
|
|
6250
6895
|
this.routingApiBaseUrl
|
|
6251
6896
|
);
|
|
6252
6897
|
notifyAgentResult(agent.id, result);
|
|
6253
6898
|
return result;
|
|
6254
6899
|
}
|
|
6255
|
-
const eligibleAgents = this.getEligibleAgents(chainId, asset);
|
|
6900
|
+
const eligibleAgents = this.getEligibleAgents(chainId, asset).filter((agent) => !options.excludeAgentIds?.includes(agent.id));
|
|
6256
6901
|
const withdrawalAgents = this.orderAgentsForWithdrawal(eligibleAgents);
|
|
6257
6902
|
if (!amount) {
|
|
6258
6903
|
const results2 = {};
|
|
6259
6904
|
const agentErrors2 = {};
|
|
6260
6905
|
for (const agent of withdrawalAgents) {
|
|
6261
6906
|
try {
|
|
6262
|
-
results2[agent.id] = await agent
|
|
6907
|
+
results2[agent.id] = await submit(agent);
|
|
6263
6908
|
notifyAgentResult(agent.id, results2[agent.id]);
|
|
6264
6909
|
} catch (err) {
|
|
6265
6910
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
6911
|
+
if (err instanceof OwneyError && err.code === "WITHDRAW_INSUFFICIENT_BALANCE" && err.details?.available === "0" && submissionEvidence.get(agent.id)?.transfer === "failed") continue;
|
|
6266
6912
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
6267
6913
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
6268
6914
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
@@ -6281,6 +6927,13 @@ var OwneySDK = class {
|
|
|
6281
6927
|
{ asset, agentErrors: agentErrors2 }
|
|
6282
6928
|
);
|
|
6283
6929
|
}
|
|
6930
|
+
if (Object.keys(agentErrors2).length > 0) {
|
|
6931
|
+
throw new OwneyError(
|
|
6932
|
+
"WITHDRAW_PARTIAL_FAILURE",
|
|
6933
|
+
"Some agents could not complete the withdrawal.",
|
|
6934
|
+
{ asset, partialResults: results2, agentErrors: agentErrors2 }
|
|
6935
|
+
);
|
|
6936
|
+
}
|
|
6284
6937
|
const amountsKnown = Object.values(results2).every(
|
|
6285
6938
|
(result) => typeof result.amount === "string" && /^\d+$/.test(result.amount)
|
|
6286
6939
|
);
|
|
@@ -6290,7 +6943,7 @@ var OwneySDK = class {
|
|
|
6290
6943
|
};
|
|
6291
6944
|
}
|
|
6292
6945
|
const requested = BigInt(amount);
|
|
6293
|
-
const aggregated = await this.getBalances();
|
|
6946
|
+
const aggregated = await this.getBalances(void 0, { chainId, asset: token });
|
|
6294
6947
|
const unavailableAgents = eligibleAgents.filter(
|
|
6295
6948
|
(agent) => !(agent.id in aggregated.agentBalances)
|
|
6296
6949
|
);
|
|
@@ -6352,12 +7005,7 @@ var OwneySDK = class {
|
|
|
6352
7005
|
const p = orderedPlans[i];
|
|
6353
7006
|
if (p.planned === 0n) continue;
|
|
6354
7007
|
try {
|
|
6355
|
-
results[p.agent.id] = await p.agent.
|
|
6356
|
-
state,
|
|
6357
|
-
chainId,
|
|
6358
|
-
token,
|
|
6359
|
-
p.planned.toString()
|
|
6360
|
-
);
|
|
7008
|
+
results[p.agent.id] = await submit(p.agent, p.planned.toString());
|
|
6361
7009
|
notifyAgentResult(p.agent.id, results[p.agent.id]);
|
|
6362
7010
|
} catch (err) {
|
|
6363
7011
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
@@ -6372,7 +7020,8 @@ var OwneySDK = class {
|
|
|
6372
7020
|
);
|
|
6373
7021
|
const failedAmount = p.planned;
|
|
6374
7022
|
p.planned = 0n;
|
|
6375
|
-
|
|
7023
|
+
if (submissionEvidence.get(p.agent.id)?.transfer === "failed")
|
|
7024
|
+
redistributeShare(orderedPlans, i, failedAmount);
|
|
6376
7025
|
}
|
|
6377
7026
|
}
|
|
6378
7027
|
if (Object.keys(results).length === 0) {
|
|
@@ -6409,12 +7058,19 @@ var OwneySDK = class {
|
|
|
6409
7058
|
* @param agentId - Optional. Agent to query. Omit for aggregated balances.
|
|
6410
7059
|
* @returns {AgentBalance} for a single agent, or {OwneyBalances} with totalBalance and per-agent breakdown
|
|
6411
7060
|
*/
|
|
6412
|
-
async getBalances(agentId) {
|
|
6413
|
-
const state = this.
|
|
6414
|
-
const
|
|
7061
|
+
async getBalances(agentId, options) {
|
|
7062
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(options?.chainId);
|
|
7063
|
+
const asset = options?.asset;
|
|
7064
|
+
const readBalance = (agent) => this.readAgent(
|
|
7065
|
+
agent,
|
|
7066
|
+
"balances",
|
|
7067
|
+
() => asset ? agent.getBalances(state, chainId, asset) : agent.getBalances(state, chainId),
|
|
7068
|
+
asset ? { asset } : null
|
|
7069
|
+
);
|
|
6415
7070
|
if (agentId) {
|
|
6416
7071
|
const agent = this.getAgent(agentId);
|
|
6417
|
-
const result = await
|
|
7072
|
+
const result = await readBalance(agent);
|
|
7073
|
+
assertCurrent();
|
|
6418
7074
|
return {
|
|
6419
7075
|
...result,
|
|
6420
7076
|
balanceComposition: agent.balanceComposition ?? "tokens-include-positions"
|
|
@@ -6425,7 +7081,7 @@ var OwneySDK = class {
|
|
|
6425
7081
|
const entries = [...this.getActiveAgents().entries()];
|
|
6426
7082
|
const balanceResults = await Promise.allSettled(
|
|
6427
7083
|
entries.map(async ([id, agent]) => {
|
|
6428
|
-
const b = await
|
|
7084
|
+
const b = await readBalance(agent);
|
|
6429
7085
|
return [
|
|
6430
7086
|
id,
|
|
6431
7087
|
{
|
|
@@ -6435,6 +7091,7 @@ var OwneySDK = class {
|
|
|
6435
7091
|
];
|
|
6436
7092
|
})
|
|
6437
7093
|
);
|
|
7094
|
+
assertCurrent();
|
|
6438
7095
|
let successCount = 0;
|
|
6439
7096
|
const agentErrors = {};
|
|
6440
7097
|
const agentRetryAt = {};
|
|
@@ -6702,12 +7359,13 @@ var OwneySDK = class {
|
|
|
6702
7359
|
* @returns {OwneyAgentHistory} `data` (up to `limit` entries), `hasMore`, `nextCursor`.
|
|
6703
7360
|
*/
|
|
6704
7361
|
async getHistory(options) {
|
|
6705
|
-
const state = this.
|
|
6706
|
-
const chainId = this.requireChainId();
|
|
7362
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(options?.chainId);
|
|
6707
7363
|
const { agentId, filters } = options ?? {};
|
|
6708
7364
|
if (agentId) {
|
|
6709
7365
|
const agent = this.getAgent(agentId);
|
|
6710
|
-
|
|
7366
|
+
const page = await this.readAgent(agent, "history", () => agent.getHistory(state, chainId, filters), filters);
|
|
7367
|
+
assertCurrent();
|
|
7368
|
+
return page;
|
|
6711
7369
|
}
|
|
6712
7370
|
const activeAgents = [...this.getActiveAgents().values()];
|
|
6713
7371
|
const cursorMap = filters?.cursor ? decodeMultiAgentCursor(filters.cursor) : {};
|
|
@@ -6717,20 +7375,35 @@ var OwneySDK = class {
|
|
|
6717
7375
|
if (filters?.cursor && agentCursor === void 0) {
|
|
6718
7376
|
return { agentId: agent.id, page: null };
|
|
6719
7377
|
}
|
|
6720
|
-
const agentFilters = { ...filters, cursor: agentCursor };
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
7378
|
+
const agentFilters = { ...filters, cursor: agentCursor || void 0 };
|
|
7379
|
+
try {
|
|
7380
|
+
const page = await this.readAgent(
|
|
7381
|
+
agent,
|
|
7382
|
+
"history",
|
|
7383
|
+
() => agent.getHistory(state, chainId, agentFilters),
|
|
7384
|
+
agentFilters
|
|
7385
|
+
);
|
|
7386
|
+
return { agentId: agent.id, page };
|
|
7387
|
+
} catch (error) {
|
|
7388
|
+
return { agentId: agent.id, page: null, error };
|
|
7389
|
+
}
|
|
6728
7390
|
})
|
|
6729
7391
|
);
|
|
7392
|
+
assertCurrent();
|
|
7393
|
+
const agentErrors = {};
|
|
7394
|
+
const agentRetryAt = {};
|
|
6730
7395
|
const merged = [];
|
|
6731
7396
|
const nextCursorMap = {};
|
|
6732
7397
|
let hasMore = false;
|
|
6733
|
-
for (const
|
|
7398
|
+
for (const result of perAgent) {
|
|
7399
|
+
const { agentId: id, page } = result;
|
|
7400
|
+
if ("error" in result) {
|
|
7401
|
+
agentErrors[id] = result.error instanceof Error ? result.error.message : String(result.error);
|
|
7402
|
+
const delay = rateLimitDelay(result.error);
|
|
7403
|
+
if (delay !== void 0) agentRetryAt[id] = Date.now() + delay;
|
|
7404
|
+
nextCursorMap[id] = cursorMap[id] ?? "";
|
|
7405
|
+
hasMore = true;
|
|
7406
|
+
}
|
|
6734
7407
|
if (!page) continue;
|
|
6735
7408
|
merged.push(...page.data);
|
|
6736
7409
|
if (page.hasMore && page.nextCursor) {
|
|
@@ -6750,6 +7423,7 @@ var OwneySDK = class {
|
|
|
6750
7423
|
});
|
|
6751
7424
|
return {
|
|
6752
7425
|
data: merged,
|
|
7426
|
+
...Object.keys(agentErrors).length ? { agentErrors, agentRetryAt } : {},
|
|
6753
7427
|
hasMore,
|
|
6754
7428
|
nextCursor: hasMore ? encodeMultiAgentCursor(nextCursorMap) : void 0
|
|
6755
7429
|
};
|
|
@@ -6831,16 +7505,16 @@ var OwneySDK = class {
|
|
|
6831
7505
|
);
|
|
6832
7506
|
}
|
|
6833
7507
|
const provider = this.requireConnectedProvider();
|
|
6834
|
-
const walletChainClient = (0,
|
|
7508
|
+
const walletChainClient = (0, import_viem13.createPublicClient)({
|
|
6835
7509
|
chain: VIEM_CHAIN2[chainId],
|
|
6836
|
-
transport: (0,
|
|
7510
|
+
transport: (0, import_viem13.custom)(provider)
|
|
6837
7511
|
});
|
|
6838
7512
|
const publicClient = this.getPaidRpcClient(chainId);
|
|
6839
7513
|
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
6840
|
-
const wallet = (0,
|
|
7514
|
+
const wallet = (0, import_viem13.createWalletClient)({
|
|
6841
7515
|
account: state.walletAddress,
|
|
6842
7516
|
chain: VIEM_CHAIN2[chainId],
|
|
6843
|
-
transport: (0,
|
|
7517
|
+
transport: (0, import_viem13.custom)(provider)
|
|
6844
7518
|
});
|
|
6845
7519
|
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
6846
7520
|
const hash = await wallet.writeContract({
|
|
@@ -7002,7 +7676,7 @@ var OwneySDK = class {
|
|
|
7002
7676
|
};
|
|
7003
7677
|
|
|
7004
7678
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
7005
|
-
var
|
|
7679
|
+
var import_viem14 = require("viem");
|
|
7006
7680
|
var import_siwe2 = require("siwe");
|
|
7007
7681
|
var import_sdk2 = require("@zyfai/sdk");
|
|
7008
7682
|
|
|
@@ -7129,7 +7803,7 @@ function buildSIWXConfig(deps) {
|
|
|
7129
7803
|
issuedAt,
|
|
7130
7804
|
toString() {
|
|
7131
7805
|
return new import_siwe2.SiweMessage({
|
|
7132
|
-
address: (0,
|
|
7806
|
+
address: (0, import_viem14.getAddress)(accountAddress),
|
|
7133
7807
|
chainId: numericChainId(chainId),
|
|
7134
7808
|
domain,
|
|
7135
7809
|
uri,
|