@owney/sdk 0.7.25-beta.12 → 0.7.25-beta.14
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 +1029 -136
- package/dist/index.d.cts +47 -4
- package/dist/index.d.ts +47 -4
- package/dist/index.js +1017 -115
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/lib/funding-evidence.ts
|
|
2
2
|
import { decodeEventLog, decodeFunctionData, erc20Abi, parseAbi } from "viem";
|
|
3
|
+
import { entryPoint06Abi, entryPoint07Abi, getUserOperationHash, toUserOperation } from "viem/account-abstraction";
|
|
3
4
|
function notifyFunding(callback, evidence) {
|
|
4
5
|
try {
|
|
5
6
|
void Promise.resolve(callback?.({ ...evidence })).catch(() => {
|
|
@@ -9,11 +10,53 @@ function notifyFunding(callback, evidence) {
|
|
|
9
10
|
}
|
|
10
11
|
var fullWithdrawalAbi = parseAbi(["function withdrawAllAssetToUser(address recipient, address asset)"]);
|
|
11
12
|
var withdrawalEventAbi = parseAbi(["event WithdrewTokenToUser(address indexed owner, address indexed recipient, address indexed token, uint256 amount)"]);
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
var accountExecutionAbi = parseAbi([
|
|
14
|
+
"function execute(address target,uint256 value,bytes data)",
|
|
15
|
+
"function executeBatch((address target,uint256 value,bytes data)[] calls)"
|
|
16
|
+
]);
|
|
17
|
+
var entryPoint06 = "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789";
|
|
18
|
+
var entryPoint07 = "0x0000000071727de22e5e9d8baf0edac6f37da032";
|
|
19
|
+
function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress, receipt) {
|
|
20
|
+
if (evidence.agentId !== "yieldseeker" || transaction.chainId !== evidence.chainId || transaction.hash.toLowerCase() !== evidence.txHash?.toLowerCase() || !evidence.agentWallet) return false;
|
|
21
|
+
const matchesFullCall = (data) => {
|
|
22
|
+
try {
|
|
23
|
+
const decoded = decodeFunctionData({ abi: fullWithdrawalAbi, data });
|
|
24
|
+
return decoded.args[0].toLowerCase() === evidence.walletAddress.toLowerCase() && decoded.args[1].toLowerCase() === tokenAddress.toLowerCase();
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
if (transaction.to?.toLowerCase() === evidence.agentWallet.toLowerCase() && transaction.from.toLowerCase() === evidence.walletAddress.toLowerCase()) return matchesFullCall(transaction.input);
|
|
14
30
|
try {
|
|
15
|
-
const
|
|
16
|
-
|
|
31
|
+
const address = transaction.to?.toLowerCase();
|
|
32
|
+
if (address !== entryPoint06 && address !== entryPoint07) return false;
|
|
33
|
+
const version = address === entryPoint06 ? "0.6" : "0.7";
|
|
34
|
+
const decoded = decodeFunctionData({ abi: version === "0.6" ? entryPoint06Abi : entryPoint07Abi, data: transaction.input });
|
|
35
|
+
if (decoded.functionName !== "handleOps") return false;
|
|
36
|
+
const operations = decoded.args[0].filter((operation2) => [evidence.agentWallet.toLowerCase(), evidence.walletAddress.toLowerCase()].includes(operation2.sender.toLowerCase()));
|
|
37
|
+
if (operations.length !== 1) return false;
|
|
38
|
+
const operation = operations[0];
|
|
39
|
+
const userOperation = toUserOperation(operation);
|
|
40
|
+
const hash = getUserOperationHash({
|
|
41
|
+
chainId: evidence.chainId,
|
|
42
|
+
entryPointAddress: transaction.to,
|
|
43
|
+
entryPointVersion: version,
|
|
44
|
+
userOperation
|
|
45
|
+
});
|
|
46
|
+
if (evidence.userOpHash && hash.toLowerCase() !== evidence.userOpHash.toLowerCase()) return false;
|
|
47
|
+
if (!receipt?.logs.some((log) => {
|
|
48
|
+
if (log.address.toLowerCase() !== address) return false;
|
|
49
|
+
try {
|
|
50
|
+
const event = decodeEventLog({ abi: entryPoint06Abi, data: log.data, topics: log.topics });
|
|
51
|
+
return event.eventName === "UserOperationEvent" && event.args.userOpHash.toLowerCase() === hash.toLowerCase() && event.args.sender.toLowerCase() === operation.sender.toLowerCase() && event.args.success;
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
})) return false;
|
|
56
|
+
if (operation.sender.toLowerCase() === evidence.agentWallet.toLowerCase()) return matchesFullCall(operation.callData);
|
|
57
|
+
const execution = decodeFunctionData({ abi: accountExecutionAbi, data: operation.callData });
|
|
58
|
+
const calls = execution.functionName === "execute" ? [{ target: execution.args[0], value: execution.args[1], data: execution.args[2] }] : execution.args[0];
|
|
59
|
+
return calls.length === 1 && calls[0].value === 0n && calls[0].target.toLowerCase() === evidence.agentWallet.toLowerCase() && matchesFullCall(calls[0].data);
|
|
17
60
|
} catch {
|
|
18
61
|
return false;
|
|
19
62
|
}
|
|
@@ -2603,14 +2646,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2603
2646
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
2604
2647
|
import {
|
|
2605
2648
|
createPublicClient as createPublicClient3,
|
|
2606
|
-
createWalletClient as
|
|
2607
|
-
custom as
|
|
2649
|
+
createWalletClient as createWalletClient3,
|
|
2650
|
+
custom as custom3,
|
|
2608
2651
|
encodeFunctionData,
|
|
2609
2652
|
erc20Abi as erc20Abi2,
|
|
2610
|
-
getAddress as
|
|
2653
|
+
getAddress as getAddress4,
|
|
2611
2654
|
isAddress as isAddress2
|
|
2612
2655
|
} from "viem";
|
|
2613
|
-
import { base as
|
|
2656
|
+
import { base as base4 } from "viem/chains";
|
|
2614
2657
|
|
|
2615
2658
|
// src/lib/chain-guard.ts
|
|
2616
2659
|
var CHAIN_NAMES = {
|
|
@@ -2653,10 +2696,10 @@ import { bytesToHex } from "viem";
|
|
|
2653
2696
|
// src/lib/sponsor-client.ts
|
|
2654
2697
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
2655
2698
|
async function postPaymasterIntent(input) {
|
|
2656
|
-
const
|
|
2699
|
+
const base6 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2657
2700
|
let res;
|
|
2658
2701
|
try {
|
|
2659
|
-
res = await fetch(`${
|
|
2702
|
+
res = await fetch(`${base6}/api/v1/sponsor/paymaster-intent`, {
|
|
2660
2703
|
method: "POST",
|
|
2661
2704
|
headers: {
|
|
2662
2705
|
"content-type": "application/json",
|
|
@@ -2692,11 +2735,11 @@ async function postPaymasterIntent(input) {
|
|
|
2692
2735
|
return parsed.data;
|
|
2693
2736
|
}
|
|
2694
2737
|
async function getSponsorRelayerAddress(input) {
|
|
2695
|
-
const
|
|
2738
|
+
const base6 = input.baseUrl ?? ROUTING_API_BASE_URL;
|
|
2696
2739
|
let res;
|
|
2697
2740
|
try {
|
|
2698
2741
|
res = await fetch(
|
|
2699
|
-
`${
|
|
2742
|
+
`${base6}/api/v1/sponsor/relayer-address?chainId=${input.chainId}`,
|
|
2700
2743
|
{
|
|
2701
2744
|
headers: { "x-owney-api-key": input.apiKey }
|
|
2702
2745
|
}
|
|
@@ -3532,6 +3575,49 @@ function actionEntry(action) {
|
|
|
3532
3575
|
rebalanceLog: []
|
|
3533
3576
|
};
|
|
3534
3577
|
}
|
|
3578
|
+
function allocationEntry(action, context) {
|
|
3579
|
+
if (action.actionType.toLowerCase() !== "autoseek") return void 0;
|
|
3580
|
+
const hash = action.details?.depositTransactionHash;
|
|
3581
|
+
const deposits = action.details?.deposits;
|
|
3582
|
+
if (typeof hash !== "string" || !/^0x[a-fA-F0-9]{64}$/.test(hash) || !Array.isArray(deposits) || deposits.length === 0) return void 0;
|
|
3583
|
+
const positions = [];
|
|
3584
|
+
let amountRaw = 0n;
|
|
3585
|
+
for (const deposit of deposits) {
|
|
3586
|
+
if (!deposit || typeof deposit !== "object") return void 0;
|
|
3587
|
+
const leg = deposit;
|
|
3588
|
+
const symbol = typeof leg.assetSymbol === "string" ? leg.assetSymbol.toUpperCase() : "";
|
|
3589
|
+
if (symbol !== context.asset && !(context.asset === "WETH" && symbol === "ETH")) return void 0;
|
|
3590
|
+
if (typeof leg.vaultAddress !== "string" || !isAddress(leg.vaultAddress)) return void 0;
|
|
3591
|
+
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;
|
|
3592
|
+
if (rawAmount === void 0 || rawAmount <= 0n) return void 0;
|
|
3593
|
+
const option = context.positions.find((position2) => position2.yieldOption.address.toLowerCase() === leg.vaultAddress.toLowerCase())?.yieldOption;
|
|
3594
|
+
positions.push({
|
|
3595
|
+
protocol: typeof leg.vaultProvider === "string" && leg.vaultProvider ? leg.vaultProvider : option?.provider || "Vault",
|
|
3596
|
+
pool: typeof leg.vaultName === "string" && leg.vaultName ? leg.vaultName : option?.name,
|
|
3597
|
+
tokenSymbol: context.asset,
|
|
3598
|
+
amount: formatUnits2(rawAmount, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
|
|
3599
|
+
});
|
|
3600
|
+
amountRaw += rawAmount;
|
|
3601
|
+
}
|
|
3602
|
+
return {
|
|
3603
|
+
agent: "yieldseeker",
|
|
3604
|
+
action: "Deposit",
|
|
3605
|
+
date: action.createdDate,
|
|
3606
|
+
oldApy: null,
|
|
3607
|
+
newApy: null,
|
|
3608
|
+
positions,
|
|
3609
|
+
rebalanceLog: [],
|
|
3610
|
+
transactions: [{
|
|
3611
|
+
txHashes: [hash],
|
|
3612
|
+
chainId: context.agent.chainId,
|
|
3613
|
+
tokenAddress: context.agent.assetAddress,
|
|
3614
|
+
tokenSymbol: context.asset,
|
|
3615
|
+
agentWallet: context.wallet.walletAddress,
|
|
3616
|
+
amountRaw: String(amountRaw),
|
|
3617
|
+
amount: formatUnits2(amountRaw, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
|
|
3618
|
+
}]
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3535
3621
|
function depositDestination(context, movement) {
|
|
3536
3622
|
const to = movement.toAddress.toLowerCase();
|
|
3537
3623
|
const option = context.positions.find(({ yieldOption }) => yieldOption.chainId === movement.chainId && yieldOption.assetAddress.toLowerCase() === context.agent.assetAddress.toLowerCase() && yieldOption.address.toLowerCase() === to)?.yieldOption;
|
|
@@ -3599,20 +3685,30 @@ function mapYieldseekerHistory(contexts, options) {
|
|
|
3599
3685
|
seenMovements.add(key2);
|
|
3600
3686
|
return true;
|
|
3601
3687
|
});
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3688
|
+
const movementEntries = movements.map(
|
|
3689
|
+
(movement) => movementEntry(
|
|
3690
|
+
movement,
|
|
3691
|
+
context.wallet,
|
|
3692
|
+
context.agent,
|
|
3693
|
+
context.asset,
|
|
3694
|
+
options.ownerAddress,
|
|
3695
|
+
options.vaultAddresses,
|
|
3696
|
+
depositDestination(context, movement)
|
|
3697
|
+
)
|
|
3698
|
+
).filter((entry) => entry !== void 0);
|
|
3699
|
+
const deposits = new Set(movementEntries.filter((entry) => entry.action === "Deposit").map((entry) => entry.transactions[0]?.txHashes[0]?.toLowerCase()));
|
|
3700
|
+
const actions = (context.actions ?? []).flatMap((action) => {
|
|
3701
|
+
const allocation = allocationEntry(action, context);
|
|
3702
|
+
if (allocation) {
|
|
3703
|
+
const hash = allocation.transactions[0].txHashes[0].toLowerCase();
|
|
3704
|
+
if (deposits.has(hash)) return [];
|
|
3705
|
+
deposits.add(hash);
|
|
3706
|
+
return [allocation];
|
|
3707
|
+
}
|
|
3708
|
+
const entry = actionEntry(action);
|
|
3709
|
+
return entry ? [entry] : [];
|
|
3710
|
+
});
|
|
3711
|
+
return [...movementEntries, ...actions];
|
|
3616
3712
|
});
|
|
3617
3713
|
const grouped = /* @__PURE__ */ new Map();
|
|
3618
3714
|
const ungrouped = [];
|
|
@@ -3722,6 +3818,404 @@ function mapYieldseekerAgentApy(options, days) {
|
|
|
3722
3818
|
};
|
|
3723
3819
|
}
|
|
3724
3820
|
|
|
3821
|
+
// src/agents/yieldseeker/yieldseeker.wallet-version.ts
|
|
3822
|
+
import {
|
|
3823
|
+
getAddress as getAddress2,
|
|
3824
|
+
isAddressEqual,
|
|
3825
|
+
parseAbi as parseAbi3
|
|
3826
|
+
} from "viem";
|
|
3827
|
+
var YIELDSEEKER_FACTORY = "0x9c7410a0faCAC60850C46eE5b58B518daec95130";
|
|
3828
|
+
var YIELDSEEKER_V1_IMPLEMENTATION = "0x32403455357589523435dB18b414057D7cE05051";
|
|
3829
|
+
var YIELDSEEKER_V2_IMPLEMENTATION = "0x613Db9c924ADB7A7A90c11b02Cce20c913E3Fe50";
|
|
3830
|
+
var ERC1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
|
3831
|
+
var walletAbi = parseAbi3([
|
|
3832
|
+
"function owner() view returns (address)",
|
|
3833
|
+
"function ownerAgentIndex() view returns (uint256)",
|
|
3834
|
+
"function baseAsset() view returns (address)",
|
|
3835
|
+
"function FACTORY() view returns (address)",
|
|
3836
|
+
"function upgradeToLatest()"
|
|
3837
|
+
]);
|
|
3838
|
+
var factoryAbi = parseAbi3([
|
|
3839
|
+
"function userWallets(address owner, uint256 index) view returns (address)",
|
|
3840
|
+
"function agentWalletImplementation() view returns (address)"
|
|
3841
|
+
]);
|
|
3842
|
+
function yieldseekerOwnerPath(code) {
|
|
3843
|
+
return !code || code === "0x" || /^0xef0100[0-9a-f]{40}$/i.test(code) ? "eoa" : "smart";
|
|
3844
|
+
}
|
|
3845
|
+
async function readYieldseekerWalletStatus(client, wallet, owner, asset) {
|
|
3846
|
+
const agentWallet = getAddress2(wallet);
|
|
3847
|
+
const expectedOwner = getAddress2(owner);
|
|
3848
|
+
const expectedAsset = getAddress2(asset);
|
|
3849
|
+
const [code, ownerCode, slot, actualOwner, index, actualAsset, actualFactory, latest] = await Promise.all([
|
|
3850
|
+
client.getBytecode({ address: agentWallet }),
|
|
3851
|
+
client.getBytecode({ address: expectedOwner }),
|
|
3852
|
+
client.getStorageAt({ address: agentWallet, slot: ERC1967_IMPLEMENTATION_SLOT }),
|
|
3853
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "owner" }),
|
|
3854
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "ownerAgentIndex" }),
|
|
3855
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "baseAsset" }),
|
|
3856
|
+
client.readContract({ address: agentWallet, abi: walletAbi, functionName: "FACTORY" }),
|
|
3857
|
+
client.readContract({ address: YIELDSEEKER_FACTORY, abi: factoryAbi, functionName: "agentWalletImplementation" })
|
|
3858
|
+
]);
|
|
3859
|
+
if (!code || code === "0x" || !slot || /^0x0+$/.test(slot)) {
|
|
3860
|
+
throw new Error("Yieldseeker wallet is not a deployed ERC1967 proxy");
|
|
3861
|
+
}
|
|
3862
|
+
const implementation = getAddress2(`0x${slot.slice(-40)}`);
|
|
3863
|
+
const mapped = await client.readContract({
|
|
3864
|
+
address: YIELDSEEKER_FACTORY,
|
|
3865
|
+
abi: factoryAbi,
|
|
3866
|
+
functionName: "userWallets",
|
|
3867
|
+
args: [expectedOwner, index]
|
|
3868
|
+
});
|
|
3869
|
+
const verified = isAddressEqual(actualOwner, expectedOwner) && isAddressEqual(actualAsset, expectedAsset) && isAddressEqual(actualFactory, YIELDSEEKER_FACTORY) && isAddressEqual(mapped, agentWallet);
|
|
3870
|
+
const version = !verified ? "unknown" : isAddressEqual(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : isAddressEqual(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
|
|
3871
|
+
return {
|
|
3872
|
+
agentWallet,
|
|
3873
|
+
owner: actualOwner,
|
|
3874
|
+
ownerPath: yieldseekerOwnerPath(ownerCode),
|
|
3875
|
+
asset: actualAsset,
|
|
3876
|
+
implementation,
|
|
3877
|
+
version,
|
|
3878
|
+
verified,
|
|
3879
|
+
upgradeAvailable: version === "v1" && isAddressEqual(latest, YIELDSEEKER_V2_IMPLEMENTATION)
|
|
3880
|
+
};
|
|
3881
|
+
}
|
|
3882
|
+
var YIELDSEEKER_UPGRADE_ABI = walletAbi;
|
|
3883
|
+
|
|
3884
|
+
// src/agents/yieldseeker/yieldseeker.sponsored-withdraw.ts
|
|
3885
|
+
import {
|
|
3886
|
+
createWalletClient as createWalletClient2,
|
|
3887
|
+
custom as custom2,
|
|
3888
|
+
getAddress as getAddress3,
|
|
3889
|
+
toHex
|
|
3890
|
+
} from "viem";
|
|
3891
|
+
import { getUserOperationHash as getUserOperationHash2 } from "viem/account-abstraction";
|
|
3892
|
+
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
|
|
3893
|
+
import { base as base3 } from "viem/chains";
|
|
3894
|
+
var ENTRY_POINT_V06 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
|
|
3895
|
+
var USER_OP_FEE_HEADROOM = 2n;
|
|
3896
|
+
var ENTRY_POINT_NONCE_ABI = [
|
|
3897
|
+
{ type: "function", name: "getNonce", stateMutability: "view", inputs: [
|
|
3898
|
+
{ name: "sender", type: "address" },
|
|
3899
|
+
{ name: "key", type: "uint192" }
|
|
3900
|
+
], outputs: [{ type: "uint256" }] }
|
|
3901
|
+
];
|
|
3902
|
+
var DEFAULT_ROUTING_API = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
3903
|
+
var SIMULATION_ACCOUNT = privateKeyToAccount(generatePrivateKey());
|
|
3904
|
+
function operationHash(operation) {
|
|
3905
|
+
return getUserOperationHash2({
|
|
3906
|
+
chainId: 8453,
|
|
3907
|
+
entryPointAddress: ENTRY_POINT_V06,
|
|
3908
|
+
entryPointVersion: "0.6",
|
|
3909
|
+
userOperation: {
|
|
3910
|
+
sender: operation.sender,
|
|
3911
|
+
nonce: BigInt(operation.nonce),
|
|
3912
|
+
initCode: operation.initCode,
|
|
3913
|
+
callData: operation.callData,
|
|
3914
|
+
callGasLimit: BigInt(operation.callGasLimit),
|
|
3915
|
+
verificationGasLimit: BigInt(operation.verificationGasLimit),
|
|
3916
|
+
preVerificationGas: BigInt(operation.preVerificationGas),
|
|
3917
|
+
maxFeePerGas: BigInt(operation.maxFeePerGas),
|
|
3918
|
+
maxPriorityFeePerGas: BigInt(operation.maxPriorityFeePerGas),
|
|
3919
|
+
paymasterAndData: operation.paymasterAndData,
|
|
3920
|
+
signature: operation.signature
|
|
3921
|
+
}
|
|
3922
|
+
});
|
|
3923
|
+
}
|
|
3924
|
+
function sponsorError(message, safeToFallback, details = {}) {
|
|
3925
|
+
return new OwneyError(
|
|
3926
|
+
"SPONSOR_REQUEST_FAILED",
|
|
3927
|
+
message,
|
|
3928
|
+
{ safeToFallback, ...details },
|
|
3929
|
+
"yieldseeker"
|
|
3930
|
+
);
|
|
3931
|
+
}
|
|
3932
|
+
async function requestIntent(input) {
|
|
3933
|
+
const baseUrl = (input.routingApiBaseUrl ?? DEFAULT_ROUTING_API).replace(/\/$/, "");
|
|
3934
|
+
let response;
|
|
3935
|
+
try {
|
|
3936
|
+
response = await fetch(`${baseUrl}/api/v1/sponsor/yieldseeker-withdraw/intent`, {
|
|
3937
|
+
method: "POST",
|
|
3938
|
+
headers: {
|
|
3939
|
+
"content-type": "application/json",
|
|
3940
|
+
"x-owney-api-key": input.apiKey,
|
|
3941
|
+
Authorization: `Signature ${input.yieldseekerSignature}`
|
|
3942
|
+
},
|
|
3943
|
+
body: JSON.stringify({
|
|
3944
|
+
chainId: 8453,
|
|
3945
|
+
owner: input.owner,
|
|
3946
|
+
recipient: input.owner,
|
|
3947
|
+
agentWallet: input.agentWallet,
|
|
3948
|
+
asset: input.asset,
|
|
3949
|
+
callData: input.callData,
|
|
3950
|
+
action: input.action,
|
|
3951
|
+
yieldseekerUserId: input.userId,
|
|
3952
|
+
yieldseekerAgentId: input.agentId
|
|
3953
|
+
})
|
|
3954
|
+
});
|
|
3955
|
+
} catch {
|
|
3956
|
+
throw sponsorError("Withdrawal sponsorship could not be requested.", true);
|
|
3957
|
+
}
|
|
3958
|
+
const envelope = await response.json().catch(() => null);
|
|
3959
|
+
const intent = envelope?.data;
|
|
3960
|
+
if (!response.ok || !envelope?.success || !intent?.intent || !["eoa", "smart"].includes(intent.ownerPath)) {
|
|
3961
|
+
throw sponsorError(
|
|
3962
|
+
"Withdrawal sponsorship is currently unavailable.",
|
|
3963
|
+
true,
|
|
3964
|
+
{ statusCode: response.status }
|
|
3965
|
+
);
|
|
3966
|
+
}
|
|
3967
|
+
const url = new URL(`${baseUrl}/api/v1/sponsor/yieldseeker-withdraw/rpc`);
|
|
3968
|
+
url.searchParams.set("intent", intent.intent);
|
|
3969
|
+
return { intent, rpcUrl: url.toString() };
|
|
3970
|
+
}
|
|
3971
|
+
async function rpc(url, method, params, phase) {
|
|
3972
|
+
let response;
|
|
3973
|
+
try {
|
|
3974
|
+
response = await fetch(url, {
|
|
3975
|
+
method: "POST",
|
|
3976
|
+
headers: { "content-type": "application/json" },
|
|
3977
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
|
|
3978
|
+
});
|
|
3979
|
+
} catch {
|
|
3980
|
+
throw sponsorError("Withdrawal sponsorship request status is unknown.", phase === "prepare");
|
|
3981
|
+
}
|
|
3982
|
+
const payload = await response.json().catch(() => null);
|
|
3983
|
+
if (!response.ok || payload?.error || payload?.result === void 0) {
|
|
3984
|
+
const rejection = payload?.error;
|
|
3985
|
+
const definitiveRejection = phase === "send" && 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 ?? ""));
|
|
3986
|
+
throw sponsorError(
|
|
3987
|
+
phase !== "prepare" && !definitiveRejection ? "Sponsored withdrawal submission status is unknown." : "Withdrawal sponsorship was declined before submission.",
|
|
3988
|
+
phase === "prepare" || definitiveRejection,
|
|
3989
|
+
{ statusCode: response.status, rpcCode: payload?.error?.code, rpcMethod: method }
|
|
3990
|
+
);
|
|
3991
|
+
}
|
|
3992
|
+
return payload.result;
|
|
3993
|
+
}
|
|
3994
|
+
async function waitForTransaction(client, txHash) {
|
|
3995
|
+
const receipt = await client.waitForTransactionReceipt({ hash: txHash, confirmations: 1 });
|
|
3996
|
+
if (receipt.status !== "success")
|
|
3997
|
+
throw new OwneyError(
|
|
3998
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
3999
|
+
`Yieldseeker withdrawal reverted (${txHash}).`,
|
|
4000
|
+
{ transactionHash: txHash },
|
|
4001
|
+
"yieldseeker"
|
|
4002
|
+
);
|
|
4003
|
+
return receipt;
|
|
4004
|
+
}
|
|
4005
|
+
async function submitEoa(input) {
|
|
4006
|
+
let nonce;
|
|
4007
|
+
let fees;
|
|
4008
|
+
try {
|
|
4009
|
+
nonce = await input.client.readContract({
|
|
4010
|
+
address: ENTRY_POINT_V06,
|
|
4011
|
+
abi: ENTRY_POINT_NONCE_ABI,
|
|
4012
|
+
functionName: "getNonce",
|
|
4013
|
+
args: [input.agentWallet, 0n]
|
|
4014
|
+
});
|
|
4015
|
+
fees = await input.client.estimateFeesPerGas();
|
|
4016
|
+
} catch {
|
|
4017
|
+
throw sponsorError("Could not prepare the sponsored withdrawal.", true);
|
|
4018
|
+
}
|
|
4019
|
+
if (fees.maxFeePerGas === void 0 || fees.maxPriorityFeePerGas === void 0)
|
|
4020
|
+
throw sponsorError("Base did not return EIP-1559 gas fees.", true);
|
|
4021
|
+
let operation = {
|
|
4022
|
+
sender: input.agentWallet,
|
|
4023
|
+
nonce: toHex(nonce),
|
|
4024
|
+
initCode: "0x",
|
|
4025
|
+
callData: input.callData,
|
|
4026
|
+
callGasLimit: toHex(3e5),
|
|
4027
|
+
verificationGasLimit: toHex(3e5),
|
|
4028
|
+
preVerificationGas: toHex(6e4),
|
|
4029
|
+
maxFeePerGas: toHex(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
|
|
4030
|
+
maxPriorityFeePerGas: toHex(fees.maxPriorityFeePerGas),
|
|
4031
|
+
paymasterAndData: "0x",
|
|
4032
|
+
signature: "0x"
|
|
4033
|
+
};
|
|
4034
|
+
operation.signature = await SIMULATION_ACCOUNT.signMessage({ message: { raw: operationHash(operation) } });
|
|
4035
|
+
const stub = await rpc(
|
|
4036
|
+
input.rpcUrl,
|
|
4037
|
+
"pm_getPaymasterStubData",
|
|
4038
|
+
[operation, ENTRY_POINT_V06, toHex(8453), {}],
|
|
4039
|
+
"prepare"
|
|
4040
|
+
);
|
|
4041
|
+
if (!stub.paymasterAndData?.startsWith("0x"))
|
|
4042
|
+
throw sponsorError("CDP did not return v0.6 paymaster data.", true);
|
|
4043
|
+
operation = { ...operation, paymasterAndData: stub.paymasterAndData };
|
|
4044
|
+
operation.signature = await SIMULATION_ACCOUNT.signMessage({ message: { raw: operationHash(operation) } });
|
|
4045
|
+
const gas = await rpc(input.rpcUrl, "eth_estimateUserOperationGas", [operation, ENTRY_POINT_V06], "prepare");
|
|
4046
|
+
if (!gas.callGasLimit || !gas.verificationGasLimit || !gas.preVerificationGas)
|
|
4047
|
+
throw sponsorError("CDP did not return UserOperation gas estimates.", true);
|
|
4048
|
+
operation = { ...operation, ...gas };
|
|
4049
|
+
if (!stub.isFinal) {
|
|
4050
|
+
const final = await rpc(
|
|
4051
|
+
input.rpcUrl,
|
|
4052
|
+
"pm_getPaymasterData",
|
|
4053
|
+
[operation, ENTRY_POINT_V06, toHex(8453), {}],
|
|
4054
|
+
"prepare"
|
|
4055
|
+
);
|
|
4056
|
+
if (!final.paymasterAndData?.startsWith("0x"))
|
|
4057
|
+
throw sponsorError("CDP did not return final paymaster data.", true);
|
|
4058
|
+
operation = { ...operation, paymasterAndData: final.paymasterAndData };
|
|
4059
|
+
}
|
|
4060
|
+
const userOpHash = operationHash(operation);
|
|
4061
|
+
const wallet = createWalletClient2({
|
|
4062
|
+
account: input.owner,
|
|
4063
|
+
chain: base3,
|
|
4064
|
+
transport: custom2(input.state.provider)
|
|
4065
|
+
});
|
|
4066
|
+
const signature = await wallet.signMessage({ message: { raw: userOpHash } });
|
|
4067
|
+
operation = { ...operation, signature };
|
|
4068
|
+
input.onPending?.({ userOpHash });
|
|
4069
|
+
const submittedHash = await rpc(
|
|
4070
|
+
input.rpcUrl,
|
|
4071
|
+
"eth_sendUserOperation",
|
|
4072
|
+
[operation, ENTRY_POINT_V06],
|
|
4073
|
+
"send"
|
|
4074
|
+
);
|
|
4075
|
+
if (submittedHash.toLowerCase() !== userOpHash.toLowerCase())
|
|
4076
|
+
throw sponsorError(
|
|
4077
|
+
"Bundler returned a different UserOperation hash.",
|
|
4078
|
+
false,
|
|
4079
|
+
{ userOpHash, submittedHash }
|
|
4080
|
+
);
|
|
4081
|
+
for (let poll = 0; poll < 40; poll++) {
|
|
4082
|
+
const response = await rpc(input.rpcUrl, "eth_getUserOperationReceipt", [userOpHash], "observe");
|
|
4083
|
+
const txHash = response?.receipt?.transactionHash;
|
|
4084
|
+
if (txHash) {
|
|
4085
|
+
input.onPending?.({ userOpHash, txHash });
|
|
4086
|
+
if (response?.success === false)
|
|
4087
|
+
throw new OwneyError(
|
|
4088
|
+
"AGENT_TRANSACTION_REVERTED",
|
|
4089
|
+
"Sponsored withdrawal execution reverted.",
|
|
4090
|
+
{ userOpHash, transactionHash: txHash },
|
|
4091
|
+
"yieldseeker"
|
|
4092
|
+
);
|
|
4093
|
+
return { txHash, receipt: await waitForTransaction(input.client, txHash) };
|
|
4094
|
+
}
|
|
4095
|
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
4096
|
+
}
|
|
4097
|
+
throw sponsorError(
|
|
4098
|
+
"Sponsored withdrawal is still pending; check status before retrying.",
|
|
4099
|
+
false,
|
|
4100
|
+
{ userOpHash }
|
|
4101
|
+
);
|
|
4102
|
+
}
|
|
4103
|
+
async function submitSmart(input) {
|
|
4104
|
+
let capabilities;
|
|
4105
|
+
try {
|
|
4106
|
+
capabilities = await input.state.provider.request({
|
|
4107
|
+
method: "wallet_getCapabilities",
|
|
4108
|
+
params: [input.owner]
|
|
4109
|
+
});
|
|
4110
|
+
} catch {
|
|
4111
|
+
throw sponsorError("This account cannot check paymaster support on Base.", true);
|
|
4112
|
+
}
|
|
4113
|
+
if (!capabilities?.[toHex(8453)]?.paymasterService?.supported && !capabilities?.["8453"]?.paymasterService?.supported)
|
|
4114
|
+
throw sponsorError("This account cannot use a paymaster on Base.", true);
|
|
4115
|
+
let sent;
|
|
4116
|
+
try {
|
|
4117
|
+
input.onPending?.({});
|
|
4118
|
+
} catch {
|
|
4119
|
+
throw sponsorError("Upgrade or withdrawal status cannot be saved.", true);
|
|
4120
|
+
}
|
|
4121
|
+
try {
|
|
4122
|
+
sent = await input.state.provider.request({
|
|
4123
|
+
method: "wallet_sendCalls",
|
|
4124
|
+
params: [{
|
|
4125
|
+
version: "2.0.0",
|
|
4126
|
+
from: input.owner,
|
|
4127
|
+
chainId: toHex(8453),
|
|
4128
|
+
atomicRequired: false,
|
|
4129
|
+
calls: [{ to: input.agentWallet, value: "0x0", data: input.callData }],
|
|
4130
|
+
capabilities: { paymasterService: { url: input.rpcUrl } }
|
|
4131
|
+
}]
|
|
4132
|
+
});
|
|
4133
|
+
} catch (error) {
|
|
4134
|
+
if (error?.code === 4001) throw error;
|
|
4135
|
+
if ([4200, -32601].includes(Number(error?.code)))
|
|
4136
|
+
throw sponsorError("This account does not support sponsored calls.", true);
|
|
4137
|
+
throw sponsorError("Wallet submission status is unknown. Check activity before retrying.", false);
|
|
4138
|
+
}
|
|
4139
|
+
const callsId = typeof sent === "string" ? sent : sent?.id;
|
|
4140
|
+
if (!callsId) throw sponsorError("Wallet returned no calls ID; submission status is unknown.", false);
|
|
4141
|
+
try {
|
|
4142
|
+
input.onPending?.({ callsId });
|
|
4143
|
+
} catch {
|
|
4144
|
+
throw sponsorError("Submitted call status could not be saved.", false, { callsId });
|
|
4145
|
+
}
|
|
4146
|
+
for (let poll = 0; poll < 40; poll++) {
|
|
4147
|
+
let status;
|
|
4148
|
+
try {
|
|
4149
|
+
status = await input.state.provider.request({
|
|
4150
|
+
method: "wallet_getCallsStatus",
|
|
4151
|
+
params: [callsId]
|
|
4152
|
+
});
|
|
4153
|
+
} catch {
|
|
4154
|
+
throw sponsorError("Sponsored withdrawal status is unknown.", false, { callsId });
|
|
4155
|
+
}
|
|
4156
|
+
if (status?.status === 400 || status?.status === 500)
|
|
4157
|
+
throw sponsorError(
|
|
4158
|
+
"Sponsored withdrawal failed in the wallet.",
|
|
4159
|
+
false,
|
|
4160
|
+
{ callsId, executionFailed: true }
|
|
4161
|
+
);
|
|
4162
|
+
const txHash = status?.receipts?.[0]?.transactionHash;
|
|
4163
|
+
if (txHash) {
|
|
4164
|
+
try {
|
|
4165
|
+
input.onPending?.({ callsId, txHash });
|
|
4166
|
+
} catch {
|
|
4167
|
+
throw sponsorError(
|
|
4168
|
+
"Submitted call receipt could not be saved.",
|
|
4169
|
+
false,
|
|
4170
|
+
{ callsId, txHash }
|
|
4171
|
+
);
|
|
4172
|
+
}
|
|
4173
|
+
return { txHash, receipt: await waitForTransaction(input.client, txHash) };
|
|
4174
|
+
}
|
|
4175
|
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
4176
|
+
}
|
|
4177
|
+
throw sponsorError(
|
|
4178
|
+
"Sponsored withdrawal is still pending; check status before retrying.",
|
|
4179
|
+
false,
|
|
4180
|
+
{ callsId }
|
|
4181
|
+
);
|
|
4182
|
+
}
|
|
4183
|
+
async function submitYieldseekerSponsoredWithdrawal(input) {
|
|
4184
|
+
const owner = getAddress3(input.state.walletAddress);
|
|
4185
|
+
const { intent, rpcUrl } = await requestIntent({
|
|
4186
|
+
apiKey: input.apiKey,
|
|
4187
|
+
routingApiBaseUrl: input.routingApiBaseUrl,
|
|
4188
|
+
yieldseekerSignature: input.yieldseekerSignature,
|
|
4189
|
+
userId: input.userId,
|
|
4190
|
+
agentId: input.agentId,
|
|
4191
|
+
owner,
|
|
4192
|
+
agentWallet: input.agentWallet,
|
|
4193
|
+
asset: input.asset,
|
|
4194
|
+
callData: input.callData,
|
|
4195
|
+
action: "withdraw"
|
|
4196
|
+
});
|
|
4197
|
+
const args = { ...input, owner, rpcUrl };
|
|
4198
|
+
return intent.ownerPath === "eoa" ? submitEoa(args) : submitSmart(args);
|
|
4199
|
+
}
|
|
4200
|
+
async function submitYieldseekerSponsoredUpgrade(input) {
|
|
4201
|
+
const owner = getAddress3(input.state.walletAddress);
|
|
4202
|
+
const { intent, rpcUrl } = await requestIntent({
|
|
4203
|
+
apiKey: input.apiKey,
|
|
4204
|
+
routingApiBaseUrl: input.routingApiBaseUrl,
|
|
4205
|
+
yieldseekerSignature: input.yieldseekerSignature,
|
|
4206
|
+
userId: input.userId,
|
|
4207
|
+
agentId: input.agentId,
|
|
4208
|
+
owner,
|
|
4209
|
+
agentWallet: input.agentWallet,
|
|
4210
|
+
asset: input.asset,
|
|
4211
|
+
callData: input.callData,
|
|
4212
|
+
action: "upgrade"
|
|
4213
|
+
});
|
|
4214
|
+
if (intent.ownerPath !== "smart")
|
|
4215
|
+
throw sponsorError("V1 upgrade sponsorship requires a Base Account owner.", true);
|
|
4216
|
+
return submitSmart({ ...input, owner, rpcUrl });
|
|
4217
|
+
}
|
|
4218
|
+
|
|
3725
4219
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
3726
4220
|
var OWNEY_AGENT_NAME = "owney";
|
|
3727
4221
|
var OWNEY_AGENT_RULE_PRESET = "explorative";
|
|
@@ -3736,6 +4230,7 @@ var YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS = 15e3;
|
|
|
3736
4230
|
var YIELDSEEKER_READ_FAILURE_COOLDOWN_MS = 6e4;
|
|
3737
4231
|
var YIELDSEEKER_SETTLEMENT_WINDOW_MS = 5 * 6e4;
|
|
3738
4232
|
var YIELDSEEKER_ACTIVITY_CACHE_MS = 6e4;
|
|
4233
|
+
var YIELDSEEKER_MAX_WITHDRAW_SWEEPS = 3;
|
|
3739
4234
|
function generateYieldseekerUsername() {
|
|
3740
4235
|
const suffix = globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, YIELDSEEKER_USERNAME_RANDOM_LENGTH).toLowerCase();
|
|
3741
4236
|
return `${YIELDSEEKER_USERNAME_PREFIX}${suffix}`;
|
|
@@ -3794,7 +4289,11 @@ var YieldseekerAgent = class {
|
|
|
3794
4289
|
api;
|
|
3795
4290
|
auth;
|
|
3796
4291
|
rpcUrls;
|
|
4292
|
+
owneyApiKey;
|
|
4293
|
+
routingApiBaseUrl;
|
|
3797
4294
|
receiptClient;
|
|
4295
|
+
// A client preparation hint only; the routing API still checks live identity.
|
|
4296
|
+
withdrawalVersionCache = /* @__PURE__ */ new Map();
|
|
3798
4297
|
transactionExecutor;
|
|
3799
4298
|
unwindReceiptWaiter;
|
|
3800
4299
|
agentContexts = /* @__PURE__ */ new Map();
|
|
@@ -3816,6 +4315,8 @@ var YieldseekerAgent = class {
|
|
|
3816
4315
|
yieldOptions = /* @__PURE__ */ new Map();
|
|
3817
4316
|
pendingYieldOptions = /* @__PURE__ */ new Map();
|
|
3818
4317
|
constructor(owneyApiKey, options = {}) {
|
|
4318
|
+
this.owneyApiKey = owneyApiKey;
|
|
4319
|
+
this.routingApiBaseUrl = options.routingApiBaseUrl;
|
|
3819
4320
|
this.api = new YieldseekerApiClient(
|
|
3820
4321
|
owneyApiKey,
|
|
3821
4322
|
options.baseUrl ?? getYieldseekerProxyBaseUrl(),
|
|
@@ -3827,10 +4328,182 @@ var YieldseekerAgent = class {
|
|
|
3827
4328
|
this.unwindReceiptWaiter = options.unwindReceiptWaiter;
|
|
3828
4329
|
}
|
|
3829
4330
|
getReceiptClient() {
|
|
3830
|
-
this.receiptClient ??= createPaidRpcClient(
|
|
4331
|
+
this.receiptClient ??= createPaidRpcClient(base4, this.rpcUrls);
|
|
3831
4332
|
return this.receiptClient;
|
|
3832
4333
|
}
|
|
4334
|
+
/** Reads the actual proxy implementation; Yieldseeker's API walletType is not a version check. */
|
|
4335
|
+
async getWalletStatus(state, chainId, asset, useCachedV2 = false) {
|
|
4336
|
+
this.assertChain(chainId);
|
|
4337
|
+
this.assertAsset(asset);
|
|
4338
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
4339
|
+
if (!context) return null;
|
|
4340
|
+
const key2 = `${chainId}:${state.walletAddress.toLowerCase()}:${context.wallet.walletAddress.toLowerCase()}:${asset}`;
|
|
4341
|
+
const cached = this.withdrawalVersionCache.get(key2);
|
|
4342
|
+
if (useCachedV2 && cached && Date.now() - cached.at < 15 * 6e4) return { ...cached.status };
|
|
4343
|
+
const status = await readYieldseekerWalletStatus(
|
|
4344
|
+
this.getReceiptClient(),
|
|
4345
|
+
getAddress4(context.wallet.walletAddress),
|
|
4346
|
+
getAddress4(state.walletAddress),
|
|
4347
|
+
YIELDSEEKER_ASSET_METADATA[asset].address
|
|
4348
|
+
);
|
|
4349
|
+
if (status.verified && status.version === "v2") {
|
|
4350
|
+
this.withdrawalVersionCache.set(key2, { at: Date.now(), status: { ...status } });
|
|
4351
|
+
if (this.withdrawalVersionCache.size > 128)
|
|
4352
|
+
this.withdrawalVersionCache.delete(this.withdrawalVersionCache.keys().next().value);
|
|
4353
|
+
} else this.withdrawalVersionCache.delete(key2);
|
|
4354
|
+
return status;
|
|
4355
|
+
}
|
|
4356
|
+
/** Explicit owner action. This never runs as a side effect of withdrawal. */
|
|
4357
|
+
async upgradeWallet(state, chainId, asset, gasMode = "paid") {
|
|
4358
|
+
const before = await this.getWalletStatus(state, chainId, asset);
|
|
4359
|
+
if (!before?.verified || !before.upgradeAvailable) {
|
|
4360
|
+
throw new OwneyError(
|
|
4361
|
+
"WITHDRAW_FAILED",
|
|
4362
|
+
"This Yieldseeker wallet cannot be upgraded to the reviewed V2 implementation.",
|
|
4363
|
+
{ version: before?.version ?? "missing" },
|
|
4364
|
+
this.id
|
|
4365
|
+
);
|
|
4366
|
+
}
|
|
4367
|
+
const data = encodeFunctionData({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
|
|
4368
|
+
let txHash;
|
|
4369
|
+
if (gasMode === "sponsored") {
|
|
4370
|
+
if (before.ownerPath !== "smart")
|
|
4371
|
+
throw new OwneyError(
|
|
4372
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4373
|
+
"EOA owners pay Base gas for the V1 upgrade.",
|
|
4374
|
+
{ safeToFallback: true },
|
|
4375
|
+
this.id
|
|
4376
|
+
);
|
|
4377
|
+
let storage5;
|
|
4378
|
+
try {
|
|
4379
|
+
storage5 = typeof window === "undefined" ? void 0 : window.localStorage;
|
|
4380
|
+
} catch {
|
|
4381
|
+
storage5 = void 0;
|
|
4382
|
+
}
|
|
4383
|
+
if (!storage5)
|
|
4384
|
+
throw new OwneyError(
|
|
4385
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4386
|
+
"Upgrade status cannot be saved in this browser.",
|
|
4387
|
+
{ safeToFallback: true },
|
|
4388
|
+
this.id
|
|
4389
|
+
);
|
|
4390
|
+
const pendingKey = `owney.yieldseeker.upgrade:${state.walletAddress.toLowerCase()}:${before.agentWallet.toLowerCase()}`;
|
|
4391
|
+
let prior = null;
|
|
4392
|
+
let saved;
|
|
4393
|
+
try {
|
|
4394
|
+
saved = storage5.getItem(pendingKey);
|
|
4395
|
+
} catch {
|
|
4396
|
+
throw new OwneyError(
|
|
4397
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4398
|
+
"Upgrade status cannot be read in this browser.",
|
|
4399
|
+
{ safeToFallback: true },
|
|
4400
|
+
this.id
|
|
4401
|
+
);
|
|
4402
|
+
}
|
|
4403
|
+
try {
|
|
4404
|
+
prior = JSON.parse(saved ?? "null");
|
|
4405
|
+
} catch {
|
|
4406
|
+
prior = { callsId: void 0 };
|
|
4407
|
+
}
|
|
4408
|
+
if (prior) {
|
|
4409
|
+
if (!prior.callsId)
|
|
4410
|
+
throw new OwneyError(
|
|
4411
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4412
|
+
"An earlier upgrade submission is unresolved. Check wallet activity before retrying.",
|
|
4413
|
+
{ safeToFallback: false },
|
|
4414
|
+
this.id
|
|
4415
|
+
);
|
|
4416
|
+
let callsStatus;
|
|
4417
|
+
try {
|
|
4418
|
+
callsStatus = await state.provider.request({
|
|
4419
|
+
method: "wallet_getCallsStatus",
|
|
4420
|
+
params: [prior.callsId]
|
|
4421
|
+
});
|
|
4422
|
+
} catch {
|
|
4423
|
+
throw new OwneyError(
|
|
4424
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4425
|
+
"The earlier upgrade status could not be checked.",
|
|
4426
|
+
{ safeToFallback: false, callsId: prior.callsId },
|
|
4427
|
+
this.id
|
|
4428
|
+
);
|
|
4429
|
+
}
|
|
4430
|
+
if (callsStatus?.status === 400 || callsStatus?.status === 500) {
|
|
4431
|
+
storage5.removeItem(pendingKey);
|
|
4432
|
+
throw new OwneyError(
|
|
4433
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4434
|
+
"The earlier sponsored upgrade failed.",
|
|
4435
|
+
{ safeToFallback: true, callsId: prior.callsId },
|
|
4436
|
+
this.id
|
|
4437
|
+
);
|
|
4438
|
+
}
|
|
4439
|
+
const earlierHash = callsStatus?.receipts?.[0]?.transactionHash;
|
|
4440
|
+
if (earlierHash) {
|
|
4441
|
+
await this.waitForReceipt(state, chainId, earlierHash);
|
|
4442
|
+
const earlierStatus = await this.getWalletStatus(state, chainId, asset);
|
|
4443
|
+
if (earlierStatus?.version === "v2" && earlierStatus.verified) {
|
|
4444
|
+
storage5.removeItem(pendingKey);
|
|
4445
|
+
return { txHash: earlierHash, status: earlierStatus };
|
|
4446
|
+
}
|
|
4447
|
+
}
|
|
4448
|
+
throw new OwneyError(
|
|
4449
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4450
|
+
"An earlier upgrade is still pending.",
|
|
4451
|
+
{ safeToFallback: false, callsId: prior.callsId },
|
|
4452
|
+
this.id
|
|
4453
|
+
);
|
|
4454
|
+
}
|
|
4455
|
+
const context = await this.findAgent(state, chainId, asset);
|
|
4456
|
+
if (!context) throw new OwneyError("NOT_CONNECTED", "Yieldseeker agent is unavailable.");
|
|
4457
|
+
try {
|
|
4458
|
+
const upgraded = await submitYieldseekerSponsoredUpgrade({
|
|
4459
|
+
state,
|
|
4460
|
+
client: this.getReceiptClient(),
|
|
4461
|
+
apiKey: this.owneyApiKey,
|
|
4462
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
4463
|
+
yieldseekerSignature: await this.auth.getToken(state, chainId),
|
|
4464
|
+
userId: context.user.userId,
|
|
4465
|
+
agentId: context.agent.agentId,
|
|
4466
|
+
agentWallet: before.agentWallet,
|
|
4467
|
+
asset: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
4468
|
+
callData: data,
|
|
4469
|
+
onPending: (pending) => {
|
|
4470
|
+
const current = storage5.getItem(pendingKey);
|
|
4471
|
+
storage5.setItem(pendingKey, JSON.stringify({
|
|
4472
|
+
startedAt: Date.now(),
|
|
4473
|
+
...current ? JSON.parse(current) : {},
|
|
4474
|
+
...pending
|
|
4475
|
+
}));
|
|
4476
|
+
}
|
|
4477
|
+
});
|
|
4478
|
+
txHash = upgraded.txHash;
|
|
4479
|
+
} catch (error) {
|
|
4480
|
+
if (error?.code === 4001 || error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && (error.details?.safeToFallback === true || error.details?.executionFailed === true)) storage5.removeItem(pendingKey);
|
|
4481
|
+
throw error;
|
|
4482
|
+
}
|
|
4483
|
+
} else {
|
|
4484
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
4485
|
+
from: getAddress4(state.walletAddress),
|
|
4486
|
+
to: before.agentWallet,
|
|
4487
|
+
data,
|
|
4488
|
+
value: "0",
|
|
4489
|
+
chainId
|
|
4490
|
+
});
|
|
4491
|
+
}
|
|
4492
|
+
const status = await this.getWalletStatus(state, chainId, asset);
|
|
4493
|
+
if (!status?.verified || status.version !== "v2") {
|
|
4494
|
+
throw new OwneyError(
|
|
4495
|
+
"WITHDRAW_FAILED",
|
|
4496
|
+
"Yieldseeker wallet upgrade was not confirmed as V2.",
|
|
4497
|
+
{ txHash, version: status?.version ?? "missing" },
|
|
4498
|
+
this.id
|
|
4499
|
+
);
|
|
4500
|
+
}
|
|
4501
|
+
if (gasMode === "sponsored" && typeof window !== "undefined")
|
|
4502
|
+
window.localStorage.removeItem(`owney.yieldseeker.upgrade:${state.walletAddress.toLowerCase()}:${before.agentWallet.toLowerCase()}`);
|
|
4503
|
+
return { txHash, status };
|
|
4504
|
+
}
|
|
3833
4505
|
async disconnect() {
|
|
4506
|
+
this.withdrawalVersionCache.clear();
|
|
3834
4507
|
this.readGeneration += 1;
|
|
3835
4508
|
this.auth.clear();
|
|
3836
4509
|
for (const key2 of this.users.keys()) {
|
|
@@ -3901,12 +4574,12 @@ var YieldseekerAgent = class {
|
|
|
3901
4574
|
receipt = await this.waitForReceipt(state, chainId, txHash);
|
|
3902
4575
|
} else {
|
|
3903
4576
|
txHash = await this.submitTransaction(state, chainId, {
|
|
3904
|
-
from:
|
|
4577
|
+
from: getAddress4(state.walletAddress),
|
|
3905
4578
|
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
3906
4579
|
data: encodeFunctionData({
|
|
3907
4580
|
abi: erc20Abi2,
|
|
3908
4581
|
functionName: "transfer",
|
|
3909
|
-
args: [
|
|
4582
|
+
args: [getAddress4(context.wallet.walletAddress), BigInt(amount)]
|
|
3910
4583
|
}),
|
|
3911
4584
|
value: "0",
|
|
3912
4585
|
chainId
|
|
@@ -3917,12 +4590,12 @@ var YieldseekerAgent = class {
|
|
|
3917
4590
|
if (this.readGeneration === generation) {
|
|
3918
4591
|
void this.refreshSnapshotAfterMovement(state, chainId, context, "deposit", movement);
|
|
3919
4592
|
}
|
|
3920
|
-
const
|
|
3921
|
-
const evidence = receipt ? inspectFundingReceipt(receipt,
|
|
4593
|
+
const base6 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount, confirmedAmount: amount, txHash, transfer: "confirmed", registration: "not-applicable", delivery: "not-applicable" };
|
|
4594
|
+
const evidence = receipt ? inspectFundingReceipt(receipt, base6, YIELDSEEKER_ASSET_METADATA[asset].address, "deposit") : base6;
|
|
3922
4595
|
notifyFunding(onProgress, evidence);
|
|
3923
4596
|
return { txHash, smartWallet: context.wallet.walletAddress, amount, evidence };
|
|
3924
4597
|
}
|
|
3925
|
-
async withdraw(state, chainId, asset, amount, onProgress) {
|
|
4598
|
+
async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto") {
|
|
3926
4599
|
let ownerTransferAttempted = false;
|
|
3927
4600
|
let evidence = {
|
|
3928
4601
|
agentId: this.id,
|
|
@@ -3940,14 +4613,20 @@ var YieldseekerAgent = class {
|
|
|
3940
4613
|
notifyFunding(onProgress, evidence);
|
|
3941
4614
|
}, () => {
|
|
3942
4615
|
ownerTransferAttempted = true;
|
|
3943
|
-
});
|
|
4616
|
+
}, gasMode);
|
|
3944
4617
|
} catch (error) {
|
|
3945
|
-
if (
|
|
3946
|
-
|
|
4618
|
+
if (error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED") this.withdrawalVersionCache.clear();
|
|
4619
|
+
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)))
|
|
4620
|
+
notifyFunding(onProgress, {
|
|
4621
|
+
...evidence,
|
|
4622
|
+
...error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true ? { userOpHash: void 0, callsId: void 0 } : {},
|
|
4623
|
+
transfer: "failed",
|
|
4624
|
+
delivery: "failed"
|
|
4625
|
+
});
|
|
3947
4626
|
throw error;
|
|
3948
4627
|
}
|
|
3949
4628
|
}
|
|
3950
|
-
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted) {
|
|
4629
|
+
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted, gasMode = "auto") {
|
|
3951
4630
|
this.assertChain(chainId);
|
|
3952
4631
|
this.assertAsset(asset);
|
|
3953
4632
|
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
@@ -3967,10 +4646,32 @@ var YieldseekerAgent = class {
|
|
|
3967
4646
|
this.id
|
|
3968
4647
|
);
|
|
3969
4648
|
}
|
|
4649
|
+
let sponsor = false;
|
|
4650
|
+
if (gasMode !== "paid") {
|
|
4651
|
+
const status = await this.getWalletStatus(state, chainId, asset, true).catch(() => {
|
|
4652
|
+
if (gasMode === "sponsored")
|
|
4653
|
+
throw new OwneyError(
|
|
4654
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4655
|
+
"Could not verify the Yieldseeker wallet for sponsorship.",
|
|
4656
|
+
{ safeToFallback: true, version: "unknown" },
|
|
4657
|
+
this.id
|
|
4658
|
+
);
|
|
4659
|
+
return null;
|
|
4660
|
+
});
|
|
4661
|
+
sponsor = !!status?.verified && status.version === "v2";
|
|
4662
|
+
if (gasMode === "sponsored" && !sponsor)
|
|
4663
|
+
throw new OwneyError(
|
|
4664
|
+
"SPONSOR_REQUEST_FAILED",
|
|
4665
|
+
"Only a verified Yieldseeker V2 Agent Wallet can use sponsored withdrawals.",
|
|
4666
|
+
{ safeToFallback: true, version: status?.version ?? "unknown" },
|
|
4667
|
+
this.id
|
|
4668
|
+
);
|
|
4669
|
+
}
|
|
3970
4670
|
const generation = this.readGeneration;
|
|
3971
4671
|
let confirmedMovement = false;
|
|
3972
4672
|
let settlement = null;
|
|
3973
4673
|
try {
|
|
4674
|
+
if (amount === void 0) this.advancePortfolioVersion(this.contextKey(state, chainId, asset));
|
|
3974
4675
|
const portfolio = await this.loadPortfolioContext(
|
|
3975
4676
|
state,
|
|
3976
4677
|
chainId,
|
|
@@ -3982,13 +4683,41 @@ var YieldseekerAgent = class {
|
|
|
3982
4683
|
address: metadata.address,
|
|
3983
4684
|
abi: erc20Abi2,
|
|
3984
4685
|
functionName: "balanceOf",
|
|
3985
|
-
args: [
|
|
4686
|
+
args: [getAddress4(context.wallet.walletAddress)]
|
|
3986
4687
|
}), chainId, "eth_call", this.id);
|
|
3987
4688
|
this.assertSession(generation, chainId, asset);
|
|
3988
4689
|
return balance;
|
|
3989
4690
|
};
|
|
3990
4691
|
const idle = await readIdle();
|
|
3991
|
-
|
|
4692
|
+
let positions = portfolio.positions;
|
|
4693
|
+
let positionsStatus = "empty";
|
|
4694
|
+
const validatePositions = (values) => {
|
|
4695
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4696
|
+
for (const value of values) {
|
|
4697
|
+
const address = value?.yieldOption?.address;
|
|
4698
|
+
if (typeof address !== "string" || !isAddress2(address) || seen.has(address.toLowerCase()) || typeof value.assetsRaw !== "string" || !/^\d+$/.test(value.assetsRaw) || typeof value.withdrawableAssetsRaw !== "string" || !/^\d+$/.test(value.withdrawableAssetsRaw))
|
|
4699
|
+
throw this.invalidResponse("yield positions");
|
|
4700
|
+
seen.add(address.toLowerCase());
|
|
4701
|
+
}
|
|
4702
|
+
return values;
|
|
4703
|
+
};
|
|
4704
|
+
const hasPositions = (values) => values.some((position2) => BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n);
|
|
4705
|
+
const refreshPositions = async () => {
|
|
4706
|
+
this.assertSession(generation, chainId, asset);
|
|
4707
|
+
const response = await this.walletRequest(
|
|
4708
|
+
state,
|
|
4709
|
+
chainId,
|
|
4710
|
+
this.agentPath(context, "yield-positions")
|
|
4711
|
+
);
|
|
4712
|
+
this.assertSession(generation, chainId, asset);
|
|
4713
|
+
if (!Array.isArray(response?.yieldPositions)) throw this.invalidResponse("yield positions");
|
|
4714
|
+
return validatePositions(response.yieldPositions);
|
|
4715
|
+
};
|
|
4716
|
+
if (amount === void 0) {
|
|
4717
|
+
validatePositions(positions);
|
|
4718
|
+
positionsStatus = hasPositions(positions) ? "remaining" : "empty";
|
|
4719
|
+
}
|
|
4720
|
+
const deployed = positions.reduce(
|
|
3992
4721
|
(total, position2) => total + BigInt(position2.withdrawableAssetsRaw),
|
|
3993
4722
|
0n
|
|
3994
4723
|
);
|
|
@@ -3996,6 +4725,13 @@ var YieldseekerAgent = class {
|
|
|
3996
4725
|
const requested = amount === void 0 ? totalAvailable : BigInt(amount);
|
|
3997
4726
|
if (requested === 0n) {
|
|
3998
4727
|
this.invalidateFundingRead(state, chainId, asset);
|
|
4728
|
+
if (amount === void 0 && positionsStatus === "remaining")
|
|
4729
|
+
throw new OwneyError(
|
|
4730
|
+
"WITHDRAW_FAILED",
|
|
4731
|
+
"Yieldseeker vault funds are not currently withdrawable.",
|
|
4732
|
+
{ asset, remainingPositions: positions.map((position2) => position2.yieldOption.address) },
|
|
4733
|
+
this.id
|
|
4734
|
+
);
|
|
3999
4735
|
throw new OwneyError(
|
|
4000
4736
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4001
4737
|
"No Yieldseeker funds are available to withdraw.",
|
|
@@ -4016,40 +4752,74 @@ var YieldseekerAgent = class {
|
|
|
4016
4752
|
);
|
|
4017
4753
|
}
|
|
4018
4754
|
let remaining = requested > idle ? requested - idle : 0n;
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
if (available <= 0n) continue;
|
|
4023
|
-
const assetsRaw = available < remaining ? available : remaining;
|
|
4755
|
+
let grossUnwound = 0n;
|
|
4756
|
+
const unwind = async (position2, assetsRaw) => {
|
|
4757
|
+
this.assertSession(generation, chainId, asset);
|
|
4024
4758
|
const response = await this.walletRequest(
|
|
4025
4759
|
state,
|
|
4026
4760
|
chainId,
|
|
4027
4761
|
this.agentPath(context, "withdraw-from-position"),
|
|
4028
4762
|
{
|
|
4029
4763
|
method: "POST",
|
|
4030
|
-
body: {
|
|
4031
|
-
chainId,
|
|
4032
|
-
vaultAddress: position2.yieldOption.address,
|
|
4033
|
-
assetsRaw: assetsRaw.toString()
|
|
4034
|
-
}
|
|
4764
|
+
body: { chainId, vaultAddress: position2.yieldOption.address, assetsRaw: assetsRaw.toString() }
|
|
4035
4765
|
}
|
|
4036
4766
|
);
|
|
4037
|
-
if (!this.isTransactionHash(response?.transactionHash))
|
|
4038
|
-
throw this.invalidResponse("position withdrawal");
|
|
4039
|
-
}
|
|
4767
|
+
if (!this.isTransactionHash(response?.transactionHash)) throw this.invalidResponse("position withdrawal");
|
|
4040
4768
|
await this.waitForReceipt(state, chainId, response.transactionHash);
|
|
4041
4769
|
confirmedMovement = true;
|
|
4042
|
-
|
|
4770
|
+
this.assertSession(generation, chainId, asset);
|
|
4771
|
+
grossUnwound += assetsRaw;
|
|
4772
|
+
};
|
|
4773
|
+
if (amount === void 0) {
|
|
4774
|
+
for (let sweep = 0; sweep < YIELDSEEKER_MAX_WITHDRAW_SWEEPS && hasPositions(positions); sweep++) {
|
|
4775
|
+
const before = new Map(positions.map((position2) => [position2.yieldOption.address.toLowerCase(), BigInt(position2.assetsRaw)]));
|
|
4776
|
+
let unwound = false;
|
|
4777
|
+
for (const position2 of positions) {
|
|
4778
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
4779
|
+
if (available <= 0n) continue;
|
|
4780
|
+
await unwind(position2, available);
|
|
4781
|
+
unwound = true;
|
|
4782
|
+
}
|
|
4783
|
+
if (!unwound) break;
|
|
4784
|
+
try {
|
|
4785
|
+
positions = await refreshPositions();
|
|
4786
|
+
positionsStatus = hasPositions(positions) ? "remaining" : "empty";
|
|
4787
|
+
} catch {
|
|
4788
|
+
this.assertSession(generation, chainId, asset);
|
|
4789
|
+
positionsStatus = "unavailable";
|
|
4790
|
+
break;
|
|
4791
|
+
}
|
|
4792
|
+
if (positions.some((position2) => BigInt(position2.assetsRaw) > 0n && before.has(position2.yieldOption.address.toLowerCase()) && BigInt(position2.assetsRaw) >= before.get(position2.yieldOption.address.toLowerCase()))) break;
|
|
4793
|
+
}
|
|
4794
|
+
} else {
|
|
4795
|
+
for (const position2 of positions) {
|
|
4796
|
+
if (remaining === 0n) break;
|
|
4797
|
+
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
4798
|
+
if (available <= 0n) continue;
|
|
4799
|
+
const assetsRaw = available < remaining ? available : remaining;
|
|
4800
|
+
await unwind(position2, assetsRaw);
|
|
4801
|
+
remaining -= assetsRaw;
|
|
4802
|
+
}
|
|
4043
4803
|
}
|
|
4044
|
-
if (remaining > 0n) {
|
|
4804
|
+
if (amount !== void 0 && remaining > 0n) {
|
|
4045
4805
|
throw this.invalidResponse("yield positions", {
|
|
4046
4806
|
reason: "Withdrawable positions could not cover the request.",
|
|
4047
4807
|
remaining: remaining.toString()
|
|
4048
4808
|
});
|
|
4049
4809
|
}
|
|
4050
4810
|
const availableForOwner = confirmedMovement ? await readIdle() : idle;
|
|
4051
|
-
|
|
4811
|
+
const feeRate = portfolio.snapshot.feeRateBps;
|
|
4812
|
+
const maxFee = confirmedMovement && Number.isInteger(feeRate) && feeRate >= 0 && feeRate <= 1e3 ? (grossUnwound * BigInt(feeRate) + 9999n) / 10000n : 0n;
|
|
4813
|
+
const shortfall = availableForOwner < requested ? requested - availableForOwner : 0n;
|
|
4814
|
+
if (availableForOwner === 0n || amount !== void 0 && shortfall > maxFee) {
|
|
4052
4815
|
this.invalidateFundingRead(state, chainId, asset);
|
|
4816
|
+
if (amount === void 0 && positionsStatus !== "empty")
|
|
4817
|
+
throw new OwneyError(
|
|
4818
|
+
"WITHDRAW_FAILED",
|
|
4819
|
+
"Yieldseeker vault funds could not be cleared for the owner transfer.",
|
|
4820
|
+
{ asset, positionsStatus },
|
|
4821
|
+
this.id
|
|
4822
|
+
);
|
|
4053
4823
|
throw new OwneyError(
|
|
4054
4824
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
4055
4825
|
"Yieldseeker funds are not available for the owner transfer.",
|
|
@@ -4057,33 +4827,80 @@ var YieldseekerAgent = class {
|
|
|
4057
4827
|
this.id
|
|
4058
4828
|
);
|
|
4059
4829
|
}
|
|
4060
|
-
const payout = amount === void 0 ? availableForOwner : requested;
|
|
4830
|
+
const payout = amount === void 0 || availableForOwner < requested ? availableForOwner : requested;
|
|
4061
4831
|
const plannedMovement = this.snapshotMovement(state, chainId, context, -payout);
|
|
4062
|
-
const account =
|
|
4832
|
+
const account = getAddress4(state.walletAddress);
|
|
4063
4833
|
let receipt;
|
|
4064
|
-
const
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
}) : encodeFunctionData({
|
|
4074
|
-
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
4075
|
-
functionName: "withdrawAssetToUser",
|
|
4076
|
-
args: [account, metadata.address, requested]
|
|
4077
|
-
}),
|
|
4078
|
-
value: "0",
|
|
4079
|
-
chainId
|
|
4080
|
-
}, (hash) => notifyFunding(onProgress, { ...base5, txHash: hash }), (value) => {
|
|
4081
|
-
receipt = value;
|
|
4834
|
+
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" };
|
|
4835
|
+
const data = amount === void 0 ? encodeFunctionData({
|
|
4836
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
4837
|
+
functionName: "withdrawAllAssetToUser",
|
|
4838
|
+
args: [account, metadata.address]
|
|
4839
|
+
}) : encodeFunctionData({
|
|
4840
|
+
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
4841
|
+
functionName: "withdrawAssetToUser",
|
|
4842
|
+
args: [account, metadata.address, payout]
|
|
4082
4843
|
});
|
|
4844
|
+
onOwnerTransferAttempted?.();
|
|
4845
|
+
let txHash;
|
|
4846
|
+
if (sponsor) {
|
|
4847
|
+
const submitted = await submitYieldseekerSponsoredWithdrawal({
|
|
4848
|
+
state,
|
|
4849
|
+
client: this.getReceiptClient(),
|
|
4850
|
+
apiKey: this.owneyApiKey,
|
|
4851
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
4852
|
+
yieldseekerSignature: await this.auth.getToken(state, chainId),
|
|
4853
|
+
userId: context.user.userId,
|
|
4854
|
+
agentId: context.agent.agentId,
|
|
4855
|
+
agentWallet: getAddress4(context.wallet.walletAddress),
|
|
4856
|
+
asset: metadata.address,
|
|
4857
|
+
callData: data,
|
|
4858
|
+
onPending: (pending) => notifyFunding(onProgress, { ...base6, ...pending })
|
|
4859
|
+
});
|
|
4860
|
+
txHash = submitted.txHash;
|
|
4861
|
+
receipt = submitted.receipt;
|
|
4862
|
+
} else {
|
|
4863
|
+
txHash = await this.submitTransaction(state, chainId, {
|
|
4864
|
+
from: account,
|
|
4865
|
+
to: getAddress4(context.wallet.walletAddress),
|
|
4866
|
+
data,
|
|
4867
|
+
value: "0",
|
|
4868
|
+
chainId
|
|
4869
|
+
}, (hash) => notifyFunding(onProgress, { ...base6, txHash: hash }), (value) => {
|
|
4870
|
+
receipt = value;
|
|
4871
|
+
});
|
|
4872
|
+
}
|
|
4083
4873
|
confirmedMovement = true;
|
|
4084
4874
|
settlement = plannedMovement;
|
|
4085
|
-
const evidence = receipt ? inspectFundingReceipt(receipt, { ...
|
|
4875
|
+
const evidence = receipt ? inspectFundingReceipt(receipt, { ...base6, txHash }, metadata.address, "withdraw") : { ...base6, txHash, confirmedAmount: payout.toString(), transfer: "confirmed", delivery: "confirmed" };
|
|
4086
4876
|
notifyFunding(onProgress, evidence);
|
|
4877
|
+
if (amount === void 0) {
|
|
4878
|
+
try {
|
|
4879
|
+
positions = await refreshPositions();
|
|
4880
|
+
positionsStatus = hasPositions(positions) ? "remaining" : "empty";
|
|
4881
|
+
} catch {
|
|
4882
|
+
positionsStatus = "unavailable";
|
|
4883
|
+
}
|
|
4884
|
+
if (positionsStatus !== "empty") {
|
|
4885
|
+
const result = { txHash, type: "partial", amount: evidence.confirmedAmount ?? payout.toString(), evidence };
|
|
4886
|
+
throw new OwneyError(
|
|
4887
|
+
"WITHDRAW_PARTIAL_FAILURE",
|
|
4888
|
+
"Yieldseeker MAX could not confirm that all vault positions were cleared.",
|
|
4889
|
+
{
|
|
4890
|
+
reason: "YIELDSEEKER_MAX_INCOMPLETE",
|
|
4891
|
+
asset,
|
|
4892
|
+
positionsStatus,
|
|
4893
|
+
remainingPositions: positionsStatus === "remaining" ? positions.map((position2) => ({
|
|
4894
|
+
vaultAddress: position2.yieldOption.address,
|
|
4895
|
+
sharesRaw: position2.assetsRaw,
|
|
4896
|
+
withdrawableAmountRaw: position2.withdrawableAssetsRaw
|
|
4897
|
+
})) : void 0,
|
|
4898
|
+
partialResults: { [this.id]: result }
|
|
4899
|
+
},
|
|
4900
|
+
this.id
|
|
4901
|
+
);
|
|
4902
|
+
}
|
|
4903
|
+
}
|
|
4087
4904
|
if (evidence.transfer === "failed" && evidence.confirmedAmount === "0")
|
|
4088
4905
|
throw new OwneyError(
|
|
4089
4906
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
@@ -4325,7 +5142,7 @@ var YieldseekerAgent = class {
|
|
|
4325
5142
|
this.users.set(key2, persisted);
|
|
4326
5143
|
return persisted;
|
|
4327
5144
|
}
|
|
4328
|
-
const walletAddress =
|
|
5145
|
+
const walletAddress = getAddress4(state.walletAddress);
|
|
4329
5146
|
const generation = this.readGeneration;
|
|
4330
5147
|
let user = null;
|
|
4331
5148
|
try {
|
|
@@ -4816,15 +5633,15 @@ var YieldseekerAgent = class {
|
|
|
4816
5633
|
return hash2;
|
|
4817
5634
|
}
|
|
4818
5635
|
this.assertTransaction(transaction, state, chainId);
|
|
4819
|
-
const account =
|
|
4820
|
-
const walletClient =
|
|
5636
|
+
const account = getAddress4(state.walletAddress);
|
|
5637
|
+
const walletClient = createWalletClient3({
|
|
4821
5638
|
account,
|
|
4822
|
-
chain:
|
|
4823
|
-
transport:
|
|
5639
|
+
chain: base4,
|
|
5640
|
+
transport: custom3(state.provider)
|
|
4824
5641
|
});
|
|
4825
5642
|
const walletChainClient = createPublicClient3({
|
|
4826
|
-
chain:
|
|
4827
|
-
transport:
|
|
5643
|
+
chain: base4,
|
|
5644
|
+
transport: custom3(state.provider)
|
|
4828
5645
|
});
|
|
4829
5646
|
await ensureWalletOnChain(
|
|
4830
5647
|
walletChainClient,
|
|
@@ -4833,8 +5650,8 @@ var YieldseekerAgent = class {
|
|
|
4833
5650
|
);
|
|
4834
5651
|
const hash = await walletClient.sendTransaction({
|
|
4835
5652
|
account,
|
|
4836
|
-
chain:
|
|
4837
|
-
to:
|
|
5653
|
+
chain: base4,
|
|
5654
|
+
to: getAddress4(transaction.to),
|
|
4838
5655
|
data: transaction.data,
|
|
4839
5656
|
value: BigInt(transaction.value)
|
|
4840
5657
|
});
|
|
@@ -4884,7 +5701,7 @@ var YieldseekerAgent = class {
|
|
|
4884
5701
|
return receipt;
|
|
4885
5702
|
}
|
|
4886
5703
|
assertTransaction(transaction, state, chainId) {
|
|
4887
|
-
if (!transaction || typeof transaction.from !== "string" || !isAddress2(transaction.from) || typeof transaction.to !== "string" || !isAddress2(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId ||
|
|
5704
|
+
if (!transaction || typeof transaction.from !== "string" || !isAddress2(transaction.from) || typeof transaction.to !== "string" || !isAddress2(transaction.to) || typeof transaction.data !== "string" || !/^0x[a-fA-F0-9]*$/.test(transaction.data) || typeof transaction.value !== "string" || !/^[0-9]+$/.test(transaction.value) || transaction.chainId !== chainId || getAddress4(transaction.from) !== getAddress4(state.walletAddress)) {
|
|
4888
5705
|
throw this.invalidResponse("transaction");
|
|
4889
5706
|
}
|
|
4890
5707
|
}
|
|
@@ -4946,6 +5763,7 @@ var YieldseekerAgent = class {
|
|
|
4946
5763
|
|
|
4947
5764
|
// src/lib/routing-api.ts
|
|
4948
5765
|
var ROUTING_API_BASE_URL2 = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
5766
|
+
var getRoutingApiBaseUrl = (override) => override ?? ROUTING_API_BASE_URL2;
|
|
4949
5767
|
async function fetchOrgAgentConfig(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
4950
5768
|
const url = `${baseUrl}/api/v1/agent/org-config`;
|
|
4951
5769
|
try {
|
|
@@ -5230,20 +6048,20 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
|
|
|
5230
6048
|
// src/client.ts
|
|
5231
6049
|
import {
|
|
5232
6050
|
createPublicClient as createPublicClient4,
|
|
5233
|
-
createWalletClient as
|
|
5234
|
-
custom as
|
|
6051
|
+
createWalletClient as createWalletClient4,
|
|
6052
|
+
custom as custom4
|
|
5235
6053
|
} from "viem";
|
|
5236
|
-
import { base as
|
|
6054
|
+
import { base as base5, arbitrum as arbitrum2, mainnet as mainnet2 } from "viem/chains";
|
|
5237
6055
|
|
|
5238
6056
|
// src/lib/sponsored-token-batch.ts
|
|
5239
6057
|
import {
|
|
5240
|
-
isAddressEqual,
|
|
6058
|
+
isAddressEqual as isAddressEqual2,
|
|
5241
6059
|
keccak256,
|
|
5242
6060
|
toBytes
|
|
5243
6061
|
} from "viem";
|
|
5244
6062
|
|
|
5245
6063
|
// src/lib/permit2-batch.ts
|
|
5246
|
-
import { parseAbi as
|
|
6064
|
+
import { parseAbi as parseAbi4, hashStruct } from "viem";
|
|
5247
6065
|
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
5248
6066
|
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
5249
6067
|
var ERC2612_PERMIT_TYPES = {
|
|
@@ -5255,7 +6073,7 @@ var ERC2612_PERMIT_TYPES = {
|
|
|
5255
6073
|
{ name: "deadline", type: "uint256" }
|
|
5256
6074
|
]
|
|
5257
6075
|
};
|
|
5258
|
-
var ERC2612_READ_ABI =
|
|
6076
|
+
var ERC2612_READ_ABI = parseAbi4([
|
|
5259
6077
|
"function name() view returns (string)",
|
|
5260
6078
|
"function version() view returns (string)",
|
|
5261
6079
|
"function nonces(address owner) view returns (uint256)"
|
|
@@ -5293,7 +6111,7 @@ var PERMIT_BATCH_TYPES = {
|
|
|
5293
6111
|
{ name: "amount", type: "uint256" }
|
|
5294
6112
|
]
|
|
5295
6113
|
};
|
|
5296
|
-
var PERMIT2_BATCH_ABI =
|
|
6114
|
+
var PERMIT2_BATCH_ABI = parseAbi4([
|
|
5297
6115
|
"struct TokenPermissions { address token; uint256 amount; }",
|
|
5298
6116
|
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
5299
6117
|
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
@@ -5433,7 +6251,7 @@ async function execute(i, key2, plan) {
|
|
|
5433
6251
|
const saved = read(key2);
|
|
5434
6252
|
if (saved) {
|
|
5435
6253
|
const previous = JSON.parse(saved);
|
|
5436
|
-
if (previous.chainId !== i.chainId || !
|
|
6254
|
+
if (previous.chainId !== i.chainId || !isAddressEqual2(previous.from, i.owner) || !isAddressEqual2(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
5437
6255
|
throw new Error(
|
|
5438
6256
|
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
5439
6257
|
);
|
|
@@ -5450,7 +6268,7 @@ async function execute(i, key2, plan) {
|
|
|
5450
6268
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
5451
6269
|
"Insufficient token balance for this deposit."
|
|
5452
6270
|
);
|
|
5453
|
-
if (allowance < total && (i.chainId !== 8453 || !
|
|
6271
|
+
if (allowance < total && (i.chainId !== 8453 || !isAddressEqual2(i.token, BASE_USDC_ADDRESS)))
|
|
5454
6272
|
throw new OwneyError(
|
|
5455
6273
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
5456
6274
|
"token deposits need a one-time Permit2 approval."
|
|
@@ -5628,7 +6446,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
|
5628
6446
|
}
|
|
5629
6447
|
|
|
5630
6448
|
// src/lib/sponsored-calls-deposit.ts
|
|
5631
|
-
import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi3, toHex } from "viem";
|
|
6449
|
+
import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi3, toHex as toHex2 } from "viem";
|
|
5632
6450
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
5633
6451
|
var DEFAULT_MAX_POLLS = 30;
|
|
5634
6452
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -5636,7 +6454,7 @@ async function paymasterSupported(provider, owner, chainId) {
|
|
|
5636
6454
|
method: "wallet_getCapabilities",
|
|
5637
6455
|
params: [owner]
|
|
5638
6456
|
});
|
|
5639
|
-
const forChain = caps?.[
|
|
6457
|
+
const forChain = caps?.[toHex2(chainId)] ?? caps?.[String(chainId)];
|
|
5640
6458
|
return Boolean(forChain?.paymasterService?.supported);
|
|
5641
6459
|
}
|
|
5642
6460
|
function makeSponsoredCallsCallback(deps) {
|
|
@@ -5712,7 +6530,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
5712
6530
|
{
|
|
5713
6531
|
version: "2.0.0",
|
|
5714
6532
|
from: deps.ownerAddress,
|
|
5715
|
-
chainId:
|
|
6533
|
+
chainId: toHex2(chainId),
|
|
5716
6534
|
atomicRequired: transfers.length > 1,
|
|
5717
6535
|
calls,
|
|
5718
6536
|
capabilities: {
|
|
@@ -5806,7 +6624,7 @@ var SPONSORED_USDC_BY_CHAIN = {
|
|
|
5806
6624
|
1: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
|
5807
6625
|
};
|
|
5808
6626
|
var VIEM_CHAIN2 = {
|
|
5809
|
-
8453:
|
|
6627
|
+
8453: base5,
|
|
5810
6628
|
42161: arbitrum2,
|
|
5811
6629
|
1: mainnet2
|
|
5812
6630
|
};
|
|
@@ -5983,12 +6801,12 @@ var OwneySDK = class {
|
|
|
5983
6801
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
5984
6802
|
getWalletChainClient: (cid) => createPublicClient4({
|
|
5985
6803
|
chain: VIEM_CHAIN2[cid],
|
|
5986
|
-
transport:
|
|
6804
|
+
transport: custom4(provider)
|
|
5987
6805
|
}),
|
|
5988
|
-
getWalletClient: (cid) =>
|
|
6806
|
+
getWalletClient: (cid) => createWalletClient4({
|
|
5989
6807
|
account: owner,
|
|
5990
6808
|
chain: VIEM_CHAIN2[cid],
|
|
5991
|
-
transport:
|
|
6809
|
+
transport: custom4(provider)
|
|
5992
6810
|
})
|
|
5993
6811
|
});
|
|
5994
6812
|
if (!onApproved && !onSubmitted) this.cachedSponsoredCallback = callback;
|
|
@@ -6036,12 +6854,12 @@ var OwneySDK = class {
|
|
|
6036
6854
|
getPublicClient: (cid) => this.getPaidRpcClient(cid),
|
|
6037
6855
|
getWalletChainClient: (cid) => createPublicClient4({
|
|
6038
6856
|
chain: VIEM_CHAIN2[cid],
|
|
6039
|
-
transport:
|
|
6857
|
+
transport: custom4(provider)
|
|
6040
6858
|
}),
|
|
6041
|
-
getWalletClient: (cid) =>
|
|
6859
|
+
getWalletClient: (cid) => createWalletClient4({
|
|
6042
6860
|
account: owner,
|
|
6043
6861
|
chain: VIEM_CHAIN2[cid],
|
|
6044
|
-
transport:
|
|
6862
|
+
transport: custom4(provider)
|
|
6045
6863
|
})
|
|
6046
6864
|
});
|
|
6047
6865
|
if (!onApproved && !onSubmitted) this.cachedWethSponsoredCallback = callback;
|
|
@@ -6162,6 +6980,7 @@ var OwneySDK = class {
|
|
|
6162
6980
|
}
|
|
6163
6981
|
if (agentId === "yieldseeker") {
|
|
6164
6982
|
return new YieldseekerAgent(this.apiKey, {
|
|
6983
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
6165
6984
|
auth: { origin: this.yieldseekerSiweOrigin },
|
|
6166
6985
|
baseUrl: this.yieldseekerApiBaseUrl ?? getYieldseekerProxyBaseUrl(this.routingApiBaseUrl),
|
|
6167
6986
|
rpcUrls: this.rpcUrls
|
|
@@ -6753,14 +7572,14 @@ var OwneySDK = class {
|
|
|
6753
7572
|
throw new OwneyError("AGENT_API_ERROR", "Funding wallet does not match this account.");
|
|
6754
7573
|
const token = SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token);
|
|
6755
7574
|
if (!token) throw new OwneyError("CHAIN_UNSUPPORTED", "Unsupported funding asset.");
|
|
6756
|
-
const
|
|
6757
|
-
const receipt = await
|
|
7575
|
+
const rpc2 = this.getPaidRpcClient(chainId);
|
|
7576
|
+
const receipt = await rpc2.getTransactionReceipt({ hash: evidence.txHash });
|
|
6758
7577
|
assertCurrent();
|
|
6759
7578
|
let verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet }, token.address, direction);
|
|
6760
7579
|
if (direction === "withdraw" && evidence.agentId === "yieldseeker" && verified.transfer === "unknown" && verified.confirmedAmount && BigInt(verified.confirmedAmount) > 0n) {
|
|
6761
|
-
const transaction = await
|
|
7580
|
+
const transaction = await rpc2.getTransaction({ hash: evidence.txHash });
|
|
6762
7581
|
assertCurrent();
|
|
6763
|
-
if (isFullYieldseekerWithdrawal(transaction, { ...evidence, agentWallet }, token.address))
|
|
7582
|
+
if (isFullYieldseekerWithdrawal(transaction, { ...evidence, agentWallet }, token.address, receipt))
|
|
6764
7583
|
verified = inspectFundingReceipt(receipt, { ...evidence, agentWallet, intendedAmount: "MAX" }, token.address, direction);
|
|
6765
7584
|
}
|
|
6766
7585
|
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${evidence.agentId}:${evidence.token}:${evidence.txHash}`;
|
|
@@ -6772,6 +7591,45 @@ var OwneySDK = class {
|
|
|
6772
7591
|
}
|
|
6773
7592
|
return verified;
|
|
6774
7593
|
}
|
|
7594
|
+
/** Resolve a saved sponsored-withdrawal ID without opening a wallet prompt. */
|
|
7595
|
+
async resolveSponsoredWithdrawalEvidence(evidence) {
|
|
7596
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(evidence.chainId);
|
|
7597
|
+
if (evidence.agentId !== "yieldseeker" || chainId !== 8453 || evidence.txHash || state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase()) return evidence;
|
|
7598
|
+
const agent = this.getAgent("yieldseeker");
|
|
7599
|
+
const wallet = await agent.getFundingWallet?.(state, chainId, evidence.token);
|
|
7600
|
+
if (!wallet || wallet.toLowerCase() !== evidence.agentWallet?.toLowerCase()) return evidence;
|
|
7601
|
+
let txHash;
|
|
7602
|
+
let executionFailed = false;
|
|
7603
|
+
if (evidence.userOpHash && /^0x[0-9a-fA-F]{64}$/.test(evidence.userOpHash)) {
|
|
7604
|
+
const response = await fetch(`${getRoutingApiBaseUrl(this.routingApiBaseUrl).replace(/\/$/, "")}/api/v1/sponsor/yieldseeker-withdraw/resolve`, {
|
|
7605
|
+
method: "POST",
|
|
7606
|
+
headers: { "content-type": "application/json", "x-owney-api-key": this.apiKey },
|
|
7607
|
+
body: JSON.stringify({
|
|
7608
|
+
owner: state.walletAddress,
|
|
7609
|
+
agentWallet: wallet,
|
|
7610
|
+
asset: SupportedAssets.find((t) => t.chainId === chainId && t.symbol === evidence.token)?.address,
|
|
7611
|
+
userOpHash: evidence.userOpHash
|
|
7612
|
+
})
|
|
7613
|
+
});
|
|
7614
|
+
if (!response.ok) throw new OwneyError("AGENT_API_ERROR", "Could not resolve sponsored withdrawal status.");
|
|
7615
|
+
const payload = await response.json();
|
|
7616
|
+
txHash = payload.data?.txHash ?? void 0;
|
|
7617
|
+
executionFailed = payload.data?.executionFailed === true;
|
|
7618
|
+
} else if (evidence.callsId) {
|
|
7619
|
+
const status = await state.provider.request({ method: "wallet_getCallsStatus", params: [evidence.callsId] });
|
|
7620
|
+
if (status?.id && status.id !== evidence.callsId || status?.chainId && Number(status.chainId) !== chainId) return evidence;
|
|
7621
|
+
executionFailed = status?.status === 400 || status?.status === 500;
|
|
7622
|
+
txHash = status?.receipts?.[0]?.transactionHash;
|
|
7623
|
+
}
|
|
7624
|
+
assertCurrent();
|
|
7625
|
+
if (executionFailed) return {
|
|
7626
|
+
...evidence,
|
|
7627
|
+
...txHash && /^0x[0-9a-fA-F]{64}$/.test(txHash) ? { txHash } : {},
|
|
7628
|
+
transfer: "failed",
|
|
7629
|
+
delivery: "failed"
|
|
7630
|
+
};
|
|
7631
|
+
return txHash && /^0x[0-9a-fA-F]{64}$/.test(txHash) ? { ...evidence, txHash } : evidence;
|
|
7632
|
+
}
|
|
6775
7633
|
/** A display acknowledgement controls polling only; it never confirms a financial outcome. */
|
|
6776
7634
|
endFundingBalanceRefresh(evidence, read2) {
|
|
6777
7635
|
const { state, chainId } = this.captureReadScope(evidence.chainId);
|
|
@@ -6796,6 +7654,39 @@ var OwneySDK = class {
|
|
|
6796
7654
|
return this.getAgent("zyfai").retryDepositRegistration(this.requireState(), verified);
|
|
6797
7655
|
}
|
|
6798
7656
|
// --- Fund operations ---
|
|
7657
|
+
/** On-chain version and ownership status for this owner's Yieldseeker agent. */
|
|
7658
|
+
async getYieldseekerWalletStatus(asset) {
|
|
7659
|
+
const state = this.requireState();
|
|
7660
|
+
const chainId = this.requireChainId();
|
|
7661
|
+
const agent = this.getAgent("yieldseeker");
|
|
7662
|
+
return agent.getWalletStatus(state, chainId, asset);
|
|
7663
|
+
}
|
|
7664
|
+
/** Deployment gate; an unavailable response keeps the current paid path. */
|
|
7665
|
+
async isYieldseekerWithdrawalSponsorshipAvailable() {
|
|
7666
|
+
return this.isYieldseekerSponsorshipAvailable("withdraw");
|
|
7667
|
+
}
|
|
7668
|
+
async isYieldseekerUpgradeSponsorshipAvailable() {
|
|
7669
|
+
return this.isYieldseekerSponsorshipAvailable("upgrade");
|
|
7670
|
+
}
|
|
7671
|
+
async isYieldseekerSponsorshipAvailable(action) {
|
|
7672
|
+
const baseUrl = getRoutingApiBaseUrl(this.routingApiBaseUrl).replace(/\/$/, "");
|
|
7673
|
+
try {
|
|
7674
|
+
const owner = this.requireState().walletAddress;
|
|
7675
|
+
const response = await fetch(`${baseUrl}/api/v1/sponsor/yieldseeker-withdraw/availability?owner=${encodeURIComponent(owner)}&action=${action}`);
|
|
7676
|
+
if (!response.ok) return false;
|
|
7677
|
+
const payload = await response.json();
|
|
7678
|
+
return payload.data?.enabled === true;
|
|
7679
|
+
} catch {
|
|
7680
|
+
return false;
|
|
7681
|
+
}
|
|
7682
|
+
}
|
|
7683
|
+
/** Explicit V1-to-V2 upgrade, confirmed on chain before returning. */
|
|
7684
|
+
async upgradeYieldseekerWallet(asset, gasMode = "paid") {
|
|
7685
|
+
const state = this.requireState();
|
|
7686
|
+
const chainId = this.requireChainId();
|
|
7687
|
+
const agent = this.getAgent("yieldseeker");
|
|
7688
|
+
return agent.upgradeWallet(state, chainId, asset, gasMode);
|
|
7689
|
+
}
|
|
6799
7690
|
/**
|
|
6800
7691
|
* Withdraw funds from a specific agent, or all agents that support the active chain+asset if agentId is omitted.
|
|
6801
7692
|
* Validates that the asset is supported by the target agent(s) on the active chain.
|
|
@@ -6859,7 +7750,7 @@ var OwneySDK = class {
|
|
|
6859
7750
|
};
|
|
6860
7751
|
progress(evidence);
|
|
6861
7752
|
try {
|
|
6862
|
-
return await agent.withdraw(state, chainId, token, requested2, progress);
|
|
7753
|
+
return await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
|
|
6863
7754
|
} catch (error) {
|
|
6864
7755
|
if (!evidence.txHash && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
6865
7756
|
progress({ ...evidence, transfer: "failed", delivery: "failed" });
|
|
@@ -6892,13 +7783,23 @@ var OwneySDK = class {
|
|
|
6892
7783
|
if (!amount) {
|
|
6893
7784
|
const results2 = {};
|
|
6894
7785
|
const agentErrors2 = {};
|
|
7786
|
+
const incompleteWithdrawals = {};
|
|
6895
7787
|
for (const agent of withdrawalAgents) {
|
|
6896
7788
|
try {
|
|
6897
7789
|
results2[agent.id] = await submit(agent);
|
|
6898
7790
|
notifyAgentResult(agent.id, results2[agent.id]);
|
|
6899
7791
|
} catch (err) {
|
|
7792
|
+
if (err instanceof OwneyError && err.code === "SPONSOR_REQUEST_FAILED" && err.details?.safeToFallback === true) throw err;
|
|
6900
7793
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
6901
7794
|
if (err instanceof OwneyError && err.code === "WITHDRAW_INSUFFICIENT_BALANCE" && err.details?.available === "0" && submissionEvidence.get(agent.id)?.transfer === "failed") continue;
|
|
7795
|
+
if (agent.id === "yieldseeker" && err instanceof OwneyError && err.code === "WITHDRAW_PARTIAL_FAILURE" && err.details?.reason === "YIELDSEEKER_MAX_INCOMPLETE") {
|
|
7796
|
+
incompleteWithdrawals[agent.id] = { positionsStatus: err.details.positionsStatus };
|
|
7797
|
+
const evidence = submissionEvidence.get(agent.id);
|
|
7798
|
+
if (evidence?.agentId === agent.id && evidence.chainId === chainId && evidence.token === token && evidence.walletAddress.toLowerCase() === state.walletAddress.toLowerCase() && evidence.intendedAmount === "MAX" && evidence.transfer === "confirmed" && evidence.delivery === "confirmed" && evidence.txHash && /^0x[\da-f]{64}$/i.test(evidence.txHash) && evidence.confirmedAmount && /^\d+$/.test(evidence.confirmedAmount) && BigInt(evidence.confirmedAmount) > 0n) {
|
|
7799
|
+
results2[agent.id] = { txHash: evidence.txHash, type: "partial", amount: evidence.confirmedAmount, evidence };
|
|
7800
|
+
notifyAgentResult(agent.id, results2[agent.id]);
|
|
7801
|
+
}
|
|
7802
|
+
}
|
|
6902
7803
|
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
6903
7804
|
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
6904
7805
|
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
@@ -6921,7 +7822,7 @@ var OwneySDK = class {
|
|
|
6921
7822
|
throw new OwneyError(
|
|
6922
7823
|
"WITHDRAW_PARTIAL_FAILURE",
|
|
6923
7824
|
"Some agents could not complete the withdrawal.",
|
|
6924
|
-
{ asset, partialResults: results2, agentErrors: agentErrors2 }
|
|
7825
|
+
{ asset, partialResults: results2, agentErrors: agentErrors2, ...Object.keys(incompleteWithdrawals).length ? { incompleteWithdrawals } : {} }
|
|
6925
7826
|
);
|
|
6926
7827
|
}
|
|
6927
7828
|
const amountsKnown = Object.values(results2).every(
|
|
@@ -6998,6 +7899,7 @@ var OwneySDK = class {
|
|
|
6998
7899
|
results[p.agent.id] = await submit(p.agent, p.planned.toString());
|
|
6999
7900
|
notifyAgentResult(p.agent.id, results[p.agent.id]);
|
|
7000
7901
|
} catch (err) {
|
|
7902
|
+
if (err instanceof OwneyError && err.code === "SPONSOR_REQUEST_FAILED" && err.details?.safeToFallback === true) throw err;
|
|
7001
7903
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
7002
7904
|
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
7003
7905
|
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
@@ -7497,14 +8399,14 @@ var OwneySDK = class {
|
|
|
7497
8399
|
const provider = this.requireConnectedProvider();
|
|
7498
8400
|
const walletChainClient = createPublicClient4({
|
|
7499
8401
|
chain: VIEM_CHAIN2[chainId],
|
|
7500
|
-
transport:
|
|
8402
|
+
transport: custom4(provider)
|
|
7501
8403
|
});
|
|
7502
8404
|
const publicClient = this.getPaidRpcClient(chainId);
|
|
7503
8405
|
const approvalAmount = permit2ApprovalAmount(requiredAmount);
|
|
7504
|
-
const wallet =
|
|
8406
|
+
const wallet = createWalletClient4({
|
|
7505
8407
|
account: state.walletAddress,
|
|
7506
8408
|
chain: VIEM_CHAIN2[chainId],
|
|
7507
|
-
transport:
|
|
8409
|
+
transport: custom4(provider)
|
|
7508
8410
|
});
|
|
7509
8411
|
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
7510
8412
|
const hash = await wallet.writeContract({
|
|
@@ -7666,7 +8568,7 @@ var OwneySDK = class {
|
|
|
7666
8568
|
};
|
|
7667
8569
|
|
|
7668
8570
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
7669
|
-
import { getAddress as
|
|
8571
|
+
import { getAddress as getAddress5 } from "viem";
|
|
7670
8572
|
import { SiweMessage as SiweMessage2 } from "siwe";
|
|
7671
8573
|
import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
|
|
7672
8574
|
|
|
@@ -7793,7 +8695,7 @@ function buildSIWXConfig(deps) {
|
|
|
7793
8695
|
issuedAt,
|
|
7794
8696
|
toString() {
|
|
7795
8697
|
return new SiweMessage2({
|
|
7796
|
-
address:
|
|
8698
|
+
address: getAddress5(accountAddress),
|
|
7797
8699
|
chainId: numericChainId(chainId),
|
|
7798
8700
|
domain,
|
|
7799
8701
|
uri,
|