@owney/sdk 0.7.27-beta.1 → 0.7.27-beta.2
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 +1516 -837
- package/dist/index.d.cts +88 -71
- package/dist/index.d.ts +88 -71
- package/dist/index.js +1386 -707
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -39,8 +39,216 @@ __export(index_exports, {
|
|
|
39
39
|
});
|
|
40
40
|
module.exports = __toCommonJS(index_exports);
|
|
41
41
|
|
|
42
|
-
// src/
|
|
42
|
+
// src/errors.ts
|
|
43
|
+
var OwneyError = class extends Error {
|
|
44
|
+
code;
|
|
45
|
+
details;
|
|
46
|
+
agentId;
|
|
47
|
+
constructor(code, message, details, agentId) {
|
|
48
|
+
const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
|
|
49
|
+
super(`${prefix} ${message}`);
|
|
50
|
+
this.name = "OwneyError";
|
|
51
|
+
this.code = code;
|
|
52
|
+
this.details = details;
|
|
53
|
+
this.agentId = agentId;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var AgentNotFoundError = class extends OwneyError {
|
|
57
|
+
constructor(agentId, available) {
|
|
58
|
+
super(
|
|
59
|
+
"AGENT_NOT_FOUND",
|
|
60
|
+
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
61
|
+
{ agentId, available },
|
|
62
|
+
agentId
|
|
63
|
+
);
|
|
64
|
+
this.name = "AgentNotFoundError";
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var NotConnectedError = class extends OwneyError {
|
|
68
|
+
constructor() {
|
|
69
|
+
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
70
|
+
this.name = "NotConnectedError";
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var AgentChainIncompatibleError = class extends OwneyError {
|
|
74
|
+
incompatibleAgents;
|
|
75
|
+
connectedChainId;
|
|
76
|
+
constructor(incompatibleAgents, connectedChainId) {
|
|
77
|
+
const details = incompatibleAgents.map(
|
|
78
|
+
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
79
|
+
).join("; ");
|
|
80
|
+
super(
|
|
81
|
+
"AGENT_CHAIN_INCOMPATIBLE",
|
|
82
|
+
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
83
|
+
{ incompatibleAgents, connectedChainId }
|
|
84
|
+
);
|
|
85
|
+
this.name = "AgentChainIncompatibleError";
|
|
86
|
+
this.incompatibleAgents = incompatibleAgents;
|
|
87
|
+
this.connectedChainId = connectedChainId;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// src/lib/swap/swap.observation.ts
|
|
92
|
+
var active = /* @__PURE__ */ new Set();
|
|
93
|
+
var isOrderObserved = (key2) => active.has(key2);
|
|
94
|
+
async function observeOrder(key2, run) {
|
|
95
|
+
const busy = () => new OwneyError(
|
|
96
|
+
"SWAP_REQUEST_FAILED",
|
|
97
|
+
"This order is already being observed.",
|
|
98
|
+
{ orderHash: key2, watchActive: true, settlementUnconfirmed: true }
|
|
99
|
+
);
|
|
100
|
+
if (active.has(key2)) throw busy();
|
|
101
|
+
active.add(key2);
|
|
102
|
+
try {
|
|
103
|
+
if (typeof navigator !== "undefined" && navigator.locks)
|
|
104
|
+
return await navigator.locks.request(`owney.swap.observe:${key2}`, { ifAvailable: true }, (lock) => {
|
|
105
|
+
if (!lock) throw busy();
|
|
106
|
+
return run();
|
|
107
|
+
});
|
|
108
|
+
return await run();
|
|
109
|
+
} finally {
|
|
110
|
+
active.delete(key2);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/lib/swap/swap.deadline.ts
|
|
115
|
+
async function bounded(work, timeoutMs, error) {
|
|
116
|
+
if (timeoutMs <= 0) throw error();
|
|
117
|
+
let timer;
|
|
118
|
+
try {
|
|
119
|
+
return await Promise.race([
|
|
120
|
+
Promise.resolve().then(work),
|
|
121
|
+
new Promise((_, reject) => {
|
|
122
|
+
timer = setTimeout(() => reject(error()), timeoutMs);
|
|
123
|
+
})
|
|
124
|
+
]);
|
|
125
|
+
} finally {
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function mapConcurrent(items, run, concurrency = 3) {
|
|
130
|
+
const results = new Array(items.length);
|
|
131
|
+
let next = 0;
|
|
132
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
133
|
+
for (; ; ) {
|
|
134
|
+
const index = next++;
|
|
135
|
+
if (index >= items.length) return;
|
|
136
|
+
results[index] = await run(items[index]);
|
|
137
|
+
}
|
|
138
|
+
}));
|
|
139
|
+
return results;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/lib/permit2-batch.ts
|
|
43
143
|
var import_viem = require("viem");
|
|
144
|
+
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
145
|
+
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
146
|
+
var ARBITRUM_USDC_ADDRESS = "0xaf88d065e77c8cc2239327c5edb3a432268e5831";
|
|
147
|
+
function supportsSponsoredErc2612(chainId, token) {
|
|
148
|
+
const expected = chainId === 8453 ? BASE_USDC_ADDRESS : chainId === 42161 ? ARBITRUM_USDC_ADDRESS : void 0;
|
|
149
|
+
return expected !== void 0 && token.toLowerCase() === expected.toLowerCase();
|
|
150
|
+
}
|
|
151
|
+
var ERC2612_PERMIT_TYPES = {
|
|
152
|
+
Permit: [
|
|
153
|
+
{ name: "owner", type: "address" },
|
|
154
|
+
{ name: "spender", type: "address" },
|
|
155
|
+
{ name: "value", type: "uint256" },
|
|
156
|
+
{ name: "nonce", type: "uint256" },
|
|
157
|
+
{ name: "deadline", type: "uint256" }
|
|
158
|
+
]
|
|
159
|
+
};
|
|
160
|
+
var ERC2612_READ_ABI = (0, import_viem.parseAbi)([
|
|
161
|
+
"function name() view returns (string)",
|
|
162
|
+
"function version() view returns (string)",
|
|
163
|
+
"function nonces(address owner) view returns (uint256)"
|
|
164
|
+
]);
|
|
165
|
+
function erc2612TypedData(input) {
|
|
166
|
+
return {
|
|
167
|
+
domain: {
|
|
168
|
+
name: input.permit.tokenName,
|
|
169
|
+
version: input.permit.tokenVersion,
|
|
170
|
+
chainId: input.chainId,
|
|
171
|
+
verifyingContract: input.token
|
|
172
|
+
},
|
|
173
|
+
types: ERC2612_PERMIT_TYPES,
|
|
174
|
+
primaryType: "Permit",
|
|
175
|
+
message: {
|
|
176
|
+
owner: input.owner,
|
|
177
|
+
spender: BATCH_PERMIT2_ADDRESS,
|
|
178
|
+
value: BigInt(input.permit.value),
|
|
179
|
+
nonce: BigInt(input.permit.nonce),
|
|
180
|
+
deadline: BigInt(input.permit.deadline)
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
var PERMIT_BATCH_TYPES = {
|
|
185
|
+
PermitBatchWitnessTransferFrom: [
|
|
186
|
+
{ name: "permitted", type: "TokenPermissions[]" },
|
|
187
|
+
{ name: "spender", type: "address" },
|
|
188
|
+
{ name: "nonce", type: "uint256" },
|
|
189
|
+
{ name: "deadline", type: "uint256" },
|
|
190
|
+
{ name: "witness", type: "Deposit" }
|
|
191
|
+
],
|
|
192
|
+
Deposit: [{ name: "recipients", type: "address[]" }],
|
|
193
|
+
TokenPermissions: [
|
|
194
|
+
{ name: "token", type: "address" },
|
|
195
|
+
{ name: "amount", type: "uint256" }
|
|
196
|
+
]
|
|
197
|
+
};
|
|
198
|
+
var PERMIT2_BATCH_ABI = (0, import_viem.parseAbi)([
|
|
199
|
+
"struct TokenPermissions { address token; uint256 amount; }",
|
|
200
|
+
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
201
|
+
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
202
|
+
"function permitWitnessTransferFrom(PermitBatchTransferFrom permit,SignatureTransferDetails[] transferDetails,address owner,bytes32 witness,string witnessTypeString,bytes signature)",
|
|
203
|
+
"function nonceBitmap(address owner,uint256 wordPos) view returns (uint256)"
|
|
204
|
+
]);
|
|
205
|
+
function batchPermit(b) {
|
|
206
|
+
return {
|
|
207
|
+
permitted: b.transfers.map((t) => ({
|
|
208
|
+
token: b.token,
|
|
209
|
+
amount: BigInt(t.amount)
|
|
210
|
+
})),
|
|
211
|
+
nonce: BigInt(b.nonce),
|
|
212
|
+
deadline: BigInt(b.deadline)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function batchTypedData(b, spender) {
|
|
216
|
+
return {
|
|
217
|
+
domain: {
|
|
218
|
+
name: "Permit2",
|
|
219
|
+
chainId: b.chainId,
|
|
220
|
+
verifyingContract: BATCH_PERMIT2_ADDRESS
|
|
221
|
+
},
|
|
222
|
+
types: PERMIT_BATCH_TYPES,
|
|
223
|
+
primaryType: "PermitBatchWitnessTransferFrom",
|
|
224
|
+
message: {
|
|
225
|
+
...batchPermit(b),
|
|
226
|
+
spender,
|
|
227
|
+
witness: { recipients: b.transfers.map((t) => t.to) }
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
var BATCH_WITNESS_TYPE = "Deposit witness)Deposit(address[] recipients)TokenPermissions(address token,uint256 amount)";
|
|
232
|
+
function batchWitness(b) {
|
|
233
|
+
return (0, import_viem.hashStruct)({
|
|
234
|
+
types: PERMIT_BATCH_TYPES,
|
|
235
|
+
primaryType: "Deposit",
|
|
236
|
+
data: { recipients: b.transfers.map((t) => t.to) }
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
function batchArgs(b) {
|
|
240
|
+
return [
|
|
241
|
+
batchPermit(b),
|
|
242
|
+
b.transfers.map((t) => ({ to: t.to, requestedAmount: BigInt(t.amount) })),
|
|
243
|
+
b.from,
|
|
244
|
+
batchWitness(b),
|
|
245
|
+
BATCH_WITNESS_TYPE,
|
|
246
|
+
b.signature
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/lib/funding-evidence.ts
|
|
251
|
+
var import_viem2 = require("viem");
|
|
44
252
|
var import_account_abstraction = require("viem/account-abstraction");
|
|
45
253
|
function notifyFunding(callback, evidence) {
|
|
46
254
|
try {
|
|
@@ -49,9 +257,9 @@ function notifyFunding(callback, evidence) {
|
|
|
49
257
|
} catch {
|
|
50
258
|
}
|
|
51
259
|
}
|
|
52
|
-
var fullWithdrawalAbi = (0,
|
|
53
|
-
var withdrawalEventAbi = (0,
|
|
54
|
-
var accountExecutionAbi = (0,
|
|
260
|
+
var fullWithdrawalAbi = (0, import_viem2.parseAbi)(["function withdrawAllAssetToUser(address recipient, address asset)"]);
|
|
261
|
+
var withdrawalEventAbi = (0, import_viem2.parseAbi)(["event WithdrewTokenToUser(address indexed owner, address indexed recipient, address indexed token, uint256 amount)"]);
|
|
262
|
+
var accountExecutionAbi = (0, import_viem2.parseAbi)([
|
|
55
263
|
"function execute(address target,uint256 value,bytes data)",
|
|
56
264
|
"function executeBatch((address target,uint256 value,bytes data)[] calls)"
|
|
57
265
|
]);
|
|
@@ -61,7 +269,7 @@ function yieldseekerExecution(receipt, evidence) {
|
|
|
61
269
|
const operationEvents = receipt.logs.flatMap((log) => {
|
|
62
270
|
if (![entryPoint06, entryPoint07].includes(log.address.toLowerCase())) return [];
|
|
63
271
|
try {
|
|
64
|
-
const event = (0,
|
|
272
|
+
const event = (0, import_viem2.decodeEventLog)({ abi: import_account_abstraction.entryPoint06Abi, data: log.data, topics: log.topics });
|
|
65
273
|
return event.eventName === "UserOperationEvent" ? [{ ...event.args, logIndex: log.logIndex }] : [];
|
|
66
274
|
} catch {
|
|
67
275
|
return [];
|
|
@@ -84,7 +292,7 @@ function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress, receip
|
|
|
84
292
|
if (evidence.agentId !== "yieldseeker" || transaction.chainId !== evidence.chainId || transaction.hash.toLowerCase() !== evidence.txHash?.toLowerCase() || !evidence.agentWallet) return false;
|
|
85
293
|
const matchesFullCall = (data) => {
|
|
86
294
|
try {
|
|
87
|
-
const decoded = (0,
|
|
295
|
+
const decoded = (0, import_viem2.decodeFunctionData)({ abi: fullWithdrawalAbi, data });
|
|
88
296
|
return decoded.args[0].toLowerCase() === evidence.walletAddress.toLowerCase() && decoded.args[1].toLowerCase() === tokenAddress.toLowerCase();
|
|
89
297
|
} catch {
|
|
90
298
|
return false;
|
|
@@ -95,7 +303,7 @@ function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress, receip
|
|
|
95
303
|
const address = transaction.to?.toLowerCase();
|
|
96
304
|
if (address !== entryPoint06 && address !== entryPoint07) return false;
|
|
97
305
|
const version = address === entryPoint06 ? "0.6" : "0.7";
|
|
98
|
-
const decoded = (0,
|
|
306
|
+
const decoded = (0, import_viem2.decodeFunctionData)({ abi: version === "0.6" ? import_account_abstraction.entryPoint06Abi : import_account_abstraction.entryPoint07Abi, data: transaction.input });
|
|
99
307
|
if (decoded.functionName !== "handleOps") return false;
|
|
100
308
|
const operations = decoded.args[0].filter((operation2) => [evidence.agentWallet.toLowerCase(), evidence.walletAddress.toLowerCase()].includes(operation2.sender.toLowerCase()));
|
|
101
309
|
if (operations.length !== 1) return false;
|
|
@@ -111,14 +319,14 @@ function isFullYieldseekerWithdrawal(transaction, evidence, tokenAddress, receip
|
|
|
111
319
|
if (!receipt?.logs.some((log) => {
|
|
112
320
|
if (log.address.toLowerCase() !== address) return false;
|
|
113
321
|
try {
|
|
114
|
-
const event = (0,
|
|
322
|
+
const event = (0, import_viem2.decodeEventLog)({ abi: import_account_abstraction.entryPoint06Abi, data: log.data, topics: log.topics });
|
|
115
323
|
return event.eventName === "UserOperationEvent" && event.args.userOpHash.toLowerCase() === hash2.toLowerCase() && event.args.sender.toLowerCase() === operation.sender.toLowerCase() && event.args.success;
|
|
116
324
|
} catch {
|
|
117
325
|
return false;
|
|
118
326
|
}
|
|
119
327
|
})) return false;
|
|
120
328
|
if (operation.sender.toLowerCase() === evidence.agentWallet.toLowerCase()) return matchesFullCall(operation.callData);
|
|
121
|
-
const execution = (0,
|
|
329
|
+
const execution = (0, import_viem2.decodeFunctionData)({ abi: accountExecutionAbi, data: operation.callData });
|
|
122
330
|
const calls = execution.functionName === "execute" ? [{ target: execution.args[0], value: execution.args[1], data: execution.args[2] }] : execution.args[0];
|
|
123
331
|
return calls.length === 1 && calls[0].value === 0n && calls[0].target.toLowerCase() === evidence.agentWallet.toLowerCase() && matchesFullCall(calls[0].data);
|
|
124
332
|
} catch {
|
|
@@ -161,7 +369,7 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction, optio
|
|
|
161
369
|
if (seen.has(log.logIndex)) continue;
|
|
162
370
|
seen.add(log.logIndex);
|
|
163
371
|
try {
|
|
164
|
-
const event = (0,
|
|
372
|
+
const event = (0, import_viem2.decodeEventLog)({ abi: import_viem2.erc20Abi, data: log.data, topics: log.topics });
|
|
165
373
|
if (event.eventName !== "Transfer") continue;
|
|
166
374
|
const { from, to, value } = event.args;
|
|
167
375
|
if (direction === "withdraw" && value === 0n && from.toLowerCase() === agent && to.toLowerCase() === owner)
|
|
@@ -185,7 +393,7 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction, optio
|
|
|
185
393
|
const emptyPayout = direction === "withdraw" && evidence.agentId === "yieldseeker" && amount === 0n && zeroTransferLogs.length > 0 && execution.logs.some((log) => {
|
|
186
394
|
if (log.address.toLowerCase() !== agent) return false;
|
|
187
395
|
try {
|
|
188
|
-
const { args } = (0,
|
|
396
|
+
const { args } = (0, import_viem2.decodeEventLog)({ abi: withdrawalEventAbi, data: log.data, topics: log.topics });
|
|
189
397
|
return args.owner.toLowerCase() === owner && args.recipient.toLowerCase() === owner && args.token.toLowerCase() === tokenAddress.toLowerCase() && args.amount === 0n;
|
|
190
398
|
} catch {
|
|
191
399
|
return false;
|
|
@@ -244,57 +452,8 @@ function registerDepositBatch(callback, transfer) {
|
|
|
244
452
|
batchCallbacks.set(callback, transfer);
|
|
245
453
|
}
|
|
246
454
|
|
|
247
|
-
// src/errors.ts
|
|
248
|
-
var OwneyError = class extends Error {
|
|
249
|
-
code;
|
|
250
|
-
details;
|
|
251
|
-
agentId;
|
|
252
|
-
constructor(code, message, details, agentId) {
|
|
253
|
-
const prefix = agentId ? `[${code}][agent:${agentId}]` : `[${code}]`;
|
|
254
|
-
super(`${prefix} ${message}`);
|
|
255
|
-
this.name = "OwneyError";
|
|
256
|
-
this.code = code;
|
|
257
|
-
this.details = details;
|
|
258
|
-
this.agentId = agentId;
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
var AgentNotFoundError = class extends OwneyError {
|
|
262
|
-
constructor(agentId, available) {
|
|
263
|
-
super(
|
|
264
|
-
"AGENT_NOT_FOUND",
|
|
265
|
-
`Unknown agent "${agentId}". Available agents: ${available.join(", ")}`,
|
|
266
|
-
{ agentId, available },
|
|
267
|
-
agentId
|
|
268
|
-
);
|
|
269
|
-
this.name = "AgentNotFoundError";
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
var NotConnectedError = class extends OwneyError {
|
|
273
|
-
constructor() {
|
|
274
|
-
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
275
|
-
this.name = "NotConnectedError";
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
var AgentChainIncompatibleError = class extends OwneyError {
|
|
279
|
-
incompatibleAgents;
|
|
280
|
-
connectedChainId;
|
|
281
|
-
constructor(incompatibleAgents, connectedChainId) {
|
|
282
|
-
const details = incompatibleAgents.map(
|
|
283
|
-
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
284
|
-
).join("; ");
|
|
285
|
-
super(
|
|
286
|
-
"AGENT_CHAIN_INCOMPATIBLE",
|
|
287
|
-
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
288
|
-
{ incompatibleAgents, connectedChainId }
|
|
289
|
-
);
|
|
290
|
-
this.name = "AgentChainIncompatibleError";
|
|
291
|
-
this.incompatibleAgents = incompatibleAgents;
|
|
292
|
-
this.connectedChainId = connectedChainId;
|
|
293
|
-
}
|
|
294
|
-
};
|
|
295
|
-
|
|
296
455
|
// src/lib/transfer-auth.ts
|
|
297
|
-
var
|
|
456
|
+
var import_viem3 = require("viem");
|
|
298
457
|
|
|
299
458
|
// src/lib/sponsor-client.ts
|
|
300
459
|
var ROUTING_API_BASE_URL = "https://owney-routing-api-243946518160.europe-west4.run.app";
|
|
@@ -410,7 +569,7 @@ async function postSponsorBatchTransfer(input) {
|
|
|
410
569
|
}
|
|
411
570
|
|
|
412
571
|
// src/lib/permit2.ts
|
|
413
|
-
var
|
|
572
|
+
var import_viem4 = require("viem");
|
|
414
573
|
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
415
574
|
var MAX_UINT256 = 2n ** 256n - 1n;
|
|
416
575
|
function permit2ApprovalAmount(requiredAmount) {
|
|
@@ -451,7 +610,7 @@ var ERC20_ALLOWANCE_ABI = [
|
|
|
451
610
|
function randomPermit2Nonce() {
|
|
452
611
|
const bytes = new Uint8Array(32);
|
|
453
612
|
globalThis.crypto.getRandomValues(bytes);
|
|
454
|
-
return BigInt((0,
|
|
613
|
+
return BigInt((0, import_viem4.bytesToHex)(bytes));
|
|
455
614
|
}
|
|
456
615
|
async function readPermit2Allowance(publicClient, token, owner, blockNumber) {
|
|
457
616
|
return publicClient.readContract({
|
|
@@ -559,21 +718,21 @@ function rateLimitDelay(error, now = Date.now()) {
|
|
|
559
718
|
}
|
|
560
719
|
if (typeof value !== "object" || seen.has(value)) return;
|
|
561
720
|
seen.add(value);
|
|
562
|
-
const
|
|
563
|
-
if ([
|
|
721
|
+
const record2 = value;
|
|
722
|
+
if ([record2.status, record2.statusCode, record2.code].some(
|
|
564
723
|
(code) => String(code) === "429"
|
|
565
724
|
)) {
|
|
566
725
|
limited = true;
|
|
567
726
|
}
|
|
568
727
|
for (const key2 of ["retryAfterSeconds", "retryAfter"]) {
|
|
569
|
-
const seconds = Number(
|
|
728
|
+
const seconds = Number(record2[key2]);
|
|
570
729
|
if (Number.isFinite(seconds) && seconds > 0)
|
|
571
730
|
delay = Math.max(delay, seconds * 1e3);
|
|
572
731
|
}
|
|
573
|
-
const retryAt = Number(
|
|
732
|
+
const retryAt = Number(record2.retryAt);
|
|
574
733
|
if (Number.isFinite(retryAt) && retryAt > now)
|
|
575
734
|
delay = Math.max(delay, retryAt - now);
|
|
576
|
-
const headers =
|
|
735
|
+
const headers = record2.headers;
|
|
577
736
|
const header = typeof headers?.get === "function" ? headers.get("Retry-After") : headers?.["retry-after"] ?? headers?.["Retry-After"];
|
|
578
737
|
if (typeof header === "string" || typeof header === "number") {
|
|
579
738
|
const seconds = Number(header);
|
|
@@ -589,10 +748,10 @@ function rateLimitDelay(error, now = Date.now()) {
|
|
|
589
748
|
"data",
|
|
590
749
|
"fields"
|
|
591
750
|
]) {
|
|
592
|
-
visit(
|
|
751
|
+
visit(record2[key2], depth + 1);
|
|
593
752
|
}
|
|
594
753
|
for (const key2 of ["agentErrors", "failures"]) {
|
|
595
|
-
const entries =
|
|
754
|
+
const entries = record2[key2];
|
|
596
755
|
if (entries && typeof entries === "object") {
|
|
597
756
|
for (const entry of Object.values(entries)) visit(entry, depth + 1);
|
|
598
757
|
}
|
|
@@ -610,7 +769,7 @@ function rateLimitDiagnostics(error) {
|
|
|
610
769
|
if (depth > 6 || !value || typeof value !== "object" || seen.has(value))
|
|
611
770
|
return;
|
|
612
771
|
seen.add(value);
|
|
613
|
-
const
|
|
772
|
+
const record2 = value;
|
|
614
773
|
for (const key2 of [
|
|
615
774
|
"rpcSource",
|
|
616
775
|
"chainId",
|
|
@@ -618,12 +777,12 @@ function rateLimitDiagnostics(error) {
|
|
|
618
777
|
"providerRequestId",
|
|
619
778
|
"statusCode"
|
|
620
779
|
]) {
|
|
621
|
-
const candidate =
|
|
780
|
+
const candidate = record2[key2];
|
|
622
781
|
if (candidate !== void 0 && details[key2] === void 0)
|
|
623
782
|
details = { ...details, [key2]: candidate };
|
|
624
783
|
}
|
|
625
784
|
for (const key2 of ["details", "cause", "response", "fields", "error"])
|
|
626
|
-
visit(
|
|
785
|
+
visit(record2[key2], depth + 1);
|
|
627
786
|
}
|
|
628
787
|
visit(error);
|
|
629
788
|
return {
|
|
@@ -691,12 +850,19 @@ var AgentReads = class {
|
|
|
691
850
|
};
|
|
692
851
|
|
|
693
852
|
// src/agents/zyfai/zyfai.withdraw-submission.ts
|
|
853
|
+
var BELOW_MINIMUM = /^Idle balance (\d+(?:\.\d+)?) (USDC|WETH|ETH|EURC) is below the minimum withdrawal of (\d+(?:\.\d+)?) \2\.?$/;
|
|
694
854
|
var listenersByClient = /* @__PURE__ */ new WeakMap();
|
|
695
855
|
function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
696
856
|
const client = sdk.httpClient;
|
|
697
857
|
if (!client || typeof client.get !== "function" || typeof client.post !== "function")
|
|
698
|
-
return {
|
|
699
|
-
|
|
858
|
+
return {
|
|
859
|
+
attempted: () => void 0,
|
|
860
|
+
rejected: () => false,
|
|
861
|
+
request: () => void 0,
|
|
862
|
+
belowMinimum: () => void 0,
|
|
863
|
+
stop: () => {
|
|
864
|
+
}
|
|
865
|
+
};
|
|
700
866
|
let listeners2 = listenersByClient.get(client);
|
|
701
867
|
if (!listeners2) {
|
|
702
868
|
listeners2 = /* @__PURE__ */ new Set();
|
|
@@ -716,6 +882,7 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
716
882
|
method: method === "get" ? "GET" : "POST",
|
|
717
883
|
path: method === "get" ? "/users/withdraw" : "/users/partial-withdraw"
|
|
718
884
|
};
|
|
885
|
+
let belowMinimum2;
|
|
719
886
|
const notify2 = (outcome) => {
|
|
720
887
|
if (outcome === "rejected" && matching.length !== 1) return;
|
|
721
888
|
const evidence = matching.length === 1 ? { ...request3 } : {
|
|
@@ -726,7 +893,7 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
726
893
|
};
|
|
727
894
|
for (const observer of matching) {
|
|
728
895
|
try {
|
|
729
|
-
observer.notify(outcome, evidence);
|
|
896
|
+
observer.notify(outcome, evidence, outcome === "rejected" ? belowMinimum2 : void 0);
|
|
730
897
|
} catch {
|
|
731
898
|
}
|
|
732
899
|
}
|
|
@@ -751,10 +918,12 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
751
918
|
return await original.apply(this, callArgs);
|
|
752
919
|
} catch (error) {
|
|
753
920
|
const message = error instanceof Error ? error.message : "";
|
|
921
|
+
const minimum = BELOW_MINIMUM.exec(message);
|
|
922
|
+
if (minimum) belowMinimum2 = { symbol: minimum[2], idle: minimum[1], minimum: minimum[3] };
|
|
754
923
|
const status = error?.response?.status;
|
|
755
924
|
if (request3.httpStatus === void 0 && typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599)
|
|
756
925
|
request3.httpStatus = status;
|
|
757
|
-
request3.failure = message === "Unauthorized: Invalid API key" ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : request3.httpStatus === 400 && message === "No balance to withdraw" ? "insufficient-balance" :
|
|
926
|
+
request3.failure = message === "Unauthorized: Invalid API key" ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : request3.httpStatus === 400 && message === "No balance to withdraw" ? "insufficient-balance" : minimum ? "insufficient-balance" : /^User doesn't have any active session key\.?$/.test(message) ? "session-key-missing" : method === "post" && request3.httpStatus === 400 && message === "Partial withdrawal already in progress" ? "withdrawal-in-progress" : message === "Network error: Unable to reach the server" ? "network" : "other";
|
|
758
927
|
if (["unauthorized", "forbidden", "rate-limited", "insufficient-balance", "session-key-missing", "withdrawal-in-progress"].includes(request3.failure) && (request3.httpStatus === void 0 || request3.httpStatus >= 400 && request3.httpStatus < 500)) notify2("rejected");
|
|
759
928
|
throw error;
|
|
760
929
|
} finally {
|
|
@@ -767,32 +936,45 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
767
936
|
let submitted = false;
|
|
768
937
|
let rejected = false;
|
|
769
938
|
let request2 = { attempted: false };
|
|
770
|
-
|
|
939
|
+
let belowMinimum;
|
|
940
|
+
const listener = { chainId, token, assertCurrent, notify: (outcome, evidence, detail) => {
|
|
771
941
|
request2 = evidence;
|
|
772
942
|
if (outcome === "attempted") submitted = true;
|
|
773
|
-
else if (outcome === "rejected")
|
|
943
|
+
else if (outcome === "rejected") {
|
|
944
|
+
rejected = true;
|
|
945
|
+
belowMinimum = detail;
|
|
946
|
+
}
|
|
774
947
|
} };
|
|
775
948
|
listeners2.add(listener);
|
|
776
|
-
return {
|
|
777
|
-
|
|
778
|
-
|
|
949
|
+
return {
|
|
950
|
+
attempted: () => submitted,
|
|
951
|
+
rejected: () => rejected,
|
|
952
|
+
request: () => ({ ...request2 }),
|
|
953
|
+
belowMinimum: () => belowMinimum && { ...belowMinimum },
|
|
954
|
+
stop: () => {
|
|
955
|
+
listeners2.delete(listener);
|
|
956
|
+
}
|
|
957
|
+
};
|
|
779
958
|
}
|
|
780
959
|
|
|
781
960
|
// src/agents/zyfai/zyfai.withdraw-empty.ts
|
|
782
|
-
|
|
783
|
-
|
|
961
|
+
var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
962
|
+
var symbol = (value) => typeof value === "string" ? value.toUpperCase() === "ETH" ? "WETH" : value.toUpperCase() : void 0;
|
|
963
|
+
var chain = (value) => {
|
|
964
|
+
if (typeof value === "number") return value;
|
|
965
|
+
if (typeof value !== "string") return void 0;
|
|
966
|
+
return /^\d+$/.test(value) ? Number(value) : { base: 8453, arbitrum: 42161, ethereum: 1, mainnet: 1 }[value.trim().toLowerCase()];
|
|
967
|
+
};
|
|
968
|
+
var zero = (value) => typeof value === "string" && /^(?:0x)?0+$/i.test(value);
|
|
969
|
+
var units = (value) => typeof value === "string" && /^(?:0x[0-9a-f]+|\d+)$/i.test(value) ? BigInt(value) : void 0;
|
|
970
|
+
function snapshotPortfolio(raw2) {
|
|
784
971
|
const response = record(raw2);
|
|
785
972
|
const portfolio = record(response?.portfolio);
|
|
786
973
|
const chains = record(portfolio?.portfolioByChain);
|
|
787
|
-
if (response?.success === false || !chains || !Array.isArray(portfolio
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
if (typeof value === "number") return value;
|
|
792
|
-
if (typeof value !== "string") return void 0;
|
|
793
|
-
return /^\d+$/.test(value) ? Number(value) : { base: 8453, arbitrum: 42161, ethereum: 1, mainnet: 1 }[value.trim().toLowerCase()];
|
|
794
|
-
};
|
|
795
|
-
const zero = (value) => typeof value === "string" && /^(?:0x)?0+$/i.test(value);
|
|
974
|
+
if (response?.success === false || !portfolio || !chains || !Array.isArray(portfolio.positions) || !Array.isArray(portfolio.staleBalances)) return void 0;
|
|
975
|
+
return { portfolio, chains, entries: [...portfolio.positions, ...portfolio.staleBalances] };
|
|
976
|
+
}
|
|
977
|
+
function chainBalancesMatch(chains, chainId, token, accept) {
|
|
796
978
|
for (const [id, assets] of Object.entries(chains)) {
|
|
797
979
|
const idNumber = chain(id);
|
|
798
980
|
if (idNumber === void 0) return false;
|
|
@@ -802,10 +984,13 @@ function hasEmptyWithdrawalSnapshot(raw2, chainId, token) {
|
|
|
802
984
|
for (const [asset, value] of Object.entries(entries)) {
|
|
803
985
|
if (symbol(asset) !== token) continue;
|
|
804
986
|
const balance = record(value);
|
|
805
|
-
if (!balance || !
|
|
987
|
+
if (!balance || !accept(balance)) return false;
|
|
806
988
|
}
|
|
807
989
|
}
|
|
808
|
-
|
|
990
|
+
return true;
|
|
991
|
+
}
|
|
992
|
+
function positionsEmpty(entries, chainId, token) {
|
|
993
|
+
for (const value of entries) {
|
|
809
994
|
const entry = record(value);
|
|
810
995
|
if (!entry) return false;
|
|
811
996
|
const id = chain(entry.chainId ?? entry.chain);
|
|
@@ -817,9 +1002,26 @@ function hasEmptyWithdrawalSnapshot(raw2, chainId, token) {
|
|
|
817
1002
|
}
|
|
818
1003
|
return true;
|
|
819
1004
|
}
|
|
1005
|
+
function hasEmptyWithdrawalSnapshot(raw2, chainId, token) {
|
|
1006
|
+
const snapshot = snapshotPortfolio(raw2);
|
|
1007
|
+
if (!snapshot) return false;
|
|
1008
|
+
const pending = snapshot.portfolio.pendingAsyncWithdrawals;
|
|
1009
|
+
if (pending !== void 0 && (!Array.isArray(pending) || pending.length > 0)) return false;
|
|
1010
|
+
return chainBalancesMatch(snapshot.chains, chainId, token, (balance) => zero(balance.balance)) && positionsEmpty(snapshot.entries, chainId, token);
|
|
1011
|
+
}
|
|
1012
|
+
function hasIdleOnlyWithdrawalSnapshot(raw2, chainId, token, decimals, idle) {
|
|
1013
|
+
const snapshot = snapshotPortfolio(raw2);
|
|
1014
|
+
if (!snapshot) return false;
|
|
1015
|
+
const pending = snapshot.portfolio.pendingAsyncWithdrawals;
|
|
1016
|
+
if (pending !== void 0 && (!Array.isArray(pending) || pending.some((value) => record(value)?.status !== "CLAIMED"))) return false;
|
|
1017
|
+
return chainBalancesMatch(snapshot.chains, chainId, token, (balance) => {
|
|
1018
|
+
const amount = units(balance.balance);
|
|
1019
|
+
return amount !== void 0 && amount <= idle && (balance.decimals === void 0 || balance.decimals === decimals);
|
|
1020
|
+
}) && positionsEmpty(snapshot.entries, chainId, token);
|
|
1021
|
+
}
|
|
820
1022
|
|
|
821
1023
|
// src/agents/zyfai/zyfai.deposit.ts
|
|
822
|
-
var
|
|
1024
|
+
var import_viem5 = require("viem");
|
|
823
1025
|
|
|
824
1026
|
// src/types/config.ts
|
|
825
1027
|
var P_SUPPORTED_CHAINS = {
|
|
@@ -869,20 +1071,20 @@ var SupportedAssets = [
|
|
|
869
1071
|
];
|
|
870
1072
|
|
|
871
1073
|
// src/agents/zyfai/zyfai.deposit.ts
|
|
872
|
-
async function submitZyfaiDeposit({ sdk, state, chain, amount, asset, smartWallet, assertCurrent, onBeforeSubmission }) {
|
|
1074
|
+
async function submitZyfaiDeposit({ sdk, state, chain: chain2, amount, asset, smartWallet, assertCurrent, onBeforeSubmission }) {
|
|
873
1075
|
const owner = state.walletAddress;
|
|
874
1076
|
const provider = state.provider;
|
|
875
|
-
const token = SupportedAssets.find((value) => value.chainId ===
|
|
876
|
-
if (!token || !(0,
|
|
877
|
-
throw new OwneyError("AGENT_INVALID_RESPONSE", "Invalid Zyfai deposit transfer.", { chainId:
|
|
1077
|
+
const token = SupportedAssets.find((value) => value.chainId === chain2.id && value.symbol === asset);
|
|
1078
|
+
if (!token || !(0, import_viem5.isAddress)(owner) || !(0, import_viem5.isAddress)(smartWallet) || !/^\d+$/.test(amount) || BigInt(amount) <= 0n)
|
|
1079
|
+
throw new OwneyError("AGENT_INVALID_RESPONSE", "Invalid Zyfai deposit transfer.", { chainId: chain2.id, asset }, "zyfai");
|
|
878
1080
|
assertCurrent();
|
|
879
|
-
const prepared = await sdk.prepareEnterPosition({ userAddress: owner, chainId:
|
|
1081
|
+
const prepared = await sdk.prepareEnterPosition({ userAddress: owner, chainId: chain2.id, amount, asset, strategy: "aggressive" });
|
|
880
1082
|
assertCurrent();
|
|
881
|
-
const expectedData = (0,
|
|
1083
|
+
const expectedData = (0, import_viem5.encodeFunctionData)({ abi: import_viem5.erc20Abi, functionName: "transfer", args: [smartWallet, BigInt(amount)] });
|
|
882
1084
|
const transfer = prepared?.transfer;
|
|
883
1085
|
if (!transfer || transfer.safeAddress?.toLowerCase() !== smartWallet.toLowerCase() || transfer.tokenAddress?.toLowerCase() !== token.address.toLowerCase() || transfer.to?.toLowerCase() !== token.address.toLowerCase() || transfer.data?.toLowerCase() !== expectedData.toLowerCase() || transfer.value !== "0")
|
|
884
|
-
throw new OwneyError("AGENT_INVALID_RESPONSE", "Zyfai prepared a different deposit transfer.", { chainId:
|
|
885
|
-
const wallet = (0,
|
|
1086
|
+
throw new OwneyError("AGENT_INVALID_RESPONSE", "Zyfai prepared a different deposit transfer.", { chainId: chain2.id, asset }, "zyfai");
|
|
1087
|
+
const wallet = (0, import_viem5.createWalletClient)({ account: owner, chain: chain2, transport: (0, import_viem5.custom)({
|
|
886
1088
|
request: async (args) => {
|
|
887
1089
|
assertCurrent();
|
|
888
1090
|
if (args.method === "eth_sendTransaction") {
|
|
@@ -891,7 +1093,7 @@ async function submitZyfaiDeposit({ sdk, state, chain, amount, asset, smartWalle
|
|
|
891
1093
|
provider.request({ method: "eth_chainId" })
|
|
892
1094
|
]);
|
|
893
1095
|
assertCurrent();
|
|
894
|
-
if (!Array.isArray(accounts) || typeof accounts[0] !== "string" || accounts[0].toLowerCase() !== owner.toLowerCase() || Number(chainId) !==
|
|
1096
|
+
if (!Array.isArray(accounts) || typeof accounts[0] !== "string" || accounts[0].toLowerCase() !== owner.toLowerCase() || Number(chainId) !== chain2.id)
|
|
895
1097
|
throw new OwneyError("NOT_CONNECTED", "Wallet changed while preparing Zyfai deposit.", void 0, "zyfai");
|
|
896
1098
|
await onBeforeSubmission?.();
|
|
897
1099
|
assertCurrent();
|
|
@@ -899,12 +1101,12 @@ async function submitZyfaiDeposit({ sdk, state, chain, amount, asset, smartWalle
|
|
|
899
1101
|
return provider.request(args);
|
|
900
1102
|
}
|
|
901
1103
|
}, { retryCount: 0 }) });
|
|
902
|
-
return wallet.sendTransaction({ account: owner, chain, to: token.address, data: expectedData, value: 0n });
|
|
1104
|
+
return wallet.sendTransaction({ account: owner, chain: chain2, to: token.address, data: expectedData, value: 0n });
|
|
903
1105
|
}
|
|
904
1106
|
|
|
905
1107
|
// src/agents/zyfai/zyfai.agent.ts
|
|
906
1108
|
var import_sdk = require("@zyfai/sdk");
|
|
907
|
-
var
|
|
1109
|
+
var import_viem8 = require("viem");
|
|
908
1110
|
var import_chains = require("viem/chains");
|
|
909
1111
|
|
|
910
1112
|
// src/lib/debug.ts
|
|
@@ -955,13 +1157,13 @@ function assetAliases(asset) {
|
|
|
955
1157
|
const normalised = asset.toLowerCase();
|
|
956
1158
|
return normalised === "weth" ? ["weth", "eth"] : [normalised];
|
|
957
1159
|
}
|
|
958
|
-
function matchesAsset(
|
|
959
|
-
return assetAliases(asset).includes(
|
|
1160
|
+
function matchesAsset(symbol2, asset) {
|
|
1161
|
+
return assetAliases(asset).includes(symbol2.toLowerCase());
|
|
960
1162
|
}
|
|
961
1163
|
|
|
962
1164
|
// src/agents/zyfai/zyfai.mapper.ts
|
|
963
|
-
var
|
|
964
|
-
var exactHexAmount = (raw2, decimals) => (0,
|
|
1165
|
+
var import_viem6 = require("viem");
|
|
1166
|
+
var exactHexAmount = (raw2, decimals) => (0, import_viem6.formatUnits)(BigInt(/^0x/i.test(raw2) ? raw2 : `0x${raw2}`), decimals);
|
|
965
1167
|
var DECIMALS_BY_SYMBOL = {
|
|
966
1168
|
usdc: 6,
|
|
967
1169
|
weth: 18,
|
|
@@ -970,10 +1172,10 @@ var DECIMALS_BY_SYMBOL = {
|
|
|
970
1172
|
wbtc: 8,
|
|
971
1173
|
cbbtc: 8
|
|
972
1174
|
};
|
|
973
|
-
function tokenDecimals(
|
|
1175
|
+
function tokenDecimals(symbol2, explicit) {
|
|
974
1176
|
if (typeof explicit === "number" && Number.isFinite(explicit))
|
|
975
1177
|
return explicit;
|
|
976
|
-
return DECIMALS_BY_SYMBOL[(
|
|
1178
|
+
return DECIMALS_BY_SYMBOL[(symbol2 ?? "").toLowerCase()] ?? 6;
|
|
977
1179
|
}
|
|
978
1180
|
function mapWithdraw(raw2) {
|
|
979
1181
|
return {
|
|
@@ -993,10 +1195,10 @@ var NAME_TO_CHAIN_ID = Object.entries(
|
|
|
993
1195
|
acc[name.toLowerCase()] = Number(id);
|
|
994
1196
|
return acc;
|
|
995
1197
|
}, {});
|
|
996
|
-
function resolveChainId(
|
|
997
|
-
const asNum = Number(
|
|
1198
|
+
function resolveChainId(chain2) {
|
|
1199
|
+
const asNum = Number(chain2);
|
|
998
1200
|
if (Number.isFinite(asNum) && asNum > 0) return asNum;
|
|
999
|
-
return NAME_TO_CHAIN_ID[
|
|
1201
|
+
return NAME_TO_CHAIN_ID[chain2.trim().toLowerCase()] ?? NaN;
|
|
1000
1202
|
}
|
|
1001
1203
|
function mapPendingAllocations(raw2) {
|
|
1002
1204
|
if (!Array.isArray(raw2)) return void 0;
|
|
@@ -1036,16 +1238,16 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
1036
1238
|
for (const [chainIdKey, tokensBySymbol] of Object.entries(portfolioByChain)) {
|
|
1037
1239
|
const positionChainId = Number(chainIdKey);
|
|
1038
1240
|
for (const [
|
|
1039
|
-
|
|
1241
|
+
symbol2,
|
|
1040
1242
|
{ balance, balanceWithFee, decimals }
|
|
1041
1243
|
] of Object.entries(tokensBySymbol ?? {})) {
|
|
1042
1244
|
if (balanceWithFee === void 0 && balance === void 0) throw new Error("Zyfai asset balance is unavailable.");
|
|
1043
|
-
const amount = exactHexAmount(balanceWithFee ?? balance, tokenDecimals(
|
|
1245
|
+
const amount = exactHexAmount(balanceWithFee ?? balance, tokenDecimals(symbol2, decimals));
|
|
1044
1246
|
totalBalance += Number(amount);
|
|
1045
1247
|
tokens.push({
|
|
1046
1248
|
chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
|
|
1047
1249
|
chainId: positionChainId,
|
|
1048
|
-
asset:
|
|
1250
|
+
asset: symbol2,
|
|
1049
1251
|
amount
|
|
1050
1252
|
});
|
|
1051
1253
|
}
|
|
@@ -1060,7 +1262,7 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
1060
1262
|
// legacy `amount` fallback is hex. Runtime
|
|
1061
1263
|
// getBalances payloads often carry one (e.g. 18 for WETH) — prefer that
|
|
1062
1264
|
// when present, otherwise fall back to the per-symbol lookup.
|
|
1063
|
-
amount: p.underlyingAmountWithFee ?? p.underlyingAmount ? (0,
|
|
1265
|
+
amount: p.underlyingAmountWithFee ?? p.underlyingAmount ? (0, import_viem6.formatUnits)(
|
|
1064
1266
|
BigInt(p.underlyingAmountWithFee ?? p.underlyingAmount ?? "0"),
|
|
1065
1267
|
tokenDecimals(
|
|
1066
1268
|
p.token_symbol,
|
|
@@ -1113,12 +1315,12 @@ function mapEarnings(raw2, smartWallet) {
|
|
|
1113
1315
|
totalEarningsByChain
|
|
1114
1316
|
)) {
|
|
1115
1317
|
const positionChainId = Number(chainIdKey);
|
|
1116
|
-
for (const [
|
|
1318
|
+
for (const [symbol2, earnings] of Object.entries(tokensBySymbol ?? {})) {
|
|
1117
1319
|
const amount = Number(earnings);
|
|
1118
1320
|
tokens.push({
|
|
1119
1321
|
chain: CHAIN_ID_TO_NAME[positionChainId] ?? "BASE",
|
|
1120
1322
|
chainId: positionChainId,
|
|
1121
|
-
asset:
|
|
1323
|
+
asset: symbol2,
|
|
1122
1324
|
amount: String(amount)
|
|
1123
1325
|
});
|
|
1124
1326
|
}
|
|
@@ -1138,8 +1340,8 @@ function mapWeightedApyByChain(raw2) {
|
|
|
1138
1340
|
const chainId = Number(chainKey);
|
|
1139
1341
|
if (!SUPPORTED_CHAIN_IDS.includes(chainId)) continue;
|
|
1140
1342
|
const perAsset = {};
|
|
1141
|
-
for (const [
|
|
1142
|
-
const token =
|
|
1343
|
+
for (const [symbol2, apy] of Object.entries(tokenApy)) {
|
|
1344
|
+
const token = symbol2;
|
|
1143
1345
|
if (!SUPPORTED_TOKENS.includes(token)) continue;
|
|
1144
1346
|
perAsset[token] = apy;
|
|
1145
1347
|
}
|
|
@@ -1370,8 +1572,8 @@ function computeAllocationApy(positions) {
|
|
|
1370
1572
|
function earningsForToken(bucket, chainId, asset) {
|
|
1371
1573
|
const tokens = bucket?.[String(chainId)];
|
|
1372
1574
|
if (!tokens) return null;
|
|
1373
|
-
for (const [
|
|
1374
|
-
if (!matchesAsset(
|
|
1575
|
+
for (const [symbol2, value] of Object.entries(tokens)) {
|
|
1576
|
+
if (!matchesAsset(symbol2, asset)) continue;
|
|
1375
1577
|
const amount = Number(value);
|
|
1376
1578
|
return Number.isFinite(amount) ? amount : null;
|
|
1377
1579
|
}
|
|
@@ -1388,8 +1590,8 @@ function assetsInSnapshot(entry, chainId) {
|
|
|
1388
1590
|
entry.current_earnings_by_token,
|
|
1389
1591
|
entry.total_earnings_by_token
|
|
1390
1592
|
]) {
|
|
1391
|
-
for (const
|
|
1392
|
-
seen.add(
|
|
1593
|
+
for (const symbol2 of Object.keys(bucket?.[key2] ?? {})) {
|
|
1594
|
+
seen.add(symbol2.toUpperCase());
|
|
1393
1595
|
}
|
|
1394
1596
|
}
|
|
1395
1597
|
return [...seen];
|
|
@@ -1724,7 +1926,7 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
1724
1926
|
}
|
|
1725
1927
|
|
|
1726
1928
|
// src/lib/paid-rpc.ts
|
|
1727
|
-
var
|
|
1929
|
+
var import_viem7 = require("viem");
|
|
1728
1930
|
var PAID_RPC_RETRY_COUNT = 3;
|
|
1729
1931
|
var PAID_RPC_RETRY_DELAY_MS = 1e3;
|
|
1730
1932
|
function configuredRpcProxyBaseUrl(baseUrl) {
|
|
@@ -1755,14 +1957,14 @@ function resolveRpcUrls(rpcUrls, apiKey = "", baseUrl) {
|
|
|
1755
1957
|
42161: resolveRpcUrl(rpcUrls, 42161, apiKey, baseUrl)
|
|
1756
1958
|
};
|
|
1757
1959
|
}
|
|
1758
|
-
function createPaidRpcClient(
|
|
1960
|
+
function createPaidRpcClient(chain2, rpcUrls) {
|
|
1759
1961
|
const url = resolveRpcUrl(
|
|
1760
1962
|
rpcUrls,
|
|
1761
|
-
|
|
1963
|
+
chain2.id
|
|
1762
1964
|
);
|
|
1763
|
-
return (0,
|
|
1764
|
-
chain,
|
|
1765
|
-
transport: (0,
|
|
1965
|
+
return (0, import_viem7.createPublicClient)({
|
|
1966
|
+
chain: chain2,
|
|
1967
|
+
transport: (0, import_viem7.http)(url, {
|
|
1766
1968
|
retryCount: PAID_RPC_RETRY_COUNT,
|
|
1767
1969
|
retryDelay: PAID_RPC_RETRY_DELAY_MS
|
|
1768
1970
|
})
|
|
@@ -1776,8 +1978,8 @@ function headerValue(error, name) {
|
|
|
1776
1978
|
return;
|
|
1777
1979
|
if (seen.has(candidate)) return;
|
|
1778
1980
|
seen.add(candidate);
|
|
1779
|
-
const
|
|
1780
|
-
const headers =
|
|
1981
|
+
const record2 = candidate;
|
|
1982
|
+
const headers = record2.headers;
|
|
1781
1983
|
let found;
|
|
1782
1984
|
if (typeof headers?.get === "function") {
|
|
1783
1985
|
found = headers.get(name);
|
|
@@ -1790,7 +1992,7 @@ function headerValue(error, name) {
|
|
|
1790
1992
|
return;
|
|
1791
1993
|
}
|
|
1792
1994
|
for (const key2 of ["cause", "details", "response", "error"])
|
|
1793
|
-
visit(
|
|
1995
|
+
visit(record2[key2], depth + 1);
|
|
1794
1996
|
}
|
|
1795
1997
|
visit(error);
|
|
1796
1998
|
return value;
|
|
@@ -1803,16 +2005,16 @@ function statusCode(error) {
|
|
|
1803
2005
|
return;
|
|
1804
2006
|
if (seen.has(candidate)) return;
|
|
1805
2007
|
seen.add(candidate);
|
|
1806
|
-
const
|
|
2008
|
+
const record2 = candidate;
|
|
1807
2009
|
for (const key2 of ["status", "statusCode"]) {
|
|
1808
|
-
const parsed = Number(
|
|
2010
|
+
const parsed = Number(record2[key2]);
|
|
1809
2011
|
if (Number.isInteger(parsed) && parsed >= 100 && parsed <= 599) {
|
|
1810
2012
|
status = parsed;
|
|
1811
2013
|
return;
|
|
1812
2014
|
}
|
|
1813
2015
|
}
|
|
1814
2016
|
for (const key2 of ["cause", "details", "response", "error"])
|
|
1815
|
-
visit(
|
|
2017
|
+
visit(record2[key2], depth + 1);
|
|
1816
2018
|
}
|
|
1817
2019
|
visit(error);
|
|
1818
2020
|
return status;
|
|
@@ -1859,7 +2061,7 @@ async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
|
|
|
1859
2061
|
}
|
|
1860
2062
|
|
|
1861
2063
|
// src/agents/zyfai/zyfai.agent.ts
|
|
1862
|
-
var ERC7579_IS_MODULE_INSTALLED_ABI = (0,
|
|
2064
|
+
var ERC7579_IS_MODULE_INSTALLED_ABI = (0, import_viem8.parseAbi)([
|
|
1863
2065
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
1864
2066
|
]);
|
|
1865
2067
|
var WETH_ADDRESS_BY_CHAIN = {
|
|
@@ -1872,6 +2074,17 @@ var SPLITTING_ASSETS = ["USDC", "WETH"];
|
|
|
1872
2074
|
var POOL_POLICY_CHAIN_IDS = [8453, 42161, 1];
|
|
1873
2075
|
var EARNINGS_REFRESH_COOLDOWN_MS = 60 * 60 * 1e3;
|
|
1874
2076
|
var WITHDRAW_BALANCE_TIMEOUT_MS = 2e3;
|
|
2077
|
+
function belowMinimumCeiling(rejection, token, decimals) {
|
|
2078
|
+
if ((rejection.symbol === "ETH" ? "WETH" : rejection.symbol) !== token) return void 0;
|
|
2079
|
+
const ceiling = (display) => {
|
|
2080
|
+
const places = display.split(".")[1]?.length ?? 0;
|
|
2081
|
+
if (places >= decimals) return void 0;
|
|
2082
|
+
return (0, import_viem8.parseUnits)(display, decimals) + 5n * 10n ** BigInt(decimals - places - 1);
|
|
2083
|
+
};
|
|
2084
|
+
const idle = ceiling(rejection.idle);
|
|
2085
|
+
const minimum = ceiling(rejection.minimum);
|
|
2086
|
+
return idle === void 0 || minimum === void 0 ? void 0 : idle < minimum ? idle : minimum;
|
|
2087
|
+
}
|
|
1875
2088
|
var ZyfaiAgent = class _ZyfaiAgent {
|
|
1876
2089
|
constructor(apiKey, rpcUrls, referralSource) {
|
|
1877
2090
|
this.apiKey = apiKey;
|
|
@@ -2026,7 +2239,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2026
2239
|
}, asset.address, entry.action === "Withdraw" ? "withdraw" : "deposit");
|
|
2027
2240
|
if (evidence.transfer === "confirmed" && evidence.confirmedAmount) {
|
|
2028
2241
|
Object.assign(tx, {
|
|
2029
|
-
amount: (0,
|
|
2242
|
+
amount: (0, import_viem8.formatUnits)(BigInt(evidence.confirmedAmount), asset.decimals),
|
|
2030
2243
|
amountRaw: evidence.confirmedAmount,
|
|
2031
2244
|
blockNumber: evidence.blockNumber,
|
|
2032
2245
|
logIndexes: evidence.logIndexes,
|
|
@@ -2828,8 +3041,10 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2828
3041
|
// --- IAgent: Fund operations ---
|
|
2829
3042
|
/** Only after a definitive endpoint rejection, combine a new provider
|
|
2830
3043
|
* snapshot with the Safe's exact idle balance. Never parse its rounded error
|
|
2831
|
-
* amount as proof of zero, or use this to skip sending a new MAX request.
|
|
2832
|
-
|
|
3044
|
+
* amount as proof of zero, or use this to skip sending a new MAX request.
|
|
3045
|
+
* After Zyfai's minimum-withdrawal rejection, exact idle-only dust that fits
|
|
3046
|
+
* within the message's rounded amounts is reported as not withdrawable. */
|
|
3047
|
+
async assertNotEmptyRejectedWithdrawal(state, chainId, token, owner, belowMinimum) {
|
|
2833
3048
|
const internals = this.sdk;
|
|
2834
3049
|
const generation = this.earningsGeneration;
|
|
2835
3050
|
let expired = false;
|
|
@@ -2853,29 +3068,38 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2853
3068
|
this.sdk.getPortfolio(owner).catch(() => void 0)
|
|
2854
3069
|
]);
|
|
2855
3070
|
assertScope();
|
|
2856
|
-
if (!(0,
|
|
3071
|
+
if (!(0, import_viem8.isAddress)(wallet.address) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase())
|
|
2857
3072
|
throw new OwneyError(
|
|
2858
3073
|
"AGENT_AUTH_FAILED",
|
|
2859
3074
|
"Could not verify Zyfai's withdrawal wallet.",
|
|
2860
3075
|
{ reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
|
|
2861
3076
|
this.id
|
|
2862
3077
|
);
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
3078
|
+
const asset = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === token);
|
|
3079
|
+
if (!asset || portfolio === void 0) return;
|
|
3080
|
+
const empty = hasEmptyWithdrawalSnapshot(portfolio, chainId, token);
|
|
3081
|
+
const ceiling = belowMinimum && belowMinimumCeiling(belowMinimum, token, asset.decimals);
|
|
3082
|
+
if (!empty && ceiling === void 0) return;
|
|
3083
|
+
const idle = await this.getPublicClient(chainId).readContract({
|
|
3084
|
+
address: asset.address,
|
|
3085
|
+
abi: import_viem8.erc20Abi,
|
|
3086
|
+
functionName: "balanceOf",
|
|
3087
|
+
args: [wallet.address]
|
|
3088
|
+
}).catch(() => void 0);
|
|
3089
|
+
assertScope();
|
|
3090
|
+
if (idle === 0n && empty) throw new OwneyError(
|
|
3091
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
3092
|
+
"No funds remain in Zyfai for this asset.",
|
|
3093
|
+
{ available: "0", chainId, token },
|
|
3094
|
+
this.id
|
|
3095
|
+
);
|
|
3096
|
+
if (typeof idle === "bigint" && idle > 0n && ceiling !== void 0 && idle < ceiling && hasIdleOnlyWithdrawalSnapshot(portfolio, chainId, token, asset.decimals, idle))
|
|
3097
|
+
throw new OwneyError(
|
|
2873
3098
|
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
2874
|
-
"
|
|
2875
|
-
{ available: "0", chainId, token },
|
|
3099
|
+
"The remaining Zyfai balance is below Zyfai's minimum withdrawal.",
|
|
3100
|
+
{ available: "0", chainId, token, reason: "ZYFAI_BELOW_MINIMUM_WITHDRAWAL", remainingRaw: idle.toString() },
|
|
2876
3101
|
this.id
|
|
2877
3102
|
);
|
|
2878
|
-
}
|
|
2879
3103
|
};
|
|
2880
3104
|
let timer;
|
|
2881
3105
|
try {
|
|
@@ -2926,7 +3150,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2926
3150
|
throw unavailable();
|
|
2927
3151
|
}
|
|
2928
3152
|
}
|
|
2929
|
-
async withdraw(state, chainId, token, amount, onProgress) {
|
|
3153
|
+
async withdraw(state, chainId, token, amount, onProgress, _gasMode, _onBeforeSubmission, assertOperation) {
|
|
2930
3154
|
if (amount !== void 0 && (typeof amount !== "string" || !/^[0-9]+$/.test(amount) || BigInt(amount) <= 0n))
|
|
2931
3155
|
throw new OwneyError("WITHDRAW_INVALID_AMOUNT", "Withdrawal amount must be a positive integer in token base units.", void 0, this.id);
|
|
2932
3156
|
const validChainId = isValidChainId(chainId);
|
|
@@ -2962,7 +3186,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2962
3186
|
throw staleSessionError;
|
|
2963
3187
|
}
|
|
2964
3188
|
};
|
|
2965
|
-
|
|
3189
|
+
const assertSubmission = () => {
|
|
3190
|
+
assertCurrent();
|
|
3191
|
+
assertOperation?.();
|
|
3192
|
+
};
|
|
3193
|
+
let observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertSubmission);
|
|
2966
3194
|
const authToken = internals.httpClient?.authToken;
|
|
2967
3195
|
let raw2;
|
|
2968
3196
|
try {
|
|
@@ -2981,7 +3209,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2981
3209
|
}
|
|
2982
3210
|
if (!amount && observation.rejected() && observation.request()?.failure === "insufficient-balance") {
|
|
2983
3211
|
try {
|
|
2984
|
-
await this.assertNotEmptyRejectedWithdrawal(state, validChainId, token, owner);
|
|
3212
|
+
await this.assertNotEmptyRejectedWithdrawal(state, validChainId, token, owner, observation.belowMinimum());
|
|
2985
3213
|
} catch (proofError) {
|
|
2986
3214
|
if (proofError instanceof OwneyError && (proofError.code === "WITHDRAW_INSUFFICIENT_BALANCE" || proofError.code === "NOT_CONNECTED")) throw proofError;
|
|
2987
3215
|
}
|
|
@@ -2994,9 +3222,10 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2994
3222
|
{ reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token },
|
|
2995
3223
|
this.id
|
|
2996
3224
|
);
|
|
3225
|
+
assertSubmission();
|
|
2997
3226
|
await this.recoverWithdrawalSession(state, validChainId, token, owner);
|
|
2998
3227
|
observation.stop();
|
|
2999
|
-
observation = observeWithdrawalSubmission(executionSdk, validChainId, token,
|
|
3228
|
+
observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertSubmission);
|
|
3000
3229
|
}
|
|
3001
3230
|
}
|
|
3002
3231
|
if (!raw2) throw new OwneyError(
|
|
@@ -3111,13 +3340,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3111
3340
|
this.sdk.getSmartWalletByEOA(owner)
|
|
3112
3341
|
]);
|
|
3113
3342
|
assertScope();
|
|
3114
|
-
if (!(0,
|
|
3343
|
+
if (!(0, import_viem8.isAddress)(wallet.address) || wallet.isDeployed !== false && (wallet.isDeployed !== true || wallet.isOwner !== true) || !registered.smartWallet || registered.smartWallet.toLowerCase() !== wallet.address.toLowerCase()) throw unavailable();
|
|
3115
3344
|
const client = this.getPublicClient(validChainId);
|
|
3116
3345
|
const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
|
|
3117
3346
|
assertScope();
|
|
3118
3347
|
const idle = await client.readContract({
|
|
3119
3348
|
address: asset.address,
|
|
3120
|
-
abi:
|
|
3349
|
+
abi: import_viem8.erc20Abi,
|
|
3121
3350
|
functionName: "balanceOf",
|
|
3122
3351
|
args: [wallet.address],
|
|
3123
3352
|
blockNumber
|
|
@@ -3174,7 +3403,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3174
3403
|
if (blockNumber < BigInt(evidence.blockNumber)) return void 0;
|
|
3175
3404
|
const idle = await client.readContract({
|
|
3176
3405
|
address: asset.address,
|
|
3177
|
-
abi:
|
|
3406
|
+
abi: import_viem8.erc20Abi,
|
|
3178
3407
|
functionName: "balanceOf",
|
|
3179
3408
|
args: [balance.smartWallet],
|
|
3180
3409
|
blockNumber
|
|
@@ -3379,12 +3608,12 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3379
3608
|
};
|
|
3380
3609
|
|
|
3381
3610
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
3382
|
-
var
|
|
3611
|
+
var import_viem14 = require("viem");
|
|
3383
3612
|
var import_chains4 = require("viem/chains");
|
|
3384
3613
|
|
|
3385
3614
|
// src/agents/yieldseeker/yieldseeker.auth.ts
|
|
3386
3615
|
var import_siwe = require("siwe");
|
|
3387
|
-
var
|
|
3616
|
+
var import_viem9 = require("viem");
|
|
3388
3617
|
var import_chains2 = require("viem/chains");
|
|
3389
3618
|
|
|
3390
3619
|
// src/agents/yieldseeker/yieldseeker.auth-cache.ts
|
|
@@ -3499,7 +3728,7 @@ function createYieldseekerSiweMessage(address, chainId, dependencies = {}) {
|
|
|
3499
3728
|
return new import_siwe.SiweMessage({
|
|
3500
3729
|
scheme: url.protocol.slice(0, -1),
|
|
3501
3730
|
domain: url.host,
|
|
3502
|
-
address: (0,
|
|
3731
|
+
address: (0, import_viem9.getAddress)(address),
|
|
3503
3732
|
uri: url.origin,
|
|
3504
3733
|
version: "1",
|
|
3505
3734
|
chainId,
|
|
@@ -3524,6 +3753,11 @@ var YieldseekerAuth = class {
|
|
|
3524
3753
|
key(state, chainId) {
|
|
3525
3754
|
return `${state.walletAddress.toLowerCase()}:${chainId}:${resolveSiweOrigin(this.dependencies.origin).origin}`;
|
|
3526
3755
|
}
|
|
3756
|
+
/** Observation must never open a sign-in prompt. */
|
|
3757
|
+
peekToken(state, chainId) {
|
|
3758
|
+
const token = this.tokens.get(this.key(state, chainId)) ?? readYieldseekerSession(state.walletAddress, chainId);
|
|
3759
|
+
return token && this.isReusable(token, state, chainId) ? token : void 0;
|
|
3760
|
+
}
|
|
3527
3761
|
async getToken(state, chainId) {
|
|
3528
3762
|
const key2 = this.key(state, chainId);
|
|
3529
3763
|
const scope = { address: state.walletAddress, chainId };
|
|
@@ -3594,15 +3828,15 @@ var YieldseekerAuth = class {
|
|
|
3594
3828
|
clearYieldseekerSession(state.walletAddress, chainId);
|
|
3595
3829
|
}
|
|
3596
3830
|
async sign(state, chainId) {
|
|
3597
|
-
const account = (0,
|
|
3598
|
-
const publicClient = (0,
|
|
3831
|
+
const account = (0, import_viem9.getAddress)(state.walletAddress);
|
|
3832
|
+
const publicClient = (0, import_viem9.createPublicClient)({
|
|
3599
3833
|
chain: import_chains2.base,
|
|
3600
|
-
transport: (0,
|
|
3834
|
+
transport: (0, import_viem9.custom)(state.provider)
|
|
3601
3835
|
});
|
|
3602
|
-
const walletClient = (0,
|
|
3836
|
+
const walletClient = (0, import_viem9.createWalletClient)({
|
|
3603
3837
|
account,
|
|
3604
3838
|
chain: import_chains2.base,
|
|
3605
|
-
transport: (0,
|
|
3839
|
+
transport: (0, import_viem9.custom)(state.provider)
|
|
3606
3840
|
});
|
|
3607
3841
|
await ensureWalletOnChain(
|
|
3608
3842
|
publicClient,
|
|
@@ -3708,10 +3942,10 @@ var YieldseekerApiError = class extends Error {
|
|
|
3708
3942
|
};
|
|
3709
3943
|
function providerError(body, fallback2) {
|
|
3710
3944
|
if (!body || typeof body !== "object") return { code: fallback2 };
|
|
3711
|
-
const
|
|
3945
|
+
const record2 = body;
|
|
3712
3946
|
return {
|
|
3713
|
-
code: typeof
|
|
3714
|
-
fields:
|
|
3947
|
+
code: typeof record2.message === "string" ? record2.message : fallback2,
|
|
3948
|
+
fields: record2.fields && typeof record2.fields === "object" ? record2.fields : void 0
|
|
3715
3949
|
};
|
|
3716
3950
|
}
|
|
3717
3951
|
var YieldseekerApiClient = class {
|
|
@@ -3769,7 +4003,7 @@ var YieldseekerApiClient = class {
|
|
|
3769
4003
|
};
|
|
3770
4004
|
|
|
3771
4005
|
// src/agents/yieldseeker/yieldseeker.mapper.ts
|
|
3772
|
-
var
|
|
4006
|
+
var import_viem10 = require("viem");
|
|
3773
4007
|
|
|
3774
4008
|
// src/lib/helpers/snapshot-apy.ts
|
|
3775
4009
|
var DAY_MS = 864e5;
|
|
@@ -3836,7 +4070,7 @@ function raw(value, endpoint) {
|
|
|
3836
4070
|
return BigInt(value);
|
|
3837
4071
|
}
|
|
3838
4072
|
function decimal(value, decimals, endpoint) {
|
|
3839
|
-
return (0,
|
|
4073
|
+
return (0, import_viem10.formatUnits)(raw(value, endpoint), decimals);
|
|
3840
4074
|
}
|
|
3841
4075
|
function balanceRaw(value, endpoint) {
|
|
3842
4076
|
const amount = raw(value, endpoint);
|
|
@@ -3844,10 +4078,10 @@ function balanceRaw(value, endpoint) {
|
|
|
3844
4078
|
return amount;
|
|
3845
4079
|
}
|
|
3846
4080
|
function balanceDecimal(value, decimals, endpoint) {
|
|
3847
|
-
return (0,
|
|
4081
|
+
return (0, import_viem10.formatUnits)(balanceRaw(value, endpoint), decimals);
|
|
3848
4082
|
}
|
|
3849
4083
|
function usd(rawAmount2, decimals, price) {
|
|
3850
|
-
return Number((0,
|
|
4084
|
+
return Number((0, import_viem10.formatUnits)(rawAmount2, decimals)) * price;
|
|
3851
4085
|
}
|
|
3852
4086
|
function percent(value) {
|
|
3853
4087
|
const result = Number(value);
|
|
@@ -3865,16 +4099,16 @@ function riskAdjustedApyForDays(option, days) {
|
|
|
3865
4099
|
if (days === "30D") return option.riskAdjustedApy30dAverage;
|
|
3866
4100
|
return (option.riskAdjustedApy7dAverage + option.riskAdjustedApy30dAverage) / 2;
|
|
3867
4101
|
}
|
|
3868
|
-
function assetAddressValue(
|
|
3869
|
-
const entry = Object.entries(
|
|
4102
|
+
function assetAddressValue(record2, address) {
|
|
4103
|
+
const entry = Object.entries(record2).find(
|
|
3870
4104
|
([key2]) => key2.toLowerCase() === address.toLowerCase()
|
|
3871
4105
|
);
|
|
3872
4106
|
return entry?.[1] ?? "0";
|
|
3873
4107
|
}
|
|
3874
4108
|
function position(value, asset, baseAssetDecimals) {
|
|
3875
|
-
const option = value?.
|
|
3876
|
-
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0,
|
|
3877
|
-
return invalid("
|
|
4109
|
+
const option = value?.vaultOption;
|
|
4110
|
+
if (!option || typeof option.provider !== "string" || typeof option.name !== "string" || typeof option.address !== "string" || !(0, import_viem10.isAddress)(option.address)) {
|
|
4111
|
+
return invalid("vault positions", "missing vault metadata");
|
|
3878
4112
|
}
|
|
3879
4113
|
return {
|
|
3880
4114
|
chain: "BASE",
|
|
@@ -3890,10 +4124,10 @@ function position(value, asset, baseAssetDecimals) {
|
|
|
3890
4124
|
amount: balanceDecimal(
|
|
3891
4125
|
value.assetsBase,
|
|
3892
4126
|
baseAssetDecimals,
|
|
3893
|
-
"
|
|
4127
|
+
"vault positions"
|
|
3894
4128
|
),
|
|
3895
|
-
amountRaw: balanceRaw(value.assetsRaw, "
|
|
3896
|
-
withdrawableAmountRaw: balanceRaw(value.withdrawableAssetsRaw, "
|
|
4129
|
+
amountRaw: balanceRaw(value.assetsRaw, "vault positions").toString(),
|
|
4130
|
+
withdrawableAmountRaw: balanceRaw(value.withdrawableAssetsRaw, "vault positions").toString(),
|
|
3897
4131
|
apy: percent(option.riskAdjustedApy),
|
|
3898
4132
|
tvl: Number(option.totalDepositsUsd),
|
|
3899
4133
|
liquidity: Number(option.withdrawableDepositsUsd)
|
|
@@ -3966,7 +4200,7 @@ function mapYieldseekerEarnings(contexts) {
|
|
|
3966
4200
|
chain: "BASE",
|
|
3967
4201
|
chainId: 8453,
|
|
3968
4202
|
asset: context.asset,
|
|
3969
|
-
amount: (0,
|
|
4203
|
+
amount: (0, import_viem10.formatUnits)(amount, context.snapshot.baseAssetDecimals)
|
|
3970
4204
|
});
|
|
3971
4205
|
lifetimeEarnings += usd(
|
|
3972
4206
|
amount,
|
|
@@ -4112,52 +4346,82 @@ function actionEntry(action) {
|
|
|
4112
4346
|
rebalanceLog: []
|
|
4113
4347
|
};
|
|
4114
4348
|
}
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
const
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
if (
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
if (symbol !== context.asset && !(context.asset === "WETH" && symbol === "ETH")) return void 0;
|
|
4127
|
-
if (typeof leg.vaultAddress !== "string" || !(0, import_viem9.isAddress)(leg.vaultAddress)) return void 0;
|
|
4349
|
+
var TX_HASH = /^0x[a-fA-F0-9]{64}$/;
|
|
4350
|
+
function allocationLegs(value, context, requireAmount) {
|
|
4351
|
+
if (!Array.isArray(value)) return void 0;
|
|
4352
|
+
const legs = [];
|
|
4353
|
+
for (const item of value) {
|
|
4354
|
+
if (!item || typeof item !== "object") return void 0;
|
|
4355
|
+
const leg = item;
|
|
4356
|
+
const symbol2 = typeof leg.assetSymbol === "string" ? leg.assetSymbol.toUpperCase() : "";
|
|
4357
|
+
if ((requireAmount || symbol2) && symbol2 !== context.asset && !(context.asset === "WETH" && symbol2 === "ETH"))
|
|
4358
|
+
return void 0;
|
|
4359
|
+
if (typeof leg.vaultAddress !== "string" || !(0, import_viem10.isAddress)(leg.vaultAddress)) return void 0;
|
|
4128
4360
|
const rawAmount2 = typeof leg.rawAmount === "string" && /^\d+$/.test(leg.rawAmount) ? BigInt(leg.rawAmount) : typeof leg.rawAmount === "number" && Number.isSafeInteger(leg.rawAmount) ? BigInt(leg.rawAmount) : void 0;
|
|
4129
|
-
if (rawAmount2 === void 0 || rawAmount2 <= 0n) return void 0;
|
|
4130
|
-
const
|
|
4131
|
-
positions.
|
|
4361
|
+
if (requireAmount && (rawAmount2 === void 0 || rawAmount2 <= 0n)) return void 0;
|
|
4362
|
+
const vault = leg.vaultAddress.toLowerCase();
|
|
4363
|
+
const option = context.positions.find((position2) => position2.vaultOption.address.toLowerCase() === vault)?.vaultOption;
|
|
4364
|
+
const receipt = context.historic?.assets?.find((asset) => asset.address.toLowerCase() === vault && !asset.isSpam);
|
|
4365
|
+
legs.push({
|
|
4132
4366
|
protocol: typeof leg.vaultProvider === "string" && leg.vaultProvider ? leg.vaultProvider : option?.provider || "Vault",
|
|
4133
|
-
pool: typeof leg.vaultName === "string" && leg.vaultName ? leg.vaultName : option?.name,
|
|
4134
|
-
|
|
4135
|
-
amount: (0, import_viem9.formatUnits)(rawAmount2, YIELDSEEKER_ASSET_METADATA[context.asset].decimals)
|
|
4367
|
+
pool: typeof leg.vaultName === "string" && leg.vaultName ? leg.vaultName : option?.name ?? (receipt?.name || receipt?.symbol || void 0),
|
|
4368
|
+
rawAmount: rawAmount2
|
|
4136
4369
|
});
|
|
4137
|
-
amountRaw += rawAmount2;
|
|
4138
4370
|
}
|
|
4371
|
+
return legs;
|
|
4372
|
+
}
|
|
4373
|
+
function allocationEntry(action, context) {
|
|
4374
|
+
if (action.actionType.toLowerCase() !== "autoseek") return void 0;
|
|
4375
|
+
const hash2 = action.details?.depositTransactionHash;
|
|
4376
|
+
const withdrawHash = action.details?.withdrawTransactionHash;
|
|
4377
|
+
if (typeof hash2 !== "string" || !TX_HASH.test(hash2)) return null;
|
|
4378
|
+
const deposits = allocationLegs(action.details?.deposits, context, true);
|
|
4379
|
+
const withdrawals = action.details?.withdrawals == null ? [] : allocationLegs(action.details.withdrawals, context, false);
|
|
4380
|
+
if (!deposits?.length || !withdrawals) return null;
|
|
4381
|
+
const decimals = YIELDSEEKER_ASSET_METADATA[context.asset].decimals;
|
|
4382
|
+
const positions = deposits.map((leg) => ({
|
|
4383
|
+
protocol: leg.protocol,
|
|
4384
|
+
pool: leg.pool,
|
|
4385
|
+
tokenSymbol: context.asset,
|
|
4386
|
+
amount: (0, import_viem10.formatUnits)(leg.rawAmount, decimals)
|
|
4387
|
+
}));
|
|
4388
|
+
const amountRaw = deposits.reduce((total, leg) => total + leg.rawAmount, 0n);
|
|
4389
|
+
const rebalance = withdrawals.length > 0;
|
|
4390
|
+
const hashes = rebalance && typeof withdrawHash === "string" && TX_HASH.test(withdrawHash) && withdrawHash.toLowerCase() !== hash2.toLowerCase() ? [hash2, withdrawHash] : [hash2];
|
|
4391
|
+
const source = withdrawals.length === 1 ? withdrawals[0] : {
|
|
4392
|
+
protocol: [...new Set(withdrawals.map((leg) => leg.protocol))].join(" + "),
|
|
4393
|
+
pool: withdrawals.map((leg) => leg.pool).filter(Boolean).join(" + ") || void 0
|
|
4394
|
+
};
|
|
4139
4395
|
return {
|
|
4140
4396
|
agent: "yieldseeker",
|
|
4141
|
-
action: "Deposit",
|
|
4397
|
+
action: rebalance ? "Rebalance" : "Deposit",
|
|
4142
4398
|
date: action.createdDate,
|
|
4143
4399
|
oldApy: null,
|
|
4144
4400
|
newApy: null,
|
|
4145
4401
|
positions,
|
|
4146
|
-
rebalanceLog:
|
|
4402
|
+
rebalanceLog: rebalance ? deposits.map((leg) => ({
|
|
4403
|
+
fromProtocol: source.protocol,
|
|
4404
|
+
...source.pool ? { fromPool: source.pool } : {},
|
|
4405
|
+
toProtocol: leg.protocol,
|
|
4406
|
+
...leg.pool ? { toPool: leg.pool } : {},
|
|
4407
|
+
tokenSymbol: context.asset,
|
|
4408
|
+
amount: (0, import_viem10.formatUnits)(leg.rawAmount, decimals),
|
|
4409
|
+
status: "success"
|
|
4410
|
+
})) : [],
|
|
4147
4411
|
transactions: [{
|
|
4148
|
-
txHashes:
|
|
4412
|
+
txHashes: hashes,
|
|
4149
4413
|
chainId: context.agent.chainId,
|
|
4150
4414
|
tokenAddress: context.agent.assetAddress,
|
|
4151
4415
|
tokenSymbol: context.asset,
|
|
4152
4416
|
agentWallet: context.wallet.walletAddress,
|
|
4153
4417
|
amountRaw: String(amountRaw),
|
|
4154
|
-
amount: (0,
|
|
4418
|
+
amount: (0, import_viem10.formatUnits)(amountRaw, decimals)
|
|
4155
4419
|
}]
|
|
4156
4420
|
};
|
|
4157
4421
|
}
|
|
4158
4422
|
function depositDestination(context, movement) {
|
|
4159
4423
|
const to = movement.toAddress.toLowerCase();
|
|
4160
|
-
const option = context.positions.find(({
|
|
4424
|
+
const option = context.positions.find(({ vaultOption }) => vaultOption.chainId === movement.chainId && vaultOption.assetAddress.toLowerCase() === context.agent.assetAddress.toLowerCase() && vaultOption.address.toLowerCase() === to)?.vaultOption;
|
|
4161
4425
|
if (option) return { protocol: option.provider || "Vault", pool: option.name, tokenSymbol: context.asset };
|
|
4162
4426
|
const receipt = context.historic?.assets.find((asset) => asset.chainId === movement.chainId && !asset.isSpam && asset.address.toLowerCase() === to && asset.address.toLowerCase() !== context.agent.assetAddress.toLowerCase());
|
|
4163
4427
|
const minted = receipt && context.historic?.movements.some((candidate) => candidate.chainId === movement.chainId && candidate.transactionHash.toLowerCase() === movement.transactionHash.toLowerCase() && candidate.assetAddress.toLowerCase() === receipt.address.toLowerCase() && candidate.fromAddress.toLowerCase() === "0x0000000000000000000000000000000000000000" && candidate.toAddress.toLowerCase() === context.wallet.walletAddress.toLowerCase() && raw(candidate.assetAmount, "historic position") > 0n);
|
|
@@ -4222,6 +4486,23 @@ function mapYieldseekerHistory(contexts, options) {
|
|
|
4222
4486
|
seenMovements.add(key2);
|
|
4223
4487
|
return true;
|
|
4224
4488
|
});
|
|
4489
|
+
const allocations = /* @__PURE__ */ new Map();
|
|
4490
|
+
const rebalanceHashes = /* @__PURE__ */ new Set();
|
|
4491
|
+
const actions = [];
|
|
4492
|
+
for (const action of context.actions ?? []) {
|
|
4493
|
+
const allocation = allocationEntry(action, context);
|
|
4494
|
+
if (allocation === null) continue;
|
|
4495
|
+
if (!allocation) {
|
|
4496
|
+
const entry = actionEntry(action);
|
|
4497
|
+
if (entry) actions.push(entry);
|
|
4498
|
+
continue;
|
|
4499
|
+
}
|
|
4500
|
+
const hash2 = allocation.transactions[0].txHashes[0].toLowerCase();
|
|
4501
|
+
if (allocations.has(hash2)) continue;
|
|
4502
|
+
allocations.set(hash2, allocation);
|
|
4503
|
+
if (allocation.action === "Rebalance")
|
|
4504
|
+
for (const value of allocation.transactions[0].txHashes) rebalanceHashes.add(value.toLowerCase());
|
|
4505
|
+
}
|
|
4225
4506
|
const movementEntries = movements.map(
|
|
4226
4507
|
(movement) => movementEntry(
|
|
4227
4508
|
movement,
|
|
@@ -4232,19 +4513,10 @@ function mapYieldseekerHistory(contexts, options) {
|
|
|
4232
4513
|
options.vaultAddresses,
|
|
4233
4514
|
depositDestination(context, movement)
|
|
4234
4515
|
)
|
|
4235
|
-
).filter((entry) => entry !== void 0);
|
|
4516
|
+
).filter((entry) => entry !== void 0 && !((entry.action === "Deposit" || entry.action === "Top up") && rebalanceHashes.has(entry.transactions[0]?.txHashes[0]?.toLowerCase() ?? "")));
|
|
4236
4517
|
const deposits = new Set(movementEntries.filter((entry) => entry.action === "Deposit").map((entry) => entry.transactions[0]?.txHashes[0]?.toLowerCase()));
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
if (allocation) {
|
|
4240
|
-
const hash2 = allocation.transactions[0].txHashes[0].toLowerCase();
|
|
4241
|
-
if (deposits.has(hash2)) return [];
|
|
4242
|
-
deposits.add(hash2);
|
|
4243
|
-
return [allocation];
|
|
4244
|
-
}
|
|
4245
|
-
const entry = actionEntry(action);
|
|
4246
|
-
return entry ? [entry] : [];
|
|
4247
|
-
});
|
|
4518
|
+
for (const [hash2, allocation] of allocations)
|
|
4519
|
+
if (allocation.action === "Rebalance" || !deposits.has(hash2)) actions.push(allocation);
|
|
4248
4520
|
return [...movementEntries, ...actions];
|
|
4249
4521
|
});
|
|
4250
4522
|
const grouped = /* @__PURE__ */ new Map();
|
|
@@ -4275,7 +4547,7 @@ function mapYieldseekerHistory(contexts, options) {
|
|
|
4275
4547
|
const known = new Set(target.logIndexes ?? []);
|
|
4276
4548
|
if (tx.logIndexes?.some((index) => !known.has(index))) {
|
|
4277
4549
|
target.amountRaw = String(BigInt(target.amountRaw ?? "0") + BigInt(tx.amountRaw));
|
|
4278
|
-
target.amount = (0,
|
|
4550
|
+
target.amount = (0, import_viem10.formatUnits)(
|
|
4279
4551
|
BigInt(target.amountRaw),
|
|
4280
4552
|
YIELDSEEKER_ASSET_METADATA[target.tokenSymbol].decimals
|
|
4281
4553
|
);
|
|
@@ -4322,8 +4594,8 @@ function mapYieldseekerProfile(address, contexts) {
|
|
|
4322
4594
|
const protocols = /* @__PURE__ */ new Set();
|
|
4323
4595
|
for (const context of contexts) {
|
|
4324
4596
|
for (const current of context.positions) {
|
|
4325
|
-
if (current.
|
|
4326
|
-
protocols.add(String(current.
|
|
4597
|
+
if (current.vaultOption?.provider) {
|
|
4598
|
+
protocols.add(String(current.vaultOption.provider));
|
|
4327
4599
|
}
|
|
4328
4600
|
}
|
|
4329
4601
|
}
|
|
@@ -4341,7 +4613,7 @@ function mapYieldseekerAgentApy(options, days) {
|
|
|
4341
4613
|
const perAsset = {};
|
|
4342
4614
|
const all = [];
|
|
4343
4615
|
for (const entry of options) {
|
|
4344
|
-
const apys = entry.
|
|
4616
|
+
const apys = entry.vaultOptions.map(
|
|
4345
4617
|
(option) => publicApyAfterYieldseekerFee(riskAdjustedApyForDays(option, days))
|
|
4346
4618
|
).filter(Number.isFinite);
|
|
4347
4619
|
if (apys.length === 0) continue;
|
|
@@ -4356,7 +4628,7 @@ function mapYieldseekerAgentApy(options, days) {
|
|
|
4356
4628
|
}
|
|
4357
4629
|
|
|
4358
4630
|
// src/lib/remaining-vault-balance.ts
|
|
4359
|
-
var
|
|
4631
|
+
var import_viem11 = require("viem");
|
|
4360
4632
|
async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber) {
|
|
4361
4633
|
const addresses = [...new Set(vaults.map((value) => value.toLowerCase()))];
|
|
4362
4634
|
if (addresses.length > 64) return void 0;
|
|
@@ -4367,8 +4639,8 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
|
|
|
4367
4639
|
blockNumber,
|
|
4368
4640
|
allowFailure: true,
|
|
4369
4641
|
contracts: batch.map((address) => ({
|
|
4370
|
-
address: (0,
|
|
4371
|
-
abi:
|
|
4642
|
+
address: (0, import_viem11.getAddress)(address),
|
|
4643
|
+
abi: import_viem11.erc20Abi,
|
|
4372
4644
|
functionName: "balanceOf",
|
|
4373
4645
|
args: [wallet]
|
|
4374
4646
|
}))
|
|
@@ -4378,15 +4650,15 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
|
|
|
4378
4650
|
for (let index = 0; index < batch.length; index++) {
|
|
4379
4651
|
const result = balances[index];
|
|
4380
4652
|
if (result?.status !== "success" || typeof result.result !== "bigint" || result.result < 0n) return void 0;
|
|
4381
|
-
if (result.result > 0n) funded.push({ address: (0,
|
|
4653
|
+
if (result.result > 0n) funded.push({ address: (0, import_viem11.getAddress)(batch[index]), shares: result.result });
|
|
4382
4654
|
}
|
|
4383
4655
|
if (!funded.length) return 0n;
|
|
4384
4656
|
const valuations = await client.multicall({
|
|
4385
4657
|
blockNumber,
|
|
4386
4658
|
allowFailure: true,
|
|
4387
4659
|
contracts: funded.flatMap(({ address, shares }) => [
|
|
4388
|
-
{ address, abi:
|
|
4389
|
-
{ address, abi:
|
|
4660
|
+
{ address, abi: import_viem11.erc4626Abi, functionName: "asset" },
|
|
4661
|
+
{ address, abi: import_viem11.erc4626Abi, functionName: "convertToAssets", args: [shares] }
|
|
4390
4662
|
])
|
|
4391
4663
|
});
|
|
4392
4664
|
if (valuations.length !== funded.length * 2) return void 0;
|
|
@@ -4402,12 +4674,12 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
|
|
|
4402
4674
|
}
|
|
4403
4675
|
|
|
4404
4676
|
// src/agents/yieldseeker/yieldseeker.wallet-version.ts
|
|
4405
|
-
var
|
|
4677
|
+
var import_viem12 = require("viem");
|
|
4406
4678
|
var YIELDSEEKER_FACTORY = "0x9c7410a0faCAC60850C46eE5b58B518daec95130";
|
|
4407
4679
|
var YIELDSEEKER_V1_IMPLEMENTATION = "0x32403455357589523435dB18b414057D7cE05051";
|
|
4408
4680
|
var YIELDSEEKER_V2_IMPLEMENTATION = "0x613Db9c924ADB7A7A90c11b02Cce20c913E3Fe50";
|
|
4409
4681
|
var ERC1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
|
4410
|
-
var walletAbi = (0,
|
|
4682
|
+
var walletAbi = (0, import_viem12.parseAbi)([
|
|
4411
4683
|
"function owner() view returns (address)",
|
|
4412
4684
|
"function ownerAgentIndex() view returns (uint256)",
|
|
4413
4685
|
"function baseAsset() view returns (address)",
|
|
@@ -4415,7 +4687,7 @@ var walletAbi = (0, import_viem11.parseAbi)([
|
|
|
4415
4687
|
"function upgradeToLatest()",
|
|
4416
4688
|
"event Upgraded(address indexed implementation)"
|
|
4417
4689
|
]);
|
|
4418
|
-
var factoryAbi = (0,
|
|
4690
|
+
var factoryAbi = (0, import_viem12.parseAbi)([
|
|
4419
4691
|
"function userWallets(address owner, uint256 index) view returns (address)",
|
|
4420
4692
|
"function agentWalletImplementation() view returns (address)"
|
|
4421
4693
|
]);
|
|
@@ -4423,9 +4695,9 @@ function yieldseekerOwnerPath(code) {
|
|
|
4423
4695
|
return !code || code === "0x" || /^0xef0100[0-9a-f]{40}$/i.test(code) ? "eoa" : "smart";
|
|
4424
4696
|
}
|
|
4425
4697
|
async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNumber) {
|
|
4426
|
-
const agentWallet = (0,
|
|
4427
|
-
const expectedOwner = (0,
|
|
4428
|
-
const expectedAsset = (0,
|
|
4698
|
+
const agentWallet = (0, import_viem12.getAddress)(wallet);
|
|
4699
|
+
const expectedOwner = (0, import_viem12.getAddress)(owner);
|
|
4700
|
+
const expectedAsset = (0, import_viem12.getAddress)(asset);
|
|
4429
4701
|
const [code, ownerCode, slot, actualOwner, index, actualAsset, actualFactory, latest] = await Promise.all([
|
|
4430
4702
|
client.getBytecode({ address: agentWallet, blockNumber }),
|
|
4431
4703
|
client.getBytecode({ address: expectedOwner, blockNumber }),
|
|
@@ -4439,7 +4711,7 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
|
|
|
4439
4711
|
if (!code || code === "0x" || !slot || /^0x0+$/.test(slot)) {
|
|
4440
4712
|
throw new Error("Yieldseeker wallet is not a deployed ERC1967 proxy");
|
|
4441
4713
|
}
|
|
4442
|
-
const implementation = (0,
|
|
4714
|
+
const implementation = (0, import_viem12.getAddress)(`0x${slot.slice(-40)}`);
|
|
4443
4715
|
const mapped = await client.readContract({
|
|
4444
4716
|
address: YIELDSEEKER_FACTORY,
|
|
4445
4717
|
abi: factoryAbi,
|
|
@@ -4447,8 +4719,8 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
|
|
|
4447
4719
|
args: [expectedOwner, index],
|
|
4448
4720
|
blockNumber
|
|
4449
4721
|
});
|
|
4450
|
-
const verified = (0,
|
|
4451
|
-
const version = !verified ? "unknown" : (0,
|
|
4722
|
+
const verified = (0, import_viem12.isAddressEqual)(actualOwner, expectedOwner) && (0, import_viem12.isAddressEqual)(actualAsset, expectedAsset) && (0, import_viem12.isAddressEqual)(actualFactory, YIELDSEEKER_FACTORY) && (0, import_viem12.isAddressEqual)(mapped, agentWallet);
|
|
4723
|
+
const version = !verified ? "unknown" : (0, import_viem12.isAddressEqual)(implementation, YIELDSEEKER_V2_IMPLEMENTATION) ? "v2" : (0, import_viem12.isAddressEqual)(implementation, YIELDSEEKER_V1_IMPLEMENTATION) ? "v1" : "unknown";
|
|
4452
4724
|
return {
|
|
4453
4725
|
agentWallet,
|
|
4454
4726
|
owner: actualOwner,
|
|
@@ -4457,13 +4729,13 @@ async function readYieldseekerWalletStatus(client, wallet, owner, asset, blockNu
|
|
|
4457
4729
|
implementation,
|
|
4458
4730
|
version,
|
|
4459
4731
|
verified,
|
|
4460
|
-
upgradeAvailable: version === "v1" && (0,
|
|
4732
|
+
upgradeAvailable: version === "v1" && (0, import_viem12.isAddressEqual)(latest, YIELDSEEKER_V2_IMPLEMENTATION)
|
|
4461
4733
|
};
|
|
4462
4734
|
}
|
|
4463
4735
|
var YIELDSEEKER_UPGRADE_ABI = walletAbi;
|
|
4464
4736
|
|
|
4465
4737
|
// src/agents/yieldseeker/yieldseeker.sponsored-withdraw.ts
|
|
4466
|
-
var
|
|
4738
|
+
var import_viem13 = require("viem");
|
|
4467
4739
|
var import_account_abstraction2 = require("viem/account-abstraction");
|
|
4468
4740
|
var import_accounts = require("viem/accounts");
|
|
4469
4741
|
var import_chains3 = require("viem/chains");
|
|
@@ -4550,7 +4822,7 @@ function rpcDiagnostics(data) {
|
|
|
4550
4822
|
details.revertName = data.revertName;
|
|
4551
4823
|
if (typeof data?.aaCode === "string" && /^AA[0-9]{2}$/.test(data.aaCode)) details.aaCode = data.aaCode;
|
|
4552
4824
|
if (typeof data?.panicCode === "string" && /^0x[0-9a-fA-F]{1,2}$/.test(data.panicCode)) {
|
|
4553
|
-
const panicCode = (0,
|
|
4825
|
+
const panicCode = (0, import_viem13.toHex)(BigInt(data.panicCode));
|
|
4554
4826
|
if (["0x0", "0x1", "0x11", "0x12", "0x21", "0x22", "0x31", "0x32", "0x41", "0x51"].includes(panicCode))
|
|
4555
4827
|
details.panicCode = panicCode;
|
|
4556
4828
|
}
|
|
@@ -4560,11 +4832,11 @@ function isHash(value) {
|
|
|
4560
4832
|
return typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value);
|
|
4561
4833
|
}
|
|
4562
4834
|
async function assertOwnerSession(state, owner) {
|
|
4563
|
-
const [
|
|
4835
|
+
const [chain2, accounts] = await Promise.all([
|
|
4564
4836
|
state.provider.request({ method: "eth_chainId" }),
|
|
4565
4837
|
state.provider.request({ method: "eth_accounts" })
|
|
4566
4838
|
]);
|
|
4567
|
-
if (Number(
|
|
4839
|
+
if (Number(chain2) !== 8453)
|
|
4568
4840
|
throw new OwneyError(
|
|
4569
4841
|
"CHAIN_MISMATCH",
|
|
4570
4842
|
"Switch back to Base before withdrawing.",
|
|
@@ -4770,14 +5042,14 @@ async function submitEoa(input) {
|
|
|
4770
5042
|
throw sponsorError("Base did not return EIP-1559 gas fees.", true);
|
|
4771
5043
|
let operation = {
|
|
4772
5044
|
sender: input.agentWallet,
|
|
4773
|
-
nonce: (0,
|
|
5045
|
+
nonce: (0, import_viem13.toHex)(nonce),
|
|
4774
5046
|
initCode: "0x",
|
|
4775
5047
|
callData: input.callData,
|
|
4776
|
-
callGasLimit: (0,
|
|
4777
|
-
verificationGasLimit: (0,
|
|
4778
|
-
preVerificationGas: (0,
|
|
4779
|
-
maxFeePerGas: (0,
|
|
4780
|
-
maxPriorityFeePerGas: (0,
|
|
5048
|
+
callGasLimit: (0, import_viem13.toHex)(3e5),
|
|
5049
|
+
verificationGasLimit: (0, import_viem13.toHex)(3e5),
|
|
5050
|
+
preVerificationGas: (0, import_viem13.toHex)(6e4),
|
|
5051
|
+
maxFeePerGas: (0, import_viem13.toHex)(fees.maxFeePerGas * USER_OP_FEE_HEADROOM),
|
|
5052
|
+
maxPriorityFeePerGas: (0, import_viem13.toHex)(fees.maxPriorityFeePerGas),
|
|
4781
5053
|
paymasterAndData: "0x",
|
|
4782
5054
|
signature: "0x"
|
|
4783
5055
|
};
|
|
@@ -4785,7 +5057,7 @@ async function submitEoa(input) {
|
|
|
4785
5057
|
const stub = await rpc(
|
|
4786
5058
|
input.rpcUrl,
|
|
4787
5059
|
"pm_getPaymasterStubData",
|
|
4788
|
-
[operation, ENTRY_POINT_V06, (0,
|
|
5060
|
+
[operation, ENTRY_POINT_V06, (0, import_viem13.toHex)(8453), {}],
|
|
4789
5061
|
"prepare"
|
|
4790
5062
|
);
|
|
4791
5063
|
if (!stub.paymasterAndData?.startsWith("0x"))
|
|
@@ -4800,7 +5072,7 @@ async function submitEoa(input) {
|
|
|
4800
5072
|
const final = await rpc(
|
|
4801
5073
|
input.rpcUrl,
|
|
4802
5074
|
"pm_getPaymasterData",
|
|
4803
|
-
[operation, ENTRY_POINT_V06, (0,
|
|
5075
|
+
[operation, ENTRY_POINT_V06, (0, import_viem13.toHex)(8453), {}],
|
|
4804
5076
|
"prepare"
|
|
4805
5077
|
);
|
|
4806
5078
|
if (!final.paymasterAndData?.startsWith("0x"))
|
|
@@ -4810,11 +5082,12 @@ async function submitEoa(input) {
|
|
|
4810
5082
|
const userOpHash = operationHash(operation);
|
|
4811
5083
|
await assertOwnerSession(input.state, input.owner);
|
|
4812
5084
|
assertIntentFresh(input.expiresAt);
|
|
4813
|
-
const wallet = (0,
|
|
5085
|
+
const wallet = (0, import_viem13.createWalletClient)({
|
|
4814
5086
|
account: input.owner,
|
|
4815
5087
|
chain: import_chains3.base,
|
|
4816
|
-
transport: (0,
|
|
5088
|
+
transport: (0, import_viem13.custom)(input.state.provider)
|
|
4817
5089
|
});
|
|
5090
|
+
input.assertCurrent?.();
|
|
4818
5091
|
const signature = await wallet.signMessage({ message: { raw: userOpHash } });
|
|
4819
5092
|
operation = { ...operation, signature };
|
|
4820
5093
|
await assertOwnerSession(input.state, input.owner);
|
|
@@ -4832,7 +5105,9 @@ async function submitEoa(input) {
|
|
|
4832
5105
|
paymasterAndData: operation.paymasterAndData
|
|
4833
5106
|
};
|
|
4834
5107
|
savePending(input.onPending, { userOpHash, userOperation }, true);
|
|
5108
|
+
input.assertCurrent?.();
|
|
4835
5109
|
await checkpoint(input.onBeforeSubmission, { userOpHash, userOperation });
|
|
5110
|
+
input.assertCurrent?.();
|
|
4836
5111
|
const submittedHash = await rpc(
|
|
4837
5112
|
input.rpcUrl,
|
|
4838
5113
|
"eth_sendUserOperation",
|
|
@@ -4891,22 +5166,24 @@ async function submitSmart(input) {
|
|
|
4891
5166
|
} catch {
|
|
4892
5167
|
throw sponsorError("This account cannot check paymaster support on Base.", true);
|
|
4893
5168
|
}
|
|
4894
|
-
const supportsPaymaster = capabilities?.[(0,
|
|
5169
|
+
const supportsPaymaster = capabilities?.[(0, import_viem13.toHex)(8453)]?.paymasterService?.supported ?? capabilities?.["8453"]?.paymasterService?.supported ?? capabilities?.["0x0"]?.paymasterService?.supported;
|
|
4895
5170
|
if (supportsPaymaster !== true)
|
|
4896
5171
|
throw sponsorError("This account cannot use a paymaster on Base.", true);
|
|
4897
5172
|
await assertOwnerSession(input.state, input.owner);
|
|
4898
5173
|
let sent;
|
|
4899
5174
|
assertIntentFresh(input.expiresAt);
|
|
4900
|
-
const requestedId = (0,
|
|
5175
|
+
const requestedId = (0, import_viem13.toHex)(globalThis.crypto.getRandomValues(new Uint8Array(32)));
|
|
4901
5176
|
savePending(input.onPending, { callsId: requestedId }, true);
|
|
5177
|
+
input.assertCurrent?.();
|
|
4902
5178
|
await checkpoint(input.onBeforeSubmission, { callsId: requestedId });
|
|
5179
|
+
input.assertCurrent?.();
|
|
4903
5180
|
try {
|
|
4904
5181
|
sent = await input.state.provider.request({
|
|
4905
5182
|
method: "wallet_sendCalls",
|
|
4906
5183
|
params: [{
|
|
4907
5184
|
version: "2.0.0",
|
|
4908
5185
|
from: input.owner,
|
|
4909
|
-
chainId: (0,
|
|
5186
|
+
chainId: (0, import_viem13.toHex)(8453),
|
|
4910
5187
|
id: requestedId,
|
|
4911
5188
|
atomicRequired: false,
|
|
4912
5189
|
calls: [{ to: input.agentWallet, value: "0x0", data: input.callData }],
|
|
@@ -4981,7 +5258,7 @@ async function submitSmart(input) {
|
|
|
4981
5258
|
);
|
|
4982
5259
|
}
|
|
4983
5260
|
async function submitYieldseekerSponsoredWithdrawal(input) {
|
|
4984
|
-
const owner = (0,
|
|
5261
|
+
const owner = (0, import_viem13.getAddress)(input.state.walletAddress);
|
|
4985
5262
|
const { intent, rpcUrl } = await requestIntent({
|
|
4986
5263
|
apiKey: input.apiKey,
|
|
4987
5264
|
routingApiBaseUrl: input.routingApiBaseUrl,
|
|
@@ -5000,7 +5277,7 @@ async function submitYieldseekerSponsoredWithdrawal(input) {
|
|
|
5000
5277
|
return intent.ownerPath === "eoa" ? submitEoa(args) : submitSmart(args);
|
|
5001
5278
|
}
|
|
5002
5279
|
async function submitYieldseekerSponsoredUpgrade(input) {
|
|
5003
|
-
const owner = (0,
|
|
5280
|
+
const owner = (0, import_viem13.getAddress)(input.state.walletAddress);
|
|
5004
5281
|
const { intent, rpcUrl } = await requestIntent({
|
|
5005
5282
|
apiKey: input.apiKey,
|
|
5006
5283
|
routingApiBaseUrl: input.routingApiBaseUrl,
|
|
@@ -5026,7 +5303,7 @@ var OWNEY_AGENT_RULE_PRESET = "explorative";
|
|
|
5026
5303
|
var YIELDSEEKER_USERNAME_PREFIX = "owney_";
|
|
5027
5304
|
var YIELDSEEKER_USERNAME_RANDOM_LENGTH = 14;
|
|
5028
5305
|
var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
5029
|
-
var
|
|
5306
|
+
var YIELDSEEKER_VAULT_OPTIONS_CACHE_MS = 6e4;
|
|
5030
5307
|
var YIELDSEEKER_AGENT_LIST_CACHE_MS = 12e4;
|
|
5031
5308
|
var YIELDSEEKER_PORTFOLIO_CACHE_MS = 12e4;
|
|
5032
5309
|
var YIELDSEEKER_SETTLEMENT_CACHE_MS = 5e3;
|
|
@@ -5129,6 +5406,7 @@ var YieldseekerAgent = class {
|
|
|
5129
5406
|
pendingReads = /* @__PURE__ */ new Map();
|
|
5130
5407
|
readGeneration = 0;
|
|
5131
5408
|
portfolioVersions = /* @__PURE__ */ new Map();
|
|
5409
|
+
liquidityVersions = /* @__PURE__ */ new Map();
|
|
5132
5410
|
snapshotFailures = /* @__PURE__ */ new Map();
|
|
5133
5411
|
standardReadFailures = /* @__PURE__ */ new Map();
|
|
5134
5412
|
reconcileUntil = /* @__PURE__ */ new Map();
|
|
@@ -5138,8 +5416,8 @@ var YieldseekerAgent = class {
|
|
|
5138
5416
|
// settlement; keep slow reconciliation after the fast window expires.
|
|
5139
5417
|
snapshotMovements = /* @__PURE__ */ new Map();
|
|
5140
5418
|
movementEpochs = /* @__PURE__ */ new Map();
|
|
5141
|
-
|
|
5142
|
-
|
|
5419
|
+
vaultOptions = /* @__PURE__ */ new Map();
|
|
5420
|
+
pendingVaultOptions = /* @__PURE__ */ new Map();
|
|
5143
5421
|
constructor(owneyApiKey, options = {}) {
|
|
5144
5422
|
this.owneyApiKey = owneyApiKey;
|
|
5145
5423
|
this.routingApiBaseUrl = options.routingApiBaseUrl;
|
|
@@ -5168,8 +5446,8 @@ var YieldseekerAgent = class {
|
|
|
5168
5446
|
if (useCachedV2 && cached && Date.now() - cached.at < 15 * 6e4) return { ...cached.status };
|
|
5169
5447
|
const status = await readYieldseekerWalletStatus(
|
|
5170
5448
|
this.getReceiptClient(),
|
|
5171
|
-
(0,
|
|
5172
|
-
(0,
|
|
5449
|
+
(0, import_viem14.getAddress)(context.wallet.walletAddress),
|
|
5450
|
+
(0, import_viem14.getAddress)(state.walletAddress),
|
|
5173
5451
|
YIELDSEEKER_ASSET_METADATA[asset].address
|
|
5174
5452
|
);
|
|
5175
5453
|
this.rememberWalletStatus(key2, status);
|
|
@@ -5199,12 +5477,12 @@ var YieldseekerAgent = class {
|
|
|
5199
5477
|
const pendingKey2 = `owney.yieldseeker.upgrade:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}`;
|
|
5200
5478
|
const confirmUpgrade = async (hash2, receipt2) => {
|
|
5201
5479
|
this.withdrawalVersionCache.delete(versionKey);
|
|
5202
|
-
const upgradeTopic = (0,
|
|
5480
|
+
const upgradeTopic = (0, import_viem14.encodeEventTopics)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded" })[0];
|
|
5203
5481
|
const lastUpgrade = receipt2?.logs.filter((log) => log.address.toLowerCase() === before.agentWallet.toLowerCase() && log.topics[0] === upgradeTopic).at(-1);
|
|
5204
5482
|
const expectedUpgrade = (() => {
|
|
5205
5483
|
if (!lastUpgrade || lastUpgrade.removed) return false;
|
|
5206
5484
|
try {
|
|
5207
|
-
const event = (0,
|
|
5485
|
+
const event = (0, import_viem14.decodeEventLog)({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded", data: lastUpgrade.data, topics: lastUpgrade.topics });
|
|
5208
5486
|
return event.args.implementation.toLowerCase() === YIELDSEEKER_V2_IMPLEMENTATION.toLowerCase();
|
|
5209
5487
|
} catch {
|
|
5210
5488
|
return false;
|
|
@@ -5342,7 +5620,7 @@ var YieldseekerAgent = class {
|
|
|
5342
5620
|
{ version: before.version },
|
|
5343
5621
|
this.id
|
|
5344
5622
|
);
|
|
5345
|
-
const data = (0,
|
|
5623
|
+
const data = (0, import_viem14.encodeFunctionData)({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
|
|
5346
5624
|
let txHash;
|
|
5347
5625
|
let receipt;
|
|
5348
5626
|
if (gasMode === "sponsored") {
|
|
@@ -5385,7 +5663,7 @@ var YieldseekerAgent = class {
|
|
|
5385
5663
|
let submittedHash;
|
|
5386
5664
|
try {
|
|
5387
5665
|
txHash = await this.submitTransaction(state, chainId, {
|
|
5388
|
-
from: (0,
|
|
5666
|
+
from: (0, import_viem14.getAddress)(state.walletAddress),
|
|
5389
5667
|
to: before.agentWallet,
|
|
5390
5668
|
data,
|
|
5391
5669
|
value: "0",
|
|
@@ -5433,6 +5711,7 @@ var YieldseekerAgent = class {
|
|
|
5433
5711
|
this.readCache.clear();
|
|
5434
5712
|
this.pendingReads.clear();
|
|
5435
5713
|
this.portfolioVersions.clear();
|
|
5714
|
+
this.liquidityVersions.clear();
|
|
5436
5715
|
this.snapshotFailures.clear();
|
|
5437
5716
|
this.standardReadFailures.clear();
|
|
5438
5717
|
this.reconcileUntil.clear();
|
|
@@ -5492,12 +5771,12 @@ var YieldseekerAgent = class {
|
|
|
5492
5771
|
} else {
|
|
5493
5772
|
try {
|
|
5494
5773
|
txHash = await this.submitTransaction(state, chainId, {
|
|
5495
|
-
from: (0,
|
|
5774
|
+
from: (0, import_viem14.getAddress)(state.walletAddress),
|
|
5496
5775
|
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
5497
|
-
data: (0,
|
|
5498
|
-
abi:
|
|
5776
|
+
data: (0, import_viem14.encodeFunctionData)({
|
|
5777
|
+
abi: import_viem14.erc20Abi,
|
|
5499
5778
|
functionName: "transfer",
|
|
5500
|
-
args: [(0,
|
|
5779
|
+
args: [(0, import_viem14.getAddress)(context.wallet.walletAddress), BigInt(amount)]
|
|
5501
5780
|
}),
|
|
5502
5781
|
value: "0",
|
|
5503
5782
|
chainId
|
|
@@ -5540,7 +5819,7 @@ var YieldseekerAgent = class {
|
|
|
5540
5819
|
notifyFunding(onProgress, evidence);
|
|
5541
5820
|
return { txHash, smartWallet: context.wallet.walletAddress, amount, evidence };
|
|
5542
5821
|
}
|
|
5543
|
-
async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto", onBeforeSubmission) {
|
|
5822
|
+
async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto", onBeforeSubmission, assertOperation) {
|
|
5544
5823
|
let ownerTransferAttempted = false;
|
|
5545
5824
|
let evidence = {
|
|
5546
5825
|
agentId: this.id,
|
|
@@ -5558,7 +5837,7 @@ var YieldseekerAgent = class {
|
|
|
5558
5837
|
notifyFunding(onProgress, evidence);
|
|
5559
5838
|
}, () => {
|
|
5560
5839
|
ownerTransferAttempted = true;
|
|
5561
|
-
}, gasMode, onBeforeSubmission);
|
|
5840
|
+
}, gasMode, onBeforeSubmission, assertOperation);
|
|
5562
5841
|
} catch (error) {
|
|
5563
5842
|
if (error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED") this.withdrawalVersionCache.clear();
|
|
5564
5843
|
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)))
|
|
@@ -5571,7 +5850,7 @@ var YieldseekerAgent = class {
|
|
|
5571
5850
|
throw error;
|
|
5572
5851
|
}
|
|
5573
5852
|
}
|
|
5574
|
-
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted, gasMode = "auto", onBeforeSubmission) {
|
|
5853
|
+
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted, gasMode = "auto", onBeforeSubmission, assertOperation) {
|
|
5575
5854
|
this.assertChain(chainId);
|
|
5576
5855
|
this.assertAsset(asset);
|
|
5577
5856
|
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
@@ -5643,7 +5922,7 @@ var YieldseekerAgent = class {
|
|
|
5643
5922
|
);
|
|
5644
5923
|
const positionsRead = this.readWithdrawalPositions(state, chainId, context);
|
|
5645
5924
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
5646
|
-
const agentWallet = (0,
|
|
5925
|
+
const agentWallet = (0, import_viem14.getAddress)(context.wallet.walletAddress);
|
|
5647
5926
|
const readOwnerFunds = async (blockNumber) => {
|
|
5648
5927
|
const feeTracker = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5649
5928
|
address: agentWallet,
|
|
@@ -5652,18 +5931,18 @@ var YieldseekerAgent = class {
|
|
|
5652
5931
|
blockNumber
|
|
5653
5932
|
}), chainId, "eth_call", this.id);
|
|
5654
5933
|
this.assertSession(generation, chainId, asset);
|
|
5655
|
-
if (!(0,
|
|
5934
|
+
if (!(0, import_viem14.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker))
|
|
5656
5935
|
throw this.invalidResponse("wallet fee tracker");
|
|
5657
5936
|
const [idle2, fees] = await Promise.all([
|
|
5658
5937
|
withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5659
5938
|
address: metadata.address,
|
|
5660
|
-
abi:
|
|
5939
|
+
abi: import_viem14.erc20Abi,
|
|
5661
5940
|
functionName: "balanceOf",
|
|
5662
5941
|
args: [agentWallet],
|
|
5663
5942
|
blockNumber
|
|
5664
5943
|
}), chainId, "eth_call", this.id),
|
|
5665
5944
|
withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5666
|
-
address: (0,
|
|
5945
|
+
address: (0, import_viem14.getAddress)(feeTracker),
|
|
5667
5946
|
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
5668
5947
|
functionName: "getFeesOwed",
|
|
5669
5948
|
args: [agentWallet],
|
|
@@ -5689,7 +5968,7 @@ var YieldseekerAgent = class {
|
|
|
5689
5968
|
const initialFunds = funds;
|
|
5690
5969
|
const idle = initialFunds.idle;
|
|
5691
5970
|
const maxLiquidity = amount === void 0 ? funds : void 0;
|
|
5692
|
-
let positions = [...funds.positions, ...funds.indexedPositions.filter((position2) => !funds.resolvedVaults.includes(position2.
|
|
5971
|
+
let positions = [...funds.positions, ...funds.indexedPositions.filter((position2) => !funds.resolvedVaults.includes(position2.vaultOption.address.toLowerCase()))];
|
|
5693
5972
|
let positionsStatus = "empty";
|
|
5694
5973
|
const validatePositions = (values) => this.validateWithdrawalPositions(values, chainId, asset);
|
|
5695
5974
|
validatePositions(positions);
|
|
@@ -5697,21 +5976,21 @@ var YieldseekerAgent = class {
|
|
|
5697
5976
|
const hasPositions = (values) => values.some((position2) => !zeroValuePositions.has(position2) && (BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n));
|
|
5698
5977
|
const knownPositions = /* @__PURE__ */ new Map();
|
|
5699
5978
|
const rememberPositions = (values) => {
|
|
5700
|
-
for (const position2 of values) knownPositions.set(position2.
|
|
5979
|
+
for (const position2 of values) knownPositions.set(position2.vaultOption.address.toLowerCase(), position2);
|
|
5701
5980
|
};
|
|
5702
5981
|
const verifyKnownPositions = async (blockNumber, values = [...knownPositions.values()], timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
|
|
5703
5982
|
const verifiedPositions = [];
|
|
5704
5983
|
if (timeoutMs <= 0) return { verified: false, positions: verifiedPositions };
|
|
5705
|
-
let
|
|
5706
|
-
const checks = this.readVaultShares(values.map((position2) => position2.
|
|
5984
|
+
let active2 = true;
|
|
5985
|
+
const checks = this.readVaultShares(values.map((position2) => position2.vaultOption), agentWallet, blockNumber).catch(() => []);
|
|
5707
5986
|
const reads = values.map(async (position2, index) => {
|
|
5708
|
-
const address = (0,
|
|
5987
|
+
const address = (0, import_viem14.getAddress)(position2.vaultOption.address);
|
|
5709
5988
|
try {
|
|
5710
5989
|
const group = (await checks).slice(index * 2, index * 2 + 2);
|
|
5711
|
-
if (!
|
|
5990
|
+
if (!active2 || group.length !== 2 || group.some((value) => value.status !== "success")) return null;
|
|
5712
5991
|
this.assertSession(generation, chainId, asset);
|
|
5713
5992
|
const [underlying, shares] = group.map((value) => value.result);
|
|
5714
|
-
if (typeof underlying !== "string" || !(0,
|
|
5993
|
+
if (typeof underlying !== "string" || !(0, import_viem14.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n) return null;
|
|
5715
5994
|
const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: "0" };
|
|
5716
5995
|
if (shares > 0n) {
|
|
5717
5996
|
let positiveValue = false;
|
|
@@ -5719,12 +5998,12 @@ var YieldseekerAgent = class {
|
|
|
5719
5998
|
try {
|
|
5720
5999
|
const value = await this.getReceiptClient().readContract({
|
|
5721
6000
|
address,
|
|
5722
|
-
abi:
|
|
6001
|
+
abi: import_viem14.erc4626Abi,
|
|
5723
6002
|
functionName,
|
|
5724
6003
|
args: [shares],
|
|
5725
6004
|
blockNumber
|
|
5726
6005
|
});
|
|
5727
|
-
if (!
|
|
6006
|
+
if (!active2 || typeof value !== "bigint" || value < 0n) return null;
|
|
5728
6007
|
this.assertSession(generation, chainId, asset);
|
|
5729
6008
|
if (value > 0n && !positiveValue) {
|
|
5730
6009
|
positiveValue = true;
|
|
@@ -5752,7 +6031,7 @@ var YieldseekerAgent = class {
|
|
|
5752
6031
|
}
|
|
5753
6032
|
});
|
|
5754
6033
|
const result = await boundedVerification(Promise.all(reads), timeoutMs);
|
|
5755
|
-
|
|
6034
|
+
active2 = false;
|
|
5756
6035
|
this.assertSession(generation, chainId, asset);
|
|
5757
6036
|
return {
|
|
5758
6037
|
verified: result !== void 0 && result.every((position2) => position2 !== null),
|
|
@@ -5764,13 +6043,14 @@ var YieldseekerAgent = class {
|
|
|
5764
6043
|
const response = await this.walletRequest(
|
|
5765
6044
|
state,
|
|
5766
6045
|
chainId,
|
|
5767
|
-
this.agentPath(context, "
|
|
6046
|
+
this.agentPath(context, "vault-positions")
|
|
5768
6047
|
);
|
|
5769
6048
|
this.assertSession(generation, chainId, asset);
|
|
5770
|
-
if (!Array.isArray(response?.
|
|
5771
|
-
return validatePositions(response.
|
|
6049
|
+
if (!Array.isArray(response?.vaultPositions)) throw this.invalidResponse("vault positions");
|
|
6050
|
+
return validatePositions(response.vaultPositions);
|
|
5772
6051
|
};
|
|
5773
6052
|
if (amount === void 0) {
|
|
6053
|
+
rememberPositions(priorPositions);
|
|
5774
6054
|
rememberPositions(funds.indexedPositions);
|
|
5775
6055
|
rememberPositions(positions);
|
|
5776
6056
|
positionsStatus = hasPositions(positions) ? "remaining" : "empty";
|
|
@@ -5797,7 +6077,7 @@ var YieldseekerAgent = class {
|
|
|
5797
6077
|
throw new OwneyError(
|
|
5798
6078
|
"WITHDRAW_FAILED",
|
|
5799
6079
|
"Yieldseeker vault funds are not currently withdrawable.",
|
|
5800
|
-
{ asset, positionsStatus, remainingPositions: positions.map((position2) => position2.
|
|
6080
|
+
{ asset, positionsStatus, remainingPositions: positions.map((position2) => position2.vaultOption.address) },
|
|
5801
6081
|
this.id
|
|
5802
6082
|
);
|
|
5803
6083
|
}
|
|
@@ -5846,6 +6126,7 @@ var YieldseekerAgent = class {
|
|
|
5846
6126
|
let latestUnwindBlock;
|
|
5847
6127
|
const unwind = async (position2, assetsRaw) => {
|
|
5848
6128
|
this.assertSession(generation, chainId, asset);
|
|
6129
|
+
assertOperation?.();
|
|
5849
6130
|
let unwindTransactionHash;
|
|
5850
6131
|
try {
|
|
5851
6132
|
const response = await this.walletRequest(
|
|
@@ -5854,7 +6135,8 @@ var YieldseekerAgent = class {
|
|
|
5854
6135
|
this.agentPath(context, "withdraw-from-position"),
|
|
5855
6136
|
{
|
|
5856
6137
|
method: "POST",
|
|
5857
|
-
body: { chainId, vaultAddress: position2.
|
|
6138
|
+
body: { chainId, vaultAddress: position2.vaultOption.address, assetsRaw: assetsRaw.toString() },
|
|
6139
|
+
assertCurrent: assertOperation
|
|
5858
6140
|
}
|
|
5859
6141
|
);
|
|
5860
6142
|
if (!this.isTransactionHash(response?.transactionHash)) throw this.invalidResponse("position withdrawal");
|
|
@@ -5873,7 +6155,7 @@ var YieldseekerAgent = class {
|
|
|
5873
6155
|
...error instanceof OwneyError ? error.details : {},
|
|
5874
6156
|
withdrawalPhase: "unwind",
|
|
5875
6157
|
reason: "YIELDSEEKER_UNWIND_UNRESOLVED",
|
|
5876
|
-
vaultAddress: position2.
|
|
6158
|
+
vaultAddress: position2.vaultOption.address,
|
|
5877
6159
|
unwindTransactionHash,
|
|
5878
6160
|
ownerTransferSubmitted: false
|
|
5879
6161
|
},
|
|
@@ -5886,7 +6168,7 @@ var YieldseekerAgent = class {
|
|
|
5886
6168
|
};
|
|
5887
6169
|
if (amount === void 0) {
|
|
5888
6170
|
for (let sweep = 0; sweep < YIELDSEEKER_MAX_WITHDRAW_SWEEPS && hasPositions(positions); sweep++) {
|
|
5889
|
-
const before = new Map(positions.map((position2) => [position2.
|
|
6171
|
+
const before = new Map(positions.map((position2) => [position2.vaultOption.address.toLowerCase(), BigInt(position2.withdrawableAssetsRaw)]));
|
|
5890
6172
|
let unwound = false;
|
|
5891
6173
|
for (const position2 of positions) {
|
|
5892
6174
|
const available = BigInt(position2.withdrawableAssetsRaw);
|
|
@@ -5903,16 +6185,16 @@ var YieldseekerAgent = class {
|
|
|
5903
6185
|
if (catalog) rememberPositions(catalog);
|
|
5904
6186
|
this.assertSession(generation, chainId, asset);
|
|
5905
6187
|
if (proof.verified) {
|
|
5906
|
-
positions = [...proof.positions, ...(catalog ?? []).filter((position2) => !knownBeforeRefresh.has(position2.
|
|
6188
|
+
positions = [...proof.positions, ...(catalog ?? []).filter((position2) => !knownBeforeRefresh.has(position2.vaultOption.address.toLowerCase()))];
|
|
5907
6189
|
} else if (catalog) {
|
|
5908
|
-
const checked = new Set(proof.positions.map((position2) => position2.
|
|
5909
|
-
positions = [...proof.positions, ...catalog.filter((position2) => !checked.has(position2.
|
|
6190
|
+
const checked = new Set(proof.positions.map((position2) => position2.vaultOption.address.toLowerCase()));
|
|
6191
|
+
positions = [...proof.positions, ...catalog.filter((position2) => !checked.has(position2.vaultOption.address.toLowerCase()))];
|
|
5910
6192
|
} else {
|
|
5911
6193
|
positionsStatus = "unavailable";
|
|
5912
6194
|
break;
|
|
5913
6195
|
}
|
|
5914
6196
|
positionsStatus = hasPositions(positions) ? "remaining" : "empty";
|
|
5915
|
-
if (positions.some((position2) => BigInt(position2.withdrawableAssetsRaw) > 0n && before.has(position2.
|
|
6197
|
+
if (positions.some((position2) => BigInt(position2.withdrawableAssetsRaw) > 0n && before.has(position2.vaultOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) >= before.get(position2.vaultOption.address.toLowerCase()))) break;
|
|
5916
6198
|
}
|
|
5917
6199
|
} else {
|
|
5918
6200
|
for (const position2 of positions) {
|
|
@@ -5925,7 +6207,7 @@ var YieldseekerAgent = class {
|
|
|
5925
6207
|
}
|
|
5926
6208
|
}
|
|
5927
6209
|
if (amount !== void 0 && remaining > 0n) {
|
|
5928
|
-
throw this.invalidResponse("
|
|
6210
|
+
throw this.invalidResponse("vault positions", {
|
|
5929
6211
|
reason: "Withdrawable positions could not cover the request.",
|
|
5930
6212
|
remaining: remaining.toString()
|
|
5931
6213
|
});
|
|
@@ -5957,15 +6239,22 @@ var YieldseekerAgent = class {
|
|
|
5957
6239
|
delta: maxSettlementBasis.delta - payout,
|
|
5958
6240
|
expectedNetDeposits: maxSettlementBasis.expectedNetDeposits - payout
|
|
5959
6241
|
} : this.snapshotMovement(state, chainId, context, -payout);
|
|
5960
|
-
const account = (0,
|
|
6242
|
+
const account = (0, import_viem14.getAddress)(state.walletAddress);
|
|
5961
6243
|
let receipt;
|
|
5962
6244
|
const base7 = { agentId: this.id, chainId, token: asset, walletAddress: state.walletAddress, agentWallet: context.wallet.walletAddress, intendedAmount: amount ?? "MAX", transfer: "pending", registration: "not-applicable", delivery: "pending" };
|
|
6245
|
+
if (amount === void 0) base7.maxCompletion = {
|
|
6246
|
+
userId: context.user.userId,
|
|
6247
|
+
agentId: context.agent.agentId,
|
|
6248
|
+
vaults: [...knownPositions.keys()],
|
|
6249
|
+
inventoryComplete: funds.complete
|
|
6250
|
+
};
|
|
6251
|
+
if (base7.maxCompletion) notifyFunding(onProgress, base7);
|
|
5963
6252
|
let submissionEvidence = base7;
|
|
5964
|
-
const data = amount === void 0 ? (0,
|
|
6253
|
+
const data = amount === void 0 ? (0, import_viem14.encodeFunctionData)({
|
|
5965
6254
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5966
6255
|
functionName: "withdrawAllAssetToUser",
|
|
5967
6256
|
args: [account, metadata.address]
|
|
5968
|
-
}) : (0,
|
|
6257
|
+
}) : (0, import_viem14.encodeFunctionData)({
|
|
5969
6258
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
5970
6259
|
functionName: "withdrawAssetToUser",
|
|
5971
6260
|
args: [account, metadata.address, payout]
|
|
@@ -5977,11 +6266,12 @@ var YieldseekerAgent = class {
|
|
|
5977
6266
|
client: this.getReceiptClient(),
|
|
5978
6267
|
apiKey: this.owneyApiKey,
|
|
5979
6268
|
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
6269
|
+
assertCurrent: assertOperation,
|
|
5980
6270
|
yieldseekerSignature: await this.auth.getToken(state, chainId),
|
|
5981
6271
|
refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
|
|
5982
6272
|
userId: context.user.userId,
|
|
5983
6273
|
agentId: context.agent.agentId,
|
|
5984
|
-
agentWallet: (0,
|
|
6274
|
+
agentWallet: (0, import_viem14.getAddress)(context.wallet.walletAddress),
|
|
5985
6275
|
asset: metadata.address,
|
|
5986
6276
|
callData: data,
|
|
5987
6277
|
onPending: (pending) => {
|
|
@@ -5989,6 +6279,7 @@ var YieldseekerAgent = class {
|
|
|
5989
6279
|
notifyFunding(onProgress, submissionEvidence);
|
|
5990
6280
|
},
|
|
5991
6281
|
onBeforeSubmission: async (pending) => {
|
|
6282
|
+
assertOperation?.();
|
|
5992
6283
|
await onBeforeSubmission?.({ ...base7, ...pending });
|
|
5993
6284
|
onOwnerTransferAttempted?.();
|
|
5994
6285
|
}
|
|
@@ -5997,15 +6288,26 @@ var YieldseekerAgent = class {
|
|
|
5997
6288
|
receipt = submitted.receipt;
|
|
5998
6289
|
} else {
|
|
5999
6290
|
onOwnerTransferAttempted?.();
|
|
6000
|
-
txHash = await this.submitTransaction(
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6291
|
+
txHash = await this.submitTransaction(
|
|
6292
|
+
state,
|
|
6293
|
+
chainId,
|
|
6294
|
+
{
|
|
6295
|
+
from: account,
|
|
6296
|
+
to: (0, import_viem14.getAddress)(context.wallet.walletAddress),
|
|
6297
|
+
data,
|
|
6298
|
+
value: "0",
|
|
6299
|
+
chainId
|
|
6300
|
+
},
|
|
6301
|
+
(hash2) => notifyFunding(onProgress, { ...base7, txHash: hash2 }),
|
|
6302
|
+
(value) => {
|
|
6303
|
+
receipt = value;
|
|
6304
|
+
},
|
|
6305
|
+
async () => {
|
|
6306
|
+
assertOperation?.();
|
|
6307
|
+
await onBeforeSubmission?.(base7);
|
|
6308
|
+
assertOperation?.();
|
|
6309
|
+
}
|
|
6310
|
+
);
|
|
6009
6311
|
}
|
|
6010
6312
|
const evidence = receipt ? inspectFundingReceipt(receipt, { ...submissionEvidence, txHash }, metadata.address, "withdraw") : { ...submissionEvidence, txHash, transfer: "pending", delivery: "pending" };
|
|
6011
6313
|
notifyFunding(onProgress, evidence);
|
|
@@ -6050,7 +6352,7 @@ var YieldseekerAgent = class {
|
|
|
6050
6352
|
asset,
|
|
6051
6353
|
positionsStatus,
|
|
6052
6354
|
remainingPositions: positionsStatus === "remaining" ? positions.filter((position2) => !zeroValuePositions.has(position2) && (BigInt(position2.assetsRaw) > 0n || BigInt(position2.withdrawableAssetsRaw) > 0n)).map((position2) => ({
|
|
6053
|
-
vaultAddress: position2.
|
|
6355
|
+
vaultAddress: position2.vaultOption.address,
|
|
6054
6356
|
sharesRaw: position2.assetsRaw,
|
|
6055
6357
|
withdrawableAmountRaw: position2.withdrawableAssetsRaw
|
|
6056
6358
|
})) : void 0,
|
|
@@ -6059,6 +6361,7 @@ var YieldseekerAgent = class {
|
|
|
6059
6361
|
this.id
|
|
6060
6362
|
);
|
|
6061
6363
|
}
|
|
6364
|
+
if (evidence.maxCompletion) evidence.maxCompletion.vaults = [...knownPositions.keys()];
|
|
6062
6365
|
if (!proof.verified) completion = "unverified";
|
|
6063
6366
|
}
|
|
6064
6367
|
if (explicitEmptyPayout)
|
|
@@ -6116,17 +6419,76 @@ var YieldseekerAgent = class {
|
|
|
6116
6419
|
);
|
|
6117
6420
|
return proof.available;
|
|
6118
6421
|
}
|
|
6422
|
+
/** Recheck MAX without re-unwinding, paying out, or requesting a signature. */
|
|
6423
|
+
async verifyWithdrawalCompletion(state, evidence) {
|
|
6424
|
+
const unknown = (reason2) => ({ status: "unavailable", reason: reason2 });
|
|
6425
|
+
const proof = evidence.maxCompletion;
|
|
6426
|
+
if (evidence.agentId !== this.id || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || evidence.chainId !== 8453 || !proof || !evidence.agentWallet || !(0, import_viem14.isAddress)(evidence.agentWallet) || !evidence.blockNumber || !/^\d+$/.test(evidence.blockNumber) || evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || !this.isTransactionHash(evidence.txHash)) return unknown("PAYOUT_PROOF_UNAVAILABLE");
|
|
6427
|
+
this.assertAsset(evidence.token);
|
|
6428
|
+
const asset = evidence.token;
|
|
6429
|
+
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
6430
|
+
const signature = this.auth.peekToken(state, evidence.chainId);
|
|
6431
|
+
if (!signature) return unknown("AUTHENTICATION_REQUIRED");
|
|
6432
|
+
let reason = "CATALOG_UNAVAILABLE";
|
|
6433
|
+
try {
|
|
6434
|
+
return await bounded(async () => {
|
|
6435
|
+
const response = await this.api.request(
|
|
6436
|
+
`/users/${encodeURIComponent(proof.userId)}/agents/${encodeURIComponent(proof.agentId)}/vault-positions`,
|
|
6437
|
+
{ signature }
|
|
6438
|
+
);
|
|
6439
|
+
if (!Array.isArray(response?.vaultPositions)) return unknown("CATALOG_INVALID");
|
|
6440
|
+
const positions = this.validateWithdrawalPositions(response.vaultPositions, evidence.chainId, asset);
|
|
6441
|
+
const vaults = new Set([...proof.vaults, ...positions.map((p) => p.vaultOption.address)].map((a) => a.toLowerCase()));
|
|
6442
|
+
if (!proof.inventoryComplete) {
|
|
6443
|
+
const catalog = await this.loadVaultOptions(asset);
|
|
6444
|
+
const eligible = catalog.filter((o) => o.chainId === evidence.chainId && o.assetAddress?.toLowerCase() === metadata.address.toLowerCase() && (0, import_viem14.isAddress)(o.address));
|
|
6445
|
+
if (!eligible.length) return unknown("INVENTORY_INCOMPLETE");
|
|
6446
|
+
eligible.forEach((o) => vaults.add(o.address.toLowerCase()));
|
|
6447
|
+
}
|
|
6448
|
+
if (vaults.size > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT || [...vaults].some((a) => !(0, import_viem14.isAddress)(a)))
|
|
6449
|
+
return unknown("INVENTORY_INVALID");
|
|
6450
|
+
reason = "VAULT_PROOF_UNAVAILABLE";
|
|
6451
|
+
const blockNumber = BigInt(evidence.blockNumber);
|
|
6452
|
+
const addresses = [...vaults];
|
|
6453
|
+
const shares = await this.readVaultShares(addresses.map((address) => ({ address })), evidence.agentWallet, blockNumber);
|
|
6454
|
+
const checks = await Promise.all(addresses.map(async (address, i) => {
|
|
6455
|
+
const group = shares.slice(i * 2, i * 2 + 2);
|
|
6456
|
+
if (group.length !== 2 || group.some((r) => r.status !== "success")) return "unavailable";
|
|
6457
|
+
const [underlying, value] = group.map((r) => r.result);
|
|
6458
|
+
if (typeof underlying !== "string" || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof value !== "bigint" || value < 0n) return "unavailable";
|
|
6459
|
+
if (value === 0n) return "complete";
|
|
6460
|
+
const amounts = await Promise.allSettled(["convertToAssets", "previewRedeem"].map((functionName) => this.getReceiptClient().readContract({
|
|
6461
|
+
address: (0, import_viem14.getAddress)(address),
|
|
6462
|
+
abi: import_viem14.erc4626Abi,
|
|
6463
|
+
functionName,
|
|
6464
|
+
args: [value],
|
|
6465
|
+
blockNumber
|
|
6466
|
+
})));
|
|
6467
|
+
if (amounts.some((r) => r.status === "fulfilled" && typeof r.value === "bigint" && r.value > 0n)) return "remaining";
|
|
6468
|
+
return amounts.every((r) => r.status === "fulfilled" && r.value === 0n) ? "complete" : "unavailable";
|
|
6469
|
+
}));
|
|
6470
|
+
const status = checks.includes("remaining") ? "remaining" : checks.includes("unavailable") ? "unavailable" : "complete";
|
|
6471
|
+
return {
|
|
6472
|
+
status,
|
|
6473
|
+
checkedVaults: addresses.length,
|
|
6474
|
+
...status === "unavailable" ? { reason } : status === "remaining" ? { reason: "VAULT_REMAINDER" } : {}
|
|
6475
|
+
};
|
|
6476
|
+
}, 8e3, () => new Error("MAX proof read timed out"));
|
|
6477
|
+
} catch {
|
|
6478
|
+
return unknown(reason);
|
|
6479
|
+
}
|
|
6480
|
+
}
|
|
6119
6481
|
async readVaultShares(options, wallet, blockNumber) {
|
|
6120
|
-
const owner = (0,
|
|
6482
|
+
const owner = (0, import_viem14.getAddress)(wallet);
|
|
6121
6483
|
const batches = Array.from({ length: Math.ceil(options.length / YIELDSEEKER_VAULTS_PER_RPC_BATCH) }, (_, index) => options.slice(index * YIELDSEEKER_VAULTS_PER_RPC_BATCH, (index + 1) * YIELDSEEKER_VAULTS_PER_RPC_BATCH));
|
|
6122
6484
|
const results = await Promise.all(batches.map((batch) => this.getReceiptClient().multicall({
|
|
6123
6485
|
blockNumber,
|
|
6124
6486
|
allowFailure: true,
|
|
6125
6487
|
contracts: batch.flatMap((option) => {
|
|
6126
|
-
const address = (0,
|
|
6488
|
+
const address = (0, import_viem14.getAddress)(option.address);
|
|
6127
6489
|
return [
|
|
6128
|
-
{ address, abi:
|
|
6129
|
-
{ address, abi:
|
|
6490
|
+
{ address, abi: import_viem14.erc4626Abi, functionName: "asset" },
|
|
6491
|
+
{ address, abi: import_viem14.erc20Abi, functionName: "balanceOf", args: [owner] }
|
|
6130
6492
|
];
|
|
6131
6493
|
})
|
|
6132
6494
|
})));
|
|
@@ -6136,23 +6498,23 @@ var YieldseekerAgent = class {
|
|
|
6136
6498
|
const underlying = YIELDSEEKER_ASSET_METADATA[asset].address.toLowerCase();
|
|
6137
6499
|
const seen = /* @__PURE__ */ new Set();
|
|
6138
6500
|
for (const value of values) {
|
|
6139
|
-
const option = value?.
|
|
6140
|
-
if (!option || typeof option.address !== "string" || !(0,
|
|
6141
|
-
throw this.invalidResponse("
|
|
6501
|
+
const option = value?.vaultOption;
|
|
6502
|
+
if (!option || typeof option.address !== "string" || !(0, import_viem14.isAddress)(option.address) || seen.has(option.address.toLowerCase()) || option.chainId !== void 0 && option.chainId !== chainId || option.assetAddress !== void 0 && (typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== underlying) || typeof value.assetsRaw !== "string" || !/^\d+$/.test(value.assetsRaw) || typeof value.withdrawableAssetsRaw !== "string" || !/^\d+$/.test(value.withdrawableAssetsRaw))
|
|
6503
|
+
throw this.invalidResponse("vault positions");
|
|
6142
6504
|
seen.add(option.address.toLowerCase());
|
|
6143
6505
|
}
|
|
6144
6506
|
return values;
|
|
6145
6507
|
}
|
|
6146
6508
|
withdrawalVaultHints(state, chainId, context) {
|
|
6147
6509
|
const key2 = this.contextKey(state, chainId, context.asset);
|
|
6148
|
-
const positions = this.readCache.get(`positions:${key2}`)?.value?.
|
|
6149
|
-
const hints = new Map(this.validateWithdrawalPositions(positions, chainId, context.asset).map((position2) => [position2.
|
|
6510
|
+
const positions = this.readCache.get(`positions:${key2}`)?.value?.vaultPositions ?? [];
|
|
6511
|
+
const hints = new Map(this.validateWithdrawalPositions(positions, chainId, context.asset).map((position2) => [position2.vaultOption.address.toLowerCase(), position2]));
|
|
6150
6512
|
const historic = this.readCache.get(`historic:${key2}`)?.value?.position;
|
|
6151
6513
|
const receivedAssets = new Set((historic?.movements ?? []).filter((movement) => movement.chainId === chainId && movement.agentWalletId === context.wallet.agentWalletId && movement.toAddress?.toLowerCase() === context.wallet.walletAddress.toLowerCase()).filter((movement) => typeof movement.assetAddress === "string").map((movement) => movement.assetAddress.toLowerCase()));
|
|
6152
|
-
for (const option of this.
|
|
6153
|
-
if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !(0,
|
|
6514
|
+
for (const option of this.vaultOptions.get(context.asset)?.value ?? []) {
|
|
6515
|
+
if (option.chainId !== chainId || typeof option.assetAddress !== "string" || option.assetAddress.toLowerCase() !== YIELDSEEKER_ASSET_METADATA[context.asset].address.toLowerCase() || !(0, import_viem14.isAddress)(option.address) || !receivedAssets.has(option.address.toLowerCase()) || hints.has(option.address.toLowerCase())) continue;
|
|
6154
6516
|
hints.set(option.address.toLowerCase(), {
|
|
6155
|
-
|
|
6517
|
+
vaultOption: option,
|
|
6156
6518
|
assetsRaw: "0",
|
|
6157
6519
|
assetsBase: "0",
|
|
6158
6520
|
assetsUsd: 0,
|
|
@@ -6186,7 +6548,7 @@ var YieldseekerAgent = class {
|
|
|
6186
6548
|
async resolveWithdrawalLiquidity(state, chainId, context, positions, previousPositions = [], requested) {
|
|
6187
6549
|
const generation = this.readGeneration;
|
|
6188
6550
|
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
6189
|
-
const version = this.
|
|
6551
|
+
const version = this.liquidityVersions.get(contextKey) ?? 0;
|
|
6190
6552
|
const metadata = YIELDSEEKER_ASSET_METADATA[context.asset];
|
|
6191
6553
|
const unavailable = () => new OwneyError(
|
|
6192
6554
|
"WITHDRAW_BALANCE_UNAVAILABLE",
|
|
@@ -6194,30 +6556,30 @@ var YieldseekerAgent = class {
|
|
|
6194
6556
|
{ asset: context.asset, reason: "YIELDSEEKER_LIQUIDITY_UNVERIFIED" },
|
|
6195
6557
|
this.id
|
|
6196
6558
|
);
|
|
6197
|
-
let
|
|
6559
|
+
let active2 = true;
|
|
6198
6560
|
let knownProof;
|
|
6199
|
-
const hasUnverifiedIndexedFunds = (proof) => proof.indexedPositions.some((position2) => !proof.resolvedVaults.includes(position2.
|
|
6561
|
+
const hasUnverifiedIndexedFunds = (proof) => proof.indexedPositions.some((position2) => !proof.resolvedVaults.includes(position2.vaultOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) > 0n);
|
|
6200
6562
|
const assertReadCurrent = () => {
|
|
6201
6563
|
this.assertSession(generation, chainId, context.asset);
|
|
6202
|
-
if (!
|
|
6564
|
+
if (!active2 || (this.liquidityVersions.get(contextKey) ?? 0) !== version) throw unavailable();
|
|
6203
6565
|
};
|
|
6204
6566
|
const read2 = async () => {
|
|
6205
6567
|
const client = this.getReceiptClient();
|
|
6206
6568
|
const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
|
|
6207
|
-
const wallet = (0,
|
|
6569
|
+
const wallet = (0, import_viem14.getAddress)(context.wallet.walletAddress);
|
|
6208
6570
|
assertReadCurrent();
|
|
6209
6571
|
if (typeof blockNumber !== "bigint") throw unavailable();
|
|
6210
6572
|
const indexedPositions = this.validateWithdrawalPositions(positions ?? [], chainId, context.asset);
|
|
6211
6573
|
const candidates = /* @__PURE__ */ new Map();
|
|
6212
6574
|
for (const position2 of this.validateWithdrawalPositions([...previousPositions], chainId, context.asset))
|
|
6213
|
-
candidates.set(position2.
|
|
6214
|
-
for (const position2 of indexedPositions) candidates.set(position2.
|
|
6575
|
+
candidates.set(position2.vaultOption.address.toLowerCase(), position2.vaultOption);
|
|
6576
|
+
for (const position2 of indexedPositions) candidates.set(position2.vaultOption.address.toLowerCase(), position2.vaultOption);
|
|
6215
6577
|
const options = [...candidates.values()];
|
|
6216
6578
|
if (options.length > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT) throw unavailable();
|
|
6217
6579
|
const [idle, feeTracker] = await Promise.all([
|
|
6218
6580
|
client.readContract({
|
|
6219
6581
|
address: metadata.address,
|
|
6220
|
-
abi:
|
|
6582
|
+
abi: import_viem14.erc20Abi,
|
|
6221
6583
|
functionName: "balanceOf",
|
|
6222
6584
|
args: [wallet],
|
|
6223
6585
|
blockNumber
|
|
@@ -6229,10 +6591,10 @@ var YieldseekerAgent = class {
|
|
|
6229
6591
|
blockNumber
|
|
6230
6592
|
})
|
|
6231
6593
|
]);
|
|
6232
|
-
if (typeof idle !== "bigint" || idle < 0n || !(0,
|
|
6594
|
+
if (typeof idle !== "bigint" || idle < 0n || !(0, import_viem14.isAddress)(feeTracker) || /^0x0{40}$/i.test(feeTracker)) throw unavailable();
|
|
6233
6595
|
assertReadCurrent();
|
|
6234
6596
|
const fees = await client.readContract({
|
|
6235
|
-
address: (0,
|
|
6597
|
+
address: (0, import_viem14.getAddress)(feeTracker),
|
|
6236
6598
|
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
6237
6599
|
functionName: "getFeesOwed",
|
|
6238
6600
|
args: [wallet],
|
|
@@ -6255,15 +6617,15 @@ var YieldseekerAgent = class {
|
|
|
6255
6617
|
assertReadCurrent();
|
|
6256
6618
|
knownProof = proof(false);
|
|
6257
6619
|
if (requested !== void 0 && knownProof.available >= requested) return knownProof;
|
|
6258
|
-
const indexed = new Map(indexedPositions.map((position2) => [position2.
|
|
6620
|
+
const indexed = new Map(indexedPositions.map((position2) => [position2.vaultOption.address.toLowerCase(), position2]));
|
|
6259
6621
|
const indexedValue = indexedPositions.reduce((sum, position2) => sum + BigInt(position2.withdrawableAssetsRaw), 0n);
|
|
6260
|
-
const missingKnownPosition = previousPositions.some((position2) => !indexed.has(position2.
|
|
6622
|
+
const missingKnownPosition = previousPositions.some((position2) => !indexed.has(position2.vaultOption.address.toLowerCase()));
|
|
6261
6623
|
const unvaluedPosition = indexedPositions.some((position2) => BigInt(position2.assetsRaw) > 0n && BigInt(position2.withdrawableAssetsRaw) === 0n);
|
|
6262
6624
|
const indexedAvailable = idle + indexedValue > fees ? idle + indexedValue - fees : 0n;
|
|
6263
6625
|
if (indexedValue > 0n && !missingKnownPosition && !unvaluedPosition && (requested === void 0 || indexedAvailable >= requested)) {
|
|
6264
6626
|
const known = this.balanceVaults.get(contextKey) ?? /* @__PURE__ */ new Set();
|
|
6265
6627
|
for (const position2 of indexedPositions) if (BigInt(position2.withdrawableAssetsRaw) > 0n)
|
|
6266
|
-
known.add(position2.
|
|
6628
|
+
known.add(position2.vaultOption.address.toLowerCase());
|
|
6267
6629
|
this.balanceVaults.set(contextKey, known);
|
|
6268
6630
|
return {
|
|
6269
6631
|
blockNumber,
|
|
@@ -6284,10 +6646,10 @@ var YieldseekerAgent = class {
|
|
|
6284
6646
|
const group = results.slice(index * 2, index * 2 + 2);
|
|
6285
6647
|
if (group.length !== 2 || group.some((value2) => value2.status !== "success")) return false;
|
|
6286
6648
|
const [underlying, shares] = group.map((value2) => value2.result);
|
|
6287
|
-
if (typeof underlying !== "string" || !(0,
|
|
6649
|
+
if (typeof underlying !== "string" || !(0, import_viem14.isAddress)(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n) return false;
|
|
6288
6650
|
if (shares > 0n) known.add(option.address.toLowerCase());
|
|
6289
6651
|
const position2 = {
|
|
6290
|
-
|
|
6652
|
+
vaultOption: option,
|
|
6291
6653
|
assetsRaw: shares.toString(),
|
|
6292
6654
|
assetsBase: "0",
|
|
6293
6655
|
assetsUsd: 0,
|
|
@@ -6301,8 +6663,8 @@ var YieldseekerAgent = class {
|
|
|
6301
6663
|
let preview;
|
|
6302
6664
|
try {
|
|
6303
6665
|
const value2 = await client.readContract({
|
|
6304
|
-
address: (0,
|
|
6305
|
-
abi:
|
|
6666
|
+
address: (0, import_viem14.getAddress)(option.address),
|
|
6667
|
+
abi: import_viem14.erc4626Abi,
|
|
6306
6668
|
functionName: "previewRedeem",
|
|
6307
6669
|
args: [shares],
|
|
6308
6670
|
blockNumber
|
|
@@ -6315,8 +6677,8 @@ var YieldseekerAgent = class {
|
|
|
6315
6677
|
if (value === void 0 || value === 0n) {
|
|
6316
6678
|
try {
|
|
6317
6679
|
const converted = await client.readContract({
|
|
6318
|
-
address: (0,
|
|
6319
|
-
abi:
|
|
6680
|
+
address: (0, import_viem14.getAddress)(option.address),
|
|
6681
|
+
abi: import_viem14.erc4626Abi,
|
|
6320
6682
|
functionName: "convertToAssets",
|
|
6321
6683
|
args: [shares],
|
|
6322
6684
|
blockNumber
|
|
@@ -6343,7 +6705,7 @@ var YieldseekerAgent = class {
|
|
|
6343
6705
|
knownProof = proof(false);
|
|
6344
6706
|
if (requested !== void 0 && knownProof.available >= requested) return knownProof;
|
|
6345
6707
|
const discrepancy = verified.some((position2) => {
|
|
6346
|
-
const row = indexed.get(position2.
|
|
6708
|
+
const row = indexed.get(position2.vaultOption.address.toLowerCase());
|
|
6347
6709
|
return row ? BigInt(position2.withdrawableAssetsRaw) < BigInt(row.withdrawableAssetsRaw) : BigInt(position2.assetsRaw) > 0n;
|
|
6348
6710
|
});
|
|
6349
6711
|
const typedCoveredByIdle = requested !== void 0 && idle >= requested + fees;
|
|
@@ -6352,11 +6714,11 @@ var YieldseekerAgent = class {
|
|
|
6352
6714
|
assertReadCurrent();
|
|
6353
6715
|
return proof(true);
|
|
6354
6716
|
}
|
|
6355
|
-
const catalog = await this.
|
|
6717
|
+
const catalog = await this.loadVaultOptions(context.asset).catch(() => void 0);
|
|
6356
6718
|
assertReadCurrent();
|
|
6357
6719
|
const eligible = /* @__PURE__ */ new Map();
|
|
6358
6720
|
for (const option of catalog ?? []) {
|
|
6359
|
-
if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() && (0,
|
|
6721
|
+
if (option?.chainId === chainId && typeof option.assetAddress === "string" && option.assetAddress.toLowerCase() === metadata.address.toLowerCase() && (0, import_viem14.isAddress)(option.address))
|
|
6360
6722
|
eligible.set(option.address.toLowerCase(), option);
|
|
6361
6723
|
}
|
|
6362
6724
|
const extra = [...eligible].filter(([address]) => !candidates.has(address)).map(([, option]) => option);
|
|
@@ -6378,7 +6740,7 @@ var YieldseekerAgent = class {
|
|
|
6378
6740
|
if (error instanceof OwneyError) throw error;
|
|
6379
6741
|
throw unavailable();
|
|
6380
6742
|
} finally {
|
|
6381
|
-
|
|
6743
|
+
active2 = false;
|
|
6382
6744
|
if (timer) clearTimeout(timer);
|
|
6383
6745
|
}
|
|
6384
6746
|
}
|
|
@@ -6392,7 +6754,7 @@ var YieldseekerAgent = class {
|
|
|
6392
6754
|
this.balanceContexts.set(read2, { context, generation: this.readGeneration });
|
|
6393
6755
|
const key2 = this.contextKey(state, chainId, context.asset);
|
|
6394
6756
|
const known = this.balanceVaults.get(key2) ?? /* @__PURE__ */ new Set();
|
|
6395
|
-
for (const position2 of context.positions) known.add(position2.
|
|
6757
|
+
for (const position2 of context.positions) known.add(position2.vaultOption.address.toLowerCase());
|
|
6396
6758
|
this.balanceVaults.set(key2, known);
|
|
6397
6759
|
}
|
|
6398
6760
|
return balance;
|
|
@@ -6407,7 +6769,7 @@ var YieldseekerAgent = class {
|
|
|
6407
6769
|
const key2 = this.contextKey(state, evidence.chainId, evidence.token);
|
|
6408
6770
|
const generation = this.readGeneration, version = this.portfolioVersion(key2);
|
|
6409
6771
|
if (read2.readVersion !== version) return void 0;
|
|
6410
|
-
const wallet = (0,
|
|
6772
|
+
const wallet = (0, import_viem14.getAddress)(context.wallet.walletAddress);
|
|
6411
6773
|
const asset = YIELDSEEKER_ASSET_METADATA[evidence.token];
|
|
6412
6774
|
const client = this.getReceiptClient();
|
|
6413
6775
|
const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
|
|
@@ -6415,10 +6777,10 @@ var YieldseekerAgent = class {
|
|
|
6415
6777
|
const vaults = [...this.balanceVaults.get(key2) ?? []];
|
|
6416
6778
|
const [invested, idle, tracker] = await Promise.all([
|
|
6417
6779
|
remainingVaultBalance(client, wallet, asset.address, vaults, blockNumber),
|
|
6418
|
-
client.readContract({ address: asset.address, abi:
|
|
6780
|
+
client.readContract({ address: asset.address, abi: import_viem14.erc20Abi, functionName: "balanceOf", args: [wallet], blockNumber }),
|
|
6419
6781
|
client.readContract({ address: wallet, abi: YIELDSEEKER_AGENT_WALLET_ABI, functionName: "feeTracker", blockNumber })
|
|
6420
6782
|
]);
|
|
6421
|
-
if (invested === void 0 || typeof idle !== "bigint" || idle < 0n || !(0,
|
|
6783
|
+
if (invested === void 0 || typeof idle !== "bigint" || idle < 0n || !(0, import_viem14.isAddress)(tracker) || /^0x0{40}$/i.test(tracker)) return void 0;
|
|
6422
6784
|
const fees = await client.readContract({
|
|
6423
6785
|
address: tracker,
|
|
6424
6786
|
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
@@ -6431,7 +6793,7 @@ var YieldseekerAgent = class {
|
|
|
6431
6793
|
const total = idle + invested > fees ? idle + invested - fees : 0n;
|
|
6432
6794
|
const displayed = balance.assetBalances?.find((item) => item.chainId === evidence.chainId && item.asset === evidence.token);
|
|
6433
6795
|
if (!displayed || !evidence.confirmedAmount || !/^\d+$/.test(evidence.confirmedAmount)) return void 0;
|
|
6434
|
-
const amount = (0,
|
|
6796
|
+
const amount = (0, import_viem14.parseUnits)(displayed.amount, asset.decimals);
|
|
6435
6797
|
if (amount < 0n) return void 0;
|
|
6436
6798
|
const displayUnit = evidence.token === "USDC" ? 5000n : 500000000000n;
|
|
6437
6799
|
const movementUnit = BigInt(evidence.confirmedAmount) / 1000n;
|
|
@@ -6486,9 +6848,9 @@ var YieldseekerAgent = class {
|
|
|
6486
6848
|
if (!context) return void 0;
|
|
6487
6849
|
const idle = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
6488
6850
|
address: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
6489
|
-
abi:
|
|
6851
|
+
abi: import_viem14.erc20Abi,
|
|
6490
6852
|
functionName: "balanceOf",
|
|
6491
|
-
args: [(0,
|
|
6853
|
+
args: [(0, import_viem14.getAddress)(context.wallet.walletAddress)]
|
|
6492
6854
|
}), chainId, "eth_call", this.id);
|
|
6493
6855
|
this.assertSession(generation, chainId, asset);
|
|
6494
6856
|
if (typeof idle !== "bigint" || idle < 0n) throw this.invalidResponse("wallet deposit balance");
|
|
@@ -6530,13 +6892,13 @@ var YieldseekerAgent = class {
|
|
|
6530
6892
|
});
|
|
6531
6893
|
const catalog = await Promise.all(
|
|
6532
6894
|
[...new Set(contexts.map((context) => context.asset))].map(
|
|
6533
|
-
(contextAsset) => this.
|
|
6895
|
+
(contextAsset) => this.loadVaultOptions(contextAsset)
|
|
6534
6896
|
)
|
|
6535
6897
|
);
|
|
6536
6898
|
const vaultAddresses = new Set(
|
|
6537
6899
|
catalog.flat().filter(
|
|
6538
|
-
(
|
|
6539
|
-
).map((
|
|
6900
|
+
(vaultOption) => vaultOption.chainId === chainId && (0, import_viem14.isAddress)(vaultOption.address)
|
|
6901
|
+
).map((vaultOption) => vaultOption.address.toLowerCase())
|
|
6540
6902
|
);
|
|
6541
6903
|
return mapYieldseekerHistory(contexts, {
|
|
6542
6904
|
limit: options?.limit ?? 10,
|
|
@@ -6561,30 +6923,30 @@ var YieldseekerAgent = class {
|
|
|
6561
6923
|
const assets = requested ? [requested] : ["USDC", "WETH"];
|
|
6562
6924
|
const values = await Promise.all(
|
|
6563
6925
|
assets.map(async (asset) => {
|
|
6564
|
-
return { asset,
|
|
6926
|
+
return { asset, vaultOptions: await this.loadVaultOptions(asset) };
|
|
6565
6927
|
})
|
|
6566
6928
|
);
|
|
6567
6929
|
return mapYieldseekerAgentApy(values, days);
|
|
6568
6930
|
}
|
|
6569
|
-
async
|
|
6570
|
-
const cached = this.
|
|
6931
|
+
async loadVaultOptions(asset) {
|
|
6932
|
+
const cached = this.vaultOptions.get(asset);
|
|
6571
6933
|
if (cached && cached.expiresAt > Date.now()) return cached.value;
|
|
6572
|
-
const pending = this.
|
|
6934
|
+
const pending = this.pendingVaultOptions.get(asset);
|
|
6573
6935
|
if (pending) return pending;
|
|
6574
6936
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
6575
6937
|
const request2 = this.api.request(
|
|
6576
|
-
`/chains/8453/assets/${metadata.address}/
|
|
6938
|
+
`/chains/8453/assets/${metadata.address}/vault-options`
|
|
6577
6939
|
).then((response) => {
|
|
6578
|
-
if (!Array.isArray(response?.
|
|
6579
|
-
throw this.invalidResponse("
|
|
6940
|
+
if (!Array.isArray(response?.vaultOptions)) {
|
|
6941
|
+
throw this.invalidResponse("vault options");
|
|
6580
6942
|
}
|
|
6581
|
-
this.
|
|
6582
|
-
expiresAt: Date.now() +
|
|
6583
|
-
value: response.
|
|
6943
|
+
this.vaultOptions.set(asset, {
|
|
6944
|
+
expiresAt: Date.now() + YIELDSEEKER_VAULT_OPTIONS_CACHE_MS,
|
|
6945
|
+
value: response.vaultOptions
|
|
6584
6946
|
});
|
|
6585
|
-
return response.
|
|
6586
|
-
}).finally(() => this.
|
|
6587
|
-
this.
|
|
6947
|
+
return response.vaultOptions;
|
|
6948
|
+
}).finally(() => this.pendingVaultOptions.delete(asset));
|
|
6949
|
+
this.pendingVaultOptions.set(asset, request2);
|
|
6588
6950
|
return request2;
|
|
6589
6951
|
}
|
|
6590
6952
|
userKey(state, chainId) {
|
|
@@ -6664,7 +7026,7 @@ var YieldseekerAgent = class {
|
|
|
6664
7026
|
`/users/${user.userId}/agents/${agent.agentId}/wallet`
|
|
6665
7027
|
).then((walletResponse) => {
|
|
6666
7028
|
this.assertSession(generation, chainId, asset);
|
|
6667
|
-
if (!walletResponse?.agentWallet || !(0,
|
|
7029
|
+
if (!walletResponse?.agentWallet || !(0, import_viem14.isAddress)(walletResponse.agentWallet.walletAddress)) {
|
|
6668
7030
|
throw this.invalidResponse("agent wallet");
|
|
6669
7031
|
}
|
|
6670
7032
|
const context = {
|
|
@@ -6694,7 +7056,7 @@ var YieldseekerAgent = class {
|
|
|
6694
7056
|
this.users.set(key2, persisted);
|
|
6695
7057
|
return persisted;
|
|
6696
7058
|
}
|
|
6697
|
-
const walletAddress = (0,
|
|
7059
|
+
const walletAddress = (0, import_viem14.getAddress)(state.walletAddress);
|
|
6698
7060
|
const generation = this.readGeneration;
|
|
6699
7061
|
let user = null;
|
|
6700
7062
|
try {
|
|
@@ -6894,9 +7256,10 @@ var YieldseekerAgent = class {
|
|
|
6894
7256
|
expectedNetDeposits: BigInt(snapshot.netDepositsBase) + delta
|
|
6895
7257
|
};
|
|
6896
7258
|
}
|
|
6897
|
-
advancePortfolioVersion(contextKey) {
|
|
7259
|
+
advancePortfolioVersion(contextKey, { snapshotOnly = false } = {}) {
|
|
6898
7260
|
const version = this.portfolioVersion(contextKey) + 1;
|
|
6899
7261
|
this.portfolioVersions.set(contextKey, version);
|
|
7262
|
+
if (!snapshotOnly) this.liquidityVersions.set(contextKey, version);
|
|
6900
7263
|
this.snapshotFailures.delete(contextKey);
|
|
6901
7264
|
for (const kind of ["snapshot", "positions", "historic", "actions"]) {
|
|
6902
7265
|
const key2 = `${kind}:${contextKey}`;
|
|
@@ -6944,7 +7307,7 @@ var YieldseekerAgent = class {
|
|
|
6944
7307
|
if ((!cached || cached.expiresAt <= Date.now()) && !this.pendingReads.has(key2)) {
|
|
6945
7308
|
const failure = this.snapshotFailures.get(contextKey);
|
|
6946
7309
|
if (failure && failure.retryAt > Date.now()) throw failure.error;
|
|
6947
|
-
this.advancePortfolioVersion(contextKey);
|
|
7310
|
+
this.advancePortfolioVersion(contextKey, { snapshotOnly: true });
|
|
6948
7311
|
}
|
|
6949
7312
|
const version = this.portfolioVersion(contextKey);
|
|
6950
7313
|
const generation = this.readGeneration;
|
|
@@ -7027,17 +7390,17 @@ var YieldseekerAgent = class {
|
|
|
7027
7390
|
}
|
|
7028
7391
|
async loadPortfolioParts(state, chainId, context, options, contextKey) {
|
|
7029
7392
|
const [positions, historic, actions] = await Promise.all([
|
|
7030
|
-
options.includePositions === false ? Promise.resolve({
|
|
7393
|
+
options.includePositions === false ? Promise.resolve({ vaultPositions: [] }) : this.cachedRead(
|
|
7031
7394
|
`positions:${contextKey}`,
|
|
7032
7395
|
this.portfolioCacheMs(contextKey),
|
|
7033
7396
|
async () => {
|
|
7034
7397
|
const response = await this.walletRequest(
|
|
7035
7398
|
state,
|
|
7036
7399
|
chainId,
|
|
7037
|
-
this.agentPath(context, "
|
|
7400
|
+
this.agentPath(context, "vault-positions")
|
|
7038
7401
|
);
|
|
7039
|
-
if (!Array.isArray(response?.
|
|
7040
|
-
throw this.invalidResponse("
|
|
7402
|
+
if (!Array.isArray(response?.vaultPositions)) {
|
|
7403
|
+
throw this.invalidResponse("vault positions");
|
|
7041
7404
|
}
|
|
7042
7405
|
return response;
|
|
7043
7406
|
}
|
|
@@ -7062,7 +7425,7 @@ var YieldseekerAgent = class {
|
|
|
7062
7425
|
) : Promise.resolve(void 0)
|
|
7063
7426
|
]);
|
|
7064
7427
|
return {
|
|
7065
|
-
positions: positions.
|
|
7428
|
+
positions: positions.vaultPositions,
|
|
7066
7429
|
...historic?.position ? { historic: historic.position } : {},
|
|
7067
7430
|
...actions?.actions ? { actions: actions.actions } : {}
|
|
7068
7431
|
};
|
|
@@ -7076,7 +7439,7 @@ var YieldseekerAgent = class {
|
|
|
7076
7439
|
this.agentPath(context, "deploy"),
|
|
7077
7440
|
{ method: "POST", body: {} }
|
|
7078
7441
|
);
|
|
7079
|
-
if (!deployed?.agentWallet || !(0,
|
|
7442
|
+
if (!deployed?.agentWallet || !(0, import_viem14.isAddress)(deployed.agentWallet.walletAddress) || deployed.agentWallet.walletAddress.toLowerCase() !== walletAddress) {
|
|
7080
7443
|
throw this.invalidResponse("agent deployment", {
|
|
7081
7444
|
reason: "Deploy did not return the expected Agent Wallet."
|
|
7082
7445
|
});
|
|
@@ -7133,10 +7496,10 @@ var YieldseekerAgent = class {
|
|
|
7133
7496
|
}
|
|
7134
7497
|
async providerRequest(state, chainId, path, options = {}) {
|
|
7135
7498
|
this.assertChain(chainId);
|
|
7136
|
-
const request2 = (signature2) =>
|
|
7137
|
-
|
|
7138
|
-
signature: signature2
|
|
7139
|
-
}
|
|
7499
|
+
const request2 = (signature2) => {
|
|
7500
|
+
options.assertCurrent?.();
|
|
7501
|
+
return this.api.request(path, { method: options.method, body: options.body, signature: signature2 });
|
|
7502
|
+
};
|
|
7140
7503
|
let signature = await this.auth.getToken(state, chainId);
|
|
7141
7504
|
try {
|
|
7142
7505
|
return await request2(signature);
|
|
@@ -7185,18 +7548,18 @@ var YieldseekerAgent = class {
|
|
|
7185
7548
|
return hash3;
|
|
7186
7549
|
}
|
|
7187
7550
|
this.assertTransaction(transaction, state, chainId);
|
|
7188
|
-
const account = (0,
|
|
7189
|
-
const walletClient = (0,
|
|
7551
|
+
const account = (0, import_viem14.getAddress)(state.walletAddress);
|
|
7552
|
+
const walletClient = (0, import_viem14.createWalletClient)({
|
|
7190
7553
|
account,
|
|
7191
7554
|
chain: import_chains4.base,
|
|
7192
|
-
transport: onBeforeSubmission ? (0,
|
|
7555
|
+
transport: onBeforeSubmission ? (0, import_viem14.custom)({ request: async (args) => {
|
|
7193
7556
|
if (args.method === "eth_sendTransaction") await onBeforeSubmission();
|
|
7194
7557
|
return state.provider.request(args);
|
|
7195
|
-
} }, { retryCount: 0 }) : (0,
|
|
7558
|
+
} }, { retryCount: 0 }) : (0, import_viem14.custom)(state.provider)
|
|
7196
7559
|
});
|
|
7197
|
-
const walletChainClient = (0,
|
|
7560
|
+
const walletChainClient = (0, import_viem14.createPublicClient)({
|
|
7198
7561
|
chain: import_chains4.base,
|
|
7199
|
-
transport: (0,
|
|
7562
|
+
transport: (0, import_viem14.custom)(state.provider)
|
|
7200
7563
|
});
|
|
7201
7564
|
await ensureWalletOnChain(
|
|
7202
7565
|
walletChainClient,
|
|
@@ -7206,7 +7569,7 @@ var YieldseekerAgent = class {
|
|
|
7206
7569
|
const hash2 = await walletClient.sendTransaction({
|
|
7207
7570
|
account,
|
|
7208
7571
|
chain: import_chains4.base,
|
|
7209
|
-
to: (0,
|
|
7572
|
+
to: (0, import_viem14.getAddress)(transaction.to),
|
|
7210
7573
|
data: transaction.data,
|
|
7211
7574
|
value: BigInt(transaction.value)
|
|
7212
7575
|
});
|
|
@@ -7310,12 +7673,12 @@ var YieldseekerAgent = class {
|
|
|
7310
7673
|
return receipt;
|
|
7311
7674
|
}
|
|
7312
7675
|
assertTransaction(transaction, state, chainId) {
|
|
7313
|
-
if (!transaction || typeof transaction.from !== "string" || !(0,
|
|
7676
|
+
if (!transaction || typeof transaction.from !== "string" || !(0, import_viem14.isAddress)(transaction.from) || typeof transaction.to !== "string" || !(0, import_viem14.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_viem14.getAddress)(transaction.from) !== (0, import_viem14.getAddress)(state.walletAddress)) {
|
|
7314
7677
|
throw this.invalidResponse("transaction");
|
|
7315
7678
|
}
|
|
7316
7679
|
}
|
|
7317
7680
|
assertAgent(agent) {
|
|
7318
|
-
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0,
|
|
7681
|
+
if (typeof agent.agentId !== "string" || typeof agent.assetAddress !== "string" || !(0, import_viem14.isAddress)(agent.assetAddress) || agent.chainId !== 8453) {
|
|
7319
7682
|
throw this.invalidResponse("agent");
|
|
7320
7683
|
}
|
|
7321
7684
|
}
|
|
@@ -7438,45 +7801,57 @@ async function fetchAgentKeys(apiKey, baseUrl = ROUTING_API_BASE_URL2) {
|
|
|
7438
7801
|
// src/lib/swap/swap-api.ts
|
|
7439
7802
|
async function request(baseUrl, apiKey, path, init) {
|
|
7440
7803
|
const url = `${baseUrl}/api/v1/swap${path}`;
|
|
7441
|
-
const
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7804
|
+
const controller = new AbortController();
|
|
7805
|
+
try {
|
|
7806
|
+
return await bounded(async () => {
|
|
7807
|
+
const res = await fetch(url, {
|
|
7808
|
+
signal: controller.signal,
|
|
7809
|
+
method: init?.method ?? "GET",
|
|
7810
|
+
headers: {
|
|
7811
|
+
"Content-Type": "application/json",
|
|
7812
|
+
"x-owney-api-key": apiKey
|
|
7813
|
+
},
|
|
7814
|
+
...init ? { body: JSON.stringify(init.body) } : {}
|
|
7815
|
+
});
|
|
7816
|
+
if (!res.ok) {
|
|
7817
|
+
const text = await res.text().catch(() => "");
|
|
7818
|
+
if (res.status === 429) {
|
|
7819
|
+
throw new OwneyError(
|
|
7820
|
+
"SWAP_RATE_LIMITED",
|
|
7821
|
+
"Swap provider is rate limiting, retry shortly",
|
|
7822
|
+
{ statusCode: res.status }
|
|
7823
|
+
);
|
|
7824
|
+
}
|
|
7825
|
+
if (res.status === 403) {
|
|
7826
|
+
throw new OwneyError(
|
|
7827
|
+
"SWAP_DISABLED",
|
|
7828
|
+
"Swap is not enabled for this organization",
|
|
7829
|
+
{ statusCode: res.status }
|
|
7830
|
+
);
|
|
7831
|
+
}
|
|
7832
|
+
throw new OwneyError(
|
|
7833
|
+
"SWAP_REQUEST_FAILED",
|
|
7834
|
+
`Swap API error ${res.status}: ${text}`,
|
|
7835
|
+
{ statusCode: res.status, responseBody: text }
|
|
7836
|
+
);
|
|
7837
|
+
}
|
|
7838
|
+
const json = await res.json();
|
|
7839
|
+
if (!json.success) {
|
|
7840
|
+
throw new OwneyError(
|
|
7841
|
+
"SWAP_REQUEST_FAILED",
|
|
7842
|
+
`Swap API request failed: ${json.message ?? "unknown error"}`,
|
|
7843
|
+
{ message: json.message }
|
|
7844
|
+
);
|
|
7845
|
+
}
|
|
7846
|
+
return json.data;
|
|
7847
|
+
}, 2e4, () => new OwneyError(
|
|
7474
7848
|
"SWAP_REQUEST_FAILED",
|
|
7475
|
-
|
|
7476
|
-
{
|
|
7477
|
-
);
|
|
7849
|
+
"Swap request timed out. Its outcome may still need confirmation.",
|
|
7850
|
+
{ requestTimedOut: true, phase: path === "/order" ? "submit" : "read" }
|
|
7851
|
+
));
|
|
7852
|
+
} finally {
|
|
7853
|
+
controller.abort();
|
|
7478
7854
|
}
|
|
7479
|
-
return json.data;
|
|
7480
7855
|
}
|
|
7481
7856
|
function createSwapApi(baseUrl, apiKey) {
|
|
7482
7857
|
return {
|
|
@@ -7600,7 +7975,7 @@ function createSwapApi(baseUrl, apiKey) {
|
|
|
7600
7975
|
}
|
|
7601
7976
|
|
|
7602
7977
|
// src/lib/swap/swap.rpc.ts
|
|
7603
|
-
var
|
|
7978
|
+
var import_viem15 = require("viem");
|
|
7604
7979
|
var DEFAULT_RPC_URLS = {
|
|
7605
7980
|
// NOT cloudflare-eth.com, which used to lead this list. It answers
|
|
7606
7981
|
// eth_chainId happily, errors on eth_blockNumber, and — the part that
|
|
@@ -7640,10 +8015,10 @@ var DEFAULT_RPC_URLS = {
|
|
|
7640
8015
|
};
|
|
7641
8016
|
function swapReadTransport(chainId, overrides) {
|
|
7642
8017
|
const override = overrides?.[chainId];
|
|
7643
|
-
if (override) return (0,
|
|
8018
|
+
if (override) return (0, import_viem15.http)(override);
|
|
7644
8019
|
const urls = DEFAULT_RPC_URLS[chainId];
|
|
7645
|
-
if (!urls || urls.length === 0) return (0,
|
|
7646
|
-
return (0,
|
|
8020
|
+
if (!urls || urls.length === 0) return (0, import_viem15.http)();
|
|
8021
|
+
return (0, import_viem15.fallback)(urls.map((url) => (0, import_viem15.http)(url)));
|
|
7647
8022
|
}
|
|
7648
8023
|
function receiptTimeoutMs(chainId) {
|
|
7649
8024
|
return chainId === 1 ? 6e5 : 18e4;
|
|
@@ -7772,8 +8147,12 @@ async function awaitBalanceIncrease(options) {
|
|
|
7772
8147
|
let lastError;
|
|
7773
8148
|
for (; ; ) {
|
|
7774
8149
|
try {
|
|
7775
|
-
const balance = await
|
|
7776
|
-
|
|
8150
|
+
const balance = await bounded(
|
|
8151
|
+
readBalance,
|
|
8152
|
+
Math.max(1, Math.min(1e4, deadline - Date.now())),
|
|
8153
|
+
() => new Error("Balance observation timed out")
|
|
8154
|
+
);
|
|
8155
|
+
if (Date.now() <= deadline && balance > baseline) {
|
|
7777
8156
|
const arrived = balance - baseline;
|
|
7778
8157
|
debugLog("owney-sdk", "arrival: funds landed", {
|
|
7779
8158
|
arrived: arrived.toString()
|
|
@@ -7799,12 +8178,12 @@ async function awaitBalanceIncrease(options) {
|
|
|
7799
8178
|
lastReadError: lastError instanceof Error ? lastError.message : String(lastError)
|
|
7800
8179
|
} : { arrived: null };
|
|
7801
8180
|
}
|
|
7802
|
-
await sleep(pollMs);
|
|
8181
|
+
await sleep(Math.min(pollMs, Math.max(0, deadline - Date.now())));
|
|
7803
8182
|
}
|
|
7804
8183
|
}
|
|
7805
8184
|
|
|
7806
8185
|
// src/lib/swap/swap.secrets.ts
|
|
7807
|
-
var
|
|
8186
|
+
var import_viem16 = require("viem");
|
|
7808
8187
|
var SECRET_BYTES = 32;
|
|
7809
8188
|
function randomBytes(length) {
|
|
7810
8189
|
const bytes = new Uint8Array(length);
|
|
@@ -7826,9 +8205,9 @@ function mintSecrets(count) {
|
|
|
7826
8205
|
const secrets = [];
|
|
7827
8206
|
const secretHashes = [];
|
|
7828
8207
|
for (let i = 0; i < count; i++) {
|
|
7829
|
-
const secret = (0,
|
|
8208
|
+
const secret = (0, import_viem16.toHex)(randomBytes(SECRET_BYTES));
|
|
7830
8209
|
secrets.push(secret);
|
|
7831
|
-
secretHashes.push((0,
|
|
8210
|
+
secretHashes.push((0, import_viem16.keccak256)(secret));
|
|
7832
8211
|
}
|
|
7833
8212
|
return { secrets, secretHashes };
|
|
7834
8213
|
}
|
|
@@ -7852,7 +8231,7 @@ var DEFAULT_POLL_MS2 = 5e3;
|
|
|
7852
8231
|
var MAX_BACKOFF_MS = 3e4;
|
|
7853
8232
|
var backoffFor = (failures, base7) => Math.min(base7 * 2 ** Math.min(failures, 5), MAX_BACKOFF_MS);
|
|
7854
8233
|
var DEFAULT_TIMEOUT_MS2 = 15 * 60 * 1e3;
|
|
7855
|
-
async function
|
|
8234
|
+
async function runFusionOrderInternal(deps, options) {
|
|
7856
8235
|
const {
|
|
7857
8236
|
orderHash,
|
|
7858
8237
|
secrets,
|
|
@@ -7861,39 +8240,51 @@ async function runFusionOrder(deps, options) {
|
|
|
7861
8240
|
timeoutMs = DEFAULT_TIMEOUT_MS2
|
|
7862
8241
|
} = options;
|
|
7863
8242
|
const deadline = deps.now() + timeoutMs;
|
|
8243
|
+
let partial = options.hasPartialFill === true;
|
|
8244
|
+
const timeout = () => new OwneyError(
|
|
8245
|
+
"SWAP_REQUEST_FAILED",
|
|
8246
|
+
"Swap settlement is not confirmed yet. Check its status before starting another swap.",
|
|
8247
|
+
{ orderHash, settlementUnconfirmed: true, outcome: partial ? "partially-filled" : "submitted-unknown" }
|
|
8248
|
+
);
|
|
8249
|
+
const assertTime = () => {
|
|
8250
|
+
if (deps.now() >= deadline) throw timeout();
|
|
8251
|
+
};
|
|
8252
|
+
const read2 = async (work) => {
|
|
8253
|
+
assertTime();
|
|
8254
|
+
const result = await bounded(work, Math.min(1e4, deadline - deps.now()), timeout);
|
|
8255
|
+
assertTime();
|
|
8256
|
+
return result;
|
|
8257
|
+
};
|
|
8258
|
+
const pause = async (ms) => {
|
|
8259
|
+
assertTime();
|
|
8260
|
+
await bounded(() => deps.sleep(Math.min(ms, deadline - deps.now())), deadline - deps.now(), timeout);
|
|
8261
|
+
assertTime();
|
|
8262
|
+
};
|
|
7864
8263
|
let failures = 0;
|
|
7865
8264
|
const published = /* @__PURE__ */ new Set();
|
|
7866
8265
|
onStage?.("swapping");
|
|
7867
8266
|
for (; ; ) {
|
|
7868
|
-
|
|
7869
|
-
throw new OwneyError(
|
|
7870
|
-
"SWAP_REQUEST_FAILED",
|
|
7871
|
-
"Timed out waiting for the swap to settle. It may still complete \u2014 check the order status before retrying.",
|
|
7872
|
-
// Marks the one failure that means "we stopped watching a live order",
|
|
7873
|
-
// as opposed to a verdict or a prompt nobody answered. The executor
|
|
7874
|
-
// checks the wallet before believing it: resolvers do not stop filling
|
|
7875
|
-
// because we stopped looking (ROUT-242, comment 15980).
|
|
7876
|
-
{ orderHash, settlementUnconfirmed: true }
|
|
7877
|
-
);
|
|
7878
|
-
}
|
|
8267
|
+
assertTime();
|
|
7879
8268
|
let ready;
|
|
7880
8269
|
try {
|
|
7881
|
-
ready = await deps.readyForSecrets(orderHash);
|
|
8270
|
+
ready = await read2(() => deps.readyForSecrets(orderHash));
|
|
7882
8271
|
} catch {
|
|
7883
8272
|
ready = {};
|
|
7884
8273
|
}
|
|
8274
|
+
assertTime();
|
|
7885
8275
|
for (const fill of ready.fills ?? []) {
|
|
8276
|
+
assertTime();
|
|
7886
8277
|
if (published.has(fill.idx)) continue;
|
|
7887
8278
|
const secret = secrets[fill.idx];
|
|
7888
8279
|
if (secret === void 0) {
|
|
7889
8280
|
throw new OwneyError(
|
|
7890
8281
|
"SWAP_REQUEST_FAILED",
|
|
7891
8282
|
`Swap needs a secret for fill ${fill.idx} that this session does not have. The order will refund once its timelock expires.`,
|
|
7892
|
-
{ orderHash, fillIndex: fill.idx }
|
|
8283
|
+
{ orderHash, fillIndex: fill.idx, settlementUnconfirmed: true, outcome: "submitted-unknown" }
|
|
7893
8284
|
);
|
|
7894
8285
|
}
|
|
7895
8286
|
try {
|
|
7896
|
-
await deps.submitSecret(orderHash, secret);
|
|
8287
|
+
await read2(() => deps.submitSecret(orderHash, secret));
|
|
7897
8288
|
published.add(fill.idx);
|
|
7898
8289
|
} catch {
|
|
7899
8290
|
failures += 1;
|
|
@@ -7901,29 +8292,34 @@ async function runFusionOrder(deps, options) {
|
|
|
7901
8292
|
}
|
|
7902
8293
|
let status;
|
|
7903
8294
|
try {
|
|
7904
|
-
|
|
8295
|
+
const state = await read2(() => deps.orderStatus(orderHash));
|
|
8296
|
+
status = state.status;
|
|
8297
|
+
partial ||= status === "partially-filled" || !!state.fills?.length;
|
|
7905
8298
|
failures = 0;
|
|
7906
8299
|
} catch {
|
|
7907
8300
|
failures += 1;
|
|
7908
|
-
await
|
|
8301
|
+
await pause(backoffFor(failures, pollIntervalMs));
|
|
7909
8302
|
continue;
|
|
7910
8303
|
}
|
|
7911
8304
|
if (status === "refunding") onStage?.("refunding");
|
|
7912
8305
|
if (isSwapTerminal(status)) {
|
|
7913
|
-
if (status === "executed") {
|
|
8306
|
+
if (status === "executed" || status === "filled") {
|
|
7914
8307
|
onStage?.("swapped");
|
|
7915
8308
|
return { status, filled: true };
|
|
7916
8309
|
}
|
|
7917
|
-
if (status === "refunded") onStage?.("refunded");
|
|
8310
|
+
if (status === "refunded") onStage?.(partial ? "swap-partial" : "refunded");
|
|
7918
8311
|
throw new OwneyError(
|
|
7919
8312
|
status === "refunded" ? "SWAP_ORDER_REFUNDED" : status === "cancelled" ? "SWAP_ORDER_CANCELLED" : "SWAP_ORDER_EXPIRED",
|
|
7920
|
-
status === "refunded" ? "The swap did not complete and your funds have been returned." : "The swap did not complete in time. Your funds will be returned once the timelock expires.",
|
|
7921
|
-
{ orderHash, status }
|
|
8313
|
+
partial ? "The swap filled partly. Check the order and both balances before retrying." : status === "refunded" ? "The swap did not complete and your funds have been returned." : "The swap did not complete in time. Your funds will be returned once the timelock expires.",
|
|
8314
|
+
{ orderHash, status, outcome: status === "refunded" && !partial ? "refunded" : partial ? "partially-filled" : "refund-pending" }
|
|
7922
8315
|
);
|
|
7923
8316
|
}
|
|
7924
|
-
await
|
|
8317
|
+
await pause(pollIntervalMs);
|
|
7925
8318
|
}
|
|
7926
8319
|
}
|
|
8320
|
+
function runFusionOrder(deps, options) {
|
|
8321
|
+
return observeOrder(options.orderHash, () => runFusionOrderInternal(deps, options));
|
|
8322
|
+
}
|
|
7927
8323
|
|
|
7928
8324
|
// src/lib/swap/swap.same-chain-runner.ts
|
|
7929
8325
|
var DEFAULT_POLL_MS3 = 5e3;
|
|
@@ -7948,7 +8344,7 @@ function explain(status) {
|
|
|
7948
8344
|
return "The swap did not complete. Nothing was exchanged.";
|
|
7949
8345
|
}
|
|
7950
8346
|
}
|
|
7951
|
-
async function
|
|
8347
|
+
async function runSameChainOrderInternal(deps, options) {
|
|
7952
8348
|
const {
|
|
7953
8349
|
orderHash,
|
|
7954
8350
|
onStage,
|
|
@@ -7956,23 +8352,39 @@ async function runSameChainOrder(deps, options) {
|
|
|
7956
8352
|
timeoutMs = DEFAULT_TIMEOUT_MS3
|
|
7957
8353
|
} = options;
|
|
7958
8354
|
const deadline = deps.now() + timeoutMs;
|
|
8355
|
+
let partial = options.hasPartialFill === true;
|
|
8356
|
+
const timeout = () => new OwneyError(
|
|
8357
|
+
"SWAP_REQUEST_FAILED",
|
|
8358
|
+
"Swap settlement is not confirmed yet. Check its status before starting another swap.",
|
|
8359
|
+
{ orderHash, settlementUnconfirmed: true, outcome: partial ? "partially-filled" : "submitted-unknown" }
|
|
8360
|
+
);
|
|
8361
|
+
const assertTime = () => {
|
|
8362
|
+
if (deps.now() >= deadline) throw timeout();
|
|
8363
|
+
};
|
|
8364
|
+
const read2 = async (work) => {
|
|
8365
|
+
assertTime();
|
|
8366
|
+
const result = await bounded(work, Math.min(1e4, deadline - deps.now()), timeout);
|
|
8367
|
+
assertTime();
|
|
8368
|
+
return result;
|
|
8369
|
+
};
|
|
8370
|
+
const pause = async (ms) => {
|
|
8371
|
+
assertTime();
|
|
8372
|
+
await bounded(() => deps.sleep(Math.min(ms, deadline - deps.now())), deadline - deps.now(), timeout);
|
|
8373
|
+
assertTime();
|
|
8374
|
+
};
|
|
7959
8375
|
let failures = 0;
|
|
7960
8376
|
onStage?.("swapping");
|
|
7961
8377
|
for (; ; ) {
|
|
7962
|
-
|
|
7963
|
-
throw new OwneyError(
|
|
7964
|
-
"SWAP_ORDER_EXPIRED",
|
|
7965
|
-
"No one filled the swap in time. Nothing was exchanged and your funds are still in your wallet.",
|
|
7966
|
-
{ orderHash }
|
|
7967
|
-
);
|
|
7968
|
-
}
|
|
8378
|
+
assertTime();
|
|
7969
8379
|
let status;
|
|
7970
8380
|
try {
|
|
7971
|
-
|
|
8381
|
+
const state = await read2(() => deps.orderStatus(orderHash));
|
|
8382
|
+
status = state.status;
|
|
8383
|
+
partial ||= status === "partially-filled" || !!state.fills?.length;
|
|
7972
8384
|
failures = 0;
|
|
7973
8385
|
} catch {
|
|
7974
8386
|
failures += 1;
|
|
7975
|
-
await
|
|
8387
|
+
await pause(backoffFor2(failures, pollIntervalMs));
|
|
7976
8388
|
continue;
|
|
7977
8389
|
}
|
|
7978
8390
|
if (isSwapTerminal(status)) {
|
|
@@ -7982,17 +8394,19 @@ async function runSameChainOrder(deps, options) {
|
|
|
7982
8394
|
}
|
|
7983
8395
|
throw new OwneyError(
|
|
7984
8396
|
status === "cancelled" ? "SWAP_ORDER_CANCELLED" : "SWAP_ORDER_EXPIRED",
|
|
7985
|
-
explain(status),
|
|
7986
|
-
{ orderHash, status }
|
|
8397
|
+
partial ? "The swap filled partly. Check the order and both balances before retrying." : explain(status),
|
|
8398
|
+
{ orderHash, status, outcome: partial ? "partially-filled" : "not-submitted" }
|
|
7987
8399
|
);
|
|
7988
8400
|
}
|
|
7989
|
-
await
|
|
8401
|
+
await pause(pollIntervalMs);
|
|
7990
8402
|
}
|
|
7991
8403
|
}
|
|
8404
|
+
function runSameChainOrder(deps, options) {
|
|
8405
|
+
return observeOrder(options.orderHash, () => runSameChainOrderInternal(deps, options));
|
|
8406
|
+
}
|
|
7992
8407
|
|
|
7993
8408
|
// src/lib/swap/swap.secret-store.ts
|
|
7994
8409
|
var KEY_PREFIX4 = "owney.swap.order";
|
|
7995
|
-
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
7996
8410
|
var storage4 = () => {
|
|
7997
8411
|
if (typeof window === "undefined") return null;
|
|
7998
8412
|
try {
|
|
@@ -8004,10 +8418,13 @@ var storage4 = () => {
|
|
|
8004
8418
|
var keyFor2 = (orderHash) => `${KEY_PREFIX4}.${orderHash}`;
|
|
8005
8419
|
function saveOrder(order) {
|
|
8006
8420
|
const store = storage4();
|
|
8007
|
-
if (!store) return;
|
|
8421
|
+
if (!store) return false;
|
|
8008
8422
|
try {
|
|
8009
|
-
|
|
8423
|
+
const value = JSON.stringify(order);
|
|
8424
|
+
store.setItem(keyFor2(order.orderHash), value);
|
|
8425
|
+
return store.getItem(keyFor2(order.orderHash)) === value;
|
|
8010
8426
|
} catch {
|
|
8427
|
+
return false;
|
|
8011
8428
|
}
|
|
8012
8429
|
}
|
|
8013
8430
|
function clearOrder(orderHash) {
|
|
@@ -8018,7 +8435,7 @@ function clearOrder(orderHash) {
|
|
|
8018
8435
|
} catch {
|
|
8019
8436
|
}
|
|
8020
8437
|
}
|
|
8021
|
-
function listOrders(
|
|
8438
|
+
function listOrders(_now = Date.now()) {
|
|
8022
8439
|
const store = storage4();
|
|
8023
8440
|
if (!store) return [];
|
|
8024
8441
|
const out = [];
|
|
@@ -8033,10 +8450,6 @@ function listOrders(now = Date.now()) {
|
|
|
8033
8450
|
if (!raw2) continue;
|
|
8034
8451
|
try {
|
|
8035
8452
|
const parsed = JSON.parse(raw2);
|
|
8036
|
-
if (now - parsed.createdAt > MAX_AGE_MS) {
|
|
8037
|
-
store.removeItem(key2);
|
|
8038
|
-
continue;
|
|
8039
|
-
}
|
|
8040
8453
|
if (Array.isArray(parsed.secrets) && parsed.secrets.length > 0) {
|
|
8041
8454
|
out.push(parsed);
|
|
8042
8455
|
}
|
|
@@ -8077,12 +8490,11 @@ async function awaitWalletPrompt(prompt, what) {
|
|
|
8077
8490
|
if (timer) clearTimeout(timer);
|
|
8078
8491
|
}
|
|
8079
8492
|
}
|
|
8080
|
-
function stoppedWatching(error) {
|
|
8081
|
-
return error instanceof OwneyError && error.details?.settlementUnconfirmed === true;
|
|
8082
|
-
}
|
|
8083
8493
|
var DEFAULT_SLIPPAGE = 1;
|
|
8084
|
-
async function affordableAmount(deps, quoted) {
|
|
8085
|
-
const
|
|
8494
|
+
async function affordableAmount(deps, quoted, reserve = 0n) {
|
|
8495
|
+
const raw2 = await deps.readSourceBalance();
|
|
8496
|
+
const balance = raw2 > reserve ? raw2 - reserve : 0n;
|
|
8497
|
+
if (balance <= 0n) throw new OwneyError("SWAP_REQUEST_FAILED", "No spendable source funds remain for this swap.", { outcome: "not-submitted" });
|
|
8086
8498
|
if (balance >= quoted) return quoted;
|
|
8087
8499
|
debugLog("owney-sdk", "swap: trimming to the current source balance", {
|
|
8088
8500
|
quoted: quoted.toString(),
|
|
@@ -8093,48 +8505,72 @@ async function affordableAmount(deps, quoted) {
|
|
|
8093
8505
|
}
|
|
8094
8506
|
async function executeSwap(deps, options) {
|
|
8095
8507
|
const { quote, walletAddress, onStage } = options;
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8508
|
+
let active2 = true;
|
|
8509
|
+
const original = deps;
|
|
8510
|
+
const assertActive = () => {
|
|
8511
|
+
if (!active2) throw new OwneyError("SWAP_REQUEST_FAILED", "This swap attempt is no longer active.");
|
|
8512
|
+
};
|
|
8513
|
+
const call = async (work, write = false) => {
|
|
8514
|
+
assertActive();
|
|
8515
|
+
if (write) options.assertCurrent?.();
|
|
8516
|
+
const value = await bounded(
|
|
8517
|
+
work,
|
|
8518
|
+
write ? 3e5 : 2e4,
|
|
8519
|
+
() => new OwneyError(
|
|
8520
|
+
"SWAP_REQUEST_FAILED",
|
|
8521
|
+
"The swap step could not be confirmed. Check its status before retrying.",
|
|
8522
|
+
{ settlementUnconfirmed: write, outcome: write ? "submitted-unknown" : "not-submitted" }
|
|
8523
|
+
)
|
|
8524
|
+
);
|
|
8525
|
+
assertActive();
|
|
8526
|
+
if (write) options.assertCurrent?.();
|
|
8527
|
+
return value;
|
|
8528
|
+
};
|
|
8529
|
+
deps = {
|
|
8530
|
+
...original,
|
|
8531
|
+
api: {
|
|
8532
|
+
...original.api,
|
|
8533
|
+
buildOrder: (args) => call(() => original.api.buildOrder(args)),
|
|
8534
|
+
swapTx: (args) => call(() => original.api.swapTx(args)),
|
|
8535
|
+
submitOrder: (args) => call(() => original.api.submitOrder(args), true)
|
|
8536
|
+
},
|
|
8537
|
+
readTargetBalance: () => call(original.readTargetBalance),
|
|
8538
|
+
readSourceBalance: () => call(original.readSourceBalance),
|
|
8539
|
+
readAllowance: (spender) => call(() => original.readAllowance(spender)),
|
|
8540
|
+
ensureChain: (chain2) => call(() => original.ensureChain(chain2), true),
|
|
8541
|
+
approve: (spender, amount) => call(() => original.approve(spender, amount), true),
|
|
8542
|
+
sendTransaction: (tx) => call(() => original.sendTransaction(tx), true),
|
|
8543
|
+
signTypedData: (data) => call(() => original.signTypedData(data), true),
|
|
8544
|
+
...original.buildPermit ? { buildPermit: (spender, amount) => call(() => original.buildPermit(spender, amount), true) } : {}
|
|
8545
|
+
};
|
|
8107
8546
|
try {
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
8547
|
+
debugLog("owney-sdk", "swap: start", {
|
|
8548
|
+
rail: quote.rail,
|
|
8549
|
+
from: `${quote.src.amount} ${quote.src.symbol} on ${quote.src.chainId}`,
|
|
8550
|
+
to: `${quote.dst.symbol} on ${quote.dst.chainId}`,
|
|
8551
|
+
expected: quote.dst.amount,
|
|
8552
|
+
floor: quote.dstAmountMin
|
|
8114
8553
|
});
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
if (railError) throw railError;
|
|
8124
|
-
throw new OwneyError(
|
|
8125
|
-
"SWAP_REQUEST_FAILED",
|
|
8126
|
-
"The swap completed but no funds arrived in the wallet. Check the transaction before retrying.",
|
|
8127
|
-
{ rail: quote.rail, ...result ?? {} }
|
|
8128
|
-
);
|
|
8129
|
-
}
|
|
8130
|
-
if (railError) {
|
|
8131
|
-
debugLog("owney-sdk", "swap: rail gave up but the funds are here", {
|
|
8132
|
-
received: arrived.toString()
|
|
8554
|
+
const before = await deps.readTargetBalance();
|
|
8555
|
+
debugLog("owney-sdk", "swap: target balance before", before.toString());
|
|
8556
|
+
const result = quote.rail === "classic" ? await runClassic(deps, options) : quote.rail === "fusion" ? await runSameChainFusion(deps, options, walletAddress) : await runFusion(deps, options, walletAddress);
|
|
8557
|
+
const { arrived } = await awaitBalanceIncrease({
|
|
8558
|
+
readBalance: () => deps.readTargetBalance(),
|
|
8559
|
+
baseline: before,
|
|
8560
|
+
...options.arrivalTimeoutMs === void 0 ? {} : { timeoutMs: options.arrivalTimeoutMs },
|
|
8561
|
+
...options.arrivalPollMs === void 0 ? {} : { pollMs: options.arrivalPollMs }
|
|
8133
8562
|
});
|
|
8134
|
-
|
|
8135
|
-
|
|
8563
|
+
if (arrived === null) {
|
|
8564
|
+
throw new OwneyError(
|
|
8565
|
+
"SWAP_REQUEST_FAILED",
|
|
8566
|
+
"The swap completed but no funds arrived in the wallet. Check the transaction before retrying.",
|
|
8567
|
+
{ rail: quote.rail, ...result, outcome: "filled", settlementUnconfirmed: true }
|
|
8568
|
+
);
|
|
8569
|
+
}
|
|
8570
|
+
return { received: arrived.toString(), ...result };
|
|
8571
|
+
} finally {
|
|
8572
|
+
active2 = false;
|
|
8136
8573
|
}
|
|
8137
|
-
return { received: arrived.toString(), ...result ?? {} };
|
|
8138
8574
|
}
|
|
8139
8575
|
async function runClassic(deps, options) {
|
|
8140
8576
|
const {
|
|
@@ -8145,7 +8581,7 @@ async function runClassic(deps, options) {
|
|
|
8145
8581
|
onStage
|
|
8146
8582
|
} = options;
|
|
8147
8583
|
const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
|
|
8148
|
-
|
|
8584
|
+
let amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount), options.sourceReserve);
|
|
8149
8585
|
const swapTxRequest = {
|
|
8150
8586
|
from: {
|
|
8151
8587
|
chainId: quote.src.chainId,
|
|
@@ -8177,25 +8613,36 @@ async function runClassic(deps, options) {
|
|
|
8177
8613
|
"owney-sdk",
|
|
8178
8614
|
"swap: re-fetching classic calldata after approval"
|
|
8179
8615
|
);
|
|
8616
|
+
amount = await affordableAmount(deps, amount, options.sourceReserve);
|
|
8617
|
+
swapTxRequest.from.amount = amount.toString();
|
|
8180
8618
|
({ tx } = await deps.api.swapTx(swapTxRequest));
|
|
8619
|
+
if (await deps.readAllowance(tx.to) < amount)
|
|
8620
|
+
throw new OwneyError("SWAP_REQUEST_FAILED", "Swap approval was not confirmed for the current spender.", { outcome: "not-submitted" });
|
|
8181
8621
|
}
|
|
8182
8622
|
}
|
|
8183
8623
|
onStage?.("signing");
|
|
8184
8624
|
await deps.ensureChain(quote.src.chainId);
|
|
8185
8625
|
debugLog("owney-sdk", "swap: sending classic swap tx", { to: tx.to });
|
|
8626
|
+
options.assertCurrent?.();
|
|
8627
|
+
options.onTransactionRequested?.();
|
|
8186
8628
|
const txHash = await deps.sendTransaction({
|
|
8187
8629
|
to: tx.to,
|
|
8188
8630
|
data: tx.data,
|
|
8189
8631
|
value: tx.value ?? "0"
|
|
8632
|
+
}).catch((error) => {
|
|
8633
|
+
if (error?.code === 4001 || error?.code === "ACTION_REJECTED")
|
|
8634
|
+
throw new OwneyError("SWAP_REQUEST_FAILED", "The swap transaction was rejected in the wallet.", { outcome: "not-submitted" });
|
|
8635
|
+
throw error;
|
|
8190
8636
|
});
|
|
8191
8637
|
onStage?.("swapped");
|
|
8192
|
-
return { txHash };
|
|
8638
|
+
return { txHash, spent: amount.toString() };
|
|
8193
8639
|
}
|
|
8194
8640
|
async function runSameChainFusion(deps, options, walletAddress) {
|
|
8195
8641
|
const { quote, direction, onStage } = options;
|
|
8196
8642
|
const isNativeSource = quote.src.address.toLowerCase().startsWith("0xeeee");
|
|
8197
|
-
|
|
8198
|
-
const permit = await authoriseSpend(deps, options, amount, isNativeSource);
|
|
8643
|
+
let amount = isNativeSource ? BigInt(quote.src.amount) : await affordableAmount(deps, BigInt(quote.src.amount), options.sourceReserve);
|
|
8644
|
+
const { permit, paid } = await authoriseSpend(deps, options, amount, isNativeSource);
|
|
8645
|
+
amount = await authorisedAmount(deps, options, amount, paid);
|
|
8199
8646
|
onStage?.("building");
|
|
8200
8647
|
debugLog("owney-sdk", "swap: building same-chain fusion order");
|
|
8201
8648
|
const built = await deps.api.buildOrder({
|
|
@@ -8219,36 +8666,42 @@ async function runSameChainFusion(deps, options, walletAddress) {
|
|
|
8219
8666
|
"sign the swap"
|
|
8220
8667
|
);
|
|
8221
8668
|
debugLog("owney-sdk", "swap: signed, submitting same-chain order");
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8669
|
+
options.assertCurrent?.();
|
|
8670
|
+
options.onOrderPrepared?.(built.orderHash, { spent: amount.toString(), expected: built.dstAmount });
|
|
8671
|
+
try {
|
|
8672
|
+
await deps.api.submitOrder({
|
|
8673
|
+
// Without this the routing API hands the order to the Fusion+ relayer,
|
|
8674
|
+
// which does not know it and answers with an error that names nothing.
|
|
8675
|
+
rail: "fusion",
|
|
8676
|
+
srcChainId: quote.src.chainId,
|
|
8677
|
+
order: built.order,
|
|
8678
|
+
signature,
|
|
8679
|
+
quoteId: built.quoteId,
|
|
8680
|
+
...built.extension ? { extension: built.extension } : {}
|
|
8681
|
+
});
|
|
8682
|
+
} catch (error) {
|
|
8683
|
+
throw submissionUnknown(error, built.orderHash);
|
|
8684
|
+
}
|
|
8232
8685
|
try {
|
|
8233
|
-
options.onOrderSubmitted?.(built.orderHash);
|
|
8686
|
+
options.onOrderSubmitted?.(built.orderHash, { spent: amount.toString(), expected: built.dstAmount, durable: true });
|
|
8234
8687
|
} catch {
|
|
8235
8688
|
}
|
|
8236
8689
|
await runSameChainOrder(deps.sameChainRunner ?? deps.runner, {
|
|
8237
8690
|
orderHash: built.orderHash,
|
|
8238
8691
|
...onStage ? { onStage } : {}
|
|
8239
8692
|
});
|
|
8240
|
-
return { orderHash: built.orderHash };
|
|
8693
|
+
return { orderHash: built.orderHash, spent: amount.toString() };
|
|
8241
8694
|
}
|
|
8242
8695
|
async function authoriseSpend(deps, options, amount, isNativeSource) {
|
|
8243
8696
|
const { quote, onStage } = options;
|
|
8244
|
-
if (!quote.spender || isNativeSource) return
|
|
8697
|
+
if (!quote.spender || isNativeSource) return { paid: false };
|
|
8245
8698
|
const current = await deps.readAllowance(quote.spender);
|
|
8246
8699
|
debugLog("owney-sdk", "swap: fusion allowance", {
|
|
8247
8700
|
spender: quote.spender,
|
|
8248
8701
|
current: current.toString(),
|
|
8249
8702
|
needed: amount.toString()
|
|
8250
8703
|
});
|
|
8251
|
-
if (current >= amount) return
|
|
8704
|
+
if (current >= amount) return { paid: false };
|
|
8252
8705
|
onStage?.("approving");
|
|
8253
8706
|
await deps.ensureChain(quote.src.chainId);
|
|
8254
8707
|
const permit = quote.permitSupported ? await awaitWalletPrompt(
|
|
@@ -8259,11 +8712,18 @@ async function authoriseSpend(deps, options, amount, isNativeSource) {
|
|
|
8259
8712
|
debugLog("owney-sdk", "swap: permitting limit order protocol", {
|
|
8260
8713
|
bytes: (permit.length - 2) / 2
|
|
8261
8714
|
});
|
|
8262
|
-
return permit;
|
|
8715
|
+
return { permit, paid: false };
|
|
8263
8716
|
}
|
|
8264
8717
|
await deps.approve(quote.spender, MAX_UINT256);
|
|
8265
8718
|
debugLog("owney-sdk", "swap: approved limit order protocol");
|
|
8266
|
-
return
|
|
8719
|
+
return { paid: true };
|
|
8720
|
+
}
|
|
8721
|
+
async function authorisedAmount(deps, options, amount, paid) {
|
|
8722
|
+
if (!paid) return amount;
|
|
8723
|
+
const remaining = await affordableAmount(deps, amount, options.sourceReserve);
|
|
8724
|
+
if (!options.quote.spender || await deps.readAllowance(options.quote.spender) < remaining)
|
|
8725
|
+
throw new OwneyError("SWAP_REQUEST_FAILED", "Swap approval was not confirmed.", { outcome: "not-submitted" });
|
|
8726
|
+
return remaining;
|
|
8267
8727
|
}
|
|
8268
8728
|
async function runFusion(deps, options, walletAddress) {
|
|
8269
8729
|
const { quote, direction, onStage } = options;
|
|
@@ -8275,8 +8735,9 @@ async function runFusion(deps, options, walletAddress) {
|
|
|
8275
8735
|
{ chainId: quote.src.chainId, symbol: quote.src.symbol }
|
|
8276
8736
|
);
|
|
8277
8737
|
}
|
|
8278
|
-
|
|
8279
|
-
const permit = await authoriseSpend(deps, options, amount, false);
|
|
8738
|
+
let amount = await affordableAmount(deps, BigInt(quote.src.amount), options.sourceReserve);
|
|
8739
|
+
const { permit, paid } = await authoriseSpend(deps, options, amount, false);
|
|
8740
|
+
amount = await authorisedAmount(deps, options, amount, paid);
|
|
8280
8741
|
const { secrets, secretHashes } = mintSecrets(quote.secretsCount ?? 1);
|
|
8281
8742
|
onStage?.("building");
|
|
8282
8743
|
debugLog("owney-sdk", "swap: building fusion order", {
|
|
@@ -8295,8 +8756,9 @@ async function runFusion(deps, options, walletAddress) {
|
|
|
8295
8756
|
...direction ? { direction } : {},
|
|
8296
8757
|
...permit ? { permit } : {}
|
|
8297
8758
|
});
|
|
8298
|
-
saveOrder({
|
|
8759
|
+
const durable = saveOrder({
|
|
8299
8760
|
orderHash: built.orderHash,
|
|
8761
|
+
walletAddress,
|
|
8300
8762
|
secrets,
|
|
8301
8763
|
srcChainId: quote.src.chainId,
|
|
8302
8764
|
srcSymbol: quote.src.symbol,
|
|
@@ -8306,33 +8768,39 @@ async function runFusion(deps, options, walletAddress) {
|
|
|
8306
8768
|
createdAt: Date.now()
|
|
8307
8769
|
});
|
|
8308
8770
|
debugLog("owney-sdk", "swap: order built", { orderHash: built.orderHash });
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
(
|
|
8316
|
-
|
|
8317
|
-
|
|
8771
|
+
let signature;
|
|
8772
|
+
try {
|
|
8773
|
+
onStage?.("signing");
|
|
8774
|
+
await deps.ensureChain(quote.src.chainId);
|
|
8775
|
+
signature = await awaitWalletPrompt(() => deps.signTypedData(built.typedData), "sign the swap");
|
|
8776
|
+
options.assertCurrent?.();
|
|
8777
|
+
options.onOrderPrepared?.(built.orderHash, { spent: amount.toString(), expected: built.dstAmount });
|
|
8778
|
+
} catch (error) {
|
|
8779
|
+
clearOrder(built.orderHash);
|
|
8780
|
+
throw error;
|
|
8781
|
+
}
|
|
8318
8782
|
debugLog("owney-sdk", "swap: signed, submitting to relayer");
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8783
|
+
try {
|
|
8784
|
+
await deps.api.submitOrder({
|
|
8785
|
+
srcChainId: quote.src.chainId,
|
|
8786
|
+
// The ORDER STRUCT, not the typed-data envelope we just signed. Sending
|
|
8787
|
+
// the envelope here gets a bare 500 from the relayer.
|
|
8788
|
+
order: built.order,
|
|
8789
|
+
signature,
|
|
8790
|
+
quoteId: built.quoteId,
|
|
8791
|
+
// Single-fill orders must NOT carry secretHashes — the relayer rejects
|
|
8792
|
+
// them with SECRET_HASHES_NOT_REQUIRED. The one hash is already inside the
|
|
8793
|
+
// order's hashlock, so repeating it here is redundant, and only a
|
|
8794
|
+
// multi-fill order (a Merkle tree of hashes) needs them listed.
|
|
8795
|
+
...secretHashes.length > 1 ? { secretHashes } : {},
|
|
8796
|
+
...built.extension ? { extension: built.extension } : {}
|
|
8797
|
+
});
|
|
8798
|
+
} catch (error) {
|
|
8799
|
+
throw submissionUnknown(error, built.orderHash);
|
|
8800
|
+
}
|
|
8333
8801
|
debugLog("owney-sdk", "swap: order submitted, polling escrows");
|
|
8334
8802
|
try {
|
|
8335
|
-
options.onOrderSubmitted?.(built.orderHash);
|
|
8803
|
+
options.onOrderSubmitted?.(built.orderHash, { spent: amount.toString(), expected: built.dstAmount, durable });
|
|
8336
8804
|
} catch {
|
|
8337
8805
|
}
|
|
8338
8806
|
try {
|
|
@@ -8342,13 +8810,25 @@ async function runFusion(deps, options, walletAddress) {
|
|
|
8342
8810
|
...onStage ? { onStage } : {}
|
|
8343
8811
|
});
|
|
8344
8812
|
} catch (error) {
|
|
8345
|
-
if (error instanceof OwneyError &&
|
|
8813
|
+
if (error instanceof OwneyError && error.code === "SWAP_ORDER_REFUNDED" && error.details?.outcome === "refunded") {
|
|
8346
8814
|
clearOrder(built.orderHash);
|
|
8347
8815
|
}
|
|
8348
8816
|
throw error;
|
|
8349
8817
|
}
|
|
8350
8818
|
clearOrder(built.orderHash);
|
|
8351
|
-
return { orderHash: built.orderHash };
|
|
8819
|
+
return { orderHash: built.orderHash, spent: amount.toString() };
|
|
8820
|
+
}
|
|
8821
|
+
function submissionUnknown(error, orderHash) {
|
|
8822
|
+
return new OwneyError(
|
|
8823
|
+
"SWAP_REQUEST_FAILED",
|
|
8824
|
+
"The order submission could not be confirmed. Check this order before starting another swap.",
|
|
8825
|
+
{
|
|
8826
|
+
orderHash,
|
|
8827
|
+
settlementUnconfirmed: true,
|
|
8828
|
+
outcome: "submitted-unknown",
|
|
8829
|
+
...error instanceof OwneyError ? { causeCode: error.code } : {}
|
|
8830
|
+
}
|
|
8831
|
+
);
|
|
8352
8832
|
}
|
|
8353
8833
|
|
|
8354
8834
|
// src/lib/swap/swap.withdrawal-settlement.ts
|
|
@@ -8358,8 +8838,8 @@ var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
8358
8838
|
async function awaitWithdrawalPayouts(options) {
|
|
8359
8839
|
const payouts = new Map(Object.entries(options.results).map(([id, result]) => [id, { result, evidence: result.evidence }]));
|
|
8360
8840
|
const timeoutMs = Math.max(0, options.timeoutMs ?? 18e4);
|
|
8361
|
-
const deadline = Date.now() + timeoutMs;
|
|
8362
|
-
let
|
|
8841
|
+
const deadline = options.deadline ?? Date.now() + timeoutMs;
|
|
8842
|
+
let active2 = true;
|
|
8363
8843
|
let timer;
|
|
8364
8844
|
const incomplete = (reason, agentId) => new OwneyError(
|
|
8365
8845
|
"WITHDRAW_SWAP_INCOMPLETE",
|
|
@@ -8377,7 +8857,7 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
8377
8857
|
);
|
|
8378
8858
|
const assertActive = () => {
|
|
8379
8859
|
options.assertCurrent();
|
|
8380
|
-
if (!
|
|
8860
|
+
if (!active2 || Date.now() > deadline) throw incomplete("PAYOUT_CONFIRMATION_TIMEOUT");
|
|
8381
8861
|
};
|
|
8382
8862
|
const pause = async () => {
|
|
8383
8863
|
assertActive();
|
|
@@ -8388,10 +8868,11 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
8388
8868
|
if (evidence.agentId !== id || evidence.chainId !== options.chainId || evidence.token !== options.token || evidence.walletAddress.toLowerCase() !== options.walletAddress.toLowerCase())
|
|
8389
8869
|
throw incomplete("PAYOUT_SCOPE_MISMATCH", id);
|
|
8390
8870
|
};
|
|
8391
|
-
const
|
|
8871
|
+
const record2 = (id, payout, evidence) => {
|
|
8392
8872
|
assertActive();
|
|
8393
8873
|
validate(id, evidence);
|
|
8394
8874
|
payout.evidence = evidence;
|
|
8875
|
+
payout.result.evidence = evidence;
|
|
8395
8876
|
if (evidence.transfer === "confirmed" && evidence.delivery === "confirmed") payout.amount = rawAmount(evidence.confirmedAmount);
|
|
8396
8877
|
options.onProgress(evidence);
|
|
8397
8878
|
};
|
|
@@ -8456,8 +8937,30 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
8456
8937
|
if (payout.amount === void 0 || payout.amount <= 0n) throw incomplete("INVALID_PAYOUT_AMOUNT", id);
|
|
8457
8938
|
if (["yieldseeker", "zyfai"].includes(id) && (!hash(evidence.txHash) || rawAmount(evidence.blockNumber) === void 0 || !evidence.logIndexes?.length))
|
|
8458
8939
|
throw incomplete("PAYOUT_RECEIPT_UNVERIFIED", id);
|
|
8459
|
-
if (options.requested === void 0 && payout.result.completion === "unverified")
|
|
8460
|
-
|
|
8940
|
+
if (options.requested === void 0 && payout.result.completion === "unverified") {
|
|
8941
|
+
if (!options.verifyCompletion || id === "yieldseeker" && !evidence.maxCompletion)
|
|
8942
|
+
throw incomplete("MAX_COMPLETION_UNVERIFIED", id);
|
|
8943
|
+
let proof = { status: "unavailable" };
|
|
8944
|
+
try {
|
|
8945
|
+
proof = await bounded(
|
|
8946
|
+
() => options.verifyCompletion(evidence),
|
|
8947
|
+
Math.min(1e4, deadline - Date.now()),
|
|
8948
|
+
() => incomplete("MAX_COMPLETION_UNVERIFIED", id)
|
|
8949
|
+
);
|
|
8950
|
+
} catch {
|
|
8951
|
+
assertActive();
|
|
8952
|
+
}
|
|
8953
|
+
assertActive();
|
|
8954
|
+
evidence.maxCompletionCheck = proof;
|
|
8955
|
+
options.onProgress(evidence);
|
|
8956
|
+
if (proof.status === "remaining") throw incomplete("MAX_REMAINDER_VERIFIED", id);
|
|
8957
|
+
if (proof.status !== "complete") {
|
|
8958
|
+
await pause();
|
|
8959
|
+
continue;
|
|
8960
|
+
}
|
|
8961
|
+
payout.result.completion = void 0;
|
|
8962
|
+
payout.result.type = "full";
|
|
8963
|
+
}
|
|
8461
8964
|
const intended = rawAmount(evidence.intendedAmount);
|
|
8462
8965
|
if (intended !== void 0 && payout.amount < intended && verifiedFee(payout) !== intended - payout.amount)
|
|
8463
8966
|
throw incomplete("PAYOUT_SHORTFALL", id);
|
|
@@ -8467,24 +8970,24 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
8467
8970
|
let next = evidence;
|
|
8468
8971
|
if (!next.txHash && (next.userOpHash || next.callsId)) {
|
|
8469
8972
|
next = await options.resolve(next);
|
|
8470
|
-
|
|
8973
|
+
record2(id, payout, next);
|
|
8471
8974
|
}
|
|
8472
8975
|
if (next.transfer === "failed" || next.delivery === "failed") continue;
|
|
8473
8976
|
if (next.txHash) {
|
|
8474
8977
|
next = await options.inspect(next, false);
|
|
8475
|
-
|
|
8978
|
+
record2(id, payout, next);
|
|
8476
8979
|
if (id === "zyfai" && next.transfer === "unknown" && rawAmount(next.blockNumber) !== void 0 && next.logIndexes?.length === 0 && next.transferLogs?.length === 0 && !next.userOpHash && !next.callsId) {
|
|
8477
8980
|
const found = await findPayout(id, next);
|
|
8478
8981
|
if (found) {
|
|
8479
8982
|
next = found;
|
|
8480
|
-
|
|
8983
|
+
record2(id, payout, next);
|
|
8481
8984
|
}
|
|
8482
8985
|
}
|
|
8483
8986
|
} else if (!next.userOpHash && !next.callsId) {
|
|
8484
8987
|
const found = await findPayout(id, next);
|
|
8485
8988
|
if (found) {
|
|
8486
8989
|
next = found;
|
|
8487
|
-
|
|
8990
|
+
record2(id, payout, next);
|
|
8488
8991
|
}
|
|
8489
8992
|
}
|
|
8490
8993
|
if (next.transfer === "confirmed" && next.delivery === "confirmed" || next.transfer === "failed" || next.delivery === "failed") continue;
|
|
@@ -8528,10 +9031,10 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
8528
9031
|
};
|
|
8529
9032
|
try {
|
|
8530
9033
|
return await Promise.race([read2(), new Promise((_, reject) => {
|
|
8531
|
-
timer = setTimeout(() => reject(incomplete("PAYOUT_CONFIRMATION_TIMEOUT")),
|
|
9034
|
+
timer = setTimeout(() => reject(incomplete("PAYOUT_CONFIRMATION_TIMEOUT")), Math.max(0, deadline - Date.now()));
|
|
8532
9035
|
})]);
|
|
8533
9036
|
} finally {
|
|
8534
|
-
|
|
9037
|
+
active2 = false;
|
|
8535
9038
|
if (timer) clearTimeout(timer);
|
|
8536
9039
|
}
|
|
8537
9040
|
}
|
|
@@ -8549,8 +9052,8 @@ function verifiedFee(payout) {
|
|
|
8549
9052
|
}
|
|
8550
9053
|
|
|
8551
9054
|
// src/lib/swap/swap.permit.ts
|
|
8552
|
-
var
|
|
8553
|
-
var PERMIT_ABI = (0,
|
|
9055
|
+
var import_viem17 = require("viem");
|
|
9056
|
+
var PERMIT_ABI = (0, import_viem17.parseAbi)([
|
|
8554
9057
|
"function nonces(address owner) view returns (uint256)",
|
|
8555
9058
|
"function name() view returns (string)",
|
|
8556
9059
|
"function version() view returns (string)",
|
|
@@ -8577,7 +9080,7 @@ async function tryRead(read2) {
|
|
|
8577
9080
|
function matchDomain(name, versions, chainId, token, onChainSeparator) {
|
|
8578
9081
|
for (const version of versions) {
|
|
8579
9082
|
const domain = { name, version, chainId, verifyingContract: token };
|
|
8580
|
-
if ((0,
|
|
9083
|
+
if ((0, import_viem17.domainSeparator)({ domain }) === onChainSeparator) return domain;
|
|
8581
9084
|
}
|
|
8582
9085
|
return null;
|
|
8583
9086
|
}
|
|
@@ -8630,9 +9133,9 @@ async function buildErc2612Permit(reads, input) {
|
|
|
8630
9133
|
primaryType: "Permit",
|
|
8631
9134
|
message: { owner, spender, value, nonce, deadline }
|
|
8632
9135
|
});
|
|
8633
|
-
const { r, s, v, yParity } = (0,
|
|
9136
|
+
const { r, s, v, yParity } = (0, import_viem17.parseSignature)(signature);
|
|
8634
9137
|
const recoveryV = v ?? BigInt(yParity + 27);
|
|
8635
|
-
return (0,
|
|
9138
|
+
return (0, import_viem17.encodeAbiParameters)(
|
|
8636
9139
|
[
|
|
8637
9140
|
{ type: "address" },
|
|
8638
9141
|
{ type: "address" },
|
|
@@ -8648,7 +9151,7 @@ async function buildErc2612Permit(reads, input) {
|
|
|
8648
9151
|
|
|
8649
9152
|
// src/lib/swap/swap.withdraw-swap.ts
|
|
8650
9153
|
var KEY_PREFIX5 = "owney.swap.withdraw";
|
|
8651
|
-
var
|
|
9154
|
+
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
8652
9155
|
var storage5 = () => {
|
|
8653
9156
|
if (typeof window === "undefined") return null;
|
|
8654
9157
|
try {
|
|
@@ -8673,15 +9176,19 @@ var notify = () => {
|
|
|
8673
9176
|
}
|
|
8674
9177
|
}
|
|
8675
9178
|
};
|
|
8676
|
-
function saveWithdrawSwap(
|
|
9179
|
+
function saveWithdrawSwap(record2) {
|
|
8677
9180
|
const store = storage5();
|
|
8678
|
-
if (!store) return;
|
|
9181
|
+
if (!store) return false;
|
|
8679
9182
|
try {
|
|
8680
|
-
store.
|
|
9183
|
+
const current = store.getItem(keyFor3(record2.orderHash));
|
|
9184
|
+
const value = JSON.stringify({ ...current ? JSON.parse(current) : {}, ...record2 });
|
|
9185
|
+
store.setItem(keyFor3(record2.orderHash), value);
|
|
9186
|
+
if (store.getItem(keyFor3(record2.orderHash)) !== value) return false;
|
|
8681
9187
|
} catch {
|
|
8682
|
-
return;
|
|
9188
|
+
return false;
|
|
8683
9189
|
}
|
|
8684
9190
|
notify();
|
|
9191
|
+
return true;
|
|
8685
9192
|
}
|
|
8686
9193
|
function updateWithdrawSwap(orderHash, patch) {
|
|
8687
9194
|
const store = storage5();
|
|
@@ -8704,13 +9211,17 @@ function clearWithdrawSwap(orderHash) {
|
|
|
8704
9211
|
const store = storage5();
|
|
8705
9212
|
if (!store) return;
|
|
8706
9213
|
try {
|
|
8707
|
-
store.
|
|
9214
|
+
const raw2 = store.getItem(keyFor3(orderHash));
|
|
9215
|
+
const record2 = raw2 ? JSON.parse(raw2) : void 0;
|
|
9216
|
+
if (record2 && !["landed", "refunded", "not-swapped"].includes(record2.status))
|
|
9217
|
+
store.setItem(keyFor3(orderHash), JSON.stringify({ ...record2, dismissed: true }));
|
|
9218
|
+
else store.removeItem(keyFor3(orderHash));
|
|
8708
9219
|
} catch {
|
|
8709
9220
|
return;
|
|
8710
9221
|
}
|
|
8711
9222
|
notify();
|
|
8712
9223
|
}
|
|
8713
|
-
function listWithdrawSwaps(now = Date.now()) {
|
|
9224
|
+
function listWithdrawSwaps(now = Date.now(), includeDismissed = false) {
|
|
8714
9225
|
const store = storage5();
|
|
8715
9226
|
if (!store) return [];
|
|
8716
9227
|
const out = [];
|
|
@@ -8725,11 +9236,11 @@ function listWithdrawSwaps(now = Date.now()) {
|
|
|
8725
9236
|
if (!raw2) continue;
|
|
8726
9237
|
try {
|
|
8727
9238
|
const parsed = JSON.parse(raw2);
|
|
8728
|
-
if (now - parsed.createdAt >
|
|
9239
|
+
if (["landed", "refunded", "not-swapped"].includes(parsed.status) && now - (parsed.settledAt ?? parsed.createdAt) > MAX_AGE_MS) {
|
|
8729
9240
|
store.removeItem(key2);
|
|
8730
9241
|
continue;
|
|
8731
9242
|
}
|
|
8732
|
-
out.push(parsed);
|
|
9243
|
+
if (includeDismissed || !parsed.dismissed) out.push(parsed);
|
|
8733
9244
|
} catch {
|
|
8734
9245
|
store.removeItem(key2);
|
|
8735
9246
|
}
|
|
@@ -8739,10 +9250,15 @@ function listWithdrawSwaps(now = Date.now()) {
|
|
|
8739
9250
|
}
|
|
8740
9251
|
return out.sort((a, b) => b.createdAt - a.createdAt);
|
|
8741
9252
|
}
|
|
9253
|
+
if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
|
|
9254
|
+
window.addEventListener("storage", (event) => {
|
|
9255
|
+
if (event.key === null || event.key.startsWith(`${KEY_PREFIX5}.`)) notify();
|
|
9256
|
+
});
|
|
9257
|
+
}
|
|
8742
9258
|
|
|
8743
9259
|
// src/lib/swap/swap.pending-deposit.ts
|
|
8744
9260
|
var KEY_PREFIX6 = "owney.swap.pending-deposit";
|
|
8745
|
-
var
|
|
9261
|
+
var MAX_AGE_MS2 = 24 * 60 * 60 * 1e3;
|
|
8746
9262
|
var storage6 = () => {
|
|
8747
9263
|
if (typeof window === "undefined") return null;
|
|
8748
9264
|
try {
|
|
@@ -8785,7 +9301,7 @@ function listPendingDeposits(now = Date.now()) {
|
|
|
8785
9301
|
if (!raw2) continue;
|
|
8786
9302
|
try {
|
|
8787
9303
|
const parsed = JSON.parse(raw2);
|
|
8788
|
-
if (now - parsed.createdAt >
|
|
9304
|
+
if (now - parsed.createdAt > MAX_AGE_MS2) {
|
|
8789
9305
|
store.removeItem(key2);
|
|
8790
9306
|
continue;
|
|
8791
9307
|
}
|
|
@@ -8834,7 +9350,7 @@ async function withFailureReporting(apiKey, agentType, fn, baseUrl) {
|
|
|
8834
9350
|
}
|
|
8835
9351
|
|
|
8836
9352
|
// src/lib/helpers/withdraw-helper.ts
|
|
8837
|
-
var
|
|
9353
|
+
var import_viem18 = require("viem");
|
|
8838
9354
|
function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
|
|
8839
9355
|
return agents.map((agent) => {
|
|
8840
9356
|
let invalidBalance = false;
|
|
@@ -8844,7 +9360,7 @@ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decima
|
|
|
8844
9360
|
return 0n;
|
|
8845
9361
|
}
|
|
8846
9362
|
const [whole, fraction] = value.split(".");
|
|
8847
|
-
return raw2 ? BigInt(value) : (0,
|
|
9363
|
+
return raw2 ? BigInt(value) : (0, import_viem18.parseUnits)(fraction === void 0 ? whole : `${whole}.${fraction.slice(0, decimals)}`, decimals);
|
|
8848
9364
|
};
|
|
8849
9365
|
const agentBalance = aggregated[agent.id];
|
|
8850
9366
|
const tokenBalance = agentBalance?.tokens.find(
|
|
@@ -9044,11 +9560,11 @@ function aggregateDailyEarnings(series, tokenSymbol) {
|
|
|
9044
9560
|
const byAsset = /* @__PURE__ */ new Map();
|
|
9045
9561
|
for (const account of series) {
|
|
9046
9562
|
for (const { asset, points } of account.assets) {
|
|
9047
|
-
const
|
|
9048
|
-
if (!points.length || wanted &&
|
|
9049
|
-
const components = byAsset.get(
|
|
9563
|
+
const symbol2 = asset.toUpperCase();
|
|
9564
|
+
if (!points.length || wanted && symbol2 !== wanted) continue;
|
|
9565
|
+
const components = byAsset.get(symbol2) ?? [];
|
|
9050
9566
|
components.push([...points].sort((a, b) => a.date.localeCompare(b.date)));
|
|
9051
|
-
byAsset.set(
|
|
9567
|
+
byAsset.set(symbol2, components);
|
|
9052
9568
|
}
|
|
9053
9569
|
}
|
|
9054
9570
|
return [...byAsset.entries()].map(([asset, components]) => {
|
|
@@ -9081,111 +9597,6 @@ var import_chains6 = require("viem/chains");
|
|
|
9081
9597
|
|
|
9082
9598
|
// src/lib/sponsored-token-batch.ts
|
|
9083
9599
|
var import_viem19 = require("viem");
|
|
9084
|
-
|
|
9085
|
-
// src/lib/permit2-batch.ts
|
|
9086
|
-
var import_viem18 = require("viem");
|
|
9087
|
-
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
9088
|
-
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
9089
|
-
var ERC2612_PERMIT_TYPES = {
|
|
9090
|
-
Permit: [
|
|
9091
|
-
{ name: "owner", type: "address" },
|
|
9092
|
-
{ name: "spender", type: "address" },
|
|
9093
|
-
{ name: "value", type: "uint256" },
|
|
9094
|
-
{ name: "nonce", type: "uint256" },
|
|
9095
|
-
{ name: "deadline", type: "uint256" }
|
|
9096
|
-
]
|
|
9097
|
-
};
|
|
9098
|
-
var ERC2612_READ_ABI = (0, import_viem18.parseAbi)([
|
|
9099
|
-
"function name() view returns (string)",
|
|
9100
|
-
"function version() view returns (string)",
|
|
9101
|
-
"function nonces(address owner) view returns (uint256)"
|
|
9102
|
-
]);
|
|
9103
|
-
function erc2612TypedData(input) {
|
|
9104
|
-
return {
|
|
9105
|
-
domain: {
|
|
9106
|
-
name: input.permit.tokenName,
|
|
9107
|
-
version: input.permit.tokenVersion,
|
|
9108
|
-
chainId: input.chainId,
|
|
9109
|
-
verifyingContract: input.token
|
|
9110
|
-
},
|
|
9111
|
-
types: ERC2612_PERMIT_TYPES,
|
|
9112
|
-
primaryType: "Permit",
|
|
9113
|
-
message: {
|
|
9114
|
-
owner: input.owner,
|
|
9115
|
-
spender: BATCH_PERMIT2_ADDRESS,
|
|
9116
|
-
value: BigInt(input.permit.value),
|
|
9117
|
-
nonce: BigInt(input.permit.nonce),
|
|
9118
|
-
deadline: BigInt(input.permit.deadline)
|
|
9119
|
-
}
|
|
9120
|
-
};
|
|
9121
|
-
}
|
|
9122
|
-
var PERMIT_BATCH_TYPES = {
|
|
9123
|
-
PermitBatchWitnessTransferFrom: [
|
|
9124
|
-
{ name: "permitted", type: "TokenPermissions[]" },
|
|
9125
|
-
{ name: "spender", type: "address" },
|
|
9126
|
-
{ name: "nonce", type: "uint256" },
|
|
9127
|
-
{ name: "deadline", type: "uint256" },
|
|
9128
|
-
{ name: "witness", type: "Deposit" }
|
|
9129
|
-
],
|
|
9130
|
-
Deposit: [{ name: "recipients", type: "address[]" }],
|
|
9131
|
-
TokenPermissions: [
|
|
9132
|
-
{ name: "token", type: "address" },
|
|
9133
|
-
{ name: "amount", type: "uint256" }
|
|
9134
|
-
]
|
|
9135
|
-
};
|
|
9136
|
-
var PERMIT2_BATCH_ABI = (0, import_viem18.parseAbi)([
|
|
9137
|
-
"struct TokenPermissions { address token; uint256 amount; }",
|
|
9138
|
-
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
9139
|
-
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
9140
|
-
"function permitWitnessTransferFrom(PermitBatchTransferFrom permit,SignatureTransferDetails[] transferDetails,address owner,bytes32 witness,string witnessTypeString,bytes signature)",
|
|
9141
|
-
"function nonceBitmap(address owner,uint256 wordPos) view returns (uint256)"
|
|
9142
|
-
]);
|
|
9143
|
-
function batchPermit(b) {
|
|
9144
|
-
return {
|
|
9145
|
-
permitted: b.transfers.map((t) => ({
|
|
9146
|
-
token: b.token,
|
|
9147
|
-
amount: BigInt(t.amount)
|
|
9148
|
-
})),
|
|
9149
|
-
nonce: BigInt(b.nonce),
|
|
9150
|
-
deadline: BigInt(b.deadline)
|
|
9151
|
-
};
|
|
9152
|
-
}
|
|
9153
|
-
function batchTypedData(b, spender) {
|
|
9154
|
-
return {
|
|
9155
|
-
domain: {
|
|
9156
|
-
name: "Permit2",
|
|
9157
|
-
chainId: b.chainId,
|
|
9158
|
-
verifyingContract: BATCH_PERMIT2_ADDRESS
|
|
9159
|
-
},
|
|
9160
|
-
types: PERMIT_BATCH_TYPES,
|
|
9161
|
-
primaryType: "PermitBatchWitnessTransferFrom",
|
|
9162
|
-
message: {
|
|
9163
|
-
...batchPermit(b),
|
|
9164
|
-
spender,
|
|
9165
|
-
witness: { recipients: b.transfers.map((t) => t.to) }
|
|
9166
|
-
}
|
|
9167
|
-
};
|
|
9168
|
-
}
|
|
9169
|
-
var BATCH_WITNESS_TYPE = "Deposit witness)Deposit(address[] recipients)TokenPermissions(address token,uint256 amount)";
|
|
9170
|
-
function batchWitness(b) {
|
|
9171
|
-
return (0, import_viem18.hashStruct)({
|
|
9172
|
-
types: PERMIT_BATCH_TYPES,
|
|
9173
|
-
primaryType: "Deposit",
|
|
9174
|
-
data: { recipients: b.transfers.map((t) => t.to) }
|
|
9175
|
-
});
|
|
9176
|
-
}
|
|
9177
|
-
function batchArgs(b) {
|
|
9178
|
-
return [
|
|
9179
|
-
batchPermit(b),
|
|
9180
|
-
b.transfers.map((t) => ({ to: t.to, requestedAmount: BigInt(t.amount) })),
|
|
9181
|
-
b.from,
|
|
9182
|
-
batchWitness(b),
|
|
9183
|
-
BATCH_WITNESS_TYPE,
|
|
9184
|
-
b.signature
|
|
9185
|
-
];
|
|
9186
|
-
}
|
|
9187
|
-
|
|
9188
|
-
// src/lib/sponsored-token-batch.ts
|
|
9189
9600
|
var memory = /* @__PURE__ */ new Map();
|
|
9190
9601
|
var settledRecords = /* @__PURE__ */ new Map();
|
|
9191
9602
|
var expiredRecords = /* @__PURE__ */ new Map();
|
|
@@ -9213,14 +9624,14 @@ async function releaseExpiredTokenBatch(i) {
|
|
|
9213
9624
|
let timer;
|
|
9214
9625
|
let expired = false;
|
|
9215
9626
|
const check = async () => {
|
|
9216
|
-
let
|
|
9217
|
-
if (
|
|
9218
|
-
const current = JSON.parse(
|
|
9627
|
+
let active2 = read(key2);
|
|
9628
|
+
if (active2 && i.expectedTxHash) {
|
|
9629
|
+
const current = JSON.parse(active2);
|
|
9219
9630
|
if (!current.serializedTransaction || (0, import_viem19.keccak256)(current.serializedTransaction).toLowerCase() !== i.expectedTxHash.toLowerCase())
|
|
9220
|
-
|
|
9631
|
+
active2 = void 0;
|
|
9221
9632
|
}
|
|
9222
9633
|
const archiveKey = i.expectedTxHash ? `${key2}:expired:${i.expectedTxHash.toLowerCase()}` : void 0;
|
|
9223
|
-
const saved =
|
|
9634
|
+
const saved = active2 ?? (archiveKey && (expiredRecords.get(archiveKey) ?? (typeof window === "undefined" ? void 0 : window.localStorage.getItem(archiveKey))));
|
|
9224
9635
|
if (!saved) return;
|
|
9225
9636
|
const body = JSON.parse(saved);
|
|
9226
9637
|
if (!body.serializedTransaction || body.chainId !== i.chainId || !(0, import_viem19.isAddressEqual)(body.from, i.owner) || !(0, import_viem19.isAddressEqual)(body.token, i.token) || !body.transfers?.length) return;
|
|
@@ -9244,8 +9655,8 @@ async function releaseExpiredTokenBatch(i) {
|
|
|
9244
9655
|
args: [i.owner, nonce >> 8n],
|
|
9245
9656
|
blockNumber: block.number
|
|
9246
9657
|
});
|
|
9247
|
-
if (expired || typeof bitmap !== "bigint" || bitmap < 0n || bitmap > MAX_UINT256 || (bitmap & 1n << (nonce & 255n)) !== 0n ||
|
|
9248
|
-
if (
|
|
9658
|
+
if (expired || typeof bitmap !== "bigint" || bitmap < 0n || bitmap > MAX_UINT256 || (bitmap & 1n << (nonce & 255n)) !== 0n || active2 && read(key2) !== active2) return;
|
|
9659
|
+
if (active2) {
|
|
9249
9660
|
const terminalKey = `${key2}:expired:${txHash.toLowerCase()}`;
|
|
9250
9661
|
expiredRecords.set(terminalKey, saved);
|
|
9251
9662
|
let persisted = typeof window === "undefined";
|
|
@@ -9303,15 +9714,15 @@ function clear(key2, body) {
|
|
|
9303
9714
|
function sponsorTokenBatch(i) {
|
|
9304
9715
|
const key2 = pendingKey(i);
|
|
9305
9716
|
const plan = planOf(i.transfers);
|
|
9306
|
-
const
|
|
9307
|
-
if (
|
|
9308
|
-
if (
|
|
9717
|
+
const active2 = inflight.get(key2);
|
|
9718
|
+
if (active2) {
|
|
9719
|
+
if (active2.plan !== plan || i.resumeTxHash && pendingTokenBatch(i)?.txHash !== i.resumeTxHash)
|
|
9309
9720
|
return Promise.reject(
|
|
9310
9721
|
new Error(
|
|
9311
9722
|
"A token deposit is already in progress. Wait for its result before depositing again."
|
|
9312
9723
|
)
|
|
9313
9724
|
);
|
|
9314
|
-
return
|
|
9725
|
+
return active2.promise;
|
|
9315
9726
|
}
|
|
9316
9727
|
const promise = execute(i, key2, plan).finally(() => inflight.delete(key2));
|
|
9317
9728
|
inflight.set(key2, { plan, promise });
|
|
@@ -9465,7 +9876,7 @@ async function execute(i, key2, plan) {
|
|
|
9465
9876
|
"DEPOSIT_INSUFFICIENT_BALANCE",
|
|
9466
9877
|
"Insufficient token balance for this deposit."
|
|
9467
9878
|
);
|
|
9468
|
-
if (allowance < total && (i.chainId
|
|
9879
|
+
if (allowance < total && !supportsSponsoredErc2612(i.chainId, i.token))
|
|
9469
9880
|
throw new OwneyError(
|
|
9470
9881
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
9471
9882
|
"token deposits need a one-time Permit2 approval."
|
|
@@ -9501,7 +9912,7 @@ async function execute(i, key2, plan) {
|
|
|
9501
9912
|
const signature2 = await i.wallet.signTypedData({
|
|
9502
9913
|
account: i.owner,
|
|
9503
9914
|
...erc2612TypedData({
|
|
9504
|
-
chainId:
|
|
9915
|
+
chainId: i.chainId,
|
|
9505
9916
|
token: i.token,
|
|
9506
9917
|
owner: i.owner,
|
|
9507
9918
|
permit: unsignedPermit
|
|
@@ -10129,12 +10540,12 @@ var OwneySDK = class {
|
|
|
10129
10540
|
return [...this.getActiveAgents().keys()].sort();
|
|
10130
10541
|
}
|
|
10131
10542
|
getActiveAgents() {
|
|
10132
|
-
const
|
|
10543
|
+
const active2 = /* @__PURE__ */ new Map();
|
|
10133
10544
|
for (const id of this.activeAgents) {
|
|
10134
10545
|
const agent = this.agents.get(id);
|
|
10135
|
-
if (agent)
|
|
10546
|
+
if (agent) active2.set(id, agent);
|
|
10136
10547
|
}
|
|
10137
|
-
return
|
|
10548
|
+
return active2;
|
|
10138
10549
|
}
|
|
10139
10550
|
/**
|
|
10140
10551
|
* Read the org's agent policy once and reuse it. fetchOrgAgentConfig never
|
|
@@ -10717,10 +11128,10 @@ var OwneySDK = class {
|
|
|
10717
11128
|
* Invokes `agent.deposit` with the resolved sponsored callback, composing
|
|
10718
11129
|
* two independent auto-recovery mechanisms:
|
|
10719
11130
|
*
|
|
10720
|
-
* 1. Missing Permit2 allowance outside the
|
|
11131
|
+
* 1. Missing Permit2 allowance outside the sponsored ERC-2612 path: when the
|
|
10721
11132
|
* app did not supply its own callback and the attempt fails with
|
|
10722
11133
|
* `PERMIT2_APPROVAL_REQUIRED`, this is the wallet's first Permit2 deposit
|
|
10723
|
-
* for that token. Base USDC
|
|
11134
|
+
* for that token. Native Base/Arbitrum USDC uses gasless ERC-2612 approval in
|
|
10724
11135
|
* its sponsored deposit and never reaches this branch. Other tokens send
|
|
10725
11136
|
* the one-time user-paid Permit2 approval via `approvePermit2()` and
|
|
10726
11137
|
* retry the SAME sponsored attempt once. Bounded to one approval attempt
|
|
@@ -11227,8 +11638,8 @@ var OwneySDK = class {
|
|
|
11227
11638
|
async ensureSwapChain(chainId) {
|
|
11228
11639
|
const provider = this.requireConnectedProvider();
|
|
11229
11640
|
const state = this.requireState();
|
|
11230
|
-
const
|
|
11231
|
-
if (!
|
|
11641
|
+
const chain2 = swapChain(chainId);
|
|
11642
|
+
if (!chain2) {
|
|
11232
11643
|
throw new OwneyError(
|
|
11233
11644
|
"CHAIN_UNSUPPORTED",
|
|
11234
11645
|
`Chain ${chainId} is not supported`,
|
|
@@ -11236,10 +11647,10 @@ var OwneySDK = class {
|
|
|
11236
11647
|
);
|
|
11237
11648
|
}
|
|
11238
11649
|
await ensureWalletOnChain(
|
|
11239
|
-
(0, import_viem21.createPublicClient)({ chain, transport: (0, import_viem21.custom)(provider) }),
|
|
11650
|
+
(0, import_viem21.createPublicClient)({ chain: chain2, transport: (0, import_viem21.custom)(provider) }),
|
|
11240
11651
|
(0, import_viem21.createWalletClient)({
|
|
11241
11652
|
account: state.walletAddress,
|
|
11242
|
-
chain,
|
|
11653
|
+
chain: chain2,
|
|
11243
11654
|
transport: (0, import_viem21.custom)(provider)
|
|
11244
11655
|
}),
|
|
11245
11656
|
chainId
|
|
@@ -11263,15 +11674,15 @@ var OwneySDK = class {
|
|
|
11263
11674
|
async wrapNativeSource(quote, walletAddress, onStage, direction) {
|
|
11264
11675
|
const wrapper = requireWrapper(quote);
|
|
11265
11676
|
const chainId = quote.src.chainId;
|
|
11266
|
-
const
|
|
11677
|
+
const chain2 = swapChain(chainId);
|
|
11267
11678
|
const provider = this.requireConnectedProvider();
|
|
11268
11679
|
const publicClient = (0, import_viem21.createPublicClient)({
|
|
11269
|
-
chain,
|
|
11680
|
+
chain: chain2,
|
|
11270
11681
|
transport: swapReadTransport(chainId, this.zyfaiRpcUrls)
|
|
11271
11682
|
});
|
|
11272
11683
|
const wallet = (0, import_viem21.createWalletClient)({
|
|
11273
11684
|
account: walletAddress,
|
|
11274
|
-
chain,
|
|
11685
|
+
chain: chain2,
|
|
11275
11686
|
transport: (0, import_viem21.custom)(provider)
|
|
11276
11687
|
});
|
|
11277
11688
|
onStage?.("wrapping");
|
|
@@ -11298,7 +11709,7 @@ var OwneySDK = class {
|
|
|
11298
11709
|
data: tx.data,
|
|
11299
11710
|
value: BigInt(tx.value),
|
|
11300
11711
|
account: walletAddress,
|
|
11301
|
-
chain
|
|
11712
|
+
chain: chain2
|
|
11302
11713
|
});
|
|
11303
11714
|
onSubmitted?.(hash2);
|
|
11304
11715
|
const receipt = await publicClient.waitForTransactionReceipt({
|
|
@@ -11368,15 +11779,27 @@ var OwneySDK = class {
|
|
|
11368
11779
|
* Kept as a builder rather than baked into the executor so the whole swap
|
|
11369
11780
|
* flow stays testable without a provider — the executor never imports viem.
|
|
11370
11781
|
*/
|
|
11371
|
-
buildSwapDeps(quote) {
|
|
11782
|
+
buildSwapDeps(quote, assertOperation, onTransactionHash) {
|
|
11372
11783
|
const state = this.requireState();
|
|
11373
11784
|
const provider = this.requireConnectedProvider();
|
|
11785
|
+
const revision = this.connectionRevision;
|
|
11786
|
+
const assertWallet = () => {
|
|
11787
|
+
assertOperation?.();
|
|
11788
|
+
if (this.state !== state || this.connectionRevision !== revision)
|
|
11789
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed before swap authorization.");
|
|
11790
|
+
};
|
|
11374
11791
|
const srcChain = swapChain(quote.src.chainId);
|
|
11375
11792
|
const dstChain = swapChain(quote.dst.chainId);
|
|
11376
11793
|
const wallet = (0, import_viem21.createWalletClient)({
|
|
11377
11794
|
account: state.walletAddress,
|
|
11378
11795
|
chain: srcChain,
|
|
11379
|
-
|
|
11796
|
+
// viem can await chain/account preparation before dispatching a wallet
|
|
11797
|
+
// request. Recheck at the transport boundary so an expired attempt or
|
|
11798
|
+
// replaced session cannot send after that internal await completes.
|
|
11799
|
+
transport: (0, import_viem21.custom)({ request: (args) => {
|
|
11800
|
+
assertWallet();
|
|
11801
|
+
return provider.request(args);
|
|
11802
|
+
} }, { retryCount: 0 })
|
|
11380
11803
|
});
|
|
11381
11804
|
const srcPublic = (0, import_viem21.createPublicClient)({
|
|
11382
11805
|
chain: srcChain,
|
|
@@ -11405,6 +11828,7 @@ var OwneySDK = class {
|
|
|
11405
11828
|
});
|
|
11406
11829
|
},
|
|
11407
11830
|
sendTransaction: async (tx) => {
|
|
11831
|
+
assertWallet();
|
|
11408
11832
|
const hash2 = await wallet.sendTransaction({
|
|
11409
11833
|
to: tx.to,
|
|
11410
11834
|
data: tx.data,
|
|
@@ -11412,24 +11836,34 @@ var OwneySDK = class {
|
|
|
11412
11836
|
account: state.walletAddress,
|
|
11413
11837
|
chain: srcChain
|
|
11414
11838
|
});
|
|
11839
|
+
onTransactionHash?.(hash2);
|
|
11415
11840
|
const receipt = await srcPublic.waitForTransactionReceipt({
|
|
11416
11841
|
timeout: receiptTimeoutMs(quote.src.chainId),
|
|
11417
11842
|
hash: hash2,
|
|
11418
11843
|
confirmations: 1
|
|
11844
|
+
}).catch(() => {
|
|
11845
|
+
throw new OwneyError(
|
|
11846
|
+
"SWAP_REQUEST_FAILED",
|
|
11847
|
+
"Swap receipt is not confirmed yet.",
|
|
11848
|
+
{ txHash: hash2, settlementUnconfirmed: true, outcome: "submitted-unknown" }
|
|
11849
|
+
);
|
|
11419
11850
|
});
|
|
11420
11851
|
if (receipt.status !== "success") {
|
|
11421
11852
|
throw new OwneyError(
|
|
11422
11853
|
"SWAP_REQUEST_FAILED",
|
|
11423
11854
|
`Swap transaction reverted (tx ${hash2})`,
|
|
11424
|
-
{
|
|
11855
|
+
{ txHash: hash2, outcome: "not-submitted", executionFailed: true }
|
|
11425
11856
|
);
|
|
11426
11857
|
}
|
|
11427
11858
|
return hash2;
|
|
11428
11859
|
},
|
|
11429
|
-
signTypedData: (typedData) =>
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11860
|
+
signTypedData: (typedData) => {
|
|
11861
|
+
assertWallet();
|
|
11862
|
+
return wallet.signTypedData({
|
|
11863
|
+
account: state.walletAddress,
|
|
11864
|
+
...typedData
|
|
11865
|
+
});
|
|
11866
|
+
},
|
|
11433
11867
|
// Chain-bound like every other read here: the wallet provider's chain is
|
|
11434
11868
|
// not ours to rely on mid-swap.
|
|
11435
11869
|
readSourceBalance: async () => {
|
|
@@ -11451,6 +11885,7 @@ var OwneySDK = class {
|
|
|
11451
11885
|
args: [state.walletAddress, spender]
|
|
11452
11886
|
}),
|
|
11453
11887
|
approve: async (spender, amount) => {
|
|
11888
|
+
assertWallet();
|
|
11454
11889
|
const hash2 = await wallet.writeContract({
|
|
11455
11890
|
address: quote.src.address,
|
|
11456
11891
|
abi: ERC20_ALLOWANCE_ABI,
|
|
@@ -11459,14 +11894,22 @@ var OwneySDK = class {
|
|
|
11459
11894
|
account: state.walletAddress,
|
|
11460
11895
|
chain: srcChain
|
|
11461
11896
|
});
|
|
11462
|
-
await srcPublic.waitForTransactionReceipt({
|
|
11897
|
+
const receipt = await srcPublic.waitForTransactionReceipt({
|
|
11463
11898
|
hash: hash2,
|
|
11464
11899
|
confirmations: 1,
|
|
11465
11900
|
timeout: receiptTimeoutMs(quote.src.chainId)
|
|
11466
11901
|
});
|
|
11902
|
+
if (receipt.status !== "success") throw new OwneyError(
|
|
11903
|
+
"SWAP_REQUEST_FAILED",
|
|
11904
|
+
"Swap approval reverted.",
|
|
11905
|
+
{ hash: hash2, phase: "approval", outcome: "not-submitted" }
|
|
11906
|
+
);
|
|
11467
11907
|
return hash2;
|
|
11468
11908
|
},
|
|
11469
|
-
ensureChain: (chainId) =>
|
|
11909
|
+
ensureChain: (chainId) => {
|
|
11910
|
+
assertWallet();
|
|
11911
|
+
return this.ensureSwapChain(chainId);
|
|
11912
|
+
},
|
|
11470
11913
|
/**
|
|
11471
11914
|
* Sign the approval instead of paying for it. Null means the token
|
|
11472
11915
|
* cannot be permitted (WETH has no `permit`) or its domain could not be
|
|
@@ -11486,10 +11929,13 @@ var OwneySDK = class {
|
|
|
11486
11929
|
spender,
|
|
11487
11930
|
value: amount,
|
|
11488
11931
|
chainId: quote.src.chainId,
|
|
11489
|
-
signTypedData: (payload) =>
|
|
11490
|
-
|
|
11491
|
-
|
|
11492
|
-
|
|
11932
|
+
signTypedData: (payload) => {
|
|
11933
|
+
assertWallet();
|
|
11934
|
+
return wallet.signTypedData({
|
|
11935
|
+
account: state.walletAddress,
|
|
11936
|
+
...payload
|
|
11937
|
+
});
|
|
11938
|
+
}
|
|
11493
11939
|
}
|
|
11494
11940
|
),
|
|
11495
11941
|
/**
|
|
@@ -11539,36 +11985,20 @@ var OwneySDK = class {
|
|
|
11539
11985
|
);
|
|
11540
11986
|
}
|
|
11541
11987
|
/**
|
|
11542
|
-
*
|
|
11543
|
-
*
|
|
11544
|
-
*
|
|
11545
|
-
*
|
|
11546
|
-
*
|
|
11547
|
-
*
|
|
11548
|
-
* Something does. A first WETH deposit needs a one-time Permit2 approval,
|
|
11549
|
-
* and that approval is a transaction. A wallet holding no native gas token -
|
|
11550
|
-
* the normal state of someone who just swapped everything INTO WETH - pays
|
|
11551
|
-
* for it out of the WETH. The deposit then asked for ~2e12 wei more than was
|
|
11552
|
-
* left and refused with DEPOSIT_INSUFFICIENT_BALANCE, under a wrapper saying
|
|
11553
|
-
* the funds were safe but unadded (ROUT-242).
|
|
11554
|
-
*
|
|
11555
|
-
* So the approval happens here, before the amount is settled, and the amount
|
|
11556
|
-
* comes from a read taken after it. Two things this deliberately is not:
|
|
11557
|
-
*
|
|
11558
|
-
* - It is not a balance sweep. `received` is a DELTA over the pre-swap
|
|
11559
|
-
* balance, so a wallet already holding WETH reads far above it; depositing
|
|
11560
|
-
* the balance would move funds the user never offered. The arrival is the
|
|
11561
|
-
* ceiling and the balance is only ever allowed to lower it.
|
|
11562
|
-
* - It does not touch USDC, which moves on signed typed data and so spends
|
|
11563
|
-
* nothing of the user's in between, nor a paymaster-configured client,
|
|
11564
|
-
* where the sponsor covers the transfer and Permit2 never applies.
|
|
11988
|
+
* Finish any user-paid Permit2 approval before measuring the deposit.
|
|
11989
|
+
* Wallets can charge approval gas in the received token (ROUT-242/544).
|
|
11990
|
+
* Cap the result at the swap arrival to preserve pre-existing wallet funds.
|
|
11991
|
+
* Native Base/Arbitrum USDC uses sponsored ERC-2612; configured paymaster
|
|
11992
|
+
* wallet calls also cover approval without spending the received tokens.
|
|
11565
11993
|
*/
|
|
11566
|
-
async settleSwapDepositAmount(chainId, token,
|
|
11567
|
-
if (
|
|
11994
|
+
async settleSwapDepositAmount(chainId, token, symbol2, received) {
|
|
11995
|
+
if (symbol2 !== "WETH" && symbol2 !== "USDC" || this.paymasterServiceUrl || supportsSponsoredErc2612(chainId, token)) {
|
|
11568
11996
|
return received.toString();
|
|
11569
11997
|
}
|
|
11570
11998
|
const allowance = await this.readTokenPermit2Allowance(chainId, token);
|
|
11571
|
-
if (allowance < received)
|
|
11999
|
+
if (allowance < received) {
|
|
12000
|
+
await this.approvePermit2(symbol2, received, chainId);
|
|
12001
|
+
}
|
|
11572
12002
|
const balance = await this.readTokenBalance(chainId, token);
|
|
11573
12003
|
return (balance < received ? balance : received).toString();
|
|
11574
12004
|
}
|
|
@@ -11695,21 +12125,30 @@ var OwneySDK = class {
|
|
|
11695
12125
|
* Every order is attempted even if an earlier one throws: they are unrelated
|
|
11696
12126
|
* escrows, and one ending badly must not abandon the next one's secrets.
|
|
11697
12127
|
* `unfinished` is not a verdict — the order may still settle, so unless the
|
|
11698
|
-
*
|
|
12128
|
+
* provider has confirmed full settlement or refund, its preimages are kept.
|
|
11699
12129
|
*/
|
|
11700
12130
|
async resumePendingSwaps(options = {}) {
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
|
|
11705
|
-
|
|
12131
|
+
await mapConcurrent(listWithdrawSwaps(Date.now(), true).filter((record2) => record2.txHash && record2.status === "unknown" && (!options.orderHash || options.orderHash === record2.orderHash)), async (record2) => {
|
|
12132
|
+
try {
|
|
12133
|
+
const rpc2 = (0, import_viem21.createPublicClient)({ chain: swapChain(record2.srcChainId), transport: swapReadTransport(record2.srcChainId, this.zyfaiRpcUrls) });
|
|
12134
|
+
const receipt = await bounded(
|
|
12135
|
+
() => rpc2.getTransactionReceipt({ hash: record2.txHash }),
|
|
12136
|
+
8e3,
|
|
12137
|
+
() => new Error("Receipt unavailable")
|
|
12138
|
+
);
|
|
12139
|
+
updateWithdrawSwap(record2.orderHash, { status: receipt.status === "success" ? "landed" : "not-swapped" });
|
|
12140
|
+
} catch {
|
|
12141
|
+
}
|
|
12142
|
+
});
|
|
12143
|
+
const pending = listOrders().filter((order) => !isOrderObserved(order.orderHash) && (!options.orderHash || order.orderHash === options.orderHash));
|
|
11706
12144
|
const runner = this.swapRunnerDeps();
|
|
11707
|
-
const
|
|
11708
|
-
|
|
12145
|
+
const sameChain = this.resumeSameChainSwaps(options);
|
|
12146
|
+
const results = await mapConcurrent(pending, async (order) => {
|
|
11709
12147
|
try {
|
|
11710
12148
|
await runFusionOrder(runner, {
|
|
11711
12149
|
orderHash: order.orderHash,
|
|
11712
12150
|
secrets: order.secrets,
|
|
12151
|
+
hasPartialFill: listWithdrawSwaps(Date.now(), true).some((record2) => record2.orderHash === order.orderHash && record2.status === "partially-filled"),
|
|
11713
12152
|
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs },
|
|
11714
12153
|
...options.onStage ? {
|
|
11715
12154
|
onStage: (stage) => options.onStage(order.orderHash, stage)
|
|
@@ -11717,25 +12156,23 @@ var OwneySDK = class {
|
|
|
11717
12156
|
});
|
|
11718
12157
|
clearOrder(order.orderHash);
|
|
11719
12158
|
updateWithdrawSwap(order.orderHash, { status: "landed" });
|
|
11720
|
-
|
|
11721
|
-
orderHash: order.orderHash,
|
|
11722
|
-
status: "completed"
|
|
11723
|
-
});
|
|
12159
|
+
return { orderHash: order.orderHash, status: "completed" };
|
|
11724
12160
|
} catch (error) {
|
|
11725
|
-
if (error instanceof OwneyError &&
|
|
12161
|
+
if (error instanceof OwneyError && error.code === "SWAP_ORDER_REFUNDED" && error.details?.outcome === "refunded") {
|
|
11726
12162
|
clearOrder(order.orderHash);
|
|
11727
12163
|
updateWithdrawSwap(order.orderHash, { status: "refunded" });
|
|
11728
12164
|
} else {
|
|
11729
|
-
|
|
12165
|
+
if (!(error instanceof OwneyError && error.details?.watchActive))
|
|
12166
|
+
updateWithdrawSwap(order.orderHash, { status: error instanceof OwneyError && error.details?.outcome === "refund-pending" ? "refund-pending" : error instanceof OwneyError && error.details?.outcome === "partially-filled" ? "partially-filled" : "unknown" });
|
|
11730
12167
|
}
|
|
11731
|
-
|
|
12168
|
+
return {
|
|
11732
12169
|
orderHash: order.orderHash,
|
|
11733
12170
|
status: "unfinished",
|
|
11734
12171
|
...error instanceof OwneyError ? { error } : {}
|
|
11735
|
-
}
|
|
12172
|
+
};
|
|
11736
12173
|
}
|
|
11737
|
-
}
|
|
11738
|
-
await
|
|
12174
|
+
});
|
|
12175
|
+
await sameChain;
|
|
11739
12176
|
return results;
|
|
11740
12177
|
}
|
|
11741
12178
|
/**
|
|
@@ -11756,34 +12193,32 @@ var OwneySDK = class {
|
|
|
11756
12193
|
* anything else.
|
|
11757
12194
|
*/
|
|
11758
12195
|
async resumeSameChainSwaps(options = {}) {
|
|
11759
|
-
const running = listWithdrawSwaps().filter(
|
|
11760
|
-
|
|
11761
|
-
);
|
|
11762
|
-
for (const record of running) {
|
|
12196
|
+
const running = listWithdrawSwaps(Date.now(), true).filter((record2) => ["swapping", "unknown", "partially-filled", "refund-pending"].includes(record2.status) && record2.srcChainId === record2.dstChainId && /^0x[0-9a-f]{64}$/i.test(record2.orderHash) && !isOrderObserved(record2.orderHash) && (!options.orderHash || record2.orderHash === options.orderHash));
|
|
12197
|
+
await mapConcurrent(running, async (record2) => {
|
|
11763
12198
|
const runner = {
|
|
11764
12199
|
// The chain bound in. That product keys its orders per chain, and the
|
|
11765
12200
|
// Fusion+ endpoint answers 404 for one — which would read as an order
|
|
11766
12201
|
// that vanished rather than one still in the auction.
|
|
11767
|
-
orderStatus: (h) => this.swapApi().orderStatus(h,
|
|
12202
|
+
orderStatus: (h) => this.swapApi().orderStatus(h, record2.srcChainId),
|
|
11768
12203
|
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
11769
12204
|
now: () => Date.now()
|
|
11770
12205
|
};
|
|
11771
12206
|
try {
|
|
11772
12207
|
await runSameChainOrder(runner, {
|
|
11773
|
-
orderHash:
|
|
12208
|
+
orderHash: record2.orderHash,
|
|
12209
|
+
hasPartialFill: record2.status === "partially-filled",
|
|
11774
12210
|
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs },
|
|
11775
12211
|
...options.onStage ? {
|
|
11776
|
-
onStage: (stage) => options.onStage(
|
|
12212
|
+
onStage: (stage) => options.onStage(record2.orderHash, stage)
|
|
11777
12213
|
} : {}
|
|
11778
12214
|
});
|
|
11779
|
-
updateWithdrawSwap(
|
|
12215
|
+
updateWithdrawSwap(record2.orderHash, { status: "landed" });
|
|
11780
12216
|
} catch (error) {
|
|
11781
|
-
|
|
11782
|
-
|
|
11783
|
-
|
|
11784
|
-
});
|
|
12217
|
+
if (error instanceof OwneyError && error.details?.watchActive) return;
|
|
12218
|
+
const outcome = error instanceof OwneyError ? error.details?.outcome : void 0;
|
|
12219
|
+
updateWithdrawSwap(record2.orderHash, { status: outcome === "not-submitted" ? "not-swapped" : outcome === "partially-filled" ? "partially-filled" : "unknown" });
|
|
11785
12220
|
}
|
|
11786
|
-
}
|
|
12221
|
+
});
|
|
11787
12222
|
}
|
|
11788
12223
|
/**
|
|
11789
12224
|
* What the wallet actually holds, across every supported chain, restricted
|
|
@@ -11939,6 +12374,92 @@ var OwneySDK = class {
|
|
|
11939
12374
|
);
|
|
11940
12375
|
}
|
|
11941
12376
|
}
|
|
12377
|
+
/** Read-only confirmation of a saved pre-swap withdrawal. Never withdraws or swaps. */
|
|
12378
|
+
async recheckWithdrawalSwap(id) {
|
|
12379
|
+
const record2 = listWithdrawSwaps(Date.now(), true).find((row) => row.orderHash === id);
|
|
12380
|
+
const state = this.requireState();
|
|
12381
|
+
if (!record2?.recovery || record2.walletAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || record2.srcChainId !== this.requireChainId() || record2.recovery.phase === "submitted")
|
|
12382
|
+
throw new OwneyError("WITHDRAW_SWAP_INCOMPLETE", "Reconnect the original wallet and source chain to check this withdrawal.");
|
|
12383
|
+
if (this.withdrawalSwapSubmissions.has(`${state.walletAddress.toLowerCase()}:${record2.srcChainId}:${record2.srcSymbol}`))
|
|
12384
|
+
throw new OwneyError("WITHDRAW_IN_PROGRESS", "This withdrawal is still being observed by its original attempt.");
|
|
12385
|
+
const revision = this.connectionRevision;
|
|
12386
|
+
const asset = SupportedAssets.find((a) => a.chainId === record2.srcChainId && a.symbol === record2.srcSymbol);
|
|
12387
|
+
if (!asset || !record2.recovery.results)
|
|
12388
|
+
throw new OwneyError(
|
|
12389
|
+
"WITHDRAW_SWAP_INCOMPLETE",
|
|
12390
|
+
"Some withdrawal results remain unknown. Check Activity and balances before continuing.",
|
|
12391
|
+
{ reason: "WITHDRAWAL_RESULTS_UNAVAILABLE", swapSubmitted: false }
|
|
12392
|
+
);
|
|
12393
|
+
const rpc2 = (0, import_viem21.createPublicClient)({
|
|
12394
|
+
chain: VIEM_CHAIN2[record2.srcChainId],
|
|
12395
|
+
transport: swapReadTransport(record2.srcChainId, this.zyfaiRpcUrls)
|
|
12396
|
+
});
|
|
12397
|
+
const recovery = record2.recovery;
|
|
12398
|
+
const assertRecoveryCurrent = () => {
|
|
12399
|
+
if (this.state !== state || this.connectionRevision !== revision)
|
|
12400
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed during confirmation.");
|
|
12401
|
+
const latest = listWithdrawSwaps(Date.now(), true).find((row) => row.orderHash === id);
|
|
12402
|
+
if (!latest?.recovery || latest.recovery.phase === "submitted")
|
|
12403
|
+
throw new OwneyError("WITHDRAW_IN_PROGRESS", "This intent has already advanced. Check its current order.");
|
|
12404
|
+
};
|
|
12405
|
+
const persist = () => {
|
|
12406
|
+
assertRecoveryCurrent();
|
|
12407
|
+
saveWithdrawSwap(record2);
|
|
12408
|
+
};
|
|
12409
|
+
for (const [id2, result] of Object.entries(recovery.results)) {
|
|
12410
|
+
const evidence = recovery.evidence[id2] ?? result.evidence;
|
|
12411
|
+
if (evidence) result.evidence = { ...evidence, transfer: "pending", delivery: "pending" };
|
|
12412
|
+
if (id2 === "yieldseeker" && recovery.requested === void 0) result.completion = "unverified";
|
|
12413
|
+
}
|
|
12414
|
+
try {
|
|
12415
|
+
const arrived = await awaitWithdrawalPayouts({
|
|
12416
|
+
results: recovery.results,
|
|
12417
|
+
requested: recovery.requested === void 0 ? void 0 : BigInt(recovery.requested),
|
|
12418
|
+
walletAddress: state.walletAddress,
|
|
12419
|
+
chainId: record2.srcChainId,
|
|
12420
|
+
token: asset.symbol,
|
|
12421
|
+
submittedAt: recovery.submittedAt,
|
|
12422
|
+
baseline: BigInt(recovery.baseline),
|
|
12423
|
+
baselineBlock: BigInt(recovery.baselineBlock),
|
|
12424
|
+
timeoutMs: 3e4,
|
|
12425
|
+
assertCurrent: assertRecoveryCurrent,
|
|
12426
|
+
readBalance: () => rpc2.readContract({ address: asset.address, abi: import_viem21.erc20Abi, functionName: "balanceOf", args: [state.walletAddress] }),
|
|
12427
|
+
inspect: (value, fromHistory) => this.inspectFunding(value, "withdraw", { requireAmountMatch: fromHistory, refreshBalance: false }),
|
|
12428
|
+
resolve: (value) => this.resolveSponsoredWithdrawalEvidence(value, { submittedAt: recovery.submittedAt }),
|
|
12429
|
+
history: (value, fromDate, filters) => this.getFundingHistory(value, fromDate, filters),
|
|
12430
|
+
verifyCompletion: (value) => this.getAgent(value.agentId).verifyWithdrawalCompletion?.(state, value) ?? Promise.resolve({ status: "unavailable" }),
|
|
12431
|
+
onProgress: (value) => {
|
|
12432
|
+
recovery.evidence[value.agentId] = value;
|
|
12433
|
+
persist();
|
|
12434
|
+
}
|
|
12435
|
+
});
|
|
12436
|
+
record2.withdrawn = arrived.toString();
|
|
12437
|
+
record2.status = "ready";
|
|
12438
|
+
recovery.phase = "ready";
|
|
12439
|
+
recovery.reason = void 0;
|
|
12440
|
+
persist();
|
|
12441
|
+
return record2;
|
|
12442
|
+
} catch (error) {
|
|
12443
|
+
record2.status = "withdrawal-incomplete";
|
|
12444
|
+
recovery.reason = error instanceof OwneyError ? String(error.details?.reason ?? error.code) : "CONFIRMATION_UNAVAILABLE";
|
|
12445
|
+
try {
|
|
12446
|
+
persist();
|
|
12447
|
+
} catch {
|
|
12448
|
+
}
|
|
12449
|
+
throw error;
|
|
12450
|
+
}
|
|
12451
|
+
}
|
|
12452
|
+
/** Explicit foreground continuation: spends verified payouts, never withdraws again. */
|
|
12453
|
+
async continueWithdrawalSwap(id, callbacks = {}) {
|
|
12454
|
+
const record2 = await this.recheckWithdrawalSwap(id);
|
|
12455
|
+
return this.withdrawAndSwap({
|
|
12456
|
+
from: { chainId: record2.srcChainId, symbol: record2.srcSymbol },
|
|
12457
|
+
to: { chainId: record2.dstChainId, symbol: record2.dstSymbol, decimals: record2.dstDecimals },
|
|
12458
|
+
...record2.recovery?.agentId ? { agentId: record2.recovery.agentId } : {},
|
|
12459
|
+
recoveryId: id,
|
|
12460
|
+
...callbacks
|
|
12461
|
+
});
|
|
12462
|
+
}
|
|
11942
12463
|
/**
|
|
11943
12464
|
* Withdraw from an agent and swap the proceeds into whatever the user wants
|
|
11944
12465
|
* to hold, delivered to their own wallet.
|
|
@@ -11967,7 +12488,14 @@ var OwneySDK = class {
|
|
|
11967
12488
|
const state = this.requireState();
|
|
11968
12489
|
const activeChainId = this.requireChainId();
|
|
11969
12490
|
const revision = this.connectionRevision;
|
|
12491
|
+
let active2 = true;
|
|
12492
|
+
let record2;
|
|
12493
|
+
let hadDurableRecord = false;
|
|
12494
|
+
const startedAt = Date.now();
|
|
12495
|
+
let confirmationDeadline = Infinity;
|
|
11970
12496
|
const assertCurrent = () => {
|
|
12497
|
+
options.assertCurrent?.();
|
|
12498
|
+
if (!active2) throw new OwneyError("WITHDRAW_SWAP_INCOMPLETE", "This withdrawal attempt is no longer active.", { swapSubmitted: false });
|
|
11971
12499
|
if (this.state !== state || this.connectionRevision !== revision || state.chainId !== activeChainId)
|
|
11972
12500
|
throw new OwneyError("NOT_CONNECTED", "Wallet changed during withdrawal and swap.");
|
|
11973
12501
|
};
|
|
@@ -12009,30 +12537,95 @@ var OwneySDK = class {
|
|
|
12009
12537
|
functionName: "balanceOf",
|
|
12010
12538
|
args: [state.walletAddress]
|
|
12011
12539
|
});
|
|
12012
|
-
const
|
|
12013
|
-
|
|
12540
|
+
const saved = options.recoveryId ? listWithdrawSwaps(Date.now(), true).find((row) => row.orderHash === options.recoveryId) : void 0;
|
|
12541
|
+
if (options.recoveryId && (!saved?.recovery?.results || saved.recovery.phase !== "ready" || saved.walletAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || saved.srcChainId !== activeChainId || saved.srcSymbol !== asset.symbol || saved.dstChainId !== options.to.chainId || saved.dstSymbol !== options.to.symbol))
|
|
12542
|
+
throw new OwneyError("WITHDRAW_SWAP_INCOMPLETE", "This withdrawal is not ready to continue.");
|
|
12543
|
+
const prepareTimeout = () => new OwneyError(
|
|
12544
|
+
"WITHDRAW_SWAP_INCOMPLETE",
|
|
12545
|
+
"Could not read the source wallet. No new withdrawal was submitted.",
|
|
12546
|
+
{ reason: "BASELINE_UNAVAILABLE", swapSubmitted: false }
|
|
12547
|
+
);
|
|
12548
|
+
const baselineBlock = saved ? BigInt(saved.recovery.baselineBlock) : await bounded(() => srcPublic.getBlockNumber({ cacheTime: 0 }), 2e4, prepareTimeout);
|
|
12549
|
+
const baseline = saved ? BigInt(saved.recovery.baseline) : await bounded(readWalletBalance, 2e4, prepareTimeout);
|
|
12014
12550
|
assertCurrent();
|
|
12015
12551
|
debugLog("owney-sdk", "withdrawAndSwap: baseline", {
|
|
12016
12552
|
asset: `${asset.symbol}@${asset.chainId}`,
|
|
12017
12553
|
baseline: baseline.toString()
|
|
12018
12554
|
});
|
|
12019
12555
|
options.onSwapProgress?.("withdrawing");
|
|
12020
|
-
const submittedAt = Date.now();
|
|
12021
|
-
const requested = options.amount === void 0 ? void 0 : (0, import_viem21.parseUnits)(options.amount, asset.decimals);
|
|
12556
|
+
const submittedAt = saved?.recovery?.submittedAt ?? Date.now();
|
|
12557
|
+
const requested = saved ? saved.recovery.requested === void 0 ? void 0 : BigInt(saved.recovery.requested) : options.amount === void 0 ? void 0 : (0, import_viem21.parseUnits)(options.amount, asset.decimals);
|
|
12558
|
+
confirmationDeadline = Date.now() + (options.arrivalTimeoutMs ?? 18e4);
|
|
12559
|
+
const attemptId = saved?.recovery?.attemptId ?? `withdraw-${globalThis.crypto.randomUUID()}`;
|
|
12560
|
+
record2 = saved ?? {
|
|
12561
|
+
orderHash: attemptId,
|
|
12562
|
+
walletAddress: state.walletAddress,
|
|
12563
|
+
status: "withdrawal-pending",
|
|
12564
|
+
withdrawn: "0",
|
|
12565
|
+
expected: "0",
|
|
12566
|
+
createdAt: Date.now(),
|
|
12567
|
+
srcSymbol: asset.symbol,
|
|
12568
|
+
srcChainId: activeChainId,
|
|
12569
|
+
srcDecimals: asset.decimals,
|
|
12570
|
+
dstSymbol: options.to.symbol,
|
|
12571
|
+
dstChainId: options.to.chainId,
|
|
12572
|
+
dstDecimals: options.to.decimals ?? 18,
|
|
12573
|
+
recovery: {
|
|
12574
|
+
attemptId,
|
|
12575
|
+
phase: "withdrawing",
|
|
12576
|
+
baseline: baseline.toString(),
|
|
12577
|
+
baselineBlock: baselineBlock.toString(),
|
|
12578
|
+
submittedAt,
|
|
12579
|
+
deadline: confirmationDeadline,
|
|
12580
|
+
requested: requested?.toString(),
|
|
12581
|
+
agentId: options.agentId,
|
|
12582
|
+
evidence: {}
|
|
12583
|
+
}
|
|
12584
|
+
};
|
|
12585
|
+
hadDurableRecord = saveWithdrawSwap(record2);
|
|
12586
|
+
const assertSubmission = () => {
|
|
12587
|
+
assertCurrent();
|
|
12588
|
+
if (Date.now() >= confirmationDeadline) throw new OwneyError(
|
|
12589
|
+
"WITHDRAW_SWAP_INCOMPLETE",
|
|
12590
|
+
"Withdrawal confirmation timed out. Check Activity before retrying.",
|
|
12591
|
+
{ reason: "PAYOUT_CONFIRMATION_TIMEOUT", swapSubmitted: false }
|
|
12592
|
+
);
|
|
12593
|
+
};
|
|
12022
12594
|
const evidence = /* @__PURE__ */ new Map();
|
|
12023
12595
|
const onProgress = (next) => {
|
|
12024
12596
|
if (next.chainId !== activeChainId || next.token !== asset.symbol || next.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase()) return;
|
|
12025
12597
|
evidence.set(next.agentId, next);
|
|
12598
|
+
if (record2?.recovery) {
|
|
12599
|
+
record2.recovery.evidence[next.agentId] = next;
|
|
12600
|
+
if (active2) saveWithdrawSwap(record2);
|
|
12601
|
+
else {
|
|
12602
|
+
const latest = listWithdrawSwaps(Date.now(), true).find((row) => row.recovery?.attemptId === record2.recovery.attemptId);
|
|
12603
|
+
if (latest?.recovery) updateWithdrawSwap(latest.orderHash, { recovery: {
|
|
12604
|
+
...latest.recovery,
|
|
12605
|
+
evidence: { ...latest.recovery.evidence, [next.agentId]: next }
|
|
12606
|
+
} });
|
|
12607
|
+
}
|
|
12608
|
+
}
|
|
12026
12609
|
notifyFunding(options.onWithdrawalProgress, next);
|
|
12027
12610
|
};
|
|
12028
12611
|
let withdraw;
|
|
12029
12612
|
try {
|
|
12030
|
-
withdraw = await this.withdraw({
|
|
12613
|
+
withdraw = saved ? { agentResult: saved.recovery.results, totalWithdrawn: null } : await bounded(() => this.withdraw({
|
|
12031
12614
|
asset: asset.symbol,
|
|
12032
12615
|
...requested === void 0 ? {} : { amount: requested.toString() },
|
|
12033
12616
|
...options.agentId ? { agentId: options.agentId } : {},
|
|
12034
|
-
onProgress
|
|
12035
|
-
|
|
12617
|
+
onProgress,
|
|
12618
|
+
assertCurrent: assertSubmission,
|
|
12619
|
+
onBeforeSubmission: (next) => {
|
|
12620
|
+
assertSubmission();
|
|
12621
|
+
onProgress(next);
|
|
12622
|
+
},
|
|
12623
|
+
observationDeadline: confirmationDeadline
|
|
12624
|
+
}), Math.max(0, confirmationDeadline - Date.now()), () => new OwneyError(
|
|
12625
|
+
"WITHDRAW_SWAP_INCOMPLETE",
|
|
12626
|
+
"Withdrawal confirmation timed out. Check Activity and balances before trying again.",
|
|
12627
|
+
{ reason: "WITHDRAWAL_DISPATCH_TIMEOUT", swapSubmitted: false, withdrawalEvidence: Object.fromEntries(evidence) }
|
|
12628
|
+
));
|
|
12036
12629
|
} catch (error) {
|
|
12037
12630
|
const details = error instanceof OwneyError ? error.details : void 0;
|
|
12038
12631
|
const moved = [...evidence.values()].some((value) => value.transfer !== "failed" && (value.delivery === "confirmed" || value.transfer === "unknown" || value.txHash || value.userOpHash || value.callsId || value.withdrawalRequest?.attempted));
|
|
@@ -12054,6 +12647,9 @@ var OwneySDK = class {
|
|
|
12054
12647
|
const results = "agentResult" in withdraw ? withdraw.agentResult : {
|
|
12055
12648
|
[options.agentId ?? withdraw.evidence?.agentId ?? "unknown"]: withdraw
|
|
12056
12649
|
};
|
|
12650
|
+
record2.recovery.results = results;
|
|
12651
|
+
record2.recovery.phase = "verifying";
|
|
12652
|
+
saveWithdrawSwap(record2);
|
|
12057
12653
|
const arrived = await awaitWithdrawalPayouts({
|
|
12058
12654
|
results,
|
|
12059
12655
|
requested,
|
|
@@ -12065,7 +12661,8 @@ var OwneySDK = class {
|
|
|
12065
12661
|
baselineBlock,
|
|
12066
12662
|
assertCurrent,
|
|
12067
12663
|
readBalance: readWalletBalance,
|
|
12068
|
-
|
|
12664
|
+
deadline: confirmationDeadline,
|
|
12665
|
+
verifyCompletion: (value) => this.getAgent(value.agentId).verifyWithdrawalCompletion?.(state, value) ?? Promise.resolve({ status: "unavailable" }),
|
|
12069
12666
|
inspect: (value, fromHistory) => this.inspectFunding(value, "withdraw", { requireAmountMatch: fromHistory, refreshBalance: false }),
|
|
12070
12667
|
resolve: (value) => this.resolveSponsoredWithdrawalEvidence(value, { submittedAt }),
|
|
12071
12668
|
history: (value, fromDate, filters) => this.getFundingHistory(value, fromDate, filters),
|
|
@@ -12073,34 +12670,89 @@ var OwneySDK = class {
|
|
|
12073
12670
|
});
|
|
12074
12671
|
assertCurrent();
|
|
12075
12672
|
const withdrawn = arrived.toString();
|
|
12673
|
+
record2.withdrawn = withdrawn;
|
|
12674
|
+
record2.status = "ready";
|
|
12675
|
+
record2.recovery.phase = "ready";
|
|
12676
|
+
hadDurableRecord = saveWithdrawSwap(record2) || hadDurableRecord;
|
|
12076
12677
|
options.onSwapProgress?.("withdrawn");
|
|
12077
12678
|
try {
|
|
12078
|
-
const quote = await this.swapApi().quote({
|
|
12679
|
+
const quote = await bounded(() => this.swapApi().quote({
|
|
12079
12680
|
from: { ...options.from, amount: withdrawn },
|
|
12080
12681
|
to: options.to,
|
|
12081
12682
|
direction: "withdraw",
|
|
12082
12683
|
walletAddress: state.walletAddress
|
|
12083
|
-
});
|
|
12684
|
+
}), 2e4, () => new OwneyError("SWAP_REQUEST_FAILED", "Swap pricing timed out.", { outcome: "not-submitted" }));
|
|
12084
12685
|
assertCurrent();
|
|
12085
|
-
await
|
|
12686
|
+
await bounded(
|
|
12687
|
+
() => this.ensureSwapChain(quote.src.chainId),
|
|
12688
|
+
18e4,
|
|
12689
|
+
() => new OwneyError("SWAP_WALLET_PROMPT_TIMEOUT", "The wallet did not confirm its chain. No swap was submitted.", { outcome: "not-submitted" })
|
|
12690
|
+
);
|
|
12086
12691
|
assertCurrent();
|
|
12087
|
-
const swap = await executeSwap(this.buildSwapDeps(quote
|
|
12692
|
+
const swap = await executeSwap(this.buildSwapDeps(quote, assertCurrent, (hash2) => {
|
|
12693
|
+
record2.txHash = hash2;
|
|
12694
|
+
saveWithdrawSwap(record2);
|
|
12695
|
+
}), {
|
|
12696
|
+
onTransactionRequested: () => {
|
|
12697
|
+
assertCurrent();
|
|
12698
|
+
record2.status = "unknown";
|
|
12699
|
+
record2.recovery.phase = "submitted";
|
|
12700
|
+
if (!saveWithdrawSwap(record2) && hadDurableRecord)
|
|
12701
|
+
throw new OwneyError("SWAP_REQUEST_FAILED", "Recovery storage is unavailable. No swap was submitted.", { outcome: "not-submitted" });
|
|
12702
|
+
},
|
|
12703
|
+
assertCurrent,
|
|
12704
|
+
sourceReserve: baseline,
|
|
12088
12705
|
quote,
|
|
12089
12706
|
walletAddress: state.walletAddress,
|
|
12090
12707
|
direction: "withdraw",
|
|
12091
12708
|
...options.slippage === void 0 ? {} : { slippage: options.slippage },
|
|
12092
12709
|
...options.onSwapProgress ? { onStage: options.onSwapProgress } : {},
|
|
12093
|
-
|
|
12710
|
+
onOrderPrepared: (orderHash, details) => {
|
|
12711
|
+
assertCurrent();
|
|
12712
|
+
const previous = record2.orderHash;
|
|
12713
|
+
record2.orderHash = orderHash;
|
|
12714
|
+
record2.status = "unknown";
|
|
12715
|
+
record2.recovery.phase = "submitted";
|
|
12716
|
+
record2.spent = details.spent;
|
|
12717
|
+
record2.expected = details.expected;
|
|
12718
|
+
if (!saveWithdrawSwap(record2) && hadDurableRecord) {
|
|
12719
|
+
record2.orderHash = previous;
|
|
12720
|
+
throw new OwneyError("SWAP_REQUEST_FAILED", "Recovery storage is unavailable. No swap was submitted.", { outcome: "not-submitted" });
|
|
12721
|
+
}
|
|
12722
|
+
if (previous !== orderHash) {
|
|
12723
|
+
updateWithdrawSwap(previous, { status: "not-swapped" });
|
|
12724
|
+
clearWithdrawSwap(previous);
|
|
12725
|
+
}
|
|
12726
|
+
},
|
|
12727
|
+
onOrderSubmitted: (orderHash, details) => {
|
|
12094
12728
|
releaseSubmission();
|
|
12095
|
-
|
|
12729
|
+
record2.orderHash = orderHash;
|
|
12730
|
+
record2.status = "swapping";
|
|
12731
|
+
record2.recovery.phase = "submitted";
|
|
12732
|
+
record2.createdAt = Date.now();
|
|
12733
|
+
const durable = saveWithdrawSwap(record2);
|
|
12734
|
+
options.onOrderSubmitted?.({
|
|
12735
|
+
orderHash,
|
|
12736
|
+
withdrawn,
|
|
12737
|
+
expected: details?.expected ?? quote.dst.amount,
|
|
12738
|
+
spent: details?.spent,
|
|
12739
|
+
durable: durable && details?.durable !== false
|
|
12740
|
+
});
|
|
12096
12741
|
}
|
|
12097
12742
|
});
|
|
12743
|
+
record2.status = "landed";
|
|
12744
|
+
record2.received = swap.received;
|
|
12745
|
+
record2.spent = swap.spent;
|
|
12746
|
+
saveWithdrawSwap(record2);
|
|
12098
12747
|
return { withdraw, withdrawn, swap };
|
|
12099
12748
|
} catch (error) {
|
|
12100
12749
|
throw new OwneyError(
|
|
12101
12750
|
"WITHDRAW_SWAP_FAILED",
|
|
12102
|
-
`
|
|
12751
|
+
`The withdrawal completed, but the swap to ${options.to.symbol} could not be confirmed. Check the order and both balances before retrying.`,
|
|
12103
12752
|
{
|
|
12753
|
+
outcome: record2.recovery?.phase === "ready" ? "not-submitted" : "submitted-unknown",
|
|
12754
|
+
...error instanceof OwneyError ? error.details : {},
|
|
12755
|
+
...record2.orderHash.startsWith("0x") ? { orderHash: record2.orderHash, settlementUnconfirmed: true } : {},
|
|
12104
12756
|
withdrawn,
|
|
12105
12757
|
asset: asset.symbol,
|
|
12106
12758
|
chainId: asset.chainId,
|
|
@@ -12111,7 +12763,25 @@ var OwneySDK = class {
|
|
|
12111
12763
|
}
|
|
12112
12764
|
);
|
|
12113
12765
|
}
|
|
12766
|
+
} catch (error) {
|
|
12767
|
+
if (record2) {
|
|
12768
|
+
if (error instanceof OwneyError && error.details) Object.assign(error.details, {
|
|
12769
|
+
attemptId: record2.recovery?.attemptId,
|
|
12770
|
+
phase: record2.recovery?.phase,
|
|
12771
|
+
elapsedMs: Date.now() - startedAt,
|
|
12772
|
+
txHash: record2.txHash,
|
|
12773
|
+
withdrawalEvidence: record2.recovery?.evidence
|
|
12774
|
+
});
|
|
12775
|
+
const details = error instanceof OwneyError ? error.details : void 0;
|
|
12776
|
+
const outcome = details?.outcome;
|
|
12777
|
+
if (record2.recovery && outcome === "not-submitted") record2.recovery.phase = "ready";
|
|
12778
|
+
record2.status = outcome === "refunded" ? "refunded" : outcome === "refund-pending" ? "refund-pending" : outcome === "partially-filled" ? "partially-filled" : record2.recovery?.phase === "submitted" || details?.settlementUnconfirmed ? "unknown" : record2.recovery?.phase === "ready" ? "ready" : "withdrawal-incomplete";
|
|
12779
|
+
if (record2.recovery) record2.recovery.reason = typeof details?.reason === "string" ? details.reason : void 0;
|
|
12780
|
+
saveWithdrawSwap(record2);
|
|
12781
|
+
}
|
|
12782
|
+
throw error;
|
|
12114
12783
|
} finally {
|
|
12784
|
+
active2 = false;
|
|
12115
12785
|
releaseSubmission();
|
|
12116
12786
|
}
|
|
12117
12787
|
}
|
|
@@ -12144,7 +12814,15 @@ var OwneySDK = class {
|
|
|
12144
12814
|
const attempted = /* @__PURE__ */ new Set();
|
|
12145
12815
|
this.withdrawalSubmissions.add(submissionKey);
|
|
12146
12816
|
try {
|
|
12147
|
-
return await this.withdrawFromAgents(options, state, chainId, attempted)
|
|
12817
|
+
return options.observationDeadline === void 0 ? await this.withdrawFromAgents(options, state, chainId, attempted) : await bounded(
|
|
12818
|
+
() => this.withdrawFromAgents(options, state, chainId, attempted),
|
|
12819
|
+
Math.max(0, options.observationDeadline - Date.now()),
|
|
12820
|
+
() => new OwneyError(
|
|
12821
|
+
"WITHDRAW_UNCONFIRMED",
|
|
12822
|
+
"Withdrawal dispatch could not be confirmed before the deadline.",
|
|
12823
|
+
{ phase: "observe", reason: "WITHDRAWAL_DISPATCH_TIMEOUT" }
|
|
12824
|
+
)
|
|
12825
|
+
);
|
|
12148
12826
|
} catch (error) {
|
|
12149
12827
|
for (const id of ids) if (!attempted.has(id)) {
|
|
12150
12828
|
notifyFunding(options.onProgress, {
|
|
@@ -12234,7 +12912,7 @@ var OwneySDK = class {
|
|
|
12234
12912
|
progress(evidence);
|
|
12235
12913
|
adapterReported = false;
|
|
12236
12914
|
try {
|
|
12237
|
-
if (options.onBeforeSubmission)
|
|
12915
|
+
if (options.onBeforeSubmission || options.assertCurrent)
|
|
12238
12916
|
return await agent.withdraw(
|
|
12239
12917
|
state,
|
|
12240
12918
|
chainId,
|
|
@@ -12242,7 +12920,8 @@ var OwneySDK = class {
|
|
|
12242
12920
|
requested2,
|
|
12243
12921
|
progress,
|
|
12244
12922
|
options.gasMode ?? "auto",
|
|
12245
|
-
(next) => options.onBeforeSubmission({ ...evidence, ...next })
|
|
12923
|
+
options.onBeforeSubmission ? (next) => options.onBeforeSubmission({ ...evidence, ...next }) : void 0,
|
|
12924
|
+
options.assertCurrent
|
|
12246
12925
|
);
|
|
12247
12926
|
return await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
|
|
12248
12927
|
} catch (error) {
|