@owney/sdk 0.7.25-beta.14 → 0.7.25-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1366 -526
- package/dist/index.d.cts +41 -6
- package/dist/index.d.ts +41 -6
- package/dist/index.js +1286 -443
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -102,6 +102,8 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
|
102
102
|
const attributed = /* @__PURE__ */ new Map();
|
|
103
103
|
const agent = evidence.agentWallet?.toLowerCase();
|
|
104
104
|
const owner = evidence.walletAddress.toLowerCase();
|
|
105
|
+
const source = direction === "deposit" ? owner : agent;
|
|
106
|
+
const destination = direction === "deposit" ? agent : owner;
|
|
105
107
|
const logIndexes = [];
|
|
106
108
|
const transferLogs = [];
|
|
107
109
|
const zeroTransferLogs = [];
|
|
@@ -116,17 +118,13 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction) {
|
|
|
116
118
|
if (direction === "withdraw" && value === 0n && from.toLowerCase() === agent && to.toLowerCase() === owner)
|
|
117
119
|
zeroTransferLogs.push(log.logIndex);
|
|
118
120
|
let matched = 0n;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (sender !== agent) attributed.set(sender, available - carried);
|
|
127
|
-
if (recipient === owner) matched = carried;
|
|
128
|
-
else if (recipient !== agent) attributed.set(recipient, (attributed.get(recipient) ?? 0n) + carried);
|
|
129
|
-
}
|
|
121
|
+
const sender = from.toLowerCase();
|
|
122
|
+
const recipient = to.toLowerCase();
|
|
123
|
+
const available = sender === source ? value : attributed.get(sender) ?? 0n;
|
|
124
|
+
const carried = available < value ? available : value;
|
|
125
|
+
if (sender !== source) attributed.set(sender, available - carried);
|
|
126
|
+
if (recipient === destination) matched = carried;
|
|
127
|
+
else if (recipient !== source) attributed.set(recipient, (attributed.get(recipient) ?? 0n) + carried);
|
|
130
128
|
if (matched === 0n) continue;
|
|
131
129
|
amount += matched;
|
|
132
130
|
transferLogs.push({ logIndex: log.logIndex, amountRaw: matched.toString() });
|
|
@@ -418,8 +416,8 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
|
418
416
|
return await original.apply(this, args);
|
|
419
417
|
} catch (error) {
|
|
420
418
|
const message = error instanceof Error ? error.message : "";
|
|
421
|
-
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";
|
|
422
|
-
if (["unauthorized", "forbidden", "rate-limited"].includes(request2.failure)) notify("rejected");
|
|
419
|
+
request2.failure = message === "Unauthorized: Invalid API key" ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : /^Idle balance \d+(?:\.\d+)? (USDC|WETH|ETH|EURC) is below the minimum withdrawal of \d+(?:\.\d+)? \1\.?$/.test(message) ? "insufficient-balance" : /^User doesn't have any active session key\.?$/.test(message) ? "session-key-missing" : message === "Network error: Unable to reach the server" ? "network" : "other";
|
|
420
|
+
if (["unauthorized", "forbidden", "rate-limited", "insufficient-balance", "session-key-missing"].includes(request2.failure)) notify("rejected");
|
|
423
421
|
throw error;
|
|
424
422
|
} finally {
|
|
425
423
|
request2.elapsedMs = Math.max(0, Date.now() - startedAt);
|
|
@@ -442,9 +440,49 @@ function observeWithdrawalSubmission(sdk, chainId, token) {
|
|
|
442
440
|
} };
|
|
443
441
|
}
|
|
444
442
|
|
|
443
|
+
// src/agents/zyfai/zyfai.withdraw-empty.ts
|
|
444
|
+
function hasEmptyWithdrawalSnapshot(raw2, chainId, token) {
|
|
445
|
+
const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
446
|
+
const response = record(raw2);
|
|
447
|
+
const portfolio = record(response?.portfolio);
|
|
448
|
+
const chains = record(portfolio?.portfolioByChain);
|
|
449
|
+
if (response?.success === false || !chains || !Array.isArray(portfolio?.positions) || !Array.isArray(portfolio.staleBalances)) return false;
|
|
450
|
+
if (portfolio.pendingAsyncWithdrawals !== void 0 && (!Array.isArray(portfolio.pendingAsyncWithdrawals) || portfolio.pendingAsyncWithdrawals.length > 0)) return false;
|
|
451
|
+
const symbol = (value) => typeof value === "string" ? value.toUpperCase() === "ETH" ? "WETH" : value.toUpperCase() : void 0;
|
|
452
|
+
const chain = (value) => {
|
|
453
|
+
if (typeof value === "number") return value;
|
|
454
|
+
if (typeof value !== "string") return void 0;
|
|
455
|
+
return /^\d+$/.test(value) ? Number(value) : { base: 8453, arbitrum: 42161, ethereum: 1, mainnet: 1 }[value.trim().toLowerCase()];
|
|
456
|
+
};
|
|
457
|
+
const zero = (value) => typeof value === "string" && /^(?:0x)?0+$/i.test(value);
|
|
458
|
+
for (const [id, assets] of Object.entries(chains)) {
|
|
459
|
+
const idNumber = chain(id);
|
|
460
|
+
if (idNumber === void 0) return false;
|
|
461
|
+
if (idNumber !== chainId) continue;
|
|
462
|
+
const entries = record(assets);
|
|
463
|
+
if (!entries) return false;
|
|
464
|
+
for (const [asset, value] of Object.entries(entries)) {
|
|
465
|
+
if (symbol(asset) !== token) continue;
|
|
466
|
+
const balance = record(value);
|
|
467
|
+
if (!balance || !zero(balance.balance)) return false;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
for (const value of [...portfolio.positions, ...portfolio.staleBalances]) {
|
|
471
|
+
const entry = record(value);
|
|
472
|
+
if (!entry) return false;
|
|
473
|
+
const id = chain(entry.chainId ?? entry.chain);
|
|
474
|
+
const asset = symbol(entry.tokenSymbol ?? entry.token_symbol);
|
|
475
|
+
if (id === void 0 || asset === void 0) return false;
|
|
476
|
+
if (id !== chainId || asset !== token) continue;
|
|
477
|
+
const amounts = [entry.balance, entry.amount, entry.underlyingAmount].filter((value2) => value2 !== void 0);
|
|
478
|
+
if (!amounts.length || !amounts.every(zero)) return false;
|
|
479
|
+
}
|
|
480
|
+
return true;
|
|
481
|
+
}
|
|
482
|
+
|
|
445
483
|
// src/agents/zyfai/zyfai.agent.ts
|
|
446
484
|
var import_sdk = require("@zyfai/sdk");
|
|
447
|
-
var
|
|
485
|
+
var import_viem4 = require("viem");
|
|
448
486
|
var import_chains = require("viem/chains");
|
|
449
487
|
|
|
450
488
|
// src/types/config.ts
|
|
@@ -531,14 +569,6 @@ function hexToDecimal(hex, decimals = 6) {
|
|
|
531
569
|
const remainder = parsed % divisor;
|
|
532
570
|
return Number(whole) + Number(remainder) / Number(divisor);
|
|
533
571
|
}
|
|
534
|
-
function normalizeDecimalPlace(value, decimals = 6) {
|
|
535
|
-
const parsed = BigInt(value);
|
|
536
|
-
if (decimals <= 0) return Number(parsed);
|
|
537
|
-
const divisor = BigInt(10 ** decimals);
|
|
538
|
-
const whole = parsed / divisor;
|
|
539
|
-
const remainder = parsed % divisor;
|
|
540
|
-
return Number(whole) + Number(remainder) / Number(divisor);
|
|
541
|
-
}
|
|
542
572
|
var DayFilterMapping = {
|
|
543
573
|
"30D": 30,
|
|
544
574
|
"14D": 14,
|
|
@@ -546,6 +576,8 @@ var DayFilterMapping = {
|
|
|
546
576
|
};
|
|
547
577
|
|
|
548
578
|
// src/agents/zyfai/zyfai.mapper.ts
|
|
579
|
+
var import_viem2 = require("viem");
|
|
580
|
+
var exactHexAmount = (raw2, decimals) => (0, import_viem2.formatUnits)(BigInt(/^0x/i.test(raw2) ? raw2 : `0x${raw2}`), decimals);
|
|
549
581
|
var DECIMALS_BY_SYMBOL = {
|
|
550
582
|
usdc: 6,
|
|
551
583
|
weth: 18,
|
|
@@ -600,15 +632,15 @@ function mapPendingAllocations(raw2) {
|
|
|
600
632
|
if (typeof e.balance !== "string") continue;
|
|
601
633
|
let amount;
|
|
602
634
|
try {
|
|
603
|
-
|
|
635
|
+
if (BigInt(/^0x/i.test(e.balance) ? e.balance : `0x${e.balance}`) <= 0n) continue;
|
|
636
|
+
amount = exactHexAmount(e.balance, tokenDecimals(e.tokenSymbol));
|
|
604
637
|
} catch {
|
|
605
638
|
continue;
|
|
606
639
|
}
|
|
607
|
-
if (!Number.isFinite(amount) || amount <= 0) continue;
|
|
608
640
|
pending.push({
|
|
609
641
|
chainId: e.chainId,
|
|
610
642
|
asset: e.tokenSymbol,
|
|
611
|
-
amount
|
|
643
|
+
amount,
|
|
612
644
|
isPending: e.isPending === true,
|
|
613
645
|
...typeof e.updatedAt === "string" && e.updatedAt !== "" ? { since: e.updatedAt } : {}
|
|
614
646
|
});
|
|
@@ -631,13 +663,13 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
631
663
|
{ balance, balanceWithFee, decimals }
|
|
632
664
|
] of Object.entries(tokensBySymbol ?? {})) {
|
|
633
665
|
if (balanceWithFee === void 0 && balance === void 0) throw new Error("Zyfai asset balance is unavailable.");
|
|
634
|
-
const amount =
|
|
635
|
-
totalBalance += amount;
|
|
666
|
+
const amount = exactHexAmount(balanceWithFee ?? balance, tokenDecimals(symbol, decimals));
|
|
667
|
+
totalBalance += Number(amount);
|
|
636
668
|
tokens.push({
|
|
637
669
|
chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
|
|
638
670
|
chainId: positionChainId,
|
|
639
671
|
asset: symbol,
|
|
640
|
-
amount
|
|
672
|
+
amount
|
|
641
673
|
});
|
|
642
674
|
}
|
|
643
675
|
}
|
|
@@ -651,19 +683,17 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
651
683
|
// legacy `amount` fallback is hex. Runtime
|
|
652
684
|
// getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
|
|
653
685
|
// when present, otherwise fall back to the per-symbol lookup.
|
|
654
|
-
amount:
|
|
655
|
-
p.underlyingAmountWithFee ?? p.underlyingAmount
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
p.decimals
|
|
666
|
-
)
|
|
686
|
+
amount: p.underlyingAmountWithFee ?? p.underlyingAmount ? (0, import_viem2.formatUnits)(
|
|
687
|
+
BigInt(p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0"),
|
|
688
|
+
tokenDecimals(
|
|
689
|
+
p.token_symbol,
|
|
690
|
+
p.decimals
|
|
691
|
+
)
|
|
692
|
+
) : exactHexAmount(
|
|
693
|
+
p.amount || "0",
|
|
694
|
+
tokenDecimals(
|
|
695
|
+
p.token_symbol,
|
|
696
|
+
p.decimals
|
|
667
697
|
)
|
|
668
698
|
),
|
|
669
699
|
apy: netApy(p.pool_apy_withFee, p.pool_apy, "pool_apy_withFee"),
|
|
@@ -1318,7 +1348,7 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
1318
1348
|
}
|
|
1319
1349
|
|
|
1320
1350
|
// src/lib/paid-rpc.ts
|
|
1321
|
-
var
|
|
1351
|
+
var import_viem3 = require("viem");
|
|
1322
1352
|
var PAID_RPC_RETRY_COUNT = 3;
|
|
1323
1353
|
var PAID_RPC_RETRY_DELAY_MS = 1e3;
|
|
1324
1354
|
function configuredRpcProxyBaseUrl(baseUrl) {
|
|
@@ -1354,9 +1384,9 @@ function createPaidRpcClient(chain, rpcUrls) {
|
|
|
1354
1384
|
rpcUrls,
|
|
1355
1385
|
chain.id
|
|
1356
1386
|
);
|
|
1357
|
-
return (0,
|
|
1387
|
+
return (0, import_viem3.createPublicClient)({
|
|
1358
1388
|
chain,
|
|
1359
|
-
transport: (0,
|
|
1389
|
+
transport: (0, import_viem3.http)(url, {
|
|
1360
1390
|
retryCount: PAID_RPC_RETRY_COUNT,
|
|
1361
1391
|
retryDelay: PAID_RPC_RETRY_DELAY_MS
|
|
1362
1392
|
})
|
|
@@ -1453,7 +1483,7 @@ async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
|
|
|
1453
1483
|
}
|
|
1454
1484
|
|
|
1455
1485
|
// src/agents/zyfai/zyfai.agent.ts
|
|
1456
|
-
var ERC7579_IS_MODULE_INSTALLED_ABI = (0,
|
|
1486
|
+
var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem4.parseAbi)([
|
|
1457
1487
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
1458
1488
|
]);
|
|
1459
1489
|
var WETH_ADDRESS_BY_CHAIN = {
|
|
@@ -1604,7 +1634,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
1604
1634
|
}, asset.address, entry.action === "Withdraw" ? "withdraw" : "deposit");
|
|
1605
1635
|
if (evidence.transfer === "confirmed" && evidence.confirmedAmount) {
|
|
1606
1636
|
Object.assign(tx, {
|
|
1607
|
-
amount: (0,
|
|
1637
|
+
amount: (0, import_viem4.formatUnits)(BigInt(evidence.confirmedAmount), asset.decimals),
|
|
1608
1638
|
amountRaw: evidence.confirmedAmount,
|
|
1609
1639
|
blockNumber: evidence.blockNumber,
|
|
1610
1640
|
logIndexes: evidence.logIndexes,
|
|
@@ -2331,6 +2361,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2331
2361
|
evidence2 = { ...evidence2, transfer: "unknown" };
|
|
2332
2362
|
}
|
|
2333
2363
|
notifyFunding(onProgress, evidence2);
|
|
2364
|
+
if (evidence2.transfer === "failed")
|
|
2365
|
+
throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
|
|
2334
2366
|
try {
|
|
2335
2367
|
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
2336
2368
|
await (tokenAddress ? this.sdk.logDeposit(validChainId, txHash, amount, tokenAddress) : this.sdk.logDeposit(validChainId, txHash, amount));
|
|
@@ -2369,12 +2401,77 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2369
2401
|
} catch {
|
|
2370
2402
|
}
|
|
2371
2403
|
notifyFunding(onProgress, evidence);
|
|
2404
|
+
if (evidence.transfer === "failed")
|
|
2405
|
+
throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash: raw2.txHash }, this.id);
|
|
2372
2406
|
return { ...mapDeposit(raw2), evidence };
|
|
2373
2407
|
} catch (error) {
|
|
2374
2408
|
throw error;
|
|
2375
2409
|
}
|
|
2376
2410
|
}
|
|
2377
2411
|
// --- IAgent: Fund operations ---
|
|
2412
|
+
/** Called only after the execution endpoint explicitly rejected a request.
|
|
2413
|
+
* A timeout or an accepted request never authorizes repair and resubmission. */
|
|
2414
|
+
async recoverWithdrawalSession(state, chainId, token, owner) {
|
|
2415
|
+
const internals = this.sdk;
|
|
2416
|
+
const assertScope = () => {
|
|
2417
|
+
const account = internals.walletClient?.account?.address;
|
|
2418
|
+
if (state.walletAddress.toLowerCase() !== owner.toLowerCase() || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
|
|
2419
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai withdrawal.", void 0, this.id);
|
|
2420
|
+
};
|
|
2421
|
+
assertScope();
|
|
2422
|
+
const [wallet, registered, portfolio] = await Promise.all([
|
|
2423
|
+
this.sdk.getSmartWalletAddress(owner, chainId),
|
|
2424
|
+
this.sdk.getSmartWalletByEOA(owner),
|
|
2425
|
+
this.sdk.getPortfolio(owner).catch(() => void 0)
|
|
2426
|
+
]);
|
|
2427
|
+
assertScope();
|
|
2428
|
+
if (!(0, import_viem4.isAddress)(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
|
|
2429
|
+
throw new OwneyError(
|
|
2430
|
+
"AGENT_AUTH_FAILED",
|
|
2431
|
+
"Could not verify Zyfai's withdrawal wallet.",
|
|
2432
|
+
{ reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
|
|
2433
|
+
this.id
|
|
2434
|
+
);
|
|
2435
|
+
if (hasEmptyWithdrawalSnapshot(portfolio, chainId, token)) {
|
|
2436
|
+
const asset = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === token);
|
|
2437
|
+
const idle = asset ? await this.getPublicClient(chainId).readContract({
|
|
2438
|
+
address: asset.address,
|
|
2439
|
+
abi: import_viem4.erc20Abi,
|
|
2440
|
+
functionName: "balanceOf",
|
|
2441
|
+
args: [wallet.address]
|
|
2442
|
+
}).catch(() => void 0) : void 0;
|
|
2443
|
+
assertScope();
|
|
2444
|
+
if (idle === 0n) throw new OwneyError(
|
|
2445
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
2446
|
+
"No funds remain in Zyfai for this asset.",
|
|
2447
|
+
{ available: "0", chainId, token },
|
|
2448
|
+
this.id
|
|
2449
|
+
);
|
|
2450
|
+
}
|
|
2451
|
+
const walletState = await this.readWalletState(owner);
|
|
2452
|
+
assertScope();
|
|
2453
|
+
const unavailable = () => new OwneyError(
|
|
2454
|
+
"AGENT_AUTH_FAILED",
|
|
2455
|
+
"Zyfai has no active withdrawal session. Its withdrawal was not submitted.",
|
|
2456
|
+
{ reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
|
|
2457
|
+
this.id
|
|
2458
|
+
);
|
|
2459
|
+
if (walletState.predeployed !== false || internals.isPredeployed === true) throw unavailable();
|
|
2460
|
+
internals.hasActiveSessionKey = false;
|
|
2461
|
+
const key2 = `${owner.toLowerCase()}:${chainId}`;
|
|
2462
|
+
this.sessionKeyAttempts.delete(key2);
|
|
2463
|
+
try {
|
|
2464
|
+
const result = await this.sdk.createSessionKey(owner, chainId);
|
|
2465
|
+
assertScope();
|
|
2466
|
+
if (result.success === false || result.alreadyActive === true) throw unavailable();
|
|
2467
|
+
this.sessionKeyAttempts.add(key2);
|
|
2468
|
+
} catch (error) {
|
|
2469
|
+
assertScope();
|
|
2470
|
+
const code = error?.code;
|
|
2471
|
+
if (code === 4001 || code === "ACTION_REJECTED" || error instanceof Error && /user (rejected|denied)|request rejected|action rejected/i.test(error.message)) throw error;
|
|
2472
|
+
throw unavailable();
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2378
2475
|
async withdraw(state, chainId, token, amount, onProgress) {
|
|
2379
2476
|
const validChainId = isValidChainId(chainId);
|
|
2380
2477
|
try {
|
|
@@ -2392,11 +2489,35 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2392
2489
|
});
|
|
2393
2490
|
throw error;
|
|
2394
2491
|
}
|
|
2395
|
-
|
|
2492
|
+
let observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
|
|
2396
2493
|
const authToken = this.sdk.httpClient?.authToken;
|
|
2494
|
+
const owner = this.getAddress();
|
|
2397
2495
|
let raw2;
|
|
2398
2496
|
try {
|
|
2399
|
-
|
|
2497
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
2498
|
+
try {
|
|
2499
|
+
raw2 = await this.sdk.withdrawFunds(owner, validChainId, amount, token);
|
|
2500
|
+
break;
|
|
2501
|
+
} catch (error) {
|
|
2502
|
+
if (!observation.rejected() || observation.request()?.failure !== "session-key-missing") throw error;
|
|
2503
|
+
if (attempt === 1)
|
|
2504
|
+
throw new OwneyError(
|
|
2505
|
+
"AGENT_AUTH_FAILED",
|
|
2506
|
+
"Zyfai has no active withdrawal session. Its withdrawal was not submitted.",
|
|
2507
|
+
{ reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
|
|
2508
|
+
this.id
|
|
2509
|
+
);
|
|
2510
|
+
await this.recoverWithdrawalSession(state, validChainId, token, owner);
|
|
2511
|
+
observation.stop();
|
|
2512
|
+
observation = observeWithdrawalSubmission(this.sdk, validChainId, token);
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
if (!raw2) throw new OwneyError(
|
|
2516
|
+
"AGENT_INVALID_RESPONSE",
|
|
2517
|
+
"Zyfai returned no withdrawal result.",
|
|
2518
|
+
{ chainId, token, phase: "observe" },
|
|
2519
|
+
this.id
|
|
2520
|
+
);
|
|
2400
2521
|
} catch (error) {
|
|
2401
2522
|
this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken);
|
|
2402
2523
|
if (observation.attempted() !== void 0) {
|
|
@@ -2675,7 +2796,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2675
2796
|
};
|
|
2676
2797
|
|
|
2677
2798
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2678
|
-
var
|
|
2799
|
+
var import_viem11 = require("viem");
|
|
2679
2800
|
var import_chains4 = require("viem/chains");
|
|
2680
2801
|
|
|
2681
2802
|
// src/lib/chain-guard.ts
|
|
@@ -2714,7 +2835,7 @@ async function ensureWalletOnChain(pub, wallet, expected) {
|
|
|
2714
2835
|
}
|
|
2715
2836
|
|
|
2716
2837
|
// src/lib/transfer-auth.ts
|
|
2717
|
-
var
|
|
2838
|
+
var import_viem5 = require("viem");
|
|
2718
2839
|
|
|
2719
2840
|
// src/lib/sponsor-client.ts
|
|
2720
2841
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
@@ -2830,7 +2951,7 @@ async function postSponsorBatchTransfer(input) {
|
|
|
2830
2951
|
}
|
|
2831
2952
|
|
|
2832
2953
|
// src/lib/permit2.ts
|
|
2833
|
-
var
|
|
2954
|
+
var import_viem6 = require("viem");
|
|
2834
2955
|
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
2835
2956
|
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
2836
2957
|
function permit2ApprovalAmount(requiredAmount) {
|
|
@@ -2871,7 +2992,7 @@ var ERC20_ALLOWANCE_ABI = [
|
|
|
2871
2992
|
function randomPermit2Nonce() {
|
|
2872
2993
|
const bytes = new Uint8Array(32);
|
|
2873
2994
|
globalThis.crypto.getRandomValues(bytes);
|
|
2874
|
-
return BigInt((0,
|
|
2995
|
+
return BigInt((0, import_viem6.bytesToHex)(bytes));
|
|
2875
2996
|
}
|
|
2876
2997
|
async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
|
|
2877
2998
|
return publicClient.readContract({
|
|
@@ -2914,7 +3035,7 @@ function makeVerificationAwareDepositCallback(implementation) {
|
|
|
2914
3035
|
|
|
2915
3036
|
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
2916
3037
|
var import_siwe = require("siwe");
|
|
2917
|
-
var
|
|
3038
|
+
var import_viem7 = require("viem");
|
|
2918
3039
|
var import_chains2 = require("viem/chains");
|
|
2919
3040
|
|
|
2920
3041
|
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
@@ -3027,7 +3148,7 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
|
3027
3148
|
return new import_siwe.SiweMessage({
|
|
3028
3149
|
scheme: url.protocol.slice(0, -1),
|
|
3029
3150
|
domain: url.host,
|
|
3030
|
-
address: (0,
|
|
3151
|
+
address: (0, import_viem7.getAddress)(address),
|
|
3031
3152
|
uri: url.origin,
|
|
3032
3153
|
version: "1",
|
|
3033
3154
|
chainId,
|
|
@@ -3113,15 +3234,15 @@ var YieldseekerAuth = class {
|
|
|
3113
3234
|
clearYieldseekerSession(state.walletAddress, chainId);
|
|
3114
3235
|
}
|
|
3115
3236
|
async sign(state, chainId) {
|
|
3116
|
-
const account = (0,
|
|
3117
|
-
const publicClient = (0,
|
|
3237
|
+
const account = (0, import_viem7.getAddress)(state.walletAddress);
|
|
3238
|
+
const publicClient = (0, import_viem7.createPublicClient)({
|
|
3118
3239
|
chain: import_chains2.base,
|
|
3119
|
-
transport: (0,
|
|
3240
|
+
transport: (0, import_viem7.custom)(state.provider)
|
|
3120
3241
|
});
|
|
3121
|
-
const walletClient = (0,
|
|
3242
|
+
const walletClient = (0, import_viem7.createWalletClient)({
|
|
3122
3243
|
account,
|
|
3123
3244
|
chain: import_chains2.base,
|
|
3124
|
-
transport: (0,
|
|
3245
|
+
transport: (0, import_viem7.custom)(state.provider)
|
|
3125
3246
|
});
|
|
3126
3247
|
await ensureWalletOnChain(
|
|
3127
3248
|
publicClient,
|
|
@@ -3288,7 +3409,7 @@ var YieldseekerApiClient = class {
|
|
|
3288
3409
|
};
|
|
3289
3410
|
|
|
3290
3411
|
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
3291
|
-
var
|
|
3412
|
+
var import_viem8 = require("viem");
|
|
3292
3413
|
|
|
3293
3414
|
// src/lib/helpers/snapshot-apy.ts
|
|
3294
3415
|
var DAY_MS = 864e5;
|
|
@@ -3355,10 +3476,10 @@ function raw(value, endpoint) {
|
|
|
3355
3476
|
return BigInt(value);
|
|
3356
3477
|
}
|
|
3357
3478
|
function decimal(value, decimals, endpoint) {
|
|
3358
|
-
return (0,
|
|
3479
|
+
return (0, import_viem8.formatUnits)(raw(value, endpoint), decimals);
|
|
3359
3480
|
}
|
|
3360
3481
|
function usd(rawAmount, decimals, price) {
|
|
3361
|
-
return Number((0,
|
|
3482
|
+
return Number((0, import_viem8.formatUnits)(rawAmount, decimals)) * price;
|
|
3362
3483
|
}
|
|
3363
3484
|
function percent(value) {
|
|
3364
3485
|
const result = Number(value);
|
|
@@ -3384,7 +3505,7 @@ function assetAddressValue(record, address) {
|
|
|
3384
3505
|
}
|
|
3385
3506
|
function position(value, asset, baseAssetDecimals) {
|
|
3386
3507
|
const option = value?.yieldOption;
|
|
3387
|
-
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0,
|
|
3508
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem8.isAddress)(option.address)) {
|
|
3388
3509
|
return invalid("yield positions", "missing vault metadata");
|
|
3389
3510
|
}
|
|
3390
3511
|
return {
|
|
@@ -3476,7 +3597,7 @@ function mapYieldseekerEarnings(contexts) {
|
|
|
3476
3597
|
chain: "BASE",
|
|
3477
3598
|
chainId: 8453,
|
|
3478
3599
|
asset: context.asset,
|
|
3479
|
-
amount: (0,
|
|
3600
|
+
amount: (0, import_viem8.formatUnits)(amount, context.snapshot.baseAssetDecimals)
|
|
3480
3601
|
});
|
|
3481
3602
|
lifetimeEarnings += usd(
|
|
3482
3603
|
amount,
|
|
@@ -3605,7 +3726,7 @@ function allocationEntry(action, context) {
|
|
|
3605
3726
|
const leg = deposit;
|
|
3606
3727
|
const symbol = typeof leg.assetSymbol === "string" ? leg.assetSymbol.toUpperCase() : "";
|
|
3607
3728
|
if (symbol !== context.asset && !(context.asset === "WETH" && symbol === "ETH")) return void 0;
|
|
3608
|
-
if (typeof leg.vaultAddress !== "string" || !(0,
|
|
3729
|
+
if (typeof leg.vaultAddress !== "string" || !(0, import_viem8.isAddress)(leg.vaultAddress)) return void 0;
|
|
3609
3730
|
const rawAmount = typeof leg.rawAmount === "string" && /^\d+$/.test(leg.rawAmount) ? BigInt(leg.rawAmount) : typeof leg.rawAmount === "number" && Number.isSafeInteger(leg.rawAmount) ? BigInt(leg.rawAmount) : void 0;
|
|
3610
3731
|
if (rawAmount === void 0 || rawAmount <= 0n) return void 0;
|
|
3611
3732
|
const option = context.positions.find((position2) => position2.yieldOption.address.toLowerCase() === leg.vaultAddress.toLowerCase())?.yieldOption;
|
|
@@ -3613,7 +3734,7 @@ function allocationEntry(action, context) {
|
|
|
3613
3734
|
protocol: typeof leg.vaultProvider === "string" && leg.vaultProvider ? leg.vaultProvider : option?.provider || "Vault",
|
|
3614
3735
|
pool: typeof leg.vaultName === "string" && leg.vaultName ? leg.vaultName : option?.name,
|
|
3615
3736
|
tokenSymbol: context.asset,
|
|
3616
|
-
amount: (0,
|
|
3737
|
+
amount: (0, import_viem8.formatUnits)(rawAmount, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
|
|
3617
3738
|
});
|
|
3618
3739
|
amountRaw += rawAmount;
|
|
3619
3740
|
}
|
|
@@ -3632,7 +3753,7 @@ function allocationEntry(action, context) {
|
|
|
3632
3753
|
tokenSymbol: context.asset,
|
|
3633
3754
|
agentWallet: context.wallet.walletAddress,
|
|
3634
3755
|
amountRaw: String(amountRaw),
|
|
3635
|
-
amount: (0,
|
|
3756
|
+
amount: (0, import_viem8.formatUnits)(amountRaw, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
|
|
3636
3757
|
}]
|
|
3637
3758
|
};
|
|
3638
3759
|
}
|
|
@@ -3756,7 +3877,7 @@ function mapYieldseekerHistory(contexts, options) {
|
|
|
3756
3877
|
const known = new Set(target.logIndexes ?? []);
|
|
3757
3878
|
if (tx.logIndexes?.some((index) => !known.has(index))) {
|
|
3758
3879
|
target.amountRaw = String(BigInt(target.amountRaw ?? "0") + BigInt(tx.amountRaw));
|
|
3759
|
-
target.amount = (0,
|
|
3880
|
+
target.amount = (0, import_viem8.formatUnits)(
|
|
3760
3881
|
BigInt(target.amountRaw),
|
|
3761
3882
|
YIELDSEEKER_ASSET_METADATA[target.tokenSymbol].decimals
|
|
3762
3883
|
);
|
|
@@ -3837,51 +3958,53 @@ function mapYieldseekerAgentApy(options, days) {
|
|
|
3837
3958
|
}
|
|
3838
3959
|
|
|
3839
3960
|
// src/agents/yieldseeker/yieldseeker.wallet-version.ts
|
|
3840
|
-
var
|
|
3961
|
+
var import_viem9 = require("viem");
|
|
3841
3962
|
var YIELDSEEKER_FACTORY = "0x9c7410a0faCAC60850C46eE5b58B518daec95130";
|
|
3842
3963
|
var YIELDSEEKER_V1_IMPLEMENTATION = "0x32403455357589523435dB18b414057D7cE05051";
|
|
3843
3964
|
var YIELDSEEKER_V2_IMPLEMENTATION = "0x613Db9c924ADB7A7A90c11b02Cce20c913E3Fe50";
|
|
3844
3965
|
var ERC1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
|
3845
|
-
var walletAbi = (0,
|
|
3966
|
+
var walletAbi = (0, import_viem9.parseAbi)([
|
|
3846
3967
|
"function owner() view returns (address)",
|
|
3847
3968
|
"function ownerAgentIndex() view returns (uint256)",
|
|
3848
3969
|
"function baseAsset() view returns (address)",
|
|
3849
3970
|
"function FACTORY() view returns (address)",
|
|
3850
|
-
"function upgradeToLatest()"
|
|
3971
|
+
"function upgradeToLatest()",
|
|
3972
|
+
"event Upgraded(address indexed implementation)"
|
|
3851
3973
|
]);
|
|
3852
|
-
var factoryAbi = (0,
|
|
3974
|
+
var factoryAbi = (0, import_viem9.parseAbi)([
|
|
3853
3975
|
"function userWallets(address owner, uint256 index) view returns (address)",
|
|
3854
3976
|
"function agentWalletImplementation() view returns (address)"
|
|
3855
3977
|
]);
|
|
3856
3978
|
function yieldseekerOwnerPath(code) {
|
|
3857
3979
|
return !code || code === "0x" || /^0xef0100[0-9a-f]{40}$/i.test(code) ? "eoa" : "smart";
|
|
3858
3980
|
}
|
|
3859
|
-
async function readYieldseekerWalletStatus(client, wallet, owner, asset) {
|
|
3860
|
-
const agentWallet = (0,
|
|
3861
|
-
const expectedOwner = (0,
|
|
3862
|
-
const expectedAsset = (0,
|
|
3981
|
+
async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNumber) {
|
|
3982
|
+
const agentWallet = (0, import_viem9.getAddress)(wallet);
|
|
3983
|
+
const expectedOwner = (0, import_viem9.getAddress)(owner);
|
|
3984
|
+
const expectedAsset = (0, import_viem9.getAddress)(asset);
|
|
3863
3985
|
const [code, ownerCode, slot, actualOwner, index, actualAsset, actualFactory, latest] = await Promise.all([
|
|
3864
|
-
client.getBytecode({ address: agentWallet }),
|
|
3865
|
-
client.getBytecode({ address: expectedOwner }),
|
|
3866
|
-
client.getStorageAt({ address: agentWallet, slot: ERC1967_IMPLEMENTATION_SLOT }),
|
|
3867
|
-
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "owner" }),
|
|
3868
|
-
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "ownerAgentIndex" }),
|
|
3869
|
-
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "baseAsset" }),
|
|
3870
|
-
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "FACTORY" }),
|
|
3871
|
-
client.readContract({ address: YIELDSEEKER_FACTORY, abi: factoryAbi, functionName: "agentWalletImplementation" })
|
|
3986
|
+
client.getBytecode({ address: agentWallet, blockNumber }),
|
|
3987
|
+
client.getBytecode({ address: expectedOwner, blockNumber }),
|
|
3988
|
+
client.getStorageAt({ address: agentWallet, slot: ERC1967_IMPLEMENTATION_SLOT, blockNumber }),
|
|
3989
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "owner", blockNumber }),
|
|
3990
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "ownerAgentIndex", blockNumber }),
|
|
3991
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "baseAsset", blockNumber }),
|
|
3992
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "FACTORY", blockNumber }),
|
|
3993
|
+
client.readContract({ address: YIELDSEEKER_FACTORY, abi: factoryAbi, functionName: "agentWalletImplementation", blockNumber })
|
|
3872
3994
|
]);
|
|
3873
3995
|
if (!code || code === "0x" || !slot || /^0x0+$/.test(slot)) {
|
|
3874
3996
|
throw new Error("Yieldseeker wallet is not a deployed ERC1967 proxy");
|
|
3875
3997
|
}
|
|
3876
|
-
const implementation = (0,
|
|
3998
|
+
const implementation = (0, import_viem9.getAddress)(`0x${slot.slice(-40)}`);
|
|
3877
3999
|
const mapped = await client.readContract({
|
|
3878
4000
|
address: YIELDSEEKER_FACTORY,
|
|
3879
4001
|
abi: factoryAbi,
|
|
3880
4002
|
functionName: "userWallets",
|
|
3881
|
-
args: [expectedOwner, index]
|
|
4003
|
+
args: [expectedOwner, index],
|
|
4004
|
+
blockNumber
|
|
3882
4005
|
});
|
|
3883
|
-
const verified = (0,
|
|
3884
|
-
const version = !verified ? "unknown" : (0,
|
|
4006
|
+
const verified = (0, import_viem9.isAddressEqual)(actualOwner, expectedOwner) && (0, import_viem9.isAddressEqual)(actualAsset, expectedAsset) && (0, import_viem9.isAddressEqual)(actualFactory, YIELDSEEKER_FACTORY) && (0, import_viem9.isAddressEqual)(mapped, agentWallet);
|
|
4007
|
+
const version = !verified ? "unknown" : (0, import_viem9.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem9.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
|
|
3885
4008
|
return {
|
|
3886
4009
|
agentWallet,
|
|
3887
4010
|
owner: actualOwner,
|
|
@@ -3890,18 +4013,20 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset) {
|
|
|
3890
4013
|
implementation,
|
|
3891
4014
|
version,
|
|
3892
4015
|
verified,
|
|
3893
|
-
upgradeAvailable: version === "v1" && (0,
|
|
4016
|
+
upgradeAvailable: version === "v1" && (0, import_viem9.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
|
|
3894
4017
|
};
|
|
3895
4018
|
}
|
|
3896
4019
|
var YIELDSEEKER_UPGRADE_ABI = walletAbi;
|
|
3897
4020
|
|
|
3898
4021
|
// src/agents/yieldseeker/yieldseeker.sponsored-withdraw.ts
|
|
3899
|
-
var
|
|
4022
|
+
var import_viem10 = require("viem");
|
|
3900
4023
|
var import_account_abstraction2 = require("viem/account-abstraction");
|
|
3901
4024
|
var import_accounts = require("viem/accounts");
|
|
3902
4025
|
var import_chains3 = require("viem/chains");
|
|
3903
4026
|
var ENTRY_POINT_V06 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
|
|
3904
4027
|
var USER_OP_FEE_HEADROOM = 2n;
|
|
4028
|
+
var OBSERVATION_WINDOW_MS = 6e4;
|
|
4029
|
+
var OBSERVATION_POLL_MS = 1500;
|
|
3905
4030
|
var ENTRY_POINT_NONCE_ABI = [
|
|
3906
4031
|
{ type: "function", name: "getNonce", stateMutability: "view", inputs: [
|
|
3907
4032
|
{ name: "sender", type: "address" },
|
|
@@ -3934,10 +4059,119 @@ function sponsorError(message, safeToFallback, details = {}) {
|
|
|
3934
4059
|
return new OwneyError(
|
|
3935
4060
|
"SPONSOR_REQUEST_FAILED",
|
|
3936
4061
|
message,
|
|
3937
|
-
{
|
|
4062
|
+
{
|
|
4063
|
+
safeToFallback,
|
|
4064
|
+
phase: safeToFallback ? "prepare" : "observe",
|
|
4065
|
+
failureCode: safeToFallback ? "SPONSOR_PREPARATION_FAILED" : "SPONSOR_OUTCOME_UNRESOLVED",
|
|
4066
|
+
...safeToFallback ? { rejectedBeforeSubmission: true } : {},
|
|
4067
|
+
...details
|
|
4068
|
+
},
|
|
3938
4069
|
"yieldseeker"
|
|
3939
4070
|
);
|
|
3940
4071
|
}
|
|
4072
|
+
function providerReason(value) {
|
|
4073
|
+
if (typeof value !== "string") return void 0;
|
|
4074
|
+
return value.replace(/https?:\/\/\S+/gi, "[url]").replace(/0x[0-9a-fA-F]{40,}/g, "[hex]").replace(/[A-Za-z0-9_+=\/-]{48,}/g, "[token]").slice(0, 300);
|
|
4075
|
+
}
|
|
4076
|
+
function rpcDiagnostics(data) {
|
|
4077
|
+
const details = {};
|
|
4078
|
+
if (typeof data?.failureCode === "string" && [
|
|
4079
|
+
"SPONSOR_RATE_LIMIT",
|
|
4080
|
+
"SPONSOR_UPSTREAM_UNAVAILABLE",
|
|
4081
|
+
"SPONSOR_RPC_UNSUPPORTED",
|
|
4082
|
+
"SPONSOR_POLICY_REJECTED",
|
|
4083
|
+
"SPONSOR_BUDGET_REJECTED",
|
|
4084
|
+
"SPONSOR_EXECUTION_REVERTED"
|
|
4085
|
+
].includes(data.failureCode))
|
|
4086
|
+
details.failureCode = data.failureCode;
|
|
4087
|
+
if (typeof data?.revertSelector === "string" && /^0x[0-9a-fA-F]{8}$/.test(data.revertSelector))
|
|
4088
|
+
details.revertSelector = data.revertSelector;
|
|
4089
|
+
if (typeof data?.revertName === "string" && [
|
|
4090
|
+
"InsufficientBalance",
|
|
4091
|
+
"InvalidAsset",
|
|
4092
|
+
"Unauthorized",
|
|
4093
|
+
"Error",
|
|
4094
|
+
"Panic",
|
|
4095
|
+
"FailedOp",
|
|
4096
|
+
"FailedOpWithRevert"
|
|
4097
|
+
].includes(data.revertName))
|
|
4098
|
+
details.revertName = data.revertName;
|
|
4099
|
+
if (typeof data?.aaCode === "string" && /^AA[0-9]{2}$/.test(data.aaCode)) details.aaCode = data.aaCode;
|
|
4100
|
+
if (typeof data?.panicCode === "string" && /^0x[0-9a-fA-F]{1,2}$/.test(data.panicCode)) {
|
|
4101
|
+
const panicCode = (0, import_viem10.toHex)(BigInt(data.panicCode));
|
|
4102
|
+
if (["0x0", "0x1", "0x11", "0x12", "0x21", "0x22", "0x31", "0x32", "0x41", "0x51"].includes(panicCode))
|
|
4103
|
+
details.panicCode = panicCode;
|
|
4104
|
+
}
|
|
4105
|
+
return details;
|
|
4106
|
+
}
|
|
4107
|
+
function isHash(value) {
|
|
4108
|
+
return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value);
|
|
4109
|
+
}
|
|
4110
|
+
async function assertOwnerSession(state, owner) {
|
|
4111
|
+
const [chain, accounts] = await Promise.all([
|
|
4112
|
+
state.provider.request({ method: "eth_chainId" }),
|
|
4113
|
+
state.provider.request({ method: "eth_accounts" })
|
|
4114
|
+
]);
|
|
4115
|
+
if (Number(chain) !== 8453)
|
|
4116
|
+
throw new OwneyError(
|
|
4117
|
+
"CHAIN_MISMATCH",
|
|
4118
|
+
"Switch back to Base before withdrawing.",
|
|
4119
|
+
{ rejectedBeforeSubmission: true, phase: "prepare" },
|
|
4120
|
+
"yieldseeker"
|
|
4121
|
+
);
|
|
4122
|
+
if (!Array.isArray(accounts) || typeof accounts[0] !== "string" || accounts[0].toLowerCase() !== owner.toLowerCase())
|
|
4123
|
+
throw new OwneyError(
|
|
4124
|
+
"NOT_CONNECTED",
|
|
4125
|
+
"The connected withdrawal account changed.",
|
|
4126
|
+
{ rejectedBeforeSubmission: true, phase: "prepare" },
|
|
4127
|
+
"yieldseeker"
|
|
4128
|
+
);
|
|
4129
|
+
}
|
|
4130
|
+
function savePending(onPending, evidence, beforeSubmission = false) {
|
|
4131
|
+
try {
|
|
4132
|
+
onPending?.(evidence);
|
|
4133
|
+
} catch {
|
|
4134
|
+
if (beforeSubmission)
|
|
4135
|
+
throw sponsorError(
|
|
4136
|
+
"Withdrawal status cannot be saved before submission.",
|
|
4137
|
+
true,
|
|
4138
|
+
{ phase: "prepare", rejectedBeforeSubmission: true, failureCode: "RECOVERY_STORAGE_UNAVAILABLE" }
|
|
4139
|
+
);
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
async function checkpoint(callback, pending) {
|
|
4143
|
+
try {
|
|
4144
|
+
await callback?.(pending);
|
|
4145
|
+
} catch {
|
|
4146
|
+
throw sponsorError(
|
|
4147
|
+
"Withdrawal status cannot be saved before submission.",
|
|
4148
|
+
true,
|
|
4149
|
+
{ phase: "prepare", rejectedBeforeSubmission: true, failureCode: "RECOVERY_STORAGE_UNAVAILABLE" }
|
|
4150
|
+
);
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
function finalPaymasterFields(value) {
|
|
4154
|
+
const result = {};
|
|
4155
|
+
for (const key2 of [
|
|
4156
|
+
"paymasterAndData",
|
|
4157
|
+
"callGasLimit",
|
|
4158
|
+
"verificationGasLimit",
|
|
4159
|
+
"preVerificationGas",
|
|
4160
|
+
"maxFeePerGas",
|
|
4161
|
+
"maxPriorityFeePerGas"
|
|
4162
|
+
]) {
|
|
4163
|
+
const field = value[key2];
|
|
4164
|
+
if (field === void 0) continue;
|
|
4165
|
+
if (typeof field !== "string" || !/^0x[0-9a-fA-F]+$/.test(field))
|
|
4166
|
+
throw sponsorError(
|
|
4167
|
+
"Paymaster returned an invalid final operation.",
|
|
4168
|
+
true,
|
|
4169
|
+
{ phase: "prepare", rejectedBeforeSubmission: true, failureCode: "INVALID_PAYMASTER_QUOTE" }
|
|
4170
|
+
);
|
|
4171
|
+
result[key2] = field;
|
|
4172
|
+
}
|
|
4173
|
+
return result;
|
|
4174
|
+
}
|
|
3941
4175
|
async function requestIntent(input) {
|
|
3942
4176
|
const baseUrl = (input.routingApiBaseUrl ?? DEFAULT_ROUTING_API).replace(/\/$/, "");
|
|
3943
4177
|
let response;
|
|
@@ -3959,18 +4193,35 @@ async function requestIntent(input) {
|
|
|
3959
4193
|
action: input.action,
|
|
3960
4194
|
yieldseekerUserId: input.userId,
|
|
3961
4195
|
yieldseekerAgentId: input.agentId
|
|
3962
|
-
})
|
|
4196
|
+
}),
|
|
4197
|
+
signal: AbortSignal.timeout(15e3)
|
|
3963
4198
|
});
|
|
3964
4199
|
} catch {
|
|
3965
|
-
throw sponsorError(
|
|
4200
|
+
throw sponsorError(
|
|
4201
|
+
"Withdrawal sponsorship could not be requested.",
|
|
4202
|
+
true,
|
|
4203
|
+
{ phase: "prepare", rejectedBeforeSubmission: true, failureCode: "INTENT_NETWORK_ERROR" }
|
|
4204
|
+
);
|
|
3966
4205
|
}
|
|
3967
4206
|
const envelope = await response.json().catch(() => null);
|
|
3968
4207
|
const intent = envelope?.data;
|
|
3969
4208
|
if (!response.ok || !envelope?.success || !intent?.intent || !["eoa", "smart"].includes(intent.ownerPath)) {
|
|
4209
|
+
const rawCode = typeof envelope?.failureCode === "string" ? envelope.failureCode : envelope?.code;
|
|
4210
|
+
const failureCode = typeof rawCode === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(rawCode) ? rawCode : void 0;
|
|
4211
|
+
if (input.refreshYieldseekerSignature && (failureCode === "YIELDSEEKER_AUTH_REJECTED" || failureCode === "AUTH_REJECTED" || envelope?.sponsorshipStage === "provider-auth" && (envelope.providerStatus === 401 || envelope.providerStatus === 403))) {
|
|
4212
|
+
const signature = await input.refreshYieldseekerSignature(input.yieldseekerSignature);
|
|
4213
|
+
return requestIntent({ ...input, yieldseekerSignature: signature, refreshYieldseekerSignature: void 0 });
|
|
4214
|
+
}
|
|
3970
4215
|
throw sponsorError(
|
|
3971
4216
|
"Withdrawal sponsorship is currently unavailable.",
|
|
3972
4217
|
true,
|
|
3973
|
-
{
|
|
4218
|
+
{
|
|
4219
|
+
statusCode: response.status,
|
|
4220
|
+
phase: "prepare",
|
|
4221
|
+
rejectedBeforeSubmission: true,
|
|
4222
|
+
failureCode: failureCode ?? "INTENT_REJECTED",
|
|
4223
|
+
providerReason: providerReason(envelope?.message)
|
|
4224
|
+
}
|
|
3974
4225
|
);
|
|
3975
4226
|
}
|
|
3976
4227
|
const url = new URL(`${baseUrl}/api/v1/sponsor/yieldseeker-withdraw/rpc`);
|
|
@@ -3983,19 +4234,55 @@ async function rpc(url, method, params, phase) {
|
|
|
3983
4234
|
response = await fetch(url, {
|
|
3984
4235
|
method: "POST",
|
|
3985
4236
|
headers: { "content-type": "application/json" },
|
|
3986
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
|
|
4237
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
4238
|
+
signal: AbortSignal.timeout(15e3)
|
|
3987
4239
|
});
|
|
3988
4240
|
} catch {
|
|
3989
|
-
throw sponsorError(
|
|
4241
|
+
throw sponsorError(
|
|
4242
|
+
"Withdrawal sponsorship request status is unknown.",
|
|
4243
|
+
phase === "prepare",
|
|
4244
|
+
{
|
|
4245
|
+
phase,
|
|
4246
|
+
rpcMethod: method,
|
|
4247
|
+
...phase === "prepare" ? { rejectedBeforeSubmission: true } : {},
|
|
4248
|
+
failureCode: "RPC_NETWORK_ERROR"
|
|
4249
|
+
}
|
|
4250
|
+
);
|
|
3990
4251
|
}
|
|
3991
4252
|
const payload = await response.json().catch(() => null);
|
|
3992
4253
|
if (!response.ok || payload?.error || payload?.result === void 0) {
|
|
3993
4254
|
const rejection = payload?.error;
|
|
3994
|
-
const
|
|
4255
|
+
const diagnostics = rpcDiagnostics(rejection?.data);
|
|
4256
|
+
const executionReverted = rejection?.code === -32521 || diagnostics.failureCode === "SPONSOR_EXECUTION_REVERTED";
|
|
4257
|
+
const details = {
|
|
4258
|
+
statusCode: response.status,
|
|
4259
|
+
rpcCode: rejection?.code,
|
|
4260
|
+
rpcMethod: method,
|
|
4261
|
+
phase,
|
|
4262
|
+
providerReason: providerReason(rejection?.data?.providerReason) ?? providerReason(rejection?.message),
|
|
4263
|
+
failureCode: "RPC_REJECTED",
|
|
4264
|
+
...diagnostics
|
|
4265
|
+
};
|
|
4266
|
+
if (phase === "prepare" && executionReverted)
|
|
4267
|
+
throw new OwneyError(
|
|
4268
|
+
"WITHDRAW_FAILED",
|
|
4269
|
+
"Yieldseeker withdrawal simulation reverted before submission.",
|
|
4270
|
+
{
|
|
4271
|
+
...details,
|
|
4272
|
+
failureCode: "SPONSOR_EXECUTION_REVERTED",
|
|
4273
|
+
safeToFallback: false,
|
|
4274
|
+
rejectedBeforeSubmission: true
|
|
4275
|
+
},
|
|
4276
|
+
"yieldseeker"
|
|
4277
|
+
);
|
|
4278
|
+
const definitiveRejection = phase === "send" && !executionReverted && response.status < 500 && !!rejection && (rejection.data?.rejectedBeforeSubmission === true || [-32501, -32502, -32503, -32504, -32505, -32507].includes(rejection.code ?? 0) || /maxFeePerGas.*(?:too low|lower than|less than|must be at least)/i.test(rejection.message ?? ""));
|
|
3995
4279
|
throw sponsorError(
|
|
3996
4280
|
phase !== "prepare" && !definitiveRejection ? "Sponsored withdrawal submission status is unknown." : "Withdrawal sponsorship was declined before submission.",
|
|
3997
4281
|
phase === "prepare" || definitiveRejection,
|
|
3998
|
-
{
|
|
4282
|
+
{
|
|
4283
|
+
...details,
|
|
4284
|
+
...phase === "prepare" || definitiveRejection ? { rejectedBeforeSubmission: true } : {}
|
|
4285
|
+
}
|
|
3999
4286
|
);
|
|
4000
4287
|
}
|
|
4001
4288
|
return payload.result;
|
|
@@ -4015,13 +4302,15 @@ async function submitEoa(input) {
|
|
|
4015
4302
|
let nonce;
|
|
4016
4303
|
let fees;
|
|
4017
4304
|
try {
|
|
4018
|
-
nonce = await
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4305
|
+
[nonce, fees] = await Promise.all([
|
|
4306
|
+
input.client.readContract({
|
|
4307
|
+
address: ENTRY_POINT_V06,
|
|
4308
|
+
abi: ENTRY_POINT_NONCE_ABI,
|
|
4309
|
+
functionName: "getNonce",
|
|
4310
|
+
args: [input.agentWallet, 0n]
|
|
4311
|
+
}),
|
|
4312
|
+
input.client.estimateFeesPerGas()
|
|
4313
|
+
]);
|
|
4025
4314
|
} catch {
|
|
4026
4315
|
throw sponsorError("Could not prepare the sponsored withdrawal.", true);
|
|
4027
4316
|
}
|
|
@@ -4029,14 +4318,14 @@ async function submitEoa(input) {
|
|
|
4029
4318
|
throw sponsorError("Base did not return EIP-1559 gas fees.", true);
|
|
4030
4319
|
let operation = {
|
|
4031
4320
|
sender: input.agentWallet,
|
|
4032
|
-
nonce: (0,
|
|
4321
|
+
nonce: (0, import_viem10.toHex)(nonce),
|
|
4033
4322
|
initCode: "0x",
|
|
4034
4323
|
callData: input.callData,
|
|
4035
|
-
callGasLimit: (0,
|
|
4036
|
-
verificationGasLimit: (0,
|
|
4037
|
-
preVerificationGas: (0,
|
|
4038
|
-
maxFeePerGas: (0,
|
|
4039
|
-
maxPriorityFeePerGas: (0,
|
|
4324
|
+
callGasLimit: (0, import_viem10.toHex)(3e5),
|
|
4325
|
+
verificationGasLimit: (0, import_viem10.toHex)(3e5),
|
|
4326
|
+
preVerificationGas: (0, import_viem10.toHex)(6e4),
|
|
4327
|
+
maxFeePerGas: (0, import_viem10.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
|
|
4328
|
+
maxPriorityFeePerGas: (0, import_viem10.toHex)(fees.maxPriorityFeePerGas),
|
|
4040
4329
|
paymasterAndData: "0x",
|
|
4041
4330
|
signature: "0x"
|
|
4042
4331
|
};
|
|
@@ -4044,7 +4333,7 @@ async function submitEoa(input) {
|
|
|
4044
4333
|
const stub = await rpc(
|
|
4045
4334
|
input.rpcUrl,
|
|
4046
4335
|
"pm_getPaymasterStubData",
|
|
4047
|
-
[operation, ENTRY_POINT_V06, (0,
|
|
4336
|
+
[operation, ENTRY_POINT_V06, (0, import_viem10.toHex)(8453), {}],
|
|
4048
4337
|
"prepare"
|
|
4049
4338
|
);
|
|
4050
4339
|
if (!stub.paymasterAndData?.startsWith("0x"))
|
|
@@ -4059,39 +4348,63 @@ async function submitEoa(input) {
|
|
|
4059
4348
|
const final = await rpc(
|
|
4060
4349
|
input.rpcUrl,
|
|
4061
4350
|
"pm_getPaymasterData",
|
|
4062
|
-
[operation, ENTRY_POINT_V06, (0,
|
|
4351
|
+
[operation, ENTRY_POINT_V06, (0, import_viem10.toHex)(8453), {}],
|
|
4063
4352
|
"prepare"
|
|
4064
4353
|
);
|
|
4065
4354
|
if (!final.paymasterAndData?.startsWith("0x"))
|
|
4066
4355
|
throw sponsorError("CDP did not return final paymaster data.", true);
|
|
4067
|
-
operation = { ...operation,
|
|
4356
|
+
operation = { ...operation, ...finalPaymasterFields(final) };
|
|
4068
4357
|
}
|
|
4069
4358
|
const userOpHash = operationHash(operation);
|
|
4070
|
-
|
|
4359
|
+
await assertOwnerSession(input.state, input.owner);
|
|
4360
|
+
const wallet = (0, import_viem10.createWalletClient)({
|
|
4071
4361
|
account: input.owner,
|
|
4072
4362
|
chain: import_chains3.base,
|
|
4073
|
-
transport: (0,
|
|
4363
|
+
transport: (0, import_viem10.custom)(input.state.provider)
|
|
4074
4364
|
});
|
|
4075
4365
|
const signature = await wallet.signMessage({ message: { raw: userOpHash } });
|
|
4076
4366
|
operation = { ...operation, signature };
|
|
4077
|
-
input.
|
|
4367
|
+
await assertOwnerSession(input.state, input.owner);
|
|
4368
|
+
const userOperation = {
|
|
4369
|
+
sender: operation.sender,
|
|
4370
|
+
nonce: operation.nonce,
|
|
4371
|
+
initCode: operation.initCode,
|
|
4372
|
+
callData: operation.callData,
|
|
4373
|
+
callGasLimit: operation.callGasLimit,
|
|
4374
|
+
verificationGasLimit: operation.verificationGasLimit,
|
|
4375
|
+
preVerificationGas: operation.preVerificationGas,
|
|
4376
|
+
maxFeePerGas: operation.maxFeePerGas,
|
|
4377
|
+
maxPriorityFeePerGas: operation.maxPriorityFeePerGas,
|
|
4378
|
+
paymasterAndData: operation.paymasterAndData
|
|
4379
|
+
};
|
|
4380
|
+
savePending(input.onPending, { userOpHash, userOperation }, true);
|
|
4381
|
+
await checkpoint(input.onBeforeSubmission, { userOpHash, userOperation });
|
|
4078
4382
|
const submittedHash = await rpc(
|
|
4079
4383
|
input.rpcUrl,
|
|
4080
4384
|
"eth_sendUserOperation",
|
|
4081
4385
|
[operation, ENTRY_POINT_V06],
|
|
4082
4386
|
"send"
|
|
4083
4387
|
);
|
|
4084
|
-
if (submittedHash.toLowerCase() !== userOpHash.toLowerCase())
|
|
4388
|
+
if (!isHash(submittedHash) || submittedHash.toLowerCase() !== userOpHash.toLowerCase())
|
|
4085
4389
|
throw sponsorError(
|
|
4086
4390
|
"Bundler returned a different UserOperation hash.",
|
|
4087
4391
|
false,
|
|
4088
|
-
{ userOpHash, submittedHash }
|
|
4392
|
+
{ userOpHash, submittedHash, phase: "send", failureCode: "BUNDLER_HASH_MISMATCH" }
|
|
4089
4393
|
);
|
|
4090
|
-
|
|
4091
|
-
|
|
4394
|
+
const deadline = Date.now() + OBSERVATION_WINDOW_MS;
|
|
4395
|
+
let lastReadError;
|
|
4396
|
+
for (let poll = 0; poll < 40 && Date.now() < deadline; poll++) {
|
|
4397
|
+
let response;
|
|
4398
|
+
try {
|
|
4399
|
+
response = await rpc(input.rpcUrl, "eth_getUserOperationReceipt", [userOpHash], "observe");
|
|
4400
|
+
} catch (error) {
|
|
4401
|
+
lastReadError = error;
|
|
4402
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(OBSERVATION_POLL_MS, Math.max(0, deadline - Date.now()))));
|
|
4403
|
+
continue;
|
|
4404
|
+
}
|
|
4092
4405
|
const txHash = response?.receipt?.transactionHash;
|
|
4093
|
-
if (txHash) {
|
|
4094
|
-
input.onPending
|
|
4406
|
+
if (isHash(txHash)) {
|
|
4407
|
+
savePending(input.onPending, { userOpHash, txHash });
|
|
4095
4408
|
if (response?.success === false)
|
|
4096
4409
|
throw new OwneyError(
|
|
4097
4410
|
"AGENT_TRANSACTION_REVERTED",
|
|
@@ -4101,12 +4414,17 @@ async function submitEoa(input) {
|
|
|
4101
4414
|
);
|
|
4102
4415
|
return { txHash, receipt: await waitForTransaction(input.client, txHash) };
|
|
4103
4416
|
}
|
|
4104
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
4417
|
+
await new Promise((resolve) => setTimeout(resolve, OBSERVATION_POLL_MS));
|
|
4105
4418
|
}
|
|
4106
4419
|
throw sponsorError(
|
|
4107
4420
|
"Sponsored withdrawal is still pending; check status before retrying.",
|
|
4108
4421
|
false,
|
|
4109
|
-
{
|
|
4422
|
+
{
|
|
4423
|
+
userOpHash,
|
|
4424
|
+
phase: "observe",
|
|
4425
|
+
failureCode: "RECEIPT_UNRESOLVED",
|
|
4426
|
+
...lastReadError instanceof OwneyError ? { lastObservationError: lastReadError.details } : {}
|
|
4427
|
+
}
|
|
4110
4428
|
);
|
|
4111
4429
|
}
|
|
4112
4430
|
async function submitSmart(input) {
|
|
@@ -4119,21 +4437,22 @@ async function submitSmart(input) {
|
|
|
4119
4437
|
} catch {
|
|
4120
4438
|
throw sponsorError("This account cannot check paymaster support on Base.", true);
|
|
4121
4439
|
}
|
|
4122
|
-
|
|
4440
|
+
const supportsPaymaster = capabilities?.[(0, import_viem10.toHex)(8453)]?.paymasterService?.supported ?? capabilities?.["8453"]?.paymasterService?.supported ?? capabilities?.["0x0"]?.paymasterService?.supported;
|
|
4441
|
+
if (supportsPaymaster !== true)
|
|
4123
4442
|
throw sponsorError("This account cannot use a paymaster on Base.", true);
|
|
4443
|
+
await assertOwnerSession(input.state, input.owner);
|
|
4124
4444
|
let sent;
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
throw sponsorError("Upgrade or withdrawal status cannot be saved.", true);
|
|
4129
|
-
}
|
|
4445
|
+
const requestedId = (0, import_viem10.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
|
|
4446
|
+
savePending(input.onPending, { callsId: requestedId }, true);
|
|
4447
|
+
await checkpoint(input.onBeforeSubmission, { callsId: requestedId });
|
|
4130
4448
|
try {
|
|
4131
4449
|
sent = await input.state.provider.request({
|
|
4132
4450
|
method: "wallet_sendCalls",
|
|
4133
4451
|
params: [{
|
|
4134
4452
|
version: "2.0.0",
|
|
4135
4453
|
from: input.owner,
|
|
4136
|
-
chainId: (0,
|
|
4454
|
+
chainId: (0, import_viem10.toHex)(8453),
|
|
4455
|
+
id: requestedId,
|
|
4137
4456
|
atomicRequired: false,
|
|
4138
4457
|
calls: [{ to: input.agentWallet, value: "0x0", data: input.callData }],
|
|
4139
4458
|
capabilities: { paymasterService: { url: input.rpcUrl } }
|
|
@@ -4142,17 +4461,27 @@ async function submitSmart(input) {
|
|
|
4142
4461
|
} catch (error) {
|
|
4143
4462
|
if (error?.code === 4001) throw error;
|
|
4144
4463
|
if ([4200, -32601].includes(Number(error?.code)))
|
|
4145
|
-
throw sponsorError(
|
|
4146
|
-
|
|
4464
|
+
throw sponsorError(
|
|
4465
|
+
"This account does not support sponsored calls.",
|
|
4466
|
+
true,
|
|
4467
|
+
{ phase: "send", failureCode: "WALLET_CALLS_UNSUPPORTED" }
|
|
4468
|
+
);
|
|
4469
|
+
throw sponsorError(
|
|
4470
|
+
"Wallet submission status is unknown. Check activity before retrying.",
|
|
4471
|
+
false,
|
|
4472
|
+
{ callsId: requestedId, phase: "send", failureCode: "WALLET_SEND_UNRESOLVED" }
|
|
4473
|
+
);
|
|
4147
4474
|
}
|
|
4148
4475
|
const callsId = typeof sent === "string" ? sent : sent?.id;
|
|
4149
|
-
if (
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4476
|
+
if (typeof callsId !== "string" || callsId.length === 0 || callsId.length > 8194)
|
|
4477
|
+
throw sponsorError(
|
|
4478
|
+
"Wallet returned no calls ID; submission status is unknown.",
|
|
4479
|
+
false,
|
|
4480
|
+
{ callsId: requestedId, phase: "send", failureCode: "WALLET_SEND_UNRESOLVED" }
|
|
4481
|
+
);
|
|
4482
|
+
savePending(input.onPending, { callsId });
|
|
4483
|
+
const deadline = Date.now() + OBSERVATION_WINDOW_MS;
|
|
4484
|
+
for (let poll = 0; poll < 40 && Date.now() < deadline; poll++) {
|
|
4156
4485
|
let status;
|
|
4157
4486
|
try {
|
|
4158
4487
|
status = await input.state.provider.request({
|
|
@@ -4160,37 +4489,44 @@ async function submitSmart(input) {
|
|
|
4160
4489
|
params: [callsId]
|
|
4161
4490
|
});
|
|
4162
4491
|
} catch {
|
|
4163
|
-
|
|
4492
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(OBSERVATION_POLL_MS, Math.max(0, deadline - Date.now()))));
|
|
4493
|
+
continue;
|
|
4164
4494
|
}
|
|
4165
|
-
if (status?.status
|
|
4495
|
+
if (status?.id !== void 0 && status.id !== callsId || status?.chainId !== void 0 && Number(status.chainId) !== 8453)
|
|
4496
|
+
throw sponsorError(
|
|
4497
|
+
"Wallet returned a different sponsored operation status.",
|
|
4498
|
+
false,
|
|
4499
|
+
{ callsId, phase: "observe", failureCode: "WALLET_STATUS_MISMATCH" }
|
|
4500
|
+
);
|
|
4501
|
+
if (Number(status?.status) === 400 || Number(status?.status) === 500)
|
|
4166
4502
|
throw sponsorError(
|
|
4167
4503
|
"Sponsored withdrawal failed in the wallet.",
|
|
4168
4504
|
false,
|
|
4169
|
-
{ callsId, executionFailed: true }
|
|
4505
|
+
{ callsId, executionFailed: true, phase: "observe", failureCode: "WALLET_EXECUTION_FAILED" }
|
|
4170
4506
|
);
|
|
4171
|
-
const
|
|
4507
|
+
const hashes = [...new Set((status?.receipts ?? []).map((receipt) => receipt.transactionHash).filter(isHash))];
|
|
4508
|
+
const txHash = hashes.length === 1 ? hashes[0] : void 0;
|
|
4172
4509
|
if (txHash) {
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
"
|
|
4178
|
-
|
|
4179
|
-
|
|
4510
|
+
savePending(input.onPending, { callsId, txHash });
|
|
4511
|
+
if (status?.receipts?.some((receipt) => receipt.transactionHash === txHash && /^(?:0x0+|0|failed|reverted)$/i.test(String(receipt.status))))
|
|
4512
|
+
throw new OwneyError(
|
|
4513
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
4514
|
+
"Sponsored withdrawal execution reverted.",
|
|
4515
|
+
{ callsId, transactionHash: txHash },
|
|
4516
|
+
"yieldseeker"
|
|
4180
4517
|
);
|
|
4181
|
-
}
|
|
4182
4518
|
return { txHash, receipt: await waitForTransaction(input.client, txHash) };
|
|
4183
4519
|
}
|
|
4184
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
4520
|
+
await new Promise((resolve) => setTimeout(resolve, OBSERVATION_POLL_MS));
|
|
4185
4521
|
}
|
|
4186
4522
|
throw sponsorError(
|
|
4187
4523
|
"Sponsored withdrawal is still pending; check status before retrying.",
|
|
4188
4524
|
false,
|
|
4189
|
-
{ callsId }
|
|
4525
|
+
{ callsId, phase: "observe", failureCode: "RECEIPT_UNRESOLVED" }
|
|
4190
4526
|
);
|
|
4191
4527
|
}
|
|
4192
4528
|
async function submitYieldseekerSponsoredWithdrawal(input) {
|
|
4193
|
-
const owner = (0,
|
|
4529
|
+
const owner = (0, import_viem10.getAddress)(input.state.walletAddress);
|
|
4194
4530
|
const { intent, rpcUrl } = await requestIntent({
|
|
4195
4531
|
apiKey: input.apiKey,
|
|
4196
4532
|
routingApiBaseUrl: input.routingApiBaseUrl,
|
|
@@ -4201,13 +4537,14 @@ async function submitYieldseekerSponsoredWithdrawal(input) {
|
|
|
4201
4537
|
agentWallet: input.agentWallet,
|
|
4202
4538
|
asset: input.asset,
|
|
4203
4539
|
callData: input.callData,
|
|
4204
|
-
action: "withdraw"
|
|
4540
|
+
action: "withdraw",
|
|
4541
|
+
refreshYieldseekerSignature: input.refreshYieldseekerSignature
|
|
4205
4542
|
});
|
|
4206
4543
|
const args = { ...input, owner, rpcUrl };
|
|
4207
4544
|
return intent.ownerPath === "eoa" ? submitEoa(args) : submitSmart(args);
|
|
4208
4545
|
}
|
|
4209
4546
|
async function submitYieldseekerSponsoredUpgrade(input) {
|
|
4210
|
-
const owner = (0,
|
|
4547
|
+
const owner = (0, import_viem10.getAddress)(input.state.walletAddress);
|
|
4211
4548
|
const { intent, rpcUrl } = await requestIntent({
|
|
4212
4549
|
apiKey: input.apiKey,
|
|
4213
4550
|
routingApiBaseUrl: input.routingApiBaseUrl,
|
|
@@ -4218,7 +4555,8 @@ async function submitYieldseekerSponsoredUpgrade(input) {
|
|
|
4218
4555
|
agentWallet: input.agentWallet,
|
|
4219
4556
|
asset: input.asset,
|
|
4220
4557
|
callData: input.callData,
|
|
4221
|
-
action: "upgrade"
|
|
4558
|
+
action: "upgrade",
|
|
4559
|
+
refreshYieldseekerSignature: input.refreshYieldseekerSignature
|
|
4222
4560
|
});
|
|
4223
4561
|
if (intent.ownerPath !== "smart")
|
|
4224
4562
|
throw sponsorError("V1 upgrade sponsorship requires a Base Account owner.", true);
|
|
@@ -4240,6 +4578,7 @@ var YIELDSEEKER_READ_FAILURE_COOLDOWN_MS = 6e4;
|
|
|
4240
4578
|
var YIELDSEEKER_SETTLEMENT_WINDOW_MS = 5 * 6e4;
|
|
4241
4579
|
var YIELDSEEKER_ACTIVITY_CACHE_MS = 6e4;
|
|
4242
4580
|
var YIELDSEEKER_MAX_WITHDRAW_SWEEPS = 3;
|
|
4581
|
+
var YIELDSEEKER_MAX_POSITION_VERIFICATION_MS = 2e3;
|
|
4243
4582
|
function generateYieldseekerUsername() {
|
|
4244
4583
|
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
4245
4584
|
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
@@ -4250,6 +4589,13 @@ function isUsernameConflict(error) {
|
|
|
4250
4589
|
return code.includes("USERNAME") && (code.includes("TAKEN") || code.includes("EXIST") || code.includes("UNAVAILABLE") || code.includes("IN_USE"));
|
|
4251
4590
|
}
|
|
4252
4591
|
var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
4592
|
+
{
|
|
4593
|
+
type: "function",
|
|
4594
|
+
name: "feeTracker",
|
|
4595
|
+
stateMutability: "view",
|
|
4596
|
+
inputs: [],
|
|
4597
|
+
outputs: [{ name: "", type: "address" }]
|
|
4598
|
+
},
|
|
4253
4599
|
{
|
|
4254
4600
|
type: "function",
|
|
4255
4601
|
name: "withdrawAssetToUser",
|
|
@@ -4272,6 +4618,13 @@ var YIELDSEEKER_AGENT_WALLET_ABI = [
|
|
|
4272
4618
|
outputs: []
|
|
4273
4619
|
}
|
|
4274
4620
|
];
|
|
4621
|
+
var YIELDSEEKER_FEE_TRACKER_ABI = [{
|
|
4622
|
+
type: "function",
|
|
4623
|
+
name: "getFeesOwed",
|
|
4624
|
+
stateMutability: "view",
|
|
4625
|
+
inputs: [{ name: "wallet", type: "address" }],
|
|
4626
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
4627
|
+
}];
|
|
4275
4628
|
function query(params) {
|
|
4276
4629
|
const search = new URLSearchParams();
|
|
4277
4630
|
for (const [key2, value] of Object.entries(params)) {
|
|
@@ -4351,21 +4704,26 @@ var YieldseekerAgent = class {
|
|
|
4351
4704
|
if (useCachedV2 && cached && Date.now() - cached.at < 15 * 6e4) return { ...cached.status };
|
|
4352
4705
|
const status = await readYieldseekerWalletStatus(
|
|
4353
4706
|
this.getReceiptClient(),
|
|
4354
|
-
(0,
|
|
4355
|
-
(0,
|
|
4707
|
+
(0, import_viem11.getAddress)(context.wallet.walletAddress),
|
|
4708
|
+
(0, import_viem11.getAddress)(state.walletAddress),
|
|
4356
4709
|
YIELDSEEKER_ASSET_METADATA[asset].address
|
|
4357
4710
|
);
|
|
4711
|
+
this.rememberWalletStatus(key2, status);
|
|
4712
|
+
return status;
|
|
4713
|
+
}
|
|
4714
|
+
rememberWalletStatus(key2, status) {
|
|
4358
4715
|
if (status.verified && status.version === "v2") {
|
|
4359
4716
|
this.withdrawalVersionCache.set(key2, { at: Date.now(), status: { ...status } });
|
|
4360
4717
|
if (this.withdrawalVersionCache.size > 128)
|
|
4361
4718
|
this.withdrawalVersionCache.delete(this.withdrawalVersionCache.keys().next().value);
|
|
4362
4719
|
} else this.withdrawalVersionCache.delete(key2);
|
|
4363
|
-
return status;
|
|
4364
4720
|
}
|
|
4365
4721
|
/** Explicit owner action. This never runs as a side effect of withdrawal. */
|
|
4366
4722
|
async upgradeWallet(state, chainId, asset, gasMode = "paid") {
|
|
4723
|
+
const generation = this.readGeneration;
|
|
4367
4724
|
const before = await this.getWalletStatus(state, chainId, asset);
|
|
4368
|
-
|
|
4725
|
+
this.assertSession(generation, chainId);
|
|
4726
|
+
if (!before?.verified || before.version === "unknown") {
|
|
4369
4727
|
throw new OwneyError(
|
|
4370
4728
|
"WITHDRAW_FAILED",
|
|
4371
4729
|
"This Yieldseeker wallet cannot be upgraded to the reviewed V2 implementation.",
|
|
@@ -4373,48 +4731,77 @@ var YieldseekerAgent = class {
|
|
|
4373
4731
|
this.id
|
|
4374
4732
|
);
|
|
4375
4733
|
}
|
|
4376
|
-
const
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
if (!storage5)
|
|
4734
|
+
const versionKey = `${chainId}:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}:${asset}`;
|
|
4735
|
+
const pendingKey2 = `owney.yieldseeker.upgrade:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}`;
|
|
4736
|
+
const confirmUpgrade = async (hash, receipt2) => {
|
|
4737
|
+
this.withdrawalVersionCache.delete(versionKey);
|
|
4738
|
+
const upgradeTopic = (0, import_viem11.encodeEventTopics)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded" })[0];
|
|
4739
|
+
const lastUpgrade = receipt2?.logs.filter((log) => log.address.toLowerCase() === before.agentWallet.toLowerCase() && log.topics[0] === upgradeTopic).at(-1);
|
|
4740
|
+
const expectedUpgrade = (() => {
|
|
4741
|
+
if (!lastUpgrade || lastUpgrade.removed) return false;
|
|
4742
|
+
try {
|
|
4743
|
+
const event = (0, import_viem11.decodeEventLog)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded", data: lastUpgrade.data, topics: lastUpgrade.topics });
|
|
4744
|
+
return event.args.implementation.toLowerCase() === YIELDSEEKER_V2_IMPLEMENTATION.toLowerCase();
|
|
4745
|
+
} catch {
|
|
4746
|
+
return false;
|
|
4747
|
+
}
|
|
4748
|
+
})();
|
|
4749
|
+
if (!receipt2 || receipt2.status !== "success" || receipt2.transactionHash.toLowerCase() !== hash.toLowerCase() || typeof receipt2.blockNumber !== "bigint" || !expectedUpgrade)
|
|
4393
4750
|
throw new OwneyError(
|
|
4394
|
-
"
|
|
4395
|
-
"
|
|
4396
|
-
{
|
|
4751
|
+
"WITHDRAW_FAILED",
|
|
4752
|
+
"Yieldseeker wallet upgrade receipt could not be verified.",
|
|
4753
|
+
{ txHash: hash, reason: "UPGRADE_RECEIPT_MISMATCH" },
|
|
4397
4754
|
this.id
|
|
4398
4755
|
);
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4756
|
+
this.assertSession(generation, chainId);
|
|
4757
|
+
const status = {
|
|
4758
|
+
...before,
|
|
4759
|
+
implementation: YIELDSEEKER_V2_IMPLEMENTATION,
|
|
4760
|
+
version: "v2",
|
|
4761
|
+
upgradeAvailable: false
|
|
4762
|
+
};
|
|
4763
|
+
this.rememberWalletStatus(versionKey, status);
|
|
4764
|
+
return { txHash: hash, status };
|
|
4765
|
+
};
|
|
4766
|
+
let storage5;
|
|
4767
|
+
try {
|
|
4768
|
+
storage5 = typeof window === "undefined" ? void 0 : window.localStorage;
|
|
4769
|
+
} catch {
|
|
4770
|
+
storage5 = void 0;
|
|
4771
|
+
}
|
|
4772
|
+
const clearConfirmedUpgrade = () => {
|
|
4402
4773
|
try {
|
|
4403
|
-
|
|
4774
|
+
storage5?.removeItem(pendingKey2);
|
|
4404
4775
|
} catch {
|
|
4405
|
-
throw new OwneyError(
|
|
4406
|
-
"SPONSOR_REQUEST_FAILED",
|
|
4407
|
-
"Upgrade status cannot be read in this browser.",
|
|
4408
|
-
{ safeToFallback: true },
|
|
4409
|
-
this.id
|
|
4410
|
-
);
|
|
4411
4776
|
}
|
|
4777
|
+
};
|
|
4778
|
+
if (!storage5 && (gasMode === "sponsored" || before.ownerPath === "smart"))
|
|
4779
|
+
throw new OwneyError(
|
|
4780
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4781
|
+
"Upgrade status cannot be saved in this browser.",
|
|
4782
|
+
{ safeToFallback: false },
|
|
4783
|
+
this.id
|
|
4784
|
+
);
|
|
4785
|
+
let prior = null;
|
|
4786
|
+
try {
|
|
4787
|
+
const saved = storage5?.getItem(pendingKey2);
|
|
4412
4788
|
try {
|
|
4413
4789
|
prior = JSON.parse(saved ?? "null");
|
|
4414
4790
|
} catch {
|
|
4415
4791
|
prior = { callsId: void 0 };
|
|
4416
4792
|
}
|
|
4417
|
-
|
|
4793
|
+
} catch {
|
|
4794
|
+
if (gasMode === "paid" && before.ownerPath === "eoa") storage5 = void 0;
|
|
4795
|
+
else throw new OwneyError(
|
|
4796
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4797
|
+
"Upgrade status cannot be read in this browser.",
|
|
4798
|
+
{ safeToFallback: false },
|
|
4799
|
+
this.id
|
|
4800
|
+
);
|
|
4801
|
+
}
|
|
4802
|
+
if (prior) {
|
|
4803
|
+
let earlierHash = this.isTransactionHash(prior.txHash) ? prior.txHash : void 0;
|
|
4804
|
+
if (!earlierHash) {
|
|
4418
4805
|
if (!prior.callsId)
|
|
4419
4806
|
throw new OwneyError(
|
|
4420
4807
|
"SPONSOR_REQUEST_FAILED",
|
|
@@ -4424,10 +4811,7 @@ var YieldseekerAgent = class {
|
|
|
4424
4811
|
);
|
|
4425
4812
|
let callsStatus;
|
|
4426
4813
|
try {
|
|
4427
|
-
callsStatus = await state.provider.request({
|
|
4428
|
-
method: "wallet_getCallsStatus",
|
|
4429
|
-
params: [prior.callsId]
|
|
4430
|
-
});
|
|
4814
|
+
callsStatus = await state.provider.request({ method: "wallet_getCallsStatus", params: [prior.callsId] });
|
|
4431
4815
|
} catch {
|
|
4432
4816
|
throw new OwneyError(
|
|
4433
4817
|
"SPONSOR_REQUEST_FAILED",
|
|
@@ -4436,8 +4820,15 @@ var YieldseekerAgent = class {
|
|
|
4436
4820
|
this.id
|
|
4437
4821
|
);
|
|
4438
4822
|
}
|
|
4439
|
-
if (callsStatus?.
|
|
4440
|
-
|
|
4823
|
+
if (callsStatus?.id !== void 0 && callsStatus.id !== prior.callsId || callsStatus?.chainId !== void 0 && Number(callsStatus.chainId) !== chainId)
|
|
4824
|
+
throw new OwneyError(
|
|
4825
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4826
|
+
"The earlier upgrade returned a different operation status.",
|
|
4827
|
+
{ safeToFallback: false, callsId: prior.callsId },
|
|
4828
|
+
this.id
|
|
4829
|
+
);
|
|
4830
|
+
if (Number(callsStatus?.status) === 400 || Number(callsStatus?.status) === 500) {
|
|
4831
|
+
storage5.removeItem(pendingKey2);
|
|
4441
4832
|
throw new OwneyError(
|
|
4442
4833
|
"SPONSOR_REQUEST_FAILED",
|
|
4443
4834
|
"The earlier sponsored upgrade failed.",
|
|
@@ -4445,22 +4836,39 @@ var YieldseekerAgent = class {
|
|
|
4445
4836
|
this.id
|
|
4446
4837
|
);
|
|
4447
4838
|
}
|
|
4448
|
-
const
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4839
|
+
const hashes = [...new Set((callsStatus?.receipts ?? []).map((value) => value.transactionHash).filter(this.isTransactionHash))];
|
|
4840
|
+
earlierHash = hashes.length === 1 ? hashes[0] : void 0;
|
|
4841
|
+
}
|
|
4842
|
+
if (earlierHash) {
|
|
4843
|
+
const recovered = await confirmUpgrade(earlierHash, await this.waitForReceipt(state, chainId, earlierHash));
|
|
4844
|
+
clearConfirmedUpgrade();
|
|
4845
|
+
return recovered;
|
|
4846
|
+
}
|
|
4847
|
+
throw new OwneyError(
|
|
4848
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4849
|
+
"An earlier upgrade is still pending.",
|
|
4850
|
+
{ safeToFallback: false, callsId: prior.callsId },
|
|
4851
|
+
this.id
|
|
4852
|
+
);
|
|
4853
|
+
}
|
|
4854
|
+
if (!before.upgradeAvailable)
|
|
4855
|
+
throw new OwneyError(
|
|
4856
|
+
"WITHDRAW_FAILED",
|
|
4857
|
+
"This Yieldseeker wallet cannot be upgraded to the reviewed V2 implementation.",
|
|
4858
|
+
{ version: before.version },
|
|
4859
|
+
this.id
|
|
4860
|
+
);
|
|
4861
|
+
const data = (0, import_viem11.encodeFunctionData)({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
|
|
4862
|
+
let txHash;
|
|
4863
|
+
let receipt;
|
|
4864
|
+
if (gasMode === "sponsored") {
|
|
4865
|
+
if (before.ownerPath !== "smart")
|
|
4457
4866
|
throw new OwneyError(
|
|
4458
4867
|
"SPONSOR_REQUEST_FAILED",
|
|
4459
|
-
"
|
|
4460
|
-
{ safeToFallback:
|
|
4868
|
+
"EOA owners pay Base gas for the V1 upgrade.",
|
|
4869
|
+
{ safeToFallback: true },
|
|
4461
4870
|
this.id
|
|
4462
4871
|
);
|
|
4463
|
-
}
|
|
4464
4872
|
const context = await this.findAgent(state, chainId, asset);
|
|
4465
4873
|
if (!context) throw new OwneyError("NOT_CONNECTED", "Yieldseeker agent is unavailable.");
|
|
4466
4874
|
try {
|
|
@@ -4470,14 +4878,15 @@ var YieldseekerAgent = class {
|
|
|
4470
4878
|
apiKey: this.owneyApiKey,
|
|
4471
4879
|
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
4472
4880
|
yieldseekerSignature: await this.auth.getToken(state, chainId),
|
|
4881
|
+
refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
|
|
4473
4882
|
userId: context.user.userId,
|
|
4474
4883
|
agentId: context.agent.agentId,
|
|
4475
4884
|
agentWallet: before.agentWallet,
|
|
4476
4885
|
asset: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
4477
4886
|
callData: data,
|
|
4478
4887
|
onPending: (pending) => {
|
|
4479
|
-
const current = storage5.getItem(
|
|
4480
|
-
storage5.setItem(
|
|
4888
|
+
const current = storage5.getItem(pendingKey2);
|
|
4889
|
+
storage5.setItem(pendingKey2, JSON.stringify({
|
|
4481
4890
|
startedAt: Date.now(),
|
|
4482
4891
|
...current ? JSON.parse(current) : {},
|
|
4483
4892
|
...pending
|
|
@@ -4485,31 +4894,25 @@ var YieldseekerAgent = class {
|
|
|
4485
4894
|
}
|
|
4486
4895
|
});
|
|
4487
4896
|
txHash = upgraded.txHash;
|
|
4897
|
+
receipt = upgraded.receipt;
|
|
4488
4898
|
} catch (error) {
|
|
4489
|
-
if (error?.code === 4001 || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) storage5.removeItem(
|
|
4899
|
+
if (error?.code === 4001 || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) storage5.removeItem(pendingKey2);
|
|
4490
4900
|
throw error;
|
|
4491
4901
|
}
|
|
4492
4902
|
} else {
|
|
4493
4903
|
txHash = await this.submitTransaction(state, chainId, {
|
|
4494
|
-
from: (0,
|
|
4904
|
+
from: (0, import_viem11.getAddress)(state.walletAddress),
|
|
4495
4905
|
to: before.agentWallet,
|
|
4496
4906
|
data,
|
|
4497
4907
|
value: "0",
|
|
4498
4908
|
chainId
|
|
4909
|
+
}, void 0, (value) => {
|
|
4910
|
+
receipt = value;
|
|
4499
4911
|
});
|
|
4500
4912
|
}
|
|
4501
|
-
const
|
|
4502
|
-
if (
|
|
4503
|
-
|
|
4504
|
-
"WITHDRAW_FAILED",
|
|
4505
|
-
"Yieldseeker wallet upgrade was not confirmed as V2.",
|
|
4506
|
-
{ txHash, version: status?.version ?? "missing" },
|
|
4507
|
-
this.id
|
|
4508
|
-
);
|
|
4509
|
-
}
|
|
4510
|
-
if (gasMode === "sponsored" && typeof window !== "undefined")
|
|
4511
|
-
window.localStorage.removeItem(`owney.yieldseeker.upgrade:${state.walletAddress.toLowerCase()}:${before.agentWallet.toLowerCase()}`);
|
|
4512
|
-
return { txHash, status };
|
|
4913
|
+
const confirmed = await confirmUpgrade(txHash, receipt ?? await this.waitForReceipt(state, chainId, txHash));
|
|
4914
|
+
if (gasMode === "sponsored") clearConfirmedUpgrade();
|
|
4915
|
+
return confirmed;
|
|
4513
4916
|
}
|
|
4514
4917
|
async disconnect() {
|
|
4515
4918
|
this.withdrawalVersionCache.clear();
|
|
@@ -4582,19 +4985,37 @@ var YieldseekerAgent = class {
|
|
|
4582
4985
|
submitted(txHash);
|
|
4583
4986
|
receipt = await this.waitForReceipt(state, chainId, txHash);
|
|
4584
4987
|
} else {
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4988
|
+
try {
|
|
4989
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
4990
|
+
from: (0, import_viem11.getAddress)(state.walletAddress),
|
|
4991
|
+
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
4992
|
+
data: (0, import_viem11.encodeFunctionData)({
|
|
4993
|
+
abi: import_viem11.erc20Abi,
|
|
4994
|
+
functionName: "transfer",
|
|
4995
|
+
args: [(0, import_viem11.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
4996
|
+
}),
|
|
4997
|
+
value: "0",
|
|
4998
|
+
chainId
|
|
4999
|
+
}, submitted, (value) => {
|
|
5000
|
+
receipt = value;
|
|
5001
|
+
});
|
|
5002
|
+
} catch (error) {
|
|
5003
|
+
if (error instanceof OwneyError && error.details?.ownerTransferNotExecuted === true && error.details.reason === "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL") {
|
|
5004
|
+
notifyFunding(onProgress, {
|
|
5005
|
+
agentId: this.id,
|
|
5006
|
+
chainId,
|
|
5007
|
+
token: asset,
|
|
5008
|
+
walletAddress: state.walletAddress,
|
|
5009
|
+
agentWallet: context.wallet.walletAddress,
|
|
5010
|
+
intendedAmount: amount,
|
|
5011
|
+
txHash: String(error.details.originalTransactionHash),
|
|
5012
|
+
transfer: "failed",
|
|
5013
|
+
registration: "not-applicable",
|
|
5014
|
+
delivery: "not-applicable"
|
|
5015
|
+
});
|
|
5016
|
+
}
|
|
5017
|
+
throw error;
|
|
5018
|
+
}
|
|
4598
5019
|
}
|
|
4599
5020
|
if (this.readGeneration === generation) {
|
|
4600
5021
|
void this.refreshSnapshotAfterMovement(state, chainId, context, "deposit", movement);
|
|
@@ -4604,7 +5025,7 @@ var YieldseekerAgent = class {
|
|
|
4604
5025
|
notifyFunding(onProgress, evidence);
|
|
4605
5026
|
return { txHash, smartWallet: context.wallet.walletAddress, amount, evidence };
|
|
4606
5027
|
}
|
|
4607
|
-
async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto") {
|
|
5028
|
+
async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto", onBeforeSubmission) {
|
|
4608
5029
|
let ownerTransferAttempted = false;
|
|
4609
5030
|
let evidence = {
|
|
4610
5031
|
agentId: this.id,
|
|
@@ -4622,10 +5043,10 @@ var YieldseekerAgent = class {
|
|
|
4622
5043
|
notifyFunding(onProgress, evidence);
|
|
4623
5044
|
}, () => {
|
|
4624
5045
|
ownerTransferAttempted = true;
|
|
4625
|
-
}, gasMode);
|
|
5046
|
+
}, gasMode, onBeforeSubmission);
|
|
4626
5047
|
} catch (error) {
|
|
4627
5048
|
if (error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED") this.withdrawalVersionCache.clear();
|
|
4628
|
-
if (!ownerTransferAttempted || error?.code === 4001 || error instanceof OwneyError && (error.code === "AGENT_TRANSACTION_REVERTED" || error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)))
|
|
5049
|
+
if (!ownerTransferAttempted || error?.code === 4001 || error instanceof OwneyError && (error.code === "AGENT_TRANSACTION_REVERTED" || error.details?.ownerTransferNotExecuted === true || error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)))
|
|
4629
5050
|
notifyFunding(onProgress, {
|
|
4630
5051
|
...evidence,
|
|
4631
5052
|
...error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true ? { userOpHash: void 0, callsId: void 0 } : {},
|
|
@@ -4635,7 +5056,7 @@ var YieldseekerAgent = class {
|
|
|
4635
5056
|
throw error;
|
|
4636
5057
|
}
|
|
4637
5058
|
}
|
|
4638
|
-
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted, gasMode = "auto") {
|
|
5059
|
+
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted, gasMode = "auto", onBeforeSubmission) {
|
|
4639
5060
|
this.assertChain(chainId);
|
|
4640
5061
|
this.assertAsset(asset);
|
|
4641
5062
|
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
@@ -4680,37 +5101,121 @@ var YieldseekerAgent = class {
|
|
|
4680
5101
|
let confirmedMovement = false;
|
|
4681
5102
|
let settlement = null;
|
|
4682
5103
|
try {
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
chainId,
|
|
4687
|
-
context
|
|
4688
|
-
);
|
|
5104
|
+
const maxSettlementBasis = amount === void 0 ? this.snapshotMovement(state, chainId, context, 0n) : null;
|
|
5105
|
+
this.advancePortfolioVersion(this.contextKey(state, chainId, asset));
|
|
5106
|
+
const portfolioRead = amount === void 0 ? this.loadActivityContext(state, chainId, context, {}).then((value) => ({ positions: value.positions, feeRateBps: 0 })) : this.loadPortfolioContext(state, chainId, context).then((value) => ({ positions: value.positions, feeRateBps: value.snapshot.feeRateBps }));
|
|
4689
5107
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
4690
|
-
const
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
5108
|
+
const agentWallet = (0, import_viem11.getAddress)(context.wallet.walletAddress);
|
|
5109
|
+
const feeTrackerRead = withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5110
|
+
address: agentWallet,
|
|
5111
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5112
|
+
functionName: "feeTracker"
|
|
5113
|
+
}), chainId, "eth_call", this.id);
|
|
5114
|
+
const readOwnerFunds = async () => {
|
|
5115
|
+
const feeTracker = await feeTrackerRead;
|
|
4697
5116
|
this.assertSession(generation, chainId, asset);
|
|
4698
|
-
|
|
5117
|
+
if (!(0, import_viem11.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
|
|
5118
|
+
throw this.invalidResponse("wallet fee tracker");
|
|
5119
|
+
const [idle2, fees] = await Promise.all([
|
|
5120
|
+
withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5121
|
+
address: metadata.address,
|
|
5122
|
+
abi: import_viem11.erc20Abi,
|
|
5123
|
+
functionName: "balanceOf",
|
|
5124
|
+
args: [agentWallet]
|
|
5125
|
+
}), chainId, "eth_call", this.id),
|
|
5126
|
+
withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5127
|
+
address: (0, import_viem11.getAddress)(feeTracker),
|
|
5128
|
+
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
5129
|
+
functionName: "getFeesOwed",
|
|
5130
|
+
args: [agentWallet]
|
|
5131
|
+
}), chainId, "eth_call", this.id)
|
|
5132
|
+
]);
|
|
5133
|
+
this.assertSession(generation, chainId, asset);
|
|
5134
|
+
if (typeof idle2 !== "bigint" || idle2 < 0n || typeof fees !== "bigint" || fees < 0n)
|
|
5135
|
+
throw this.invalidResponse("wallet withdrawal balance");
|
|
5136
|
+
return { idle: idle2, fees, available: idle2 > fees ? idle2 - fees : 0n };
|
|
4699
5137
|
};
|
|
4700
|
-
const
|
|
5138
|
+
const [portfolio, initialFunds] = await Promise.all([portfolioRead, readOwnerFunds()]);
|
|
5139
|
+
const idle = initialFunds.idle;
|
|
4701
5140
|
let positions = portfolio.positions;
|
|
4702
5141
|
let positionsStatus = "empty";
|
|
4703
5142
|
const validatePositions = (values) => {
|
|
4704
5143
|
const seen = /* @__PURE__ */ new Set();
|
|
4705
5144
|
for (const value of values) {
|
|
4706
5145
|
const address = value?.yieldOption?.address;
|
|
4707
|
-
if (typeof address !== "string" || !(0,
|
|
5146
|
+
if (typeof address !== "string" || !(0, import_viem11.isAddress)(address) || seen.has(address.toLowerCase()) || typeof value.assetsRaw !== "string" || !/^\d+$/.test(value.assetsRaw) || typeof value.withdrawableAssetsRaw !== "string" || !/^\d+$/.test(value.withdrawableAssetsRaw))
|
|
4708
5147
|
throw this.invalidResponse("yield positions");
|
|
4709
5148
|
seen.add(address.toLowerCase());
|
|
4710
5149
|
}
|
|
4711
5150
|
return values;
|
|
4712
5151
|
};
|
|
4713
|
-
const
|
|
5152
|
+
const zeroValuePositions = /* @__PURE__ */ new WeakSet();
|
|
5153
|
+
const hasPositions = (values) => values.some((position2) => !zeroValuePositions.has(position2) && (BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n));
|
|
5154
|
+
const knownPositions = /* @__PURE__ */ new Map();
|
|
5155
|
+
const rememberPositions = (values) => {
|
|
5156
|
+
for (const position2 of values) knownPositions.set(position2.yieldOption.address.toLowerCase(), position2);
|
|
5157
|
+
};
|
|
5158
|
+
const boundedVerification = async (read2, timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
|
|
5159
|
+
let timer;
|
|
5160
|
+
try {
|
|
5161
|
+
return await Promise.race([read2, new Promise((resolve) => {
|
|
5162
|
+
timer = setTimeout(() => resolve(void 0), Math.max(0, timeoutMs));
|
|
5163
|
+
})]);
|
|
5164
|
+
} finally {
|
|
5165
|
+
if (timer) clearTimeout(timer);
|
|
5166
|
+
}
|
|
5167
|
+
};
|
|
5168
|
+
const verifyKnownPositions = async (blockNumber, values = [...knownPositions.values()], timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
|
|
5169
|
+
const verifiedPositions = [];
|
|
5170
|
+
if (timeoutMs <= 0) return { verified: false, positions: verifiedPositions };
|
|
5171
|
+
const reads = values.map(async (position2) => {
|
|
5172
|
+
const address = (0, import_viem11.getAddress)(position2.yieldOption.address);
|
|
5173
|
+
try {
|
|
5174
|
+
const [underlying, shares, withdrawable] = await Promise.all([
|
|
5175
|
+
this.getReceiptClient().readContract({ address, abi: import_viem11.erc4626Abi, functionName: "asset", blockNumber }),
|
|
5176
|
+
this.getReceiptClient().readContract({ address, abi: import_viem11.erc20Abi, functionName: "balanceOf", args: [agentWallet], blockNumber }),
|
|
5177
|
+
this.getReceiptClient().readContract({ address, abi: import_viem11.erc4626Abi, functionName: "maxWithdraw", args: [agentWallet], blockNumber })
|
|
5178
|
+
]);
|
|
5179
|
+
if (typeof underlying !== "string" || !(0, import_viem11.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n || typeof withdrawable !== "bigint" || withdrawable < 0n) return null;
|
|
5180
|
+
const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: withdrawable.toString() };
|
|
5181
|
+
if (shares > 0n && withdrawable === 0n) {
|
|
5182
|
+
let positiveValue = false;
|
|
5183
|
+
const valuations = await Promise.all(["convertToAssets", "previewRedeem"].map(async (functionName) => {
|
|
5184
|
+
try {
|
|
5185
|
+
const value = await this.getReceiptClient().readContract({
|
|
5186
|
+
address,
|
|
5187
|
+
abi: import_viem11.erc4626Abi,
|
|
5188
|
+
functionName,
|
|
5189
|
+
args: [shares],
|
|
5190
|
+
blockNumber
|
|
5191
|
+
});
|
|
5192
|
+
if (typeof value !== "bigint" || value < 0n) return null;
|
|
5193
|
+
if (value > 0n && !positiveValue) {
|
|
5194
|
+
positiveValue = true;
|
|
5195
|
+
verifiedPositions.push(verified);
|
|
5196
|
+
}
|
|
5197
|
+
return value;
|
|
5198
|
+
} catch {
|
|
5199
|
+
return null;
|
|
5200
|
+
}
|
|
5201
|
+
}));
|
|
5202
|
+
if (positiveValue) return verified;
|
|
5203
|
+
if (blockNumber === void 0 || valuations.some((value) => value !== 0n)) return null;
|
|
5204
|
+
zeroValuePositions.add(verified);
|
|
5205
|
+
}
|
|
5206
|
+
verifiedPositions.push(verified);
|
|
5207
|
+
return verified;
|
|
5208
|
+
} catch {
|
|
5209
|
+
return null;
|
|
5210
|
+
}
|
|
5211
|
+
});
|
|
5212
|
+
const result = await boundedVerification(Promise.all(reads), timeoutMs);
|
|
5213
|
+
this.assertSession(generation, chainId, asset);
|
|
5214
|
+
return {
|
|
5215
|
+
verified: result !== void 0 && result.every((position2) => position2 !== null),
|
|
5216
|
+
positions: [...verifiedPositions]
|
|
5217
|
+
};
|
|
5218
|
+
};
|
|
4714
5219
|
const refreshPositions = async () => {
|
|
4715
5220
|
this.assertSession(generation, chainId, asset);
|
|
4716
5221
|
const response = await this.walletRequest(
|
|
@@ -4724,21 +5229,40 @@ var YieldseekerAgent = class {
|
|
|
4724
5229
|
};
|
|
4725
5230
|
if (amount === void 0) {
|
|
4726
5231
|
validatePositions(positions);
|
|
5232
|
+
rememberPositions(positions);
|
|
4727
5233
|
positionsStatus = hasPositions(positions) ? "remaining" : "empty";
|
|
4728
5234
|
}
|
|
4729
5235
|
const deployed = positions.reduce(
|
|
4730
5236
|
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
4731
5237
|
0n
|
|
4732
5238
|
);
|
|
4733
|
-
const
|
|
5239
|
+
const grossAvailable = idle + deployed;
|
|
5240
|
+
const totalAvailable = grossAvailable > initialFunds.fees ? grossAvailable - initialFunds.fees : 0n;
|
|
4734
5241
|
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
4735
5242
|
if (requested === 0n) {
|
|
4736
5243
|
this.invalidateFundingRead(state, chainId, asset);
|
|
4737
|
-
|
|
5244
|
+
const zeroPayoutCandidates = positions.filter((position2) => BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n);
|
|
5245
|
+
if (amount === void 0 && zeroPayoutCandidates.length) {
|
|
5246
|
+
const deadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
|
|
5247
|
+
const blockNumber = await boundedVerification(
|
|
5248
|
+
Promise.resolve().then(() => this.getReceiptClient().getBlockNumber()).catch(() => void 0)
|
|
5249
|
+
);
|
|
5250
|
+
const proof = typeof blockNumber === "bigint" ? await verifyKnownPositions(blockNumber, zeroPayoutCandidates, deadline - Date.now()) : { verified: false, positions: [] };
|
|
5251
|
+
positions = proof.positions;
|
|
5252
|
+
positionsStatus = hasPositions(positions) ? "remaining" : proof.verified ? "empty" : "unavailable";
|
|
5253
|
+
if (positionsStatus !== "empty")
|
|
5254
|
+
throw new OwneyError(
|
|
5255
|
+
"WITHDRAW_FAILED",
|
|
5256
|
+
"Yieldseeker vault funds are not currently withdrawable.",
|
|
5257
|
+
{ asset, positionsStatus, remainingPositions: positions.map((position2) => position2.yieldOption.address) },
|
|
5258
|
+
this.id
|
|
5259
|
+
);
|
|
5260
|
+
}
|
|
5261
|
+
if (grossAvailable > 0n && totalAvailable === 0n)
|
|
4738
5262
|
throw new OwneyError(
|
|
4739
|
-
"
|
|
4740
|
-
"Yieldseeker
|
|
4741
|
-
{ asset,
|
|
5263
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
5264
|
+
"No owner-withdrawable Yieldseeker funds remain after reserved fees.",
|
|
5265
|
+
{ asset, available: "0", reason: "YIELDSEEKER_FEES_ONLY", feesReserved: initialFunds.fees.toString() },
|
|
4742
5266
|
this.id
|
|
4743
5267
|
);
|
|
4744
5268
|
throw new OwneyError(
|
|
@@ -4760,28 +5284,50 @@ var YieldseekerAgent = class {
|
|
|
4760
5284
|
this.id
|
|
4761
5285
|
);
|
|
4762
5286
|
}
|
|
4763
|
-
|
|
5287
|
+
const requiredIdle = requested + initialFunds.fees;
|
|
5288
|
+
let remaining = requiredIdle > idle ? requiredIdle - idle : 0n;
|
|
4764
5289
|
let grossUnwound = 0n;
|
|
5290
|
+
let latestUnwindBlock;
|
|
4765
5291
|
const unwind = async (position2, assetsRaw) => {
|
|
4766
5292
|
this.assertSession(generation, chainId, asset);
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
5293
|
+
let unwindTransactionHash;
|
|
5294
|
+
try {
|
|
5295
|
+
const response = await this.walletRequest(
|
|
5296
|
+
state,
|
|
5297
|
+
chainId,
|
|
5298
|
+
this.agentPath(context, "withdraw-from-position"),
|
|
5299
|
+
{
|
|
5300
|
+
method: "POST",
|
|
5301
|
+
body: { chainId, vaultAddress: position2.yieldOption.address, assetsRaw: assetsRaw.toString() }
|
|
5302
|
+
}
|
|
5303
|
+
);
|
|
5304
|
+
if (!this.isTransactionHash(response?.transactionHash)) throw this.invalidResponse("position withdrawal");
|
|
5305
|
+
unwindTransactionHash = response.transactionHash;
|
|
5306
|
+
const unwindReceipt = await this.waitForReceipt(state, chainId, unwindTransactionHash);
|
|
5307
|
+
if (unwindReceipt) latestUnwindBlock = unwindReceipt.blockNumber;
|
|
5308
|
+
} catch (error) {
|
|
5309
|
+
this.invalidateFundingRead(state, chainId, asset);
|
|
5310
|
+
throw new OwneyError(
|
|
5311
|
+
error instanceof OwneyError ? error.code : "WITHDRAW_FAILED",
|
|
5312
|
+
"Could not confirm the Yieldseeker vault release. No owner transfer was submitted.",
|
|
5313
|
+
{
|
|
5314
|
+
...error instanceof OwneyError ? error.details : {},
|
|
5315
|
+
withdrawalPhase: "unwind",
|
|
5316
|
+
reason: "YIELDSEEKER_UNWIND_UNRESOLVED",
|
|
5317
|
+
vaultAddress: position2.yieldOption.address,
|
|
5318
|
+
unwindTransactionHash,
|
|
5319
|
+
ownerTransferSubmitted: false
|
|
5320
|
+
},
|
|
5321
|
+
this.id
|
|
5322
|
+
);
|
|
5323
|
+
}
|
|
4778
5324
|
confirmedMovement = true;
|
|
4779
5325
|
this.assertSession(generation, chainId, asset);
|
|
4780
5326
|
grossUnwound += assetsRaw;
|
|
4781
5327
|
};
|
|
4782
5328
|
if (amount === void 0) {
|
|
4783
5329
|
for (let sweep = 0; sweep < YIELDSEEKER_MAX_WITHDRAW_SWEEPS && hasPositions(positions); sweep++) {
|
|
4784
|
-
const before = new Map(positions.map((position2) => [position2.yieldOption.address.toLowerCase(), BigInt(position2.
|
|
5330
|
+
const before = new Map(positions.map((position2) => [position2.yieldOption.address.toLowerCase(), BigInt(position2.withdrawableAssetsRaw)]));
|
|
4785
5331
|
let unwound = false;
|
|
4786
5332
|
for (const position2 of positions) {
|
|
4787
5333
|
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
@@ -4790,15 +5336,22 @@ var YieldseekerAgent = class {
|
|
|
4790
5336
|
unwound = true;
|
|
4791
5337
|
}
|
|
4792
5338
|
if (!unwound) break;
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
5339
|
+
const knownBeforeRefresh = new Set(knownPositions.keys());
|
|
5340
|
+
const [catalog, proof] = await Promise.all([
|
|
5341
|
+
boundedVerification(refreshPositions().catch(() => void 0)),
|
|
5342
|
+
verifyKnownPositions(latestUnwindBlock)
|
|
5343
|
+
]);
|
|
5344
|
+
if (catalog) rememberPositions(catalog);
|
|
5345
|
+
this.assertSession(generation, chainId, asset);
|
|
5346
|
+
if (proof.verified) {
|
|
5347
|
+
positions = [...proof.positions, ...(catalog ?? []).filter((position2) => !knownBeforeRefresh.has(position2.yieldOption.address.toLowerCase()))];
|
|
5348
|
+
} else if (catalog) positions = catalog;
|
|
5349
|
+
else {
|
|
4798
5350
|
positionsStatus = "unavailable";
|
|
4799
5351
|
break;
|
|
4800
5352
|
}
|
|
4801
|
-
|
|
5353
|
+
positionsStatus = hasPositions(positions) ? "remaining" : "empty";
|
|
5354
|
+
if (positions.some((position2) => BigInt(position2.withdrawableAssetsRaw) > 0n && before.has(position2.yieldOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) >= before.get(position2.yieldOption.address.toLowerCase()))) break;
|
|
4802
5355
|
}
|
|
4803
5356
|
} else {
|
|
4804
5357
|
for (const position2 of positions) {
|
|
@@ -4816,8 +5369,8 @@ var YieldseekerAgent = class {
|
|
|
4816
5369
|
remaining: remaining.toString()
|
|
4817
5370
|
});
|
|
4818
5371
|
}
|
|
4819
|
-
const availableForOwner = confirmedMovement ? await
|
|
4820
|
-
const feeRate = portfolio.
|
|
5372
|
+
const availableForOwner = confirmedMovement ? (await readOwnerFunds()).available : initialFunds.available;
|
|
5373
|
+
const feeRate = portfolio.feeRateBps;
|
|
4821
5374
|
const maxFee = confirmedMovement && Number.isInteger(feeRate) && feeRate >= 0 && feeRate <= 1e3 ? (grossUnwound * BigInt(feeRate) + 9999n) / 10000n : 0n;
|
|
4822
5375
|
const shortfall = availableForOwner < requested ? requested - availableForOwner : 0n;
|
|
4823
5376
|
if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee) {
|
|
@@ -4837,20 +5390,23 @@ var YieldseekerAgent = class {
|
|
|
4837
5390
|
);
|
|
4838
5391
|
}
|
|
4839
5392
|
const payout = amount === void 0 || availableForOwner < requested ? availableForOwner : requested;
|
|
4840
|
-
const plannedMovement =
|
|
4841
|
-
|
|
5393
|
+
const plannedMovement = amount === void 0 && maxSettlementBasis ? {
|
|
5394
|
+
...maxSettlementBasis,
|
|
5395
|
+
delta: maxSettlementBasis.delta - payout,
|
|
5396
|
+
expectedNetDeposits: maxSettlementBasis.expectedNetDeposits - payout
|
|
5397
|
+
} : this.snapshotMovement(state, chainId, context, -payout);
|
|
5398
|
+
const account = (0, import_viem11.getAddress)(state.walletAddress);
|
|
4842
5399
|
let receipt;
|
|
4843
5400
|
const base6 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount === void 0 ? "MAX" : payout.toString(), transfer: "pending", registration: "not-applicable", delivery: "pending" };
|
|
4844
|
-
const data = amount === void 0 ? (0,
|
|
5401
|
+
const data = amount === void 0 ? (0, import_viem11.encodeFunctionData)({
|
|
4845
5402
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
4846
5403
|
functionName: "withdrawAllAssetToUser",
|
|
4847
5404
|
args: [account, metadata.address]
|
|
4848
|
-
}) : (0,
|
|
5405
|
+
}) : (0, import_viem11.encodeFunctionData)({
|
|
4849
5406
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
4850
5407
|
functionName: "withdrawAssetToUser",
|
|
4851
5408
|
args: [account, metadata.address, payout]
|
|
4852
5409
|
});
|
|
4853
|
-
onOwnerTransferAttempted?.();
|
|
4854
5410
|
let txHash;
|
|
4855
5411
|
if (sponsor) {
|
|
4856
5412
|
const submitted = await submitYieldseekerSponsoredWithdrawal({
|
|
@@ -4859,19 +5415,25 @@ var YieldseekerAgent = class {
|
|
|
4859
5415
|
apiKey: this.owneyApiKey,
|
|
4860
5416
|
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
4861
5417
|
yieldseekerSignature: await this.auth.getToken(state, chainId),
|
|
5418
|
+
refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
|
|
4862
5419
|
userId: context.user.userId,
|
|
4863
5420
|
agentId: context.agent.agentId,
|
|
4864
|
-
agentWallet: (0,
|
|
5421
|
+
agentWallet: (0, import_viem11.getAddress)(context.wallet.walletAddress),
|
|
4865
5422
|
asset: metadata.address,
|
|
4866
5423
|
callData: data,
|
|
4867
|
-
onPending: (pending) => notifyFunding(onProgress, { ...base6, ...pending })
|
|
5424
|
+
onPending: (pending) => notifyFunding(onProgress, { ...base6, ...pending }),
|
|
5425
|
+
onBeforeSubmission: async (pending) => {
|
|
5426
|
+
await onBeforeSubmission?.({ ...base6, ...pending });
|
|
5427
|
+
onOwnerTransferAttempted?.();
|
|
5428
|
+
}
|
|
4868
5429
|
});
|
|
4869
5430
|
txHash = submitted.txHash;
|
|
4870
5431
|
receipt = submitted.receipt;
|
|
4871
5432
|
} else {
|
|
5433
|
+
onOwnerTransferAttempted?.();
|
|
4872
5434
|
txHash = await this.submitTransaction(state, chainId, {
|
|
4873
5435
|
from: account,
|
|
4874
|
-
to: (0,
|
|
5436
|
+
to: (0, import_viem11.getAddress)(context.wallet.walletAddress),
|
|
4875
5437
|
data,
|
|
4876
5438
|
value: "0",
|
|
4877
5439
|
chainId
|
|
@@ -4883,14 +5445,26 @@ var YieldseekerAgent = class {
|
|
|
4883
5445
|
settlement = plannedMovement;
|
|
4884
5446
|
const evidence = receipt ? inspectFundingReceipt(receipt, { ...base6, txHash }, metadata.address, "withdraw") : { ...base6, txHash, confirmedAmount: payout.toString(), transfer: "confirmed", delivery: "confirmed" };
|
|
4885
5447
|
notifyFunding(onProgress, evidence);
|
|
5448
|
+
let completion;
|
|
4886
5449
|
if (amount === void 0) {
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
5450
|
+
const verificationDeadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
|
|
5451
|
+
const knownAtPayout = new Set(knownPositions.keys());
|
|
5452
|
+
const [catalogVerified, knownProof] = await Promise.all([
|
|
5453
|
+
boundedVerification(refreshPositions().then((values) => {
|
|
5454
|
+
rememberPositions(values);
|
|
5455
|
+
return true;
|
|
5456
|
+
}).catch(() => false)),
|
|
5457
|
+
verifyKnownPositions(receipt?.blockNumber)
|
|
5458
|
+
]);
|
|
5459
|
+
const newPositions = [...knownPositions].filter(([address]) => !knownAtPayout.has(address)).map(([, position2]) => position2);
|
|
5460
|
+
const newProof = newPositions.length ? await verifyKnownPositions(receipt?.blockNumber, newPositions, verificationDeadline - Date.now()) : { verified: true, positions: [] };
|
|
5461
|
+
const proof = {
|
|
5462
|
+
verified: catalogVerified === true && knownProof.verified && newProof.verified,
|
|
5463
|
+
positions: [...knownProof.positions, ...newProof.positions]
|
|
5464
|
+
};
|
|
5465
|
+
positions = proof.positions;
|
|
5466
|
+
positionsStatus = hasPositions(positions) ? "remaining" : proof.verified ? "empty" : "unavailable";
|
|
5467
|
+
if (positionsStatus === "remaining") {
|
|
4894
5468
|
const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? payout.toString(), evidence };
|
|
4895
5469
|
throw new OwneyError(
|
|
4896
5470
|
"WITHDRAW_PARTIAL_FAILURE",
|
|
@@ -4899,7 +5473,7 @@ var YieldseekerAgent = class {
|
|
|
4899
5473
|
reason: "YIELDSEEKER_MAX_INCOMPLETE",
|
|
4900
5474
|
asset,
|
|
4901
5475
|
positionsStatus,
|
|
4902
|
-
remainingPositions: positionsStatus === "remaining" ? positions.map((position2) => ({
|
|
5476
|
+
remainingPositions: positionsStatus === "remaining" ? positions.filter((position2) => !zeroValuePositions.has(position2) && (BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n)).map((position2) => ({
|
|
4903
5477
|
vaultAddress: position2.yieldOption.address,
|
|
4904
5478
|
sharesRaw: position2.assetsRaw,
|
|
4905
5479
|
withdrawableAmountRaw: position2.withdrawableAssetsRaw
|
|
@@ -4909,6 +5483,7 @@ var YieldseekerAgent = class {
|
|
|
4909
5483
|
this.id
|
|
4910
5484
|
);
|
|
4911
5485
|
}
|
|
5486
|
+
if (!proof.verified) completion = "unverified";
|
|
4912
5487
|
}
|
|
4913
5488
|
if (evidence.transfer === "failed" && evidence.confirmedAmount === "0")
|
|
4914
5489
|
throw new OwneyError(
|
|
@@ -4917,7 +5492,13 @@ var YieldseekerAgent = class {
|
|
|
4917
5492
|
{ asset, available: "0", txHash },
|
|
4918
5493
|
this.id
|
|
4919
5494
|
);
|
|
4920
|
-
return {
|
|
5495
|
+
return {
|
|
5496
|
+
txHash,
|
|
5497
|
+
type: amount === void 0 && !completion ? "full" : "partial",
|
|
5498
|
+
...completion ? { completion } : {},
|
|
5499
|
+
amount: evidence.confirmedAmount ?? payout.toString(),
|
|
5500
|
+
evidence
|
|
5501
|
+
};
|
|
4921
5502
|
} finally {
|
|
4922
5503
|
if (confirmedMovement && this.readGeneration === generation) {
|
|
4923
5504
|
void this.refreshSnapshotAfterMovement(
|
|
@@ -4964,6 +5545,23 @@ var YieldseekerAgent = class {
|
|
|
4964
5545
|
if (!context) throw new OwneyError("NOT_CONNECTED", "Reconnect the agent before recovering funding.");
|
|
4965
5546
|
return context.wallet.walletAddress;
|
|
4966
5547
|
}
|
|
5548
|
+
async hasExistingDepositFunds(state, chainId, asset) {
|
|
5549
|
+
this.assertChain(chainId);
|
|
5550
|
+
this.assertAsset(asset);
|
|
5551
|
+
const generation = this.readGeneration;
|
|
5552
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
5553
|
+
this.assertSession(generation, chainId, asset);
|
|
5554
|
+
if (!context) return void 0;
|
|
5555
|
+
const idle = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5556
|
+
address: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
5557
|
+
abi: import_viem11.erc20Abi,
|
|
5558
|
+
functionName: "balanceOf",
|
|
5559
|
+
args: [(0, import_viem11.getAddress)(context.wallet.walletAddress)]
|
|
5560
|
+
}), chainId, "eth_call", this.id);
|
|
5561
|
+
this.assertSession(generation, chainId, asset);
|
|
5562
|
+
if (typeof idle !== "bigint" || idle < 0n) throw this.invalidResponse("wallet deposit balance");
|
|
5563
|
+
return idle > 0n ? true : void 0;
|
|
5564
|
+
}
|
|
4967
5565
|
async getEarnings(state, chainId) {
|
|
4968
5566
|
this.assertChain(chainId);
|
|
4969
5567
|
return mapYieldseekerEarnings(await this.loadPortfolio(state, chainId, {}));
|
|
@@ -4994,7 +5592,7 @@ var YieldseekerAgent = class {
|
|
|
4994
5592
|
);
|
|
4995
5593
|
const vaultAddresses = new Set(
|
|
4996
5594
|
catalog.flat().filter(
|
|
4997
|
-
(yieldOption) => yieldOption.chainId === chainId && (0,
|
|
5595
|
+
(yieldOption) => yieldOption.chainId === chainId && (0, import_viem11.isAddress)(yieldOption.address)
|
|
4998
5596
|
).map((yieldOption) => yieldOption.address.toLowerCase())
|
|
4999
5597
|
);
|
|
5000
5598
|
return mapYieldseekerHistory(contexts, {
|
|
@@ -5121,7 +5719,7 @@ var YieldseekerAgent = class {
|
|
|
5121
5719
|
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
5122
5720
|
).then((walletResponse) => {
|
|
5123
5721
|
this.assertSession(generation, chainId, asset);
|
|
5124
|
-
if (!walletResponse?.agentWallet || !(0,
|
|
5722
|
+
if (!walletResponse?.agentWallet || !(0, import_viem11.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
5125
5723
|
throw this.invalidResponse("agent wallet");
|
|
5126
5724
|
}
|
|
5127
5725
|
const context = {
|
|
@@ -5151,7 +5749,7 @@ var YieldseekerAgent = class {
|
|
|
5151
5749
|
this.users.set(key2, persisted);
|
|
5152
5750
|
return persisted;
|
|
5153
5751
|
}
|
|
5154
|
-
const walletAddress = (0,
|
|
5752
|
+
const walletAddress = (0, import_viem11.getAddress)(state.walletAddress);
|
|
5155
5753
|
const generation = this.readGeneration;
|
|
5156
5754
|
let user = null;
|
|
5157
5755
|
try {
|
|
@@ -5534,7 +6132,7 @@ var YieldseekerAgent = class {
|
|
|
5534
6132
|
this.agentPath(context, "deploy"),
|
|
5535
6133
|
{ method: "POST", body: {} }
|
|
5536
6134
|
);
|
|
5537
|
-
if (!deployed?.agentWallet || !(0,
|
|
6135
|
+
if (!deployed?.agentWallet || !(0, import_viem11.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
|
|
5538
6136
|
throw this.invalidResponse("agent deployment", {
|
|
5539
6137
|
reason: "Deploy did not return the expected Agent Wallet."
|
|
5540
6138
|
});
|
|
@@ -5642,15 +6240,15 @@ var YieldseekerAgent = class {
|
|
|
5642
6240
|
return hash2;
|
|
5643
6241
|
}
|
|
5644
6242
|
this.assertTransaction(transaction, state, chainId);
|
|
5645
|
-
const account = (0,
|
|
5646
|
-
const walletClient = (0,
|
|
6243
|
+
const account = (0, import_viem11.getAddress)(state.walletAddress);
|
|
6244
|
+
const walletClient = (0, import_viem11.createWalletClient)({
|
|
5647
6245
|
account,
|
|
5648
6246
|
chain: import_chains4.base,
|
|
5649
|
-
transport: (0,
|
|
6247
|
+
transport: (0, import_viem11.custom)(state.provider)
|
|
5650
6248
|
});
|
|
5651
|
-
const walletChainClient = (0,
|
|
6249
|
+
const walletChainClient = (0, import_viem11.createPublicClient)({
|
|
5652
6250
|
chain: import_chains4.base,
|
|
5653
|
-
transport: (0,
|
|
6251
|
+
transport: (0, import_viem11.custom)(state.provider)
|
|
5654
6252
|
});
|
|
5655
6253
|
await ensureWalletOnChain(
|
|
5656
6254
|
walletChainClient,
|
|
@@ -5660,30 +6258,84 @@ var YieldseekerAgent = class {
|
|
|
5660
6258
|
const hash = await walletClient.sendTransaction({
|
|
5661
6259
|
account,
|
|
5662
6260
|
chain: import_chains4.base,
|
|
5663
|
-
to: (0,
|
|
6261
|
+
to: (0, import_viem11.getAddress)(transaction.to),
|
|
5664
6262
|
data: transaction.data,
|
|
5665
6263
|
value: BigInt(transaction.value)
|
|
5666
6264
|
});
|
|
5667
6265
|
onSubmitted?.(hash);
|
|
6266
|
+
let replacement;
|
|
5668
6267
|
const receipt = await withPaidRpcDiagnostics(
|
|
5669
6268
|
() => this.getReceiptClient().waitForTransactionReceipt({
|
|
5670
6269
|
hash,
|
|
5671
|
-
confirmations: 1
|
|
6270
|
+
confirmations: 1,
|
|
6271
|
+
onReplaced: (value) => {
|
|
6272
|
+
replacement = value;
|
|
6273
|
+
}
|
|
5672
6274
|
}),
|
|
5673
6275
|
8453,
|
|
5674
6276
|
"eth_getTransactionReceipt",
|
|
5675
6277
|
this.id
|
|
5676
6278
|
);
|
|
6279
|
+
const actualHash = receipt.transactionHash;
|
|
6280
|
+
if (!this.isTransactionHash(actualHash))
|
|
6281
|
+
throw new OwneyError(
|
|
6282
|
+
"WITHDRAW_FAILED",
|
|
6283
|
+
"The confirmed transaction hash could not be verified.",
|
|
6284
|
+
{ originalTransactionHash: hash, ownerTransferNotExecuted: false },
|
|
6285
|
+
this.id
|
|
6286
|
+
);
|
|
6287
|
+
if (actualHash.toLowerCase() !== hash.toLowerCase()) {
|
|
6288
|
+
if (!replacement || replacement.transaction.hash.toLowerCase() !== actualHash.toLowerCase()) {
|
|
6289
|
+
try {
|
|
6290
|
+
const [original2, actual2] = await Promise.all([
|
|
6291
|
+
this.getReceiptClient().getTransaction({ hash }),
|
|
6292
|
+
this.getReceiptClient().getTransaction({ hash: actualHash })
|
|
6293
|
+
]);
|
|
6294
|
+
replacement = { replacedTransaction: original2, transaction: actual2 };
|
|
6295
|
+
} catch {
|
|
6296
|
+
replacement = void 0;
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6299
|
+
const original = replacement?.replacedTransaction;
|
|
6300
|
+
const actual = replacement?.transaction;
|
|
6301
|
+
const sameNonce = !!original && !!actual && original.hash.toLowerCase() === hash.toLowerCase() && actual.hash.toLowerCase() === actualHash.toLowerCase() && original.from.toLowerCase() === account.toLowerCase() && actual.from.toLowerCase() === account.toLowerCase() && original.nonce === actual.nonce && (actual.chainId === void 0 || actual.chainId === chainId) && receipt.blockNumber !== null && receipt.blockNumber !== void 0;
|
|
6302
|
+
if (!sameNonce)
|
|
6303
|
+
throw new OwneyError(
|
|
6304
|
+
"WITHDRAW_FAILED",
|
|
6305
|
+
"The replacement transaction could not be verified.",
|
|
6306
|
+
{
|
|
6307
|
+
originalTransactionHash: hash,
|
|
6308
|
+
replacementTransactionHash: actualHash,
|
|
6309
|
+
reason: "TRANSACTION_REPLACEMENT_UNRESOLVED",
|
|
6310
|
+
ownerTransferNotExecuted: false
|
|
6311
|
+
},
|
|
6312
|
+
this.id
|
|
6313
|
+
);
|
|
6314
|
+
const sameCall = actual.to?.toLowerCase() === transaction.to.toLowerCase() && actual.input?.toLowerCase() === transaction.data.toLowerCase() && actual.value === BigInt(transaction.value);
|
|
6315
|
+
if (!sameCall)
|
|
6316
|
+
throw new OwneyError(
|
|
6317
|
+
"WITHDRAW_FAILED",
|
|
6318
|
+
"The transaction was replaced without executing this withdrawal.",
|
|
6319
|
+
{
|
|
6320
|
+
originalTransactionHash: hash,
|
|
6321
|
+
replacementTransactionHash: actualHash,
|
|
6322
|
+
reason: "TRANSACTION_REPLACED_WITH_DIFFERENT_CALL",
|
|
6323
|
+
ownerTransferNotExecuted: true
|
|
6324
|
+
},
|
|
6325
|
+
this.id
|
|
6326
|
+
);
|
|
6327
|
+
onSubmitted?.(actualHash);
|
|
6328
|
+
}
|
|
5677
6329
|
if (receipt.status !== "success") {
|
|
5678
6330
|
throw new OwneyError(
|
|
5679
6331
|
"AGENT_TRANSACTION_REVERTED",
|
|
5680
|
-
`Yieldseeker transaction reverted (${
|
|
5681
|
-
{ transactionHash:
|
|
6332
|
+
`Yieldseeker transaction reverted (${actualHash}).`,
|
|
6333
|
+
{ transactionHash: actualHash },
|
|
5682
6334
|
this.id
|
|
5683
6335
|
);
|
|
5684
6336
|
}
|
|
5685
6337
|
onReceipt?.(receipt);
|
|
5686
|
-
return
|
|
6338
|
+
return actualHash;
|
|
5687
6339
|
}
|
|
5688
6340
|
async waitForReceipt(state, chainId, transactionHash) {
|
|
5689
6341
|
if (this.unwindReceiptWaiter) {
|
|
@@ -5710,12 +6362,12 @@ var YieldseekerAgent = class {
|
|
|
5710
6362
|
return receipt;
|
|
5711
6363
|
}
|
|
5712
6364
|
assertTransaction(transaction, state, chainId) {
|
|
5713
|
-
if (!transaction || typeof transaction.from !== "string" || !(0,
|
|
6365
|
+
if (!transaction || typeof transaction.from !== "string" || !(0, import_viem11.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem11.isAddress)(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || (0, import_viem11.getAddress)(transaction.from) !== (0, import_viem11.getAddress)(state.walletAddress)) {
|
|
5714
6366
|
throw this.invalidResponse("transaction");
|
|
5715
6367
|
}
|
|
5716
6368
|
}
|
|
5717
6369
|
assertAgent(agent) {
|
|
5718
|
-
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0,
|
|
6370
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem11.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
|
|
5719
6371
|
throw this.invalidResponse("agent");
|
|
5720
6372
|
}
|
|
5721
6373
|
}
|
|
@@ -5869,7 +6521,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
|
5869
6521
|
}
|
|
5870
6522
|
|
|
5871
6523
|
// src/lib/helpers/withdraw-helper.ts
|
|
5872
|
-
var
|
|
6524
|
+
var import_viem12 = require("viem");
|
|
5873
6525
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
5874
6526
|
const target = asset.toUpperCase();
|
|
5875
6527
|
return agents.map((agent) => {
|
|
@@ -5877,7 +6529,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
5877
6529
|
const tokenBalance = agentBalance?.tokens.find(
|
|
5878
6530
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
5879
6531
|
);
|
|
5880
|
-
let balance = tokenBalance ? (0,
|
|
6532
|
+
let balance = tokenBalance ? (0, import_viem12.parseUnits)(tokenBalance.amount, decimals) : 0n;
|
|
5881
6533
|
if (agent.balanceComposition === "tokens-plus-positions") {
|
|
5882
6534
|
const chainNameById = {
|
|
5883
6535
|
1: "ETHEREUM",
|
|
@@ -5889,14 +6541,14 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
5889
6541
|
const positionChain = position2.chain.trim().toUpperCase();
|
|
5890
6542
|
const matchesChain = positionChain === String(chainId) || targetChain !== void 0 && positionChain === targetChain;
|
|
5891
6543
|
if (!matchesChain || position2.asset.toUpperCase() !== target) continue;
|
|
5892
|
-
balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0,
|
|
6544
|
+
balance += position2.withdrawableAmountRaw !== void 0 ? BigInt(position2.withdrawableAmountRaw) : (0, import_viem12.parseUnits)(position2.amount, decimals);
|
|
5893
6545
|
}
|
|
5894
6546
|
}
|
|
5895
6547
|
const assetTotal = agentBalance?.assetBalances?.find(
|
|
5896
6548
|
(t) => t.chainId === chainId && t.asset.toUpperCase() === target
|
|
5897
6549
|
);
|
|
5898
6550
|
if (assetTotal) {
|
|
5899
|
-
const total = (0,
|
|
6551
|
+
const total = (0, import_viem12.parseUnits)(assetTotal.amount, decimals);
|
|
5900
6552
|
if (total < balance) balance = total > 0n ? total : 0n;
|
|
5901
6553
|
}
|
|
5902
6554
|
return { agent, balance };
|
|
@@ -6055,14 +6707,14 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
6055
6707
|
}
|
|
6056
6708
|
|
|
6057
6709
|
// src/client.ts
|
|
6058
|
-
var
|
|
6710
|
+
var import_viem16 = require("viem");
|
|
6059
6711
|
var import_chains5 = require("viem/chains");
|
|
6060
6712
|
|
|
6061
6713
|
// src/lib/sponsored-token-batch.ts
|
|
6062
|
-
var
|
|
6714
|
+
var import_viem14 = require("viem");
|
|
6063
6715
|
|
|
6064
6716
|
// src/lib/permit2-batch.ts
|
|
6065
|
-
var
|
|
6717
|
+
var import_viem13 = require("viem");
|
|
6066
6718
|
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
6067
6719
|
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
6068
6720
|
var ERC2612_PERMIT_TYPES = {
|
|
@@ -6074,7 +6726,7 @@ var ERC2612_PERMIT_TYPES = {
|
|
|
6074
6726
|
{ name: "deadline", type: "uint256" }
|
|
6075
6727
|
]
|
|
6076
6728
|
};
|
|
6077
|
-
var ERC2612_READ_ABI = (0,
|
|
6729
|
+
var ERC2612_READ_ABI = (0, import_viem13.parseAbi)([
|
|
6078
6730
|
"function name() view returns (string)",
|
|
6079
6731
|
"function version() view returns (string)",
|
|
6080
6732
|
"function nonces(address owner) view returns (uint256)"
|
|
@@ -6112,7 +6764,7 @@ var PERMIT_BATCH_TYPES = {
|
|
|
6112
6764
|
{ name: "amount", type: "uint256" }
|
|
6113
6765
|
]
|
|
6114
6766
|
};
|
|
6115
|
-
var PERMIT2_BATCH_ABI = (0,
|
|
6767
|
+
var PERMIT2_BATCH_ABI = (0, import_viem13.parseAbi)([
|
|
6116
6768
|
"struct TokenPermissions { address token; uint256 amount; }",
|
|
6117
6769
|
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
6118
6770
|
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
@@ -6155,6 +6807,18 @@ var planOf = (transfers) => JSON.stringify(
|
|
|
6155
6807
|
amount: BigInt(t.amount).toString()
|
|
6156
6808
|
}))
|
|
6157
6809
|
);
|
|
6810
|
+
var pendingKey = (i) => `owney.token-batch.v1:${(0, import_viem14.keccak256)((0, import_viem14.toBytes)(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
|
|
6811
|
+
function pendingTokenBatch(i) {
|
|
6812
|
+
try {
|
|
6813
|
+
const saved = read(pendingKey(i));
|
|
6814
|
+
if (!saved) return;
|
|
6815
|
+
const body = JSON.parse(saved);
|
|
6816
|
+
if (!body.serializedTransaction || body.chainId !== i.chainId || !(0, import_viem14.isAddressEqual)(body.from, i.owner) || !(0, import_viem14.isAddressEqual)(body.token, i.token) || !body.transfers?.length) return;
|
|
6817
|
+
return { txHash: (0, import_viem14.keccak256)(body.serializedTransaction), transfers: body.transfers.map(({ to, amount }) => ({ to, amount })) };
|
|
6818
|
+
} catch {
|
|
6819
|
+
return;
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6158
6822
|
function read(key2) {
|
|
6159
6823
|
return typeof window === "undefined" ? memory.get(key2) : window.localStorage.getItem(key2);
|
|
6160
6824
|
}
|
|
@@ -6171,11 +6835,11 @@ function clear(key2) {
|
|
|
6171
6835
|
else window.localStorage.removeItem(key2);
|
|
6172
6836
|
}
|
|
6173
6837
|
function sponsorTokenBatch(i) {
|
|
6174
|
-
const key2 =
|
|
6838
|
+
const key2 = pendingKey(i);
|
|
6175
6839
|
const plan = planOf(i.transfers);
|
|
6176
6840
|
const active = inflight.get(key2);
|
|
6177
6841
|
if (active) {
|
|
6178
|
-
if (active.plan !== plan)
|
|
6842
|
+
if (active.plan !== plan || i.resumeTxHash && pendingTokenBatch(i)?.txHash !== i.resumeTxHash)
|
|
6179
6843
|
return Promise.reject(
|
|
6180
6844
|
new Error(
|
|
6181
6845
|
"A token deposit is already in progress. Wait for its result before depositing again."
|
|
@@ -6202,7 +6866,7 @@ async function execute(i, key2, plan) {
|
|
|
6202
6866
|
baseUrl: i.baseUrl,
|
|
6203
6867
|
body
|
|
6204
6868
|
});
|
|
6205
|
-
if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0,
|
|
6869
|
+
if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || (0, import_viem14.keccak256)(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
|
|
6206
6870
|
throw new Error(
|
|
6207
6871
|
"Sponsorship API did not return a valid prepared permit transaction."
|
|
6208
6872
|
);
|
|
@@ -6218,7 +6882,7 @@ async function execute(i, key2, plan) {
|
|
|
6218
6882
|
baseUrl: i.baseUrl,
|
|
6219
6883
|
body
|
|
6220
6884
|
});
|
|
6221
|
-
if (!prepared.serializedTransaction || (0,
|
|
6885
|
+
if (!prepared.serializedTransaction || (0, import_viem14.keccak256)(prepared.serializedTransaction) !== prepared.txHash)
|
|
6222
6886
|
throw new Error(
|
|
6223
6887
|
"Sponsorship API did not return a valid prepared transaction."
|
|
6224
6888
|
);
|
|
@@ -6229,7 +6893,7 @@ async function execute(i, key2, plan) {
|
|
|
6229
6893
|
save(key2, body);
|
|
6230
6894
|
}
|
|
6231
6895
|
try {
|
|
6232
|
-
i.onSubmitted?.((0,
|
|
6896
|
+
i.onSubmitted?.((0, import_viem14.keccak256)(body.serializedTransaction));
|
|
6233
6897
|
} catch {
|
|
6234
6898
|
}
|
|
6235
6899
|
const result = await postSponsorBatchTransfer({
|
|
@@ -6237,7 +6901,7 @@ async function execute(i, key2, plan) {
|
|
|
6237
6901
|
baseUrl: i.baseUrl,
|
|
6238
6902
|
body
|
|
6239
6903
|
});
|
|
6240
|
-
if (result.txHash !== (0,
|
|
6904
|
+
if (result.txHash !== (0, import_viem14.keccak256)(body.serializedTransaction))
|
|
6241
6905
|
throw new Error(
|
|
6242
6906
|
"Sponsorship receipt does not match the pending transaction."
|
|
6243
6907
|
);
|
|
@@ -6250,9 +6914,11 @@ async function execute(i, key2, plan) {
|
|
|
6250
6914
|
}
|
|
6251
6915
|
};
|
|
6252
6916
|
const saved = read(key2);
|
|
6917
|
+
if (i.resumeTxHash && pendingTokenBatch(i)?.txHash !== i.resumeTxHash)
|
|
6918
|
+
throw new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit changed. Check its status before depositing again.");
|
|
6253
6919
|
if (saved) {
|
|
6254
6920
|
const previous = JSON.parse(saved);
|
|
6255
|
-
if (previous.chainId !== i.chainId || !(0,
|
|
6921
|
+
if (previous.chainId !== i.chainId || !(0, import_viem14.isAddressEqual)(previous.from, i.owner) || !(0, import_viem14.isAddressEqual)(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
6256
6922
|
throw new Error(
|
|
6257
6923
|
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
6258
6924
|
);
|
|
@@ -6269,7 +6935,7 @@ async function execute(i, key2, plan) {
|
|
|
6269
6935
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
6270
6936
|
"Insufficient token balance for this deposit."
|
|
6271
6937
|
);
|
|
6272
|
-
if (allowance < total && (i.chainId !== 8453 || !(0,
|
|
6938
|
+
if (allowance < total && (i.chainId !== 8453 || !(0, import_viem14.isAddressEqual)(i.token, BASE_USDC_ADDRESS)))
|
|
6273
6939
|
throw new OwneyError(
|
|
6274
6940
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
6275
6941
|
"token deposits need a one-time Permit2 approval."
|
|
@@ -6361,7 +7027,8 @@ function makeSponsoredTokenCallback(deps) {
|
|
|
6361
7027
|
pub,
|
|
6362
7028
|
wallet,
|
|
6363
7029
|
onApproved: deps.onApproved,
|
|
6364
|
-
onSubmitted: (hash) => deps.onSubmitted?.(hash, transfers)
|
|
7030
|
+
onSubmitted: (hash) => deps.onSubmitted?.(hash, transfers),
|
|
7031
|
+
resumeTxHash: deps.resumeTxHash
|
|
6365
7032
|
});
|
|
6366
7033
|
};
|
|
6367
7034
|
const callback = makeVerificationAwareDepositCallback(
|
|
@@ -6447,7 +7114,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
|
6447
7114
|
}
|
|
6448
7115
|
|
|
6449
7116
|
// src/lib/sponsored-calls-deposit.ts
|
|
6450
|
-
var
|
|
7117
|
+
var import_viem15 = require("viem");
|
|
6451
7118
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
6452
7119
|
var DEFAULT_MAX_POLLS = 30;
|
|
6453
7120
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -6455,7 +7122,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
6455
7122
|
method: "wallet_getCapabilities",
|
|
6456
7123
|
params: [owner]
|
|
6457
7124
|
});
|
|
6458
|
-
const forChain = caps?.[(0,
|
|
7125
|
+
const forChain = caps?.[(0, import_viem15.toHex)(chainId)] ?? caps?.[String(chainId)];
|
|
6459
7126
|
return Boolean(forChain?.paymasterService?.supported);
|
|
6460
7127
|
}
|
|
6461
7128
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -6492,8 +7159,8 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
6492
7159
|
const calls = transfers.map((transfer) => ({
|
|
6493
7160
|
to: token,
|
|
6494
7161
|
value: "0x0",
|
|
6495
|
-
data: (0,
|
|
6496
|
-
abi:
|
|
7162
|
+
data: (0, import_viem15.encodeFunctionData)({
|
|
7163
|
+
abi: import_viem15.erc20Abi,
|
|
6497
7164
|
functionName: "transfer",
|
|
6498
7165
|
args: [transfer.to, BigInt(transfer.amount)]
|
|
6499
7166
|
})
|
|
@@ -6525,50 +7192,73 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
6525
7192
|
url.searchParams.append("owneyIntent", intent);
|
|
6526
7193
|
paymasterUrl = url.toString();
|
|
6527
7194
|
}
|
|
6528
|
-
const
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
7195
|
+
const requestedId = (0, import_viem15.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
|
|
7196
|
+
const progress = (id, transfer) => {
|
|
7197
|
+
try {
|
|
7198
|
+
deps.onCallsProgress?.(id, transfers, transfer);
|
|
7199
|
+
} catch {
|
|
7200
|
+
}
|
|
7201
|
+
};
|
|
7202
|
+
progress(requestedId, "pending");
|
|
7203
|
+
let sendResult;
|
|
7204
|
+
try {
|
|
7205
|
+
sendResult = await deps.provider.request({
|
|
7206
|
+
method: "wallet_sendCalls",
|
|
7207
|
+
params: [
|
|
7208
|
+
{
|
|
7209
|
+
version: "2.0.0",
|
|
7210
|
+
id: requestedId,
|
|
7211
|
+
from: deps.ownerAddress,
|
|
7212
|
+
chainId: (0, import_viem15.toHex)(chainId),
|
|
7213
|
+
atomicRequired: transfers.length > 1,
|
|
7214
|
+
calls,
|
|
7215
|
+
capabilities: {
|
|
7216
|
+
paymasterService: { url: paymasterUrl }
|
|
7217
|
+
}
|
|
6539
7218
|
}
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
})
|
|
6543
|
-
|
|
7219
|
+
]
|
|
7220
|
+
});
|
|
7221
|
+
} catch (error) {
|
|
7222
|
+
if (error?.code === 4001 || error?.code === "ACTION_REJECTED")
|
|
7223
|
+
progress(requestedId, "failed");
|
|
7224
|
+
throw error;
|
|
7225
|
+
}
|
|
7226
|
+
try {
|
|
7227
|
+
deps.onApproved?.();
|
|
7228
|
+
} catch {
|
|
7229
|
+
}
|
|
6544
7230
|
const callsId = typeof sendResult === "string" ? sendResult : sendResult?.id;
|
|
6545
7231
|
if (!callsId) {
|
|
6546
7232
|
throw new OwneyError(
|
|
6547
7233
|
"SPONSORED_CALLS_NO_ID",
|
|
6548
7234
|
"Wallet accepted wallet_sendCalls but returned no calls id.",
|
|
6549
|
-
{ chainId }
|
|
7235
|
+
{ chainId, callsId: requestedId }
|
|
6550
7236
|
);
|
|
6551
7237
|
}
|
|
7238
|
+
progress(callsId, "pending");
|
|
6552
7239
|
for (let attempt = 0; attempt < maxPolls; attempt += 1) {
|
|
6553
7240
|
const status = await deps.provider.request({
|
|
6554
7241
|
method: "wallet_getCallsStatus",
|
|
6555
7242
|
params: [callsId]
|
|
6556
7243
|
});
|
|
7244
|
+
if (status?.id && status.id !== callsId || status?.chainId && Number(status.chainId) !== chainId)
|
|
7245
|
+
throw new OwneyError("SPONSORED_CALLS_NO_RECEIPT", "Wallet returned status for a different deposit batch.", { chainId, callsId });
|
|
6557
7246
|
const txHash = status?.receipts?.[0]?.transactionHash;
|
|
6558
|
-
if (txHash && !status?.receipts?.some((receipt) => receipt.transactionHash !== txHash)) {
|
|
7247
|
+
if (txHash && /^0x[\da-f]{64}$/i.test(txHash) && !status?.receipts?.some((receipt) => receipt.transactionHash !== txHash)) {
|
|
6559
7248
|
try {
|
|
6560
7249
|
deps.onSubmitted?.(txHash, transfers);
|
|
6561
7250
|
} catch {
|
|
6562
7251
|
}
|
|
6563
7252
|
}
|
|
6564
|
-
if (status?.
|
|
7253
|
+
if (status?.status === 400 || status?.status === 500) {
|
|
7254
|
+
progress(callsId, "failed");
|
|
6565
7255
|
throw new OwneyError(
|
|
6566
7256
|
"SPONSOR_REQUEST_FAILED",
|
|
6567
7257
|
"The sponsored deposit did not complete successfully.",
|
|
6568
7258
|
{ chainId, callsId, safeToFallback: false }
|
|
6569
7259
|
);
|
|
6570
7260
|
}
|
|
6571
|
-
if (txHash && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
|
|
7261
|
+
if (txHash && /^0x[\da-f]{64}$/i.test(txHash) && (transfers.length === 1 || status?.status === 200 || status?.status === "CONFIRMED")) {
|
|
6572
7262
|
if (status?.receipts?.some(
|
|
6573
7263
|
(receipt) => receipt.transactionHash !== txHash
|
|
6574
7264
|
))
|
|
@@ -6784,8 +7474,8 @@ var OwneySDK = class {
|
|
|
6784
7474
|
return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
|
|
6785
7475
|
}
|
|
6786
7476
|
/** Builds the default USDC batch callback for the connected wallet. */
|
|
6787
|
-
getDefaultSponsoredCallback(onApproved, onSubmitted) {
|
|
6788
|
-
if (!onApproved && !onSubmitted && this.cachedSponsoredCallback)
|
|
7477
|
+
getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash) {
|
|
7478
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && this.cachedSponsoredCallback)
|
|
6789
7479
|
return this.cachedSponsoredCallback;
|
|
6790
7480
|
const provider = this.requireConnectedProvider();
|
|
6791
7481
|
const owner = this.state.walletAddress;
|
|
@@ -6795,28 +7485,29 @@ var OwneySDK = class {
|
|
|
6795
7485
|
ownerAddress: owner,
|
|
6796
7486
|
onApproved,
|
|
6797
7487
|
onSubmitted,
|
|
7488
|
+
resumeTxHash,
|
|
6798
7489
|
tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
|
|
6799
7490
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
6800
7491
|
// PublicClient/WalletClient param types — structurally identical at
|
|
6801
7492
|
// runtime, but the two share a name TS treats as unrelated.
|
|
6802
7493
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
6803
|
-
getWalletChainClient: (cid) => (0,
|
|
7494
|
+
getWalletChainClient: (cid) => (0, import_viem16.createPublicClient)({
|
|
6804
7495
|
chain: VIEM_CHAIN2[cid],
|
|
6805
|
-
transport: (0,
|
|
7496
|
+
transport: (0, import_viem16.custom)(provider)
|
|
6806
7497
|
}),
|
|
6807
|
-
getWalletClient: (cid) => (0,
|
|
7498
|
+
getWalletClient: (cid) => (0, import_viem16.createWalletClient)({
|
|
6808
7499
|
account: owner,
|
|
6809
7500
|
chain: VIEM_CHAIN2[cid],
|
|
6810
|
-
transport: (0,
|
|
7501
|
+
transport: (0, import_viem16.custom)(provider)
|
|
6811
7502
|
})
|
|
6812
7503
|
});
|
|
6813
|
-
if (!onApproved && !onSubmitted) this.cachedSponsoredCallback = callback;
|
|
7504
|
+
if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedSponsoredCallback = callback;
|
|
6814
7505
|
return callback;
|
|
6815
7506
|
}
|
|
6816
7507
|
/** Builds the wallet-native sponsored calls callback for compatible paymasters. */
|
|
6817
|
-
getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted) {
|
|
7508
|
+
getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress) {
|
|
6818
7509
|
const cached = this.cachedSponsoredCallsCallbacks.get(asset);
|
|
6819
|
-
if (!onApproved && !onSubmitted && cached) return cached;
|
|
7510
|
+
if (!onApproved && !onSubmitted && !onCallsProgress && cached) return cached;
|
|
6820
7511
|
const provider = this.requireConnectedProvider();
|
|
6821
7512
|
const callback = makeSponsoredCallsCallback({
|
|
6822
7513
|
apiKey: this.apiKey,
|
|
@@ -6826,9 +7517,10 @@ var OwneySDK = class {
|
|
|
6826
7517
|
paymasterServiceUrl: this.paymasterServiceUrl,
|
|
6827
7518
|
onApproved,
|
|
6828
7519
|
onSubmitted,
|
|
7520
|
+
onCallsProgress,
|
|
6829
7521
|
tokenAddressByChain: sponsoredTokensFor(asset)
|
|
6830
7522
|
});
|
|
6831
|
-
if (!onApproved && !onSubmitted) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
7523
|
+
if (!onApproved && !onSubmitted && !onCallsProgress) this.cachedSponsoredCallsCallbacks.set(asset, callback);
|
|
6832
7524
|
return callback;
|
|
6833
7525
|
}
|
|
6834
7526
|
/**
|
|
@@ -6837,8 +7529,8 @@ var OwneySDK = class {
|
|
|
6837
7529
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
6838
7530
|
* single-use batch authorization instead of an EIP-3009 authorization.
|
|
6839
7531
|
*/
|
|
6840
|
-
getDefaultWethSponsoredCallback(onApproved, onSubmitted) {
|
|
6841
|
-
if (!onApproved && !onSubmitted && this.cachedWethSponsoredCallback)
|
|
7532
|
+
getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash) {
|
|
7533
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && this.cachedWethSponsoredCallback)
|
|
6842
7534
|
return this.cachedWethSponsoredCallback;
|
|
6843
7535
|
const provider = this.requireConnectedProvider();
|
|
6844
7536
|
const owner = this.state.walletAddress;
|
|
@@ -6848,22 +7540,23 @@ var OwneySDK = class {
|
|
|
6848
7540
|
ownerAddress: owner,
|
|
6849
7541
|
onApproved,
|
|
6850
7542
|
onSubmitted,
|
|
7543
|
+
resumeTxHash,
|
|
6851
7544
|
tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
|
|
6852
7545
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
6853
7546
|
// PublicClient/WalletClient param types — structurally identical at
|
|
6854
7547
|
// runtime, but the two share a name TS treats as unrelated.
|
|
6855
7548
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
6856
|
-
getWalletChainClient: (cid) => (0,
|
|
7549
|
+
getWalletChainClient: (cid) => (0, import_viem16.createPublicClient)({
|
|
6857
7550
|
chain: VIEM_CHAIN2[cid],
|
|
6858
|
-
transport: (0,
|
|
7551
|
+
transport: (0, import_viem16.custom)(provider)
|
|
6859
7552
|
}),
|
|
6860
|
-
getWalletClient: (cid) => (0,
|
|
7553
|
+
getWalletClient: (cid) => (0, import_viem16.createWalletClient)({
|
|
6861
7554
|
account: owner,
|
|
6862
7555
|
chain: VIEM_CHAIN2[cid],
|
|
6863
|
-
transport: (0,
|
|
7556
|
+
transport: (0, import_viem16.custom)(provider)
|
|
6864
7557
|
})
|
|
6865
7558
|
});
|
|
6866
|
-
if (!onApproved && !onSubmitted) this.cachedWethSponsoredCallback = callback;
|
|
7559
|
+
if (!onApproved && !onSubmitted && !resumeTxHash) this.cachedWethSponsoredCallback = callback;
|
|
6867
7560
|
return callback;
|
|
6868
7561
|
}
|
|
6869
7562
|
getAgent(agentId) {
|
|
@@ -7172,6 +7865,7 @@ var OwneySDK = class {
|
|
|
7172
7865
|
async deposit(options) {
|
|
7173
7866
|
const { amount, asset, depositCallback, agentId, onApproved } = options;
|
|
7174
7867
|
const prepared = /* @__PURE__ */ new Map();
|
|
7868
|
+
const paidFallbackAgents = /* @__PURE__ */ new Set();
|
|
7175
7869
|
const onProgress = options.onProgress ? (evidence) => {
|
|
7176
7870
|
if (evidence.agentWallet) prepared.set(evidence.agentWallet.toLowerCase(), evidence);
|
|
7177
7871
|
notifyFunding(options.onProgress, evidence);
|
|
@@ -7182,152 +7876,184 @@ var OwneySDK = class {
|
|
|
7182
7876
|
if (evidence) notifyFunding(onProgress, { ...evidence, txHash: hash, transfer: "pending" });
|
|
7183
7877
|
}
|
|
7184
7878
|
} : void 0;
|
|
7879
|
+
const onCallsProgress = onProgress ? (callsId, destinations, transfer) => {
|
|
7880
|
+
for (const destination of destinations) {
|
|
7881
|
+
const evidence = prepared.get(destination.to.toLowerCase());
|
|
7882
|
+
if (evidence) notifyFunding(onProgress, { ...evidence, callsId, transfer });
|
|
7883
|
+
}
|
|
7884
|
+
} : void 0;
|
|
7185
7885
|
if (BigInt(amount) === 0n) {
|
|
7186
7886
|
return { agentResults: {} };
|
|
7187
7887
|
}
|
|
7188
7888
|
const state = this.requireState();
|
|
7189
7889
|
const chainId = this.requireChainId();
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
|
|
7202
|
-
return this.depositWithFallback(
|
|
7203
|
-
agent,
|
|
7204
|
-
state,
|
|
7205
|
-
chainId,
|
|
7206
|
-
amount,
|
|
7207
|
-
asset,
|
|
7208
|
-
effectiveCallback,
|
|
7209
|
-
depositCallback,
|
|
7210
|
-
onProgress
|
|
7211
|
-
);
|
|
7212
|
-
}
|
|
7213
|
-
const totalAmount = BigInt(amount);
|
|
7214
|
-
const eligibleAgents = this.getEligibleAgents(chainId, asset, {
|
|
7215
|
-
excludeDisabled: true
|
|
7216
|
-
});
|
|
7217
|
-
const exemptFlags = await Promise.all(
|
|
7218
|
-
eligibleAgents.map(
|
|
7219
|
-
(a) => this.hasExistingBalance(a, state, chainId, asset, true)
|
|
7220
|
-
)
|
|
7221
|
-
);
|
|
7222
|
-
const exempt = new Set(
|
|
7223
|
-
eligibleAgents.filter((_, i) => exemptFlags[i]).map((a) => a.id)
|
|
7224
|
-
);
|
|
7225
|
-
const agentAmounts = this.splitDepositAmount(
|
|
7226
|
-
totalAmount,
|
|
7227
|
-
eligibleAgents,
|
|
7228
|
-
chainId,
|
|
7229
|
-
asset,
|
|
7230
|
-
exempt
|
|
7231
|
-
);
|
|
7232
|
-
if (agentAmounts.length === 0) {
|
|
7233
|
-
const agentsRequiringActivation = eligibleAgents.filter(
|
|
7234
|
-
(agent) => !exempt.has(agent.id)
|
|
7235
|
-
);
|
|
7236
|
-
const perAgentMinimum = agentsRequiringActivation.reduce(
|
|
7237
|
-
(highest, agent) => {
|
|
7238
|
-
const minimum = this.getMinDepositAmount(agent, chainId, asset);
|
|
7239
|
-
return minimum > highest ? minimum : highest;
|
|
7240
|
-
},
|
|
7241
|
-
0n
|
|
7242
|
-
);
|
|
7243
|
-
const minimumRequired = agentsRequiringActivation.length > 0 ? (perAgentMinimum > 0n ? perAgentMinimum : 1n) * BigInt(agentsRequiringActivation.length) : BigInt(eligibleAgents.length);
|
|
7244
|
-
throw new OwneyError(
|
|
7245
|
-
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
7246
|
-
`Amount "${amount}" cannot activate every eligible agent. The combined minimum is "${minimumRequired.toString()}" for ${asset}.`,
|
|
7247
|
-
{
|
|
7248
|
-
amount,
|
|
7249
|
-
asset,
|
|
7250
|
-
chainId,
|
|
7251
|
-
minDepositAmount: minimumRequired.toString(),
|
|
7252
|
-
perAgentDepositAmount: perAgentMinimum.toString(),
|
|
7253
|
-
agentMinimums: agentsRequiringActivation.map((agent) => ({
|
|
7254
|
-
agentId: agent.id,
|
|
7255
|
-
minDepositAmount: this.getMinDepositAmount(
|
|
7256
|
-
agent,
|
|
7257
|
-
chainId,
|
|
7258
|
-
asset
|
|
7259
|
-
).toString()
|
|
7260
|
-
}))
|
|
7261
|
-
}
|
|
7262
|
-
);
|
|
7263
|
-
}
|
|
7264
|
-
const batchTransfer = getDepositBatchTransfer(effectiveCallback);
|
|
7265
|
-
if (!depositCallback && batchTransfer) {
|
|
7266
|
-
return runAgentDepositBatch(
|
|
7267
|
-
chainId,
|
|
7268
|
-
agentAmounts.map(({ agent, amount: amount2 }) => ({
|
|
7269
|
-
id: agent.id,
|
|
7270
|
-
amount: amount2,
|
|
7271
|
-
run: (callback) => withFailureReporting(
|
|
7272
|
-
this.apiKey,
|
|
7273
|
-
agent.id,
|
|
7274
|
-
() => onProgress ? agent.deposit(state, chainId, amount2, asset, callback, onProgress) : agent.deposit(state, chainId, amount2, asset, callback),
|
|
7275
|
-
this.routingApiBaseUrl
|
|
7276
|
-
)
|
|
7277
|
-
})),
|
|
7278
|
-
async (cid, transfers) => {
|
|
7279
|
-
try {
|
|
7280
|
-
return await batchTransfer(cid, transfers);
|
|
7281
|
-
} catch (error) {
|
|
7282
|
-
if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
|
|
7283
|
-
throw error;
|
|
7284
|
-
const requiredAmount = transfers.reduce(
|
|
7285
|
-
(sum, transfer) => sum + BigInt(transfer.amount),
|
|
7286
|
-
0n
|
|
7287
|
-
);
|
|
7288
|
-
await this.approvePermit2(
|
|
7289
|
-
asset,
|
|
7290
|
-
requiredAmount,
|
|
7291
|
-
cid
|
|
7292
|
-
);
|
|
7293
|
-
return batchTransfer(cid, transfers);
|
|
7294
|
-
}
|
|
7890
|
+
if (options.resumeTxHash && (depositCallback || this.paymasterServiceUrl || !/^0x[\da-f]{64}$/i.test(options.resumeTxHash)))
|
|
7891
|
+
throw new OwneyError("DEPOSIT_IN_PROGRESS", "This deposit cannot resume the saved relayer transaction.");
|
|
7892
|
+
const effectiveCallback = depositCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash) : this.getDefaultSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash));
|
|
7893
|
+
try {
|
|
7894
|
+
if (agentId) {
|
|
7895
|
+
if (this.isAgentDisabled(agentId)) {
|
|
7896
|
+
throw new OwneyError(
|
|
7897
|
+
"AGENT_DISABLED",
|
|
7898
|
+
`Cannot deposit into disabled agent "${agentId}". Existing balances remain visible and withdrawable.`,
|
|
7899
|
+
{ agentId }
|
|
7900
|
+
);
|
|
7295
7901
|
}
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
index,
|
|
7301
|
-
{ agent, amount: agentAmount }
|
|
7302
|
-
] of agentAmounts.entries()) {
|
|
7303
|
-
try {
|
|
7304
|
-
agentResults[agent.id] = await this.depositWithFallback(
|
|
7902
|
+
const agent = this.getAgent(agentId);
|
|
7903
|
+
this.validateAssetSupport(agent, chainId, asset);
|
|
7904
|
+
await this.validateMinDepositAmount(agent, state, chainId, asset, amount);
|
|
7905
|
+
return await this.depositWithFallback(
|
|
7305
7906
|
agent,
|
|
7306
7907
|
state,
|
|
7307
7908
|
chainId,
|
|
7308
|
-
|
|
7909
|
+
amount,
|
|
7309
7910
|
asset,
|
|
7310
7911
|
effectiveCallback,
|
|
7311
7912
|
depositCallback,
|
|
7312
|
-
onProgress
|
|
7913
|
+
onProgress,
|
|
7914
|
+
() => paidFallbackAgents.add(agent.id),
|
|
7915
|
+
!!options.resumeTxHash
|
|
7313
7916
|
);
|
|
7314
|
-
}
|
|
7315
|
-
|
|
7917
|
+
}
|
|
7918
|
+
const totalAmount = BigInt(amount);
|
|
7919
|
+
const eligibleAgents = this.getEligibleAgents(chainId, asset, {
|
|
7920
|
+
excludeDisabled: true
|
|
7921
|
+
});
|
|
7922
|
+
const prospectiveShare = totalAmount / BigInt(eligibleAgents.length);
|
|
7923
|
+
const meetsEveryMinimum = eligibleAgents.every((agent) => prospectiveShare >= this.getMinDepositAmount(agent, chainId, asset));
|
|
7924
|
+
const exemptFlags = meetsEveryMinimum ? eligibleAgents.map(() => false) : await Promise.all(eligibleAgents.map((agent) => this.hasExistingBalance(agent, state, chainId, asset, true)));
|
|
7925
|
+
const exempt = new Set(
|
|
7926
|
+
eligibleAgents.filter((_, i) => exemptFlags[i]).map((a) => a.id)
|
|
7927
|
+
);
|
|
7928
|
+
const agentAmounts = this.splitDepositAmount(
|
|
7929
|
+
totalAmount,
|
|
7930
|
+
eligibleAgents,
|
|
7931
|
+
chainId,
|
|
7932
|
+
asset,
|
|
7933
|
+
exempt
|
|
7934
|
+
);
|
|
7935
|
+
if (agentAmounts.length === 0) {
|
|
7936
|
+
const agentsRequiringActivation = eligibleAgents.filter(
|
|
7937
|
+
(agent) => !exempt.has(agent.id)
|
|
7938
|
+
);
|
|
7939
|
+
const perAgentMinimum = agentsRequiringActivation.reduce(
|
|
7940
|
+
(highest, agent) => {
|
|
7941
|
+
const minimum = this.getMinDepositAmount(agent, chainId, asset);
|
|
7942
|
+
return minimum > highest ? minimum : highest;
|
|
7943
|
+
},
|
|
7944
|
+
0n
|
|
7945
|
+
);
|
|
7946
|
+
const minimumRequired = agentsRequiringActivation.length > 0 ? (perAgentMinimum > 0n ? perAgentMinimum : 1n) * BigInt(agentsRequiringActivation.length) : BigInt(eligibleAgents.length);
|
|
7316
7947
|
throw new OwneyError(
|
|
7317
|
-
"
|
|
7318
|
-
"
|
|
7948
|
+
"DEPOSIT_AMOUNT_BELOW_MINIMUM",
|
|
7949
|
+
`Amount "${amount}" cannot activate every eligible agent. The combined minimum is "${minimumRequired.toString()}" for ${asset}.`,
|
|
7319
7950
|
{
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7951
|
+
amount,
|
|
7952
|
+
asset,
|
|
7953
|
+
chainId,
|
|
7954
|
+
minDepositAmount: minimumRequired.toString(),
|
|
7955
|
+
perAgentDepositAmount: perAgentMinimum.toString(),
|
|
7956
|
+
agentMinimums: agentsRequiringActivation.map((agent) => ({
|
|
7957
|
+
agentId: agent.id,
|
|
7958
|
+
minDepositAmount: this.getMinDepositAmount(
|
|
7959
|
+
agent,
|
|
7960
|
+
chainId,
|
|
7961
|
+
asset
|
|
7962
|
+
).toString()
|
|
7963
|
+
}))
|
|
7964
|
+
}
|
|
7965
|
+
);
|
|
7966
|
+
}
|
|
7967
|
+
const batchTransfer = getDepositBatchTransfer(effectiveCallback);
|
|
7968
|
+
if (!depositCallback && batchTransfer) {
|
|
7969
|
+
return await runAgentDepositBatch(
|
|
7970
|
+
chainId,
|
|
7971
|
+
agentAmounts.map(({ agent, amount: amount2 }) => ({
|
|
7972
|
+
id: agent.id,
|
|
7973
|
+
amount: amount2,
|
|
7974
|
+
run: (callback) => withFailureReporting(
|
|
7975
|
+
this.apiKey,
|
|
7976
|
+
agent.id,
|
|
7977
|
+
() => onProgress ? agent.deposit(state, chainId, amount2, asset, callback, onProgress) : agent.deposit(state, chainId, amount2, asset, callback),
|
|
7978
|
+
this.routingApiBaseUrl
|
|
7979
|
+
)
|
|
7980
|
+
})),
|
|
7981
|
+
async (cid, transfers) => {
|
|
7982
|
+
try {
|
|
7983
|
+
return await batchTransfer(cid, transfers);
|
|
7984
|
+
} catch (error) {
|
|
7985
|
+
if (!(error instanceof OwneyError) || error.code !== "PERMIT2_APPROVAL_REQUIRED")
|
|
7986
|
+
throw error;
|
|
7987
|
+
const requiredAmount = transfers.reduce(
|
|
7988
|
+
(sum, transfer) => sum + BigInt(transfer.amount),
|
|
7989
|
+
0n
|
|
7990
|
+
);
|
|
7991
|
+
await this.approvePermit2(
|
|
7992
|
+
asset,
|
|
7993
|
+
requiredAmount,
|
|
7994
|
+
cid
|
|
7995
|
+
);
|
|
7996
|
+
return batchTransfer(cid, transfers);
|
|
7997
|
+
}
|
|
7998
|
+
}
|
|
7327
7999
|
);
|
|
7328
8000
|
}
|
|
8001
|
+
const agentResults = {};
|
|
8002
|
+
for (const [
|
|
8003
|
+
index,
|
|
8004
|
+
{ agent, amount: agentAmount }
|
|
8005
|
+
] of agentAmounts.entries()) {
|
|
8006
|
+
try {
|
|
8007
|
+
agentResults[agent.id] = await this.depositWithFallback(
|
|
8008
|
+
agent,
|
|
8009
|
+
state,
|
|
8010
|
+
chainId,
|
|
8011
|
+
agentAmount,
|
|
8012
|
+
asset,
|
|
8013
|
+
effectiveCallback,
|
|
8014
|
+
depositCallback,
|
|
8015
|
+
onProgress,
|
|
8016
|
+
() => paidFallbackAgents.add(agent.id),
|
|
8017
|
+
!!options.resumeTxHash
|
|
8018
|
+
);
|
|
8019
|
+
} catch (error) {
|
|
8020
|
+
if (Object.keys(agentResults).length === 0) throw error;
|
|
8021
|
+
throw new OwneyError(
|
|
8022
|
+
"DEPOSIT_PARTIAL_FAILURE",
|
|
8023
|
+
"Some deposits completed before another agent failed. Check activity and balances before depositing again; the failed transfer may still settle.",
|
|
8024
|
+
{
|
|
8025
|
+
agentResults,
|
|
8026
|
+
failedAgentId: agent.id,
|
|
8027
|
+
failedAmount: agentAmount,
|
|
8028
|
+
unattemptedAgentIds: agentAmounts.slice(index + 1).map(({ agent: agent2 }) => agent2.id),
|
|
8029
|
+
cause: error
|
|
8030
|
+
},
|
|
8031
|
+
agent.id
|
|
8032
|
+
);
|
|
8033
|
+
}
|
|
8034
|
+
}
|
|
8035
|
+
return { agentResults };
|
|
8036
|
+
} catch (error) {
|
|
8037
|
+
if (!depositCallback && onProgress && !options.resumeTxHash) {
|
|
8038
|
+
const ids = agentId ? [agentId] : this.getEligibleAgents(chainId, asset).map((agent) => agent.id);
|
|
8039
|
+
for (const id of ids) {
|
|
8040
|
+
const evidence = [...prepared.values()].find((value) => value.agentId === id);
|
|
8041
|
+
if (paidFallbackAgents.has(id) || evidence?.transfer === "confirmed" || evidence?.transfer === "failed") continue;
|
|
8042
|
+
const rejected = error instanceof OwneyError && error.details?.notSubmitted === true;
|
|
8043
|
+
if (!rejected && (evidence?.txHash || evidence?.userOpHash || evidence?.callsId)) continue;
|
|
8044
|
+
notifyFunding(onProgress, { ...evidence ?? {
|
|
8045
|
+
agentId: id,
|
|
8046
|
+
chainId,
|
|
8047
|
+
token: asset,
|
|
8048
|
+
walletAddress: state.walletAddress,
|
|
8049
|
+
intendedAmount: amount,
|
|
8050
|
+
registration: "unknown",
|
|
8051
|
+
delivery: "not-applicable"
|
|
8052
|
+
}, transfer: "failed" });
|
|
8053
|
+
}
|
|
8054
|
+
}
|
|
8055
|
+
throw error;
|
|
7329
8056
|
}
|
|
7330
|
-
return { agentResults };
|
|
7331
8057
|
}
|
|
7332
8058
|
/**
|
|
7333
8059
|
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
@@ -7356,7 +8082,7 @@ var OwneySDK = class {
|
|
|
7356
8082
|
* propagates unchanged. Scoped per agent invocation so one agent's failure
|
|
7357
8083
|
* doesn't force a retry of sibling agents in a multi-agent split.
|
|
7358
8084
|
*/
|
|
7359
|
-
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress) {
|
|
8085
|
+
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress, onPaidFallback, resumeOnly = false) {
|
|
7360
8086
|
const attempt = (callback) => withFailureReporting(
|
|
7361
8087
|
this.apiKey,
|
|
7362
8088
|
agent.id,
|
|
@@ -7368,6 +8094,7 @@ var OwneySDK = class {
|
|
|
7368
8094
|
try {
|
|
7369
8095
|
return await attempt(effectiveCallback);
|
|
7370
8096
|
} catch (error) {
|
|
8097
|
+
if (resumeOnly) throw error;
|
|
7371
8098
|
if (!approvalAttempted && appCallback === void 0 && (asset === "WETH" || asset === "USDC") && error instanceof OwneyError && error.code === "PERMIT2_APPROVAL_REQUIRED") {
|
|
7372
8099
|
approvalAttempted = true;
|
|
7373
8100
|
console.warn(
|
|
@@ -7385,6 +8112,7 @@ var OwneySDK = class {
|
|
|
7385
8112
|
`[owney-sdk] ${asset} sponsorship unavailable, falling back to user-paid deposit:`,
|
|
7386
8113
|
error
|
|
7387
8114
|
);
|
|
8115
|
+
onPaidFallback?.();
|
|
7388
8116
|
return attempt(void 0);
|
|
7389
8117
|
}
|
|
7390
8118
|
}
|
|
@@ -7467,7 +8195,12 @@ var OwneySDK = class {
|
|
|
7467
8195
|
*/
|
|
7468
8196
|
async hasExistingBalance(agent, state, chainId, asset, requireReliableRead = false) {
|
|
7469
8197
|
try {
|
|
7470
|
-
|
|
8198
|
+
if (agent.hasExistingDepositFunds && await agent.hasExistingDepositFunds(
|
|
8199
|
+
state,
|
|
8200
|
+
chainId,
|
|
8201
|
+
asset
|
|
8202
|
+
).catch(() => void 0) === true) return true;
|
|
8203
|
+
const balance = await agent.getBalances(state, chainId, asset);
|
|
7471
8204
|
const target = asset.toLowerCase();
|
|
7472
8205
|
const token = balance.tokens.find(
|
|
7473
8206
|
(t) => t.chainId === chainId && t.asset.toLowerCase() === target
|
|
@@ -7593,7 +8326,8 @@ var OwneySDK = class {
|
|
|
7593
8326
|
return verified;
|
|
7594
8327
|
}
|
|
7595
8328
|
/** Resolve a saved sponsored-withdrawal ID without opening a wallet prompt. */
|
|
7596
|
-
async resolveSponsoredWithdrawalEvidence(evidence) {
|
|
8329
|
+
async resolveSponsoredWithdrawalEvidence(evidence, options = {}) {
|
|
8330
|
+
if (evidence.callsId && !evidence.userOpHash) return this.resolveWalletCallsFundingEvidence(evidence, "withdraw");
|
|
7597
8331
|
const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
7598
8332
|
if (evidence.agentId !== "yieldseeker" || chainId !== 8453 || evidence.txHash || state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase()) return evidence;
|
|
7599
8333
|
const agent = this.getAgent("yieldseeker");
|
|
@@ -7609,18 +8343,26 @@ var OwneySDK = class {
|
|
|
7609
8343
|
owner: state.walletAddress,
|
|
7610
8344
|
agentWallet: wallet,
|
|
7611
8345
|
asset: SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token)?.address,
|
|
7612
|
-
userOpHash: evidence.userOpHash
|
|
8346
|
+
userOpHash: evidence.userOpHash,
|
|
8347
|
+
...evidence.userOperation ? { userOperation: Object.fromEntries([
|
|
8348
|
+
"sender",
|
|
8349
|
+
"nonce",
|
|
8350
|
+
"initCode",
|
|
8351
|
+
"callData",
|
|
8352
|
+
"callGasLimit",
|
|
8353
|
+
"verificationGasLimit",
|
|
8354
|
+
"preVerificationGas",
|
|
8355
|
+
"maxFeePerGas",
|
|
8356
|
+
"maxPriorityFeePerGas",
|
|
8357
|
+
"paymasterAndData"
|
|
8358
|
+
].map((key2) => [key2, evidence.userOperation[key2]])) } : {},
|
|
8359
|
+
...Number.isFinite(options.submittedAt) && options.submittedAt > 0 ? { submittedAt: options.submittedAt } : {}
|
|
7613
8360
|
})
|
|
7614
8361
|
});
|
|
7615
8362
|
if (!response.ok) throw new OwneyError("AGENT_API_ERROR", "Could not resolve sponsored withdrawal status.");
|
|
7616
8363
|
const payload = await response.json();
|
|
7617
8364
|
txHash = payload.data?.txHash ?? void 0;
|
|
7618
8365
|
executionFailed = payload.data?.executionFailed === true;
|
|
7619
|
-
} else if (evidence.callsId) {
|
|
7620
|
-
const status = await state.provider.request({ method: "wallet_getCallsStatus", params: [evidence.callsId] });
|
|
7621
|
-
if (status?.id && status.id !== evidence.callsId || status?.chainId && Number(status.chainId) !== chainId) return evidence;
|
|
7622
|
-
executionFailed = status?.status === 400 || status?.status === 500;
|
|
7623
|
-
txHash = status?.receipts?.[0]?.transactionHash;
|
|
7624
8366
|
}
|
|
7625
8367
|
assertCurrent();
|
|
7626
8368
|
if (executionFailed) return {
|
|
@@ -7631,6 +8373,60 @@ var OwneySDK = class {
|
|
|
7631
8373
|
};
|
|
7632
8374
|
return txHash && /^0x[0-9a-fA-F]{64}$/.test(txHash) ? { ...evidence, txHash } : evidence;
|
|
7633
8375
|
}
|
|
8376
|
+
/** Recover an EIP-5792 deposit or payout using this account's exact batch ID. */
|
|
8377
|
+
async resolveWalletCallsFundingEvidence(evidence, direction) {
|
|
8378
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
8379
|
+
if (!evidence.callsId || evidence.txHash || state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase()) return evidence;
|
|
8380
|
+
const agent = this.getAgent(evidence.agentId);
|
|
8381
|
+
const wallet = agent.getFundingWallet ? await agent.getFundingWallet(state, chainId, evidence.token) : (await agent.getUserProfile(state, chainId)).smartWallet;
|
|
8382
|
+
if (!wallet || wallet.toLowerCase() !== evidence.agentWallet?.toLowerCase()) return evidence;
|
|
8383
|
+
const status = await state.provider.request({ method: "wallet_getCallsStatus", params: [evidence.callsId] });
|
|
8384
|
+
assertCurrent();
|
|
8385
|
+
if (status?.id !== void 0 && status.id !== evidence.callsId || status?.chainId !== void 0 && Number(status.chainId) !== chainId) return evidence;
|
|
8386
|
+
const statusCode2 = Number(status?.status);
|
|
8387
|
+
if (statusCode2 === 400 || statusCode2 === 500) return {
|
|
8388
|
+
...evidence,
|
|
8389
|
+
transfer: "failed",
|
|
8390
|
+
delivery: direction === "withdraw" ? "failed" : "not-applicable"
|
|
8391
|
+
};
|
|
8392
|
+
const hashes = [...new Set((status?.receipts ?? []).map((receipt) => receipt.transactionHash?.toLowerCase()).filter((hash) => !!hash && /^0x[\da-f]{64}$/i.test(hash)))];
|
|
8393
|
+
if (hashes.length === 1) return { ...evidence, txHash: hashes[0] };
|
|
8394
|
+
if (hashes.length > 16) return evidence;
|
|
8395
|
+
const matches = [];
|
|
8396
|
+
for (const txHash of hashes) {
|
|
8397
|
+
const inspected = await this.inspectFunding({ ...evidence, txHash }, direction);
|
|
8398
|
+
assertCurrent();
|
|
8399
|
+
if (inspected.transfer === "confirmed") matches.push(inspected);
|
|
8400
|
+
}
|
|
8401
|
+
return matches.length === 1 ? matches[0] : evidence;
|
|
8402
|
+
}
|
|
8403
|
+
/** Whether a foreground deposit can resume the same signed batch, never a
|
|
8404
|
+
* fresh authorization. The batch rail independently rechecks the exact plan. */
|
|
8405
|
+
async canResumePendingDeposit(evidences, amount) {
|
|
8406
|
+
if (this.paymasterServiceUrl || !evidences.length || !/^\d+$/.test(amount)) return false;
|
|
8407
|
+
const first = evidences[0];
|
|
8408
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(first.chainId);
|
|
8409
|
+
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === first.token);
|
|
8410
|
+
if (!token) return false;
|
|
8411
|
+
const pending = pendingTokenBatch({
|
|
8412
|
+
apiKey: this.apiKey,
|
|
8413
|
+
baseUrl: this.routingApiBaseUrl,
|
|
8414
|
+
owner: state.walletAddress,
|
|
8415
|
+
chainId,
|
|
8416
|
+
token: token.address
|
|
8417
|
+
});
|
|
8418
|
+
if (!pending || pending.transfers.length !== evidences.length || pending.transfers.reduce((sum, transfer) => sum + BigInt(transfer.amount), 0n).toString() !== amount) return false;
|
|
8419
|
+
const destinations = /* @__PURE__ */ new Set();
|
|
8420
|
+
for (const evidence of evidences) {
|
|
8421
|
+
if (evidence.chainId !== chainId || evidence.token !== first.token || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || evidence.callsId || evidence.userOpHash || evidence.txHash?.toLowerCase() !== pending.txHash.toLowerCase()) return false;
|
|
8422
|
+
const agent = this.getAgent(evidence.agentId);
|
|
8423
|
+
const wallet = agent.getFundingWallet ? await agent.getFundingWallet(state, chainId, evidence.token) : (await agent.getUserProfile(state, chainId)).smartWallet;
|
|
8424
|
+
if (!wallet || destinations.has(wallet.toLowerCase()) || !pending.transfers.some((transfer) => transfer.to.toLowerCase() === wallet.toLowerCase() && transfer.amount === evidence.intendedAmount)) return false;
|
|
8425
|
+
destinations.add(wallet.toLowerCase());
|
|
8426
|
+
}
|
|
8427
|
+
assertCurrent();
|
|
8428
|
+
return true;
|
|
8429
|
+
}
|
|
7634
8430
|
/** A display acknowledgement controls polling only; it never confirms a financial outcome. */
|
|
7635
8431
|
endFundingBalanceRefresh(evidence, read2) {
|
|
7636
8432
|
const { state, chainId } = this.captureReadScope(evidence.chainId);
|
|
@@ -7731,6 +8527,30 @@ var OwneySDK = class {
|
|
|
7731
8527
|
}
|
|
7732
8528
|
};
|
|
7733
8529
|
const token = asset;
|
|
8530
|
+
const agentFailures = {};
|
|
8531
|
+
const recordAgentFailure = (id, error) => {
|
|
8532
|
+
if (!(error instanceof OwneyError)) return;
|
|
8533
|
+
const details = error.details ?? {};
|
|
8534
|
+
const codeValue = (value) => typeof value === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(value) ? value : void 0;
|
|
8535
|
+
const revertNames = ["InsufficientBalance", "InvalidAsset", "Unauthorized", "Error", "Panic", "FailedOp", "FailedOpWithRevert"];
|
|
8536
|
+
const panicCodes = [0, 1, 17, 18, 33, 34, 49, 50, 65, 81];
|
|
8537
|
+
agentFailures[id] = {
|
|
8538
|
+
code: error.code,
|
|
8539
|
+
phase: ["prepare", "send", "observe"].includes(String(details.phase)) ? details.phase : void 0,
|
|
8540
|
+
failureCode: codeValue(details.failureCode),
|
|
8541
|
+
reason: codeValue(details.reason),
|
|
8542
|
+
withdrawalPhase: details.withdrawalPhase === "unwind" ? "unwind" : void 0,
|
|
8543
|
+
statusCode: typeof details.statusCode === "number" ? details.statusCode : void 0,
|
|
8544
|
+
rpcCode: typeof details.rpcCode === "number" ? details.rpcCode : void 0,
|
|
8545
|
+
rpcMethod: typeof details.rpcMethod === "string" && /^[A-Za-z_]{1,80}$/.test(details.rpcMethod) ? details.rpcMethod : void 0,
|
|
8546
|
+
revertSelector: typeof details.revertSelector === "string" && /^0x[0-9a-f]{8}$/i.test(details.revertSelector) ? details.revertSelector : void 0,
|
|
8547
|
+
revertName: typeof details.revertName === "string" && revertNames.includes(details.revertName) ? details.revertName : void 0,
|
|
8548
|
+
aaCode: typeof details.aaCode === "string" && /^AA[0-9]{2}$/.test(details.aaCode) ? details.aaCode : void 0,
|
|
8549
|
+
panicCode: typeof details.panicCode === "string" && /^0x[0-9a-f]{1,2}$/i.test(details.panicCode) && panicCodes.includes(Number.parseInt(details.panicCode.slice(2), 16)) ? details.panicCode : void 0,
|
|
8550
|
+
safeToFallback: typeof details.safeToFallback === "boolean" ? details.safeToFallback : void 0,
|
|
8551
|
+
executionFailed: typeof details.executionFailed === "boolean" ? details.executionFailed : void 0
|
|
8552
|
+
};
|
|
8553
|
+
};
|
|
7734
8554
|
const submissionEvidence = /* @__PURE__ */ new Map();
|
|
7735
8555
|
const submit = async (agent, requested2) => {
|
|
7736
8556
|
attempted.add(agent.id);
|
|
@@ -7751,9 +8571,19 @@ var OwneySDK = class {
|
|
|
7751
8571
|
};
|
|
7752
8572
|
progress(evidence);
|
|
7753
8573
|
try {
|
|
8574
|
+
if (options.onBeforeSubmission)
|
|
8575
|
+
return await agent.withdraw(
|
|
8576
|
+
state,
|
|
8577
|
+
chainId,
|
|
8578
|
+
token,
|
|
8579
|
+
requested2,
|
|
8580
|
+
progress,
|
|
8581
|
+
options.gasMode ?? "auto",
|
|
8582
|
+
(next) => options.onBeforeSubmission({ ...evidence, ...next })
|
|
8583
|
+
);
|
|
7754
8584
|
return await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
|
|
7755
8585
|
} catch (error) {
|
|
7756
|
-
if (!evidence.txHash && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
8586
|
+
if (!evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
7757
8587
|
progress({ ...evidence, transfer: "failed", delivery: "failed" });
|
|
7758
8588
|
throw error;
|
|
7759
8589
|
}
|
|
@@ -7803,6 +8633,7 @@ var OwneySDK = class {
|
|
|
7803
8633
|
}
|
|
7804
8634
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
7805
8635
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
8636
|
+
recordAgentFailure(agent.id, err);
|
|
7806
8637
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
7807
8638
|
void reportAgentFailure(
|
|
7808
8639
|
this.apiKey,
|
|
@@ -7813,17 +8644,23 @@ var OwneySDK = class {
|
|
|
7813
8644
|
}
|
|
7814
8645
|
}
|
|
7815
8646
|
if (Object.keys(results2).length === 0) {
|
|
8647
|
+
if (Object.keys(agentErrors2).length === 0)
|
|
8648
|
+
throw new OwneyError(
|
|
8649
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
8650
|
+
"No funds are currently available to withdraw for this asset.",
|
|
8651
|
+
{ asset, available: "0" }
|
|
8652
|
+
);
|
|
7816
8653
|
throw new OwneyError(
|
|
7817
8654
|
"WITHDRAW_ALL_FAILED",
|
|
7818
8655
|
`Failed to withdraw "${asset}" from all eligible agents.`,
|
|
7819
|
-
{ asset, agentErrors: agentErrors2 }
|
|
8656
|
+
{ asset, agentErrors: agentErrors2, agentFailures }
|
|
7820
8657
|
);
|
|
7821
8658
|
}
|
|
7822
8659
|
if (Object.keys(agentErrors2).length > 0) {
|
|
7823
8660
|
throw new OwneyError(
|
|
7824
8661
|
"WITHDRAW_PARTIAL_FAILURE",
|
|
7825
8662
|
"Some agents could not complete the withdrawal.",
|
|
7826
|
-
{ asset, partialResults: results2, agentErrors: agentErrors2, ...Object.keys(incompleteWithdrawals).length ? { incompleteWithdrawals } : {} }
|
|
8663
|
+
{ asset, partialResults: results2, agentErrors: agentErrors2, agentFailures, ...Object.keys(incompleteWithdrawals).length ? { incompleteWithdrawals } : {} }
|
|
7827
8664
|
);
|
|
7828
8665
|
}
|
|
7829
8666
|
const amountsKnown = Object.values(results2).every(
|
|
@@ -7904,6 +8741,7 @@ var OwneySDK = class {
|
|
|
7904
8741
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
7905
8742
|
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
7906
8743
|
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
8744
|
+
recordAgentFailure(p.agent.id, err);
|
|
7907
8745
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
7908
8746
|
void reportAgentFailure(
|
|
7909
8747
|
this.apiKey,
|
|
@@ -7924,7 +8762,8 @@ var OwneySDK = class {
|
|
|
7924
8762
|
{
|
|
7925
8763
|
asset,
|
|
7926
8764
|
amount,
|
|
7927
|
-
agentErrors
|
|
8765
|
+
agentErrors,
|
|
8766
|
+
agentFailures
|
|
7928
8767
|
}
|
|
7929
8768
|
);
|
|
7930
8769
|
}
|
|
@@ -7939,6 +8778,7 @@ var OwneySDK = class {
|
|
|
7939
8778
|
withdrawn: withdrawn.toString(),
|
|
7940
8779
|
partialResults: results,
|
|
7941
8780
|
agentErrors,
|
|
8781
|
+
agentFailures,
|
|
7942
8782
|
...unavailableAgentIds.length > 0 ? { unavailableAgents: unavailableAgentIds } : {}
|
|
7943
8783
|
}
|
|
7944
8784
|
);
|
|
@@ -8398,16 +9238,16 @@ var OwneySDK = class {
|
|
|
8398
9238
|
);
|
|
8399
9239
|
}
|
|
8400
9240
|
const provider = this.requireConnectedProvider();
|
|
8401
|
-
const walletChainClient = (0,
|
|
9241
|
+
const walletChainClient = (0, import_viem16.createPublicClient)({
|
|
8402
9242
|
chain: VIEM_CHAIN2[chainId],
|
|
8403
|
-
transport: (0,
|
|
9243
|
+
transport: (0, import_viem16.custom)(provider)
|
|
8404
9244
|
});
|
|
8405
9245
|
const publicClient = this.getPaidRpcClient(chainId);
|
|
8406
9246
|
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
8407
|
-
const wallet = (0,
|
|
9247
|
+
const wallet = (0, import_viem16.createWalletClient)({
|
|
8408
9248
|
account: state.walletAddress,
|
|
8409
9249
|
chain: VIEM_CHAIN2[chainId],
|
|
8410
|
-
transport: (0,
|
|
9250
|
+
transport: (0, import_viem16.custom)(provider)
|
|
8411
9251
|
});
|
|
8412
9252
|
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
8413
9253
|
const hash = await wallet.writeContract({
|
|
@@ -8569,7 +9409,7 @@ var OwneySDK = class {
|
|
|
8569
9409
|
};
|
|
8570
9410
|
|
|
8571
9411
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
8572
|
-
var
|
|
9412
|
+
var import_viem17 = require("viem");
|
|
8573
9413
|
var import_siwe2 = require("siwe");
|
|
8574
9414
|
var import_sdk2 = require("@zyfai/sdk");
|
|
8575
9415
|
|
|
@@ -8696,7 +9536,7 @@ function buildSIWXConfig(deps) {
|
|
|
8696
9536
|
issuedAt,
|
|
8697
9537
|
toString() {
|
|
8698
9538
|
return new import_siwe2.SiweMessage({
|
|
8699
|
-
address: (0,
|
|
9539
|
+
address: (0, import_viem17.getAddress)(accountAddress),
|
|
8700
9540
|
chainId: numericChainId(chainId),
|
|
8701
9541
|
domain,
|
|
8702
9542
|
uri,
|