@owney/sdk 0.7.27-beta.2 → 0.7.27-beta.3
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/chunk-LUQ4H72X.js +542 -0
- package/dist/index.cjs +1810 -715
- package/dist/index.d.cts +33 -2
- package/dist/index.d.ts +33 -2
- package/dist/index.js +1038 -529
- package/dist/token-batch-family-VW6ZJLK5.js +26 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,213 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
this.name = "AgentNotFoundError";
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
var NotConnectedError = class extends OwneyError {
|
|
27
|
-
constructor() {
|
|
28
|
-
super("NOT_CONNECTED", "Not connected. Call sdk.connect(provider) first.");
|
|
29
|
-
this.name = "NotConnectedError";
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
var AgentChainIncompatibleError = class extends OwneyError {
|
|
33
|
-
incompatibleAgents;
|
|
34
|
-
connectedChainId;
|
|
35
|
-
constructor(incompatibleAgents, connectedChainId) {
|
|
36
|
-
const details = incompatibleAgents.map(
|
|
37
|
-
({ agentId, supportedChainIds }) => `"${agentId}" supports chains [${supportedChainIds.join(", ")}]`
|
|
38
|
-
).join("; ");
|
|
39
|
-
super(
|
|
40
|
-
"AGENT_CHAIN_INCOMPATIBLE",
|
|
41
|
-
`Chain ${connectedChainId} is not supported by the following agents: ${details}`,
|
|
42
|
-
{ incompatibleAgents, connectedChainId }
|
|
43
|
-
);
|
|
44
|
-
this.name = "AgentChainIncompatibleError";
|
|
45
|
-
this.incompatibleAgents = incompatibleAgents;
|
|
46
|
-
this.connectedChainId = connectedChainId;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// src/lib/swap/swap.observation.ts
|
|
51
|
-
var active = /* @__PURE__ */ new Set();
|
|
52
|
-
var isOrderObserved = (key2) => active.has(key2);
|
|
53
|
-
async function observeOrder(key2, run) {
|
|
54
|
-
const busy = () => new OwneyError(
|
|
55
|
-
"SWAP_REQUEST_FAILED",
|
|
56
|
-
"This order is already being observed.",
|
|
57
|
-
{ orderHash: key2, watchActive: true, settlementUnconfirmed: true }
|
|
58
|
-
);
|
|
59
|
-
if (active.has(key2)) throw busy();
|
|
60
|
-
active.add(key2);
|
|
61
|
-
try {
|
|
62
|
-
if (typeof navigator !== "undefined" && navigator.locks)
|
|
63
|
-
return await navigator.locks.request(`owney.swap.observe:${key2}`, { ifAvailable: true }, (lock) => {
|
|
64
|
-
if (!lock) throw busy();
|
|
65
|
-
return run();
|
|
66
|
-
});
|
|
67
|
-
return await run();
|
|
68
|
-
} finally {
|
|
69
|
-
active.delete(key2);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// src/lib/swap/swap.deadline.ts
|
|
74
|
-
async function bounded(work, timeoutMs, error) {
|
|
75
|
-
if (timeoutMs <= 0) throw error();
|
|
76
|
-
let timer;
|
|
77
|
-
try {
|
|
78
|
-
return await Promise.race([
|
|
79
|
-
Promise.resolve().then(work),
|
|
80
|
-
new Promise((_, reject) => {
|
|
81
|
-
timer = setTimeout(() => reject(error()), timeoutMs);
|
|
82
|
-
})
|
|
83
|
-
]);
|
|
84
|
-
} finally {
|
|
85
|
-
clearTimeout(timer);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
async function mapConcurrent(items, run, concurrency = 3) {
|
|
89
|
-
const results = new Array(items.length);
|
|
90
|
-
let next = 0;
|
|
91
|
-
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
92
|
-
for (; ; ) {
|
|
93
|
-
const index = next++;
|
|
94
|
-
if (index >= items.length) return;
|
|
95
|
-
results[index] = await run(items[index]);
|
|
96
|
-
}
|
|
97
|
-
}));
|
|
98
|
-
return results;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// src/lib/permit2-batch.ts
|
|
102
|
-
import { parseAbi, hashStruct } from "viem";
|
|
103
|
-
var BATCH_PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
104
|
-
var BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
105
|
-
var ARBITRUM_USDC_ADDRESS = "0xaf88d065e77c8cc2239327c5edb3a432268e5831";
|
|
106
|
-
function supportsSponsoredErc2612(chainId, token) {
|
|
107
|
-
const expected = chainId === 8453 ? BASE_USDC_ADDRESS : chainId === 42161 ? ARBITRUM_USDC_ADDRESS : void 0;
|
|
108
|
-
return expected !== void 0 && token.toLowerCase() === expected.toLowerCase();
|
|
109
|
-
}
|
|
110
|
-
var ERC2612_PERMIT_TYPES = {
|
|
111
|
-
Permit: [
|
|
112
|
-
{ name: "owner", type: "address" },
|
|
113
|
-
{ name: "spender", type: "address" },
|
|
114
|
-
{ name: "value", type: "uint256" },
|
|
115
|
-
{ name: "nonce", type: "uint256" },
|
|
116
|
-
{ name: "deadline", type: "uint256" }
|
|
117
|
-
]
|
|
118
|
-
};
|
|
119
|
-
var ERC2612_READ_ABI = parseAbi([
|
|
120
|
-
"function name() view returns (string)",
|
|
121
|
-
"function version() view returns (string)",
|
|
122
|
-
"function nonces(address owner) view returns (uint256)"
|
|
123
|
-
]);
|
|
124
|
-
function erc2612TypedData(input) {
|
|
125
|
-
return {
|
|
126
|
-
domain: {
|
|
127
|
-
name: input.permit.tokenName,
|
|
128
|
-
version: input.permit.tokenVersion,
|
|
129
|
-
chainId: input.chainId,
|
|
130
|
-
verifyingContract: input.token
|
|
131
|
-
},
|
|
132
|
-
types: ERC2612_PERMIT_TYPES,
|
|
133
|
-
primaryType: "Permit",
|
|
134
|
-
message: {
|
|
135
|
-
owner: input.owner,
|
|
136
|
-
spender: BATCH_PERMIT2_ADDRESS,
|
|
137
|
-
value: BigInt(input.permit.value),
|
|
138
|
-
nonce: BigInt(input.permit.nonce),
|
|
139
|
-
deadline: BigInt(input.permit.deadline)
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
var PERMIT_BATCH_TYPES = {
|
|
144
|
-
PermitBatchWitnessTransferFrom: [
|
|
145
|
-
{ name: "permitted", type: "TokenPermissions[]" },
|
|
146
|
-
{ name: "spender", type: "address" },
|
|
147
|
-
{ name: "nonce", type: "uint256" },
|
|
148
|
-
{ name: "deadline", type: "uint256" },
|
|
149
|
-
{ name: "witness", type: "Deposit" }
|
|
150
|
-
],
|
|
151
|
-
Deposit: [{ name: "recipients", type: "address[]" }],
|
|
152
|
-
TokenPermissions: [
|
|
153
|
-
{ name: "token", type: "address" },
|
|
154
|
-
{ name: "amount", type: "uint256" }
|
|
155
|
-
]
|
|
156
|
-
};
|
|
157
|
-
var PERMIT2_BATCH_ABI = parseAbi([
|
|
158
|
-
"struct TokenPermissions { address token; uint256 amount; }",
|
|
159
|
-
"struct PermitBatchTransferFrom { TokenPermissions[] permitted; uint256 nonce; uint256 deadline; }",
|
|
160
|
-
"struct SignatureTransferDetails { address to; uint256 requestedAmount; }",
|
|
161
|
-
"function permitWitnessTransferFrom(PermitBatchTransferFrom permit,SignatureTransferDetails[] transferDetails,address owner,bytes32 witness,string witnessTypeString,bytes signature)",
|
|
162
|
-
"function nonceBitmap(address owner,uint256 wordPos) view returns (uint256)"
|
|
163
|
-
]);
|
|
164
|
-
function batchPermit(b) {
|
|
165
|
-
return {
|
|
166
|
-
permitted: b.transfers.map((t) => ({
|
|
167
|
-
token: b.token,
|
|
168
|
-
amount: BigInt(t.amount)
|
|
169
|
-
})),
|
|
170
|
-
nonce: BigInt(b.nonce),
|
|
171
|
-
deadline: BigInt(b.deadline)
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
function batchTypedData(b, spender) {
|
|
175
|
-
return {
|
|
176
|
-
domain: {
|
|
177
|
-
name: "Permit2",
|
|
178
|
-
chainId: b.chainId,
|
|
179
|
-
verifyingContract: BATCH_PERMIT2_ADDRESS
|
|
180
|
-
},
|
|
181
|
-
types: PERMIT_BATCH_TYPES,
|
|
182
|
-
primaryType: "PermitBatchWitnessTransferFrom",
|
|
183
|
-
message: {
|
|
184
|
-
...batchPermit(b),
|
|
185
|
-
spender,
|
|
186
|
-
witness: { recipients: b.transfers.map((t) => t.to) }
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
var BATCH_WITNESS_TYPE = "Deposit witness)Deposit(address[] recipients)TokenPermissions(address token,uint256 amount)";
|
|
191
|
-
function batchWitness(b) {
|
|
192
|
-
return hashStruct({
|
|
193
|
-
types: PERMIT_BATCH_TYPES,
|
|
194
|
-
primaryType: "Deposit",
|
|
195
|
-
data: { recipients: b.transfers.map((t) => t.to) }
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
function batchArgs(b) {
|
|
199
|
-
return [
|
|
200
|
-
batchPermit(b),
|
|
201
|
-
b.transfers.map((t) => ({ to: t.to, requestedAmount: BigInt(t.amount) })),
|
|
202
|
-
b.from,
|
|
203
|
-
batchWitness(b),
|
|
204
|
-
BATCH_WITNESS_TYPE,
|
|
205
|
-
b.signature
|
|
206
|
-
];
|
|
207
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
AgentChainIncompatibleError,
|
|
3
|
+
AgentNotFoundError,
|
|
4
|
+
BATCH_PERMIT2_ADDRESS,
|
|
5
|
+
ERC2612_READ_ABI,
|
|
6
|
+
NotConnectedError,
|
|
7
|
+
OwneyError,
|
|
8
|
+
PERMIT2_BATCH_ABI,
|
|
9
|
+
batchArgs,
|
|
10
|
+
batchTypedData,
|
|
11
|
+
erc2612TypedData,
|
|
12
|
+
finishTokenBatchFamily,
|
|
13
|
+
loadTokenBatchFamily,
|
|
14
|
+
saveTokenBatchAttempt,
|
|
15
|
+
supportsSponsoredErc2612,
|
|
16
|
+
tokenBatchClear,
|
|
17
|
+
tokenBatchKey,
|
|
18
|
+
tokenBatchRead,
|
|
19
|
+
tokenBatchStoredBody,
|
|
20
|
+
tokenBatchWrite,
|
|
21
|
+
validateTokenBatchAttempt
|
|
22
|
+
} from "./chunk-LUQ4H72X.js";
|
|
208
23
|
|
|
209
24
|
// src/lib/funding-evidence.ts
|
|
210
|
-
import { decodeEventLog, decodeFunctionData, erc20Abi, parseAbi
|
|
25
|
+
import { decodeEventLog, decodeFunctionData, erc20Abi, parseAbi } from "viem";
|
|
211
26
|
import { entryPoint06Abi, entryPoint07Abi, getUserOperationHash, toUserOperation } from "viem/account-abstraction";
|
|
212
27
|
function notifyFunding(callback, evidence) {
|
|
213
28
|
try {
|
|
@@ -216,9 +31,9 @@ function notifyFunding(callback, evidence) {
|
|
|
216
31
|
} catch {
|
|
217
32
|
}
|
|
218
33
|
}
|
|
219
|
-
var fullWithdrawalAbi =
|
|
220
|
-
var withdrawalEventAbi =
|
|
221
|
-
var accountExecutionAbi =
|
|
34
|
+
var fullWithdrawalAbi = parseAbi(["function withdrawAllAssetToUser(address recipient, address asset)"]);
|
|
35
|
+
var withdrawalEventAbi = parseAbi(["event WithdrewTokenToUser(address indexed owner, address indexed recipient, address indexed token, uint256 amount)"]);
|
|
36
|
+
var accountExecutionAbi = parseAbi([
|
|
222
37
|
"function execute(address target,uint256 value,bytes data)",
|
|
223
38
|
"function executeBatch((address target,uint256 value,bytes data)[] calls)"
|
|
224
39
|
]);
|
|
@@ -384,6 +199,213 @@ function inspectFundingReceipt(receipt, evidence, tokenAddress, direction, optio
|
|
|
384
199
|
};
|
|
385
200
|
}
|
|
386
201
|
|
|
202
|
+
// src/lib/token-batch-observer.ts
|
|
203
|
+
var cursors = /* @__PURE__ */ new Map();
|
|
204
|
+
async function observeTokenBatchFamily(input) {
|
|
205
|
+
let expired = false, timer;
|
|
206
|
+
const assertCurrent = () => {
|
|
207
|
+
if (expired || Date.now() >= input.deadlineAt)
|
|
208
|
+
throw Error("observation ended");
|
|
209
|
+
input.assertCurrent();
|
|
210
|
+
};
|
|
211
|
+
const run = async () => {
|
|
212
|
+
assertCurrent();
|
|
213
|
+
const { scope, pub } = input;
|
|
214
|
+
const family = await loadTokenBatchFamily(
|
|
215
|
+
scope,
|
|
216
|
+
input.family.attempts[0].hash
|
|
217
|
+
);
|
|
218
|
+
assertCurrent();
|
|
219
|
+
if (!family || family.authorizationId !== input.family.authorizationId)
|
|
220
|
+
return { status: "unresolved" };
|
|
221
|
+
const attempts = family.attempts.filter((a) => a.phase === "deposit");
|
|
222
|
+
const key2 = `${tokenBatchKey(scope)}:${family.authorizationId}`;
|
|
223
|
+
const start = (cursors.get(key2) ?? 0) % Math.max(1, attempts.length);
|
|
224
|
+
const ordered = [...attempts.slice(start), ...attempts.slice(0, start)];
|
|
225
|
+
const reverted = [];
|
|
226
|
+
for (let index = 0; index < ordered.length; index += 3) {
|
|
227
|
+
assertCurrent();
|
|
228
|
+
const group = ordered.slice(index, index + 3);
|
|
229
|
+
cursors.set(key2, (start + index + group.length) % attempts.length);
|
|
230
|
+
const observations = await Promise.all(
|
|
231
|
+
group.map(async (attempt) => {
|
|
232
|
+
try {
|
|
233
|
+
await validateTokenBatchAttempt(
|
|
234
|
+
scope,
|
|
235
|
+
family.body,
|
|
236
|
+
attempt,
|
|
237
|
+
family.relayer
|
|
238
|
+
);
|
|
239
|
+
assertCurrent();
|
|
240
|
+
const receipt = await pub.getTransactionReceipt({
|
|
241
|
+
hash: attempt.hash
|
|
242
|
+
});
|
|
243
|
+
assertCurrent();
|
|
244
|
+
if (receipt.transactionHash?.toLowerCase() !== attempt.hash.toLowerCase() || typeof receipt.blockNumber !== "bigint" || !receipt.blockHash)
|
|
245
|
+
return;
|
|
246
|
+
if (receipt.status === "reverted")
|
|
247
|
+
return { kind: "reverted", receipt };
|
|
248
|
+
if (receipt.status !== "success" || receipt.from?.toLowerCase() !== family.relayer.toLowerCase() || receipt.to?.toLowerCase() !== BATCH_PERMIT2_ADDRESS.toLowerCase())
|
|
249
|
+
return;
|
|
250
|
+
const exact = family.body.transfers.every(
|
|
251
|
+
(t) => inspectFundingReceipt(
|
|
252
|
+
receipt,
|
|
253
|
+
{
|
|
254
|
+
agentId: "yieldseeker",
|
|
255
|
+
chainId: family.body.chainId,
|
|
256
|
+
token: "USDC",
|
|
257
|
+
walletAddress: family.body.from,
|
|
258
|
+
agentWallet: t.to,
|
|
259
|
+
intendedAmount: t.amount,
|
|
260
|
+
txHash: attempt.hash,
|
|
261
|
+
transfer: "pending",
|
|
262
|
+
registration: "pending",
|
|
263
|
+
delivery: "not-applicable"
|
|
264
|
+
},
|
|
265
|
+
family.body.token,
|
|
266
|
+
"deposit",
|
|
267
|
+
{ requireAmountMatch: true }
|
|
268
|
+
).transfer === "confirmed"
|
|
269
|
+
);
|
|
270
|
+
return exact ? { kind: "confirmed", receipt } : void 0;
|
|
271
|
+
} catch {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
);
|
|
276
|
+
assertCurrent();
|
|
277
|
+
const winner = observations.find((o) => o?.kind === "confirmed");
|
|
278
|
+
if (winner) {
|
|
279
|
+
const latest2 = await loadTokenBatchFamily(
|
|
280
|
+
scope,
|
|
281
|
+
winner.receipt.transactionHash
|
|
282
|
+
);
|
|
283
|
+
assertCurrent();
|
|
284
|
+
if (!latest2) return { status: "unresolved" };
|
|
285
|
+
finishTokenBatchFamily(scope, latest2, {
|
|
286
|
+
kind: "confirmed",
|
|
287
|
+
txHash: winner.receipt.transactionHash,
|
|
288
|
+
blockNumber: winner.receipt.blockNumber.toString()
|
|
289
|
+
});
|
|
290
|
+
return {
|
|
291
|
+
status: "confirmed",
|
|
292
|
+
txHash: winner.receipt.transactionHash,
|
|
293
|
+
receipt: winner.receipt
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
for (const o of observations)
|
|
297
|
+
if (o?.kind === "reverted") reverted.push(o.receipt);
|
|
298
|
+
}
|
|
299
|
+
const latest = await loadTokenBatchFamily(scope, family.attempts[0].hash);
|
|
300
|
+
assertCurrent();
|
|
301
|
+
if (!latest) return { status: "unresolved" };
|
|
302
|
+
if (attempts.length > 0 && reverted.length === latest.attempts.filter((a) => a.phase === "deposit").length) {
|
|
303
|
+
const block2 = reverted.reduce(
|
|
304
|
+
(n, r) => r.blockNumber > n ? r.blockNumber : n,
|
|
305
|
+
0n
|
|
306
|
+
);
|
|
307
|
+
finishTokenBatchFamily(scope, latest, {
|
|
308
|
+
kind: "failed",
|
|
309
|
+
blockNumber: block2.toString()
|
|
310
|
+
});
|
|
311
|
+
return { status: "failed", receipts: reverted };
|
|
312
|
+
}
|
|
313
|
+
const block = await pub.getBlock({ blockTag: "finalized" });
|
|
314
|
+
assertCurrent();
|
|
315
|
+
if (typeof block.number !== "bigint" || !block.hash || typeof block.timestamp !== "bigint" || block.timestamp <= BigInt(family.body.deadline))
|
|
316
|
+
return { status: "unresolved" };
|
|
317
|
+
const nonce = BigInt(family.body.nonce);
|
|
318
|
+
const bitmap = await pub.readContract({
|
|
319
|
+
address: BATCH_PERMIT2_ADDRESS,
|
|
320
|
+
abi: PERMIT2_BATCH_ABI,
|
|
321
|
+
functionName: "nonceBitmap",
|
|
322
|
+
args: [family.body.from, nonce >> 8n],
|
|
323
|
+
blockNumber: block.number
|
|
324
|
+
});
|
|
325
|
+
assertCurrent();
|
|
326
|
+
if (typeof bitmap !== "bigint" || bitmap < 0n || bitmap >= 1n << 256n || (bitmap & 1n << (nonce & 255n)) !== 0n)
|
|
327
|
+
return { status: "unresolved" };
|
|
328
|
+
if ((await pub.getBlock({ blockNumber: block.number })).hash !== block.hash)
|
|
329
|
+
return { status: "unresolved" };
|
|
330
|
+
assertCurrent();
|
|
331
|
+
finishTokenBatchFamily(scope, latest, {
|
|
332
|
+
kind: "expired",
|
|
333
|
+
blockNumber: block.number.toString()
|
|
334
|
+
});
|
|
335
|
+
return { status: "expired", blockNumber: block.number };
|
|
336
|
+
};
|
|
337
|
+
try {
|
|
338
|
+
return await Promise.race([
|
|
339
|
+
run(),
|
|
340
|
+
new Promise((resolve) => {
|
|
341
|
+
timer = setTimeout(
|
|
342
|
+
() => {
|
|
343
|
+
expired = true;
|
|
344
|
+
resolve({ status: "unresolved" });
|
|
345
|
+
},
|
|
346
|
+
Math.max(0, input.deadlineAt - Date.now())
|
|
347
|
+
);
|
|
348
|
+
})
|
|
349
|
+
]);
|
|
350
|
+
} catch {
|
|
351
|
+
return { status: "unresolved" };
|
|
352
|
+
} finally {
|
|
353
|
+
expired = true;
|
|
354
|
+
if (timer) clearTimeout(timer);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/lib/swap/swap.observation.ts
|
|
359
|
+
var active = /* @__PURE__ */ new Set();
|
|
360
|
+
var isOrderObserved = (key2) => active.has(key2);
|
|
361
|
+
async function observeOrder(key2, run) {
|
|
362
|
+
const busy = () => new OwneyError(
|
|
363
|
+
"SWAP_REQUEST_FAILED",
|
|
364
|
+
"This order is already being observed.",
|
|
365
|
+
{ orderHash: key2, watchActive: true, settlementUnconfirmed: true }
|
|
366
|
+
);
|
|
367
|
+
if (active.has(key2)) throw busy();
|
|
368
|
+
active.add(key2);
|
|
369
|
+
try {
|
|
370
|
+
if (typeof navigator !== "undefined" && navigator.locks)
|
|
371
|
+
return await navigator.locks.request(`owney.swap.observe:${key2}`, { ifAvailable: true }, (lock) => {
|
|
372
|
+
if (!lock) throw busy();
|
|
373
|
+
return run();
|
|
374
|
+
});
|
|
375
|
+
return await run();
|
|
376
|
+
} finally {
|
|
377
|
+
active.delete(key2);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/lib/swap/swap.deadline.ts
|
|
382
|
+
async function bounded(work, timeoutMs, error) {
|
|
383
|
+
if (timeoutMs <= 0) throw error();
|
|
384
|
+
let timer;
|
|
385
|
+
try {
|
|
386
|
+
return await Promise.race([
|
|
387
|
+
Promise.resolve().then(work),
|
|
388
|
+
new Promise((_, reject) => {
|
|
389
|
+
timer = setTimeout(() => reject(error()), timeoutMs);
|
|
390
|
+
})
|
|
391
|
+
]);
|
|
392
|
+
} finally {
|
|
393
|
+
clearTimeout(timer);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function mapConcurrent(items, run, concurrency = 3) {
|
|
397
|
+
const results = new Array(items.length);
|
|
398
|
+
let next = 0;
|
|
399
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
400
|
+
for (; ; ) {
|
|
401
|
+
const index = next++;
|
|
402
|
+
if (index >= items.length) return;
|
|
403
|
+
results[index] = await run(items[index]);
|
|
404
|
+
}
|
|
405
|
+
}));
|
|
406
|
+
return results;
|
|
407
|
+
}
|
|
408
|
+
|
|
387
409
|
// src/lib/helpers/funding-amount.ts
|
|
388
410
|
function isFundingAmount(value, allowZero = false) {
|
|
389
411
|
if (typeof value !== "string" || !/^\d{1,78}$/.test(value)) return false;
|
|
@@ -498,6 +520,7 @@ async function postSponsorBatchTransfer(input) {
|
|
|
498
520
|
`${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch`,
|
|
499
521
|
{
|
|
500
522
|
method: "POST",
|
|
523
|
+
...input.signal ? { signal: input.signal } : {},
|
|
501
524
|
headers: {
|
|
502
525
|
"content-type": "application/json",
|
|
503
526
|
"x-owney-api-key": input.apiKey
|
|
@@ -526,6 +549,45 @@ async function postSponsorBatchTransfer(input) {
|
|
|
526
549
|
}
|
|
527
550
|
return parsed.data;
|
|
528
551
|
}
|
|
552
|
+
async function postSponsorBatchRecovery(input) {
|
|
553
|
+
const failure = () => new OwneyError("SPONSOR_REQUEST_FAILED", "Deposit recovery is unavailable. The existing transaction remains tracked.", { safeToFallback: false });
|
|
554
|
+
try {
|
|
555
|
+
input.signal?.throwIfAborted();
|
|
556
|
+
const response = await fetch(`${input.baseUrl ?? ROUTING_API_BASE_URL}/api/v1/sponsor/permit2-batch/recover`, { method: "POST", headers: { "content-type": "application/json", "x-owney-api-key": input.apiKey }, body: JSON.stringify(input.body), signal: input.signal });
|
|
557
|
+
input.signal?.throwIfAborted();
|
|
558
|
+
if (response.status === 404) return;
|
|
559
|
+
const payload = await response.json();
|
|
560
|
+
input.signal?.throwIfAborted();
|
|
561
|
+
if (!response.ok || payload?.success !== true) throw failure();
|
|
562
|
+
const value = payload.data;
|
|
563
|
+
const { isAddress: isAddress5, keccak256: keccak2564, parseTransaction: parseTransaction3, recoverTransactionAddress } = await import("viem");
|
|
564
|
+
const { validateTokenBatchAttempt: validateTokenBatchAttempt2 } = await import("./token-batch-family-VW6ZJLK5.js");
|
|
565
|
+
const phase = input.body.phase;
|
|
566
|
+
const original = phase === "approval" ? input.body.serializedPermitTransaction : input.body.serializedTransaction;
|
|
567
|
+
if (!original) throw failure();
|
|
568
|
+
const hash2 = keccak2564(original), relayer = await recoverTransactionAddress({ serializedTransaction: original });
|
|
569
|
+
const validHash = (v) => typeof v === "string" && /^0x[\da-f]{64}$/i.test(v);
|
|
570
|
+
if (!value || value.protocol !== 1 || value.phase !== phase || !validHash(value.txHash) || !isAddress5(value.relayer) || value.relayer.toLowerCase() !== relayer.toLowerCase()) throw failure();
|
|
571
|
+
if (value.status !== "prepared" && value.txHash.toLowerCase() !== hash2.toLowerCase()) throw failure();
|
|
572
|
+
if (value.status === "unresolved") {
|
|
573
|
+
if (!["nonce-not-consumed", "authorization-used", "authorization-expired", "state-unavailable", "preparation-rejected", "relayer-changed"].includes(value.reason)) throw failure();
|
|
574
|
+
} else {
|
|
575
|
+
if (!("blockNumber" in value) || typeof value.blockNumber !== "string" || !/^\d{1,78}$/.test(value.blockNumber) || BigInt(value.blockNumber) >= 1n << 256n || !validHash(value.blockHash)) throw failure();
|
|
576
|
+
if (value.status === "prepared") {
|
|
577
|
+
if (value.predecessorHash?.toLowerCase() !== hash2.toLowerCase()) throw failure();
|
|
578
|
+
const tx = parseTransaction3(value.serializedTransaction), old = parseTransaction3(original);
|
|
579
|
+
if (!Number.isSafeInteger(tx.nonce) || tx.nonce <= old.nonce) throw failure();
|
|
580
|
+
await validateTokenBatchAttempt2({ apiKey: input.apiKey, baseUrl: input.baseUrl, chainId: input.body.chainId, owner: input.body.from, token: input.body.token }, input.body, { phase, hash: value.txHash, serializedTransaction: value.serializedTransaction, nonce: String(tx.nonce), predecessorHash: hash2, preparedAt: Date.now(), submissionAttempted: false }, relayer);
|
|
581
|
+
} else if (value.status === "included") {
|
|
582
|
+
if (value.outcome !== "success" && value.outcome !== "reverted") throw failure();
|
|
583
|
+
} else if (value.status !== "approval-satisfied" || phase !== "approval") throw failure();
|
|
584
|
+
}
|
|
585
|
+
input.signal?.throwIfAborted();
|
|
586
|
+
return value;
|
|
587
|
+
} catch {
|
|
588
|
+
throw failure();
|
|
589
|
+
}
|
|
590
|
+
}
|
|
529
591
|
|
|
530
592
|
// src/lib/permit2.ts
|
|
531
593
|
import { bytesToHex as bytesToHex2 } from "viem";
|
|
@@ -1065,7 +1127,7 @@ async function submitZyfaiDeposit({ sdk, state, chain: chain2, amount, asset, sm
|
|
|
1065
1127
|
|
|
1066
1128
|
// src/agents/zyfai/zyfai.agent.ts
|
|
1067
1129
|
import { SMART_SESSIONS_VALIDATOR, ZyfaiSDK } from "@zyfai/sdk";
|
|
1068
|
-
import { erc20Abi as erc20Abi3, formatUnits as formatUnits2, isAddress as isAddress2, parseAbi as
|
|
1130
|
+
import { erc20Abi as erc20Abi3, formatUnits as formatUnits2, isAddress as isAddress2, parseAbi as parseAbi2, parseUnits } from "viem";
|
|
1069
1131
|
import { base, arbitrum, mainnet } from "viem/chains";
|
|
1070
1132
|
|
|
1071
1133
|
// src/lib/debug.ts
|
|
@@ -2023,7 +2085,7 @@ async function withPaidRpcDiagnostics(operation, chainId, rpcMethod, agentId) {
|
|
|
2023
2085
|
}
|
|
2024
2086
|
|
|
2025
2087
|
// src/agents/zyfai/zyfai.agent.ts
|
|
2026
|
-
var ERC7579_IS_MODULE_INSTALLED_ABI =
|
|
2088
|
+
var ERC7579_IS_MODULE_INSTALLED_ABI = parseAbi2([
|
|
2027
2089
|
"function isModuleInstalled(uint256 moduleTypeId, address module, bytes additionalContext) view returns (bool)"
|
|
2028
2090
|
]);
|
|
2029
2091
|
var WETH_ADDRESS_BY_CHAIN = {
|
|
@@ -2093,6 +2155,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2093
2155
|
connectedAddress = null;
|
|
2094
2156
|
connectedChainId = null;
|
|
2095
2157
|
smartWalletCache = /* @__PURE__ */ new Map();
|
|
2158
|
+
smartWalletReads = /* @__PURE__ */ new Map();
|
|
2096
2159
|
/**
|
|
2097
2160
|
* `${eoa}:${chainId}` keys we have already tried createSessionKey for. Guards
|
|
2098
2161
|
* against re-running it on every activation/deposit when Zyfai's
|
|
@@ -2110,6 +2173,9 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2110
2173
|
fundingHistoryCache = /* @__PURE__ */ new Map();
|
|
2111
2174
|
earningsReads = /* @__PURE__ */ new Map();
|
|
2112
2175
|
earningsRefreshes = /* @__PURE__ */ new Map();
|
|
2176
|
+
// Recalculation is wallet-wide across assets/chains. Bound attempts locally,
|
|
2177
|
+
// including failures; a negative result must not trigger a POST every poll.
|
|
2178
|
+
earningsCalculationAttempts = /* @__PURE__ */ new Map();
|
|
2113
2179
|
earningsSnapshot = null;
|
|
2114
2180
|
earningsGeneration = 0;
|
|
2115
2181
|
balanceSnapshots = /* @__PURE__ */ new WeakMap();
|
|
@@ -2285,6 +2351,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2285
2351
|
this.sdkHasConnected = true;
|
|
2286
2352
|
const executionSdk = this.sdk;
|
|
2287
2353
|
const generation = ++this.connectionGeneration;
|
|
2354
|
+
this.smartWalletCache.clear();
|
|
2355
|
+
this.smartWalletReads.clear();
|
|
2288
2356
|
this.connectedAddress = null;
|
|
2289
2357
|
this.connectedChainId = null;
|
|
2290
2358
|
const pending = (async () => {
|
|
@@ -2396,18 +2464,29 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2396
2464
|
await this.connectAuth(state, chainId);
|
|
2397
2465
|
const validChainId = isValidChainId(chainId);
|
|
2398
2466
|
const ownerAddress = this.getAddress();
|
|
2399
|
-
const
|
|
2400
|
-
let smartWallet = this.smartWalletCache.get(cacheKey);
|
|
2401
|
-
if (!smartWallet) {
|
|
2402
|
-
const { address } = await this.sdk.getSmartWalletAddress(
|
|
2403
|
-
ownerAddress,
|
|
2404
|
-
validChainId
|
|
2405
|
-
);
|
|
2406
|
-
smartWallet = address;
|
|
2407
|
-
this.smartWalletCache.set(cacheKey, smartWallet);
|
|
2408
|
-
}
|
|
2467
|
+
const smartWallet = await this.readSmartWallet(ownerAddress, validChainId);
|
|
2409
2468
|
return { smartWallet, chainId: validChainId };
|
|
2410
2469
|
}
|
|
2470
|
+
/** Read-only resolution shared with recovery; never enters authentication. */
|
|
2471
|
+
readSmartWallet(owner, chainId) {
|
|
2472
|
+
const key2 = `${owner.toLowerCase()}_${chainId}`;
|
|
2473
|
+
const cached = this.smartWalletCache.get(key2);
|
|
2474
|
+
if (cached) return Promise.resolve(cached);
|
|
2475
|
+
const existing = this.smartWalletReads.get(key2);
|
|
2476
|
+
if (existing) return existing;
|
|
2477
|
+
const generation = this.connectionGeneration;
|
|
2478
|
+
const executionSdk = this.sdk;
|
|
2479
|
+
const pending = Promise.resolve().then(() => executionSdk.getSmartWalletAddress(owner, chainId)).then(({ address }) => {
|
|
2480
|
+
if (generation !== this.connectionGeneration || executionSdk !== this.sdk)
|
|
2481
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while resolving Zyfai.", void 0, this.id);
|
|
2482
|
+
this.smartWalletCache.set(key2, address);
|
|
2483
|
+
return address;
|
|
2484
|
+
}).finally(() => {
|
|
2485
|
+
if (this.smartWalletReads.get(key2) === pending) this.smartWalletReads.delete(key2);
|
|
2486
|
+
});
|
|
2487
|
+
this.smartWalletReads.set(key2, pending);
|
|
2488
|
+
return pending;
|
|
2489
|
+
}
|
|
2411
2490
|
// --- IAgent: Connection lifecycle ---
|
|
2412
2491
|
async disconnect() {
|
|
2413
2492
|
const executionSdk = this.sdk;
|
|
@@ -2415,6 +2494,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2415
2494
|
this.earningsGeneration++;
|
|
2416
2495
|
this.earningsReads.clear();
|
|
2417
2496
|
this.earningsRefreshes.clear();
|
|
2497
|
+
this.earningsCalculationAttempts.clear();
|
|
2418
2498
|
this.earningsSnapshot = null;
|
|
2419
2499
|
if (this.connectedAddress && this.connectedChainId !== null) {
|
|
2420
2500
|
clearSession(this.connectedAddress, this.connectedChainId);
|
|
@@ -2424,6 +2504,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
2424
2504
|
this.connectAuthPromise = null;
|
|
2425
2505
|
this.connectAuthScope = null;
|
|
2426
2506
|
this.smartWalletCache.clear();
|
|
2507
|
+
this.smartWalletReads.clear();
|
|
2427
2508
|
this.sdk = this.createSdk();
|
|
2428
2509
|
this.sdkHasConnected = false;
|
|
2429
2510
|
await executionSdk.disconnectAccount();
|
|
@@ -3269,7 +3350,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3269
3350
|
}
|
|
3270
3351
|
// --- IAgent: Portfolio reads ---
|
|
3271
3352
|
async getFundingWallet(state, chainId) {
|
|
3272
|
-
return
|
|
3353
|
+
return this.readSmartWallet(state.walletAddress, isValidChainId(chainId));
|
|
3273
3354
|
}
|
|
3274
3355
|
/** A positive deployed Safe balance is a conservative planning floor. Zero
|
|
3275
3356
|
* requires both the mapped Safe's exact idle balance and a fresh complete
|
|
@@ -3384,8 +3465,13 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3384
3465
|
const existing = this.earningsReads.get(key2);
|
|
3385
3466
|
if (existing) return existing;
|
|
3386
3467
|
const generation = this.earningsGeneration;
|
|
3387
|
-
const
|
|
3388
|
-
|
|
3468
|
+
const executionSdk = this.sdk;
|
|
3469
|
+
const pending = Promise.resolve().then(() => {
|
|
3470
|
+
if (generation !== this.earningsGeneration || executionSdk !== this.sdk)
|
|
3471
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while reading Zyfai earnings.", void 0, this.id);
|
|
3472
|
+
return executionSdk.getOnchainEarnings(smartWallet);
|
|
3473
|
+
}).then((raw2) => {
|
|
3474
|
+
if (generation === this.earningsGeneration && executionSdk === this.sdk) {
|
|
3389
3475
|
this.earningsSnapshot = { key: key2, raw: raw2, at: Date.now() };
|
|
3390
3476
|
}
|
|
3391
3477
|
return raw2;
|
|
@@ -3405,20 +3491,37 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3405
3491
|
return mapEarnings(raw2, smartWallet);
|
|
3406
3492
|
}
|
|
3407
3493
|
async refreshEarnings(state, chainId) {
|
|
3494
|
+
const generation = this.earningsGeneration;
|
|
3495
|
+
await this.connectAuth(state, chainId);
|
|
3496
|
+
const executionSdk = this.sdk;
|
|
3497
|
+
const connectionGeneration = this.connectionGeneration;
|
|
3498
|
+
const assertCurrent = () => {
|
|
3499
|
+
if (generation !== this.earningsGeneration || connectionGeneration !== this.connectionGeneration || executionSdk !== this.sdk)
|
|
3500
|
+
throw new OwneyError("NOT_CONNECTED", "Wallet changed while refreshing Zyfai earnings.", void 0, this.id);
|
|
3501
|
+
};
|
|
3502
|
+
assertCurrent();
|
|
3408
3503
|
const { smartWallet } = await this.resolveSmartWallet(state, chainId);
|
|
3504
|
+
assertCurrent();
|
|
3409
3505
|
const key2 = this.earningsKey(state, chainId, smartWallet);
|
|
3410
3506
|
const existing = this.earningsRefreshes.get(key2);
|
|
3411
3507
|
if (existing) return existing;
|
|
3412
|
-
const generation = this.earningsGeneration;
|
|
3413
3508
|
const pending = (async () => {
|
|
3414
3509
|
const recent = this.earningsSnapshot;
|
|
3415
3510
|
const current = recent?.key === key2 && Date.now() - recent.at < 5e3 ? recent.raw : await this.readEarnings(key2, smartWallet);
|
|
3416
3511
|
const lastCheck = current.data.lastCheckTimestamp ? Date.parse(current.data.lastCheckTimestamp) : Number.NaN;
|
|
3417
|
-
const
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3512
|
+
const mapped = mapEarnings(current, smartWallet);
|
|
3513
|
+
assertCurrent();
|
|
3514
|
+
const now = Date.now();
|
|
3515
|
+
const isFresh = Number.isFinite(lastCheck) && lastCheck <= now && now - lastCheck < EARNINGS_REFRESH_COOLDOWN_MS;
|
|
3516
|
+
const negative = mapped.lifetimeEarnings < 0 || mapped.tokens.some((token) => Number(token.amount) < 0);
|
|
3517
|
+
const calculationKey = JSON.stringify([state.walletAddress?.toLowerCase(), smartWallet.toLowerCase()]);
|
|
3518
|
+
const attemptedAt = this.earningsCalculationAttempts.get(calculationKey);
|
|
3519
|
+
const canCalculate = attemptedAt === void 0 || now - attemptedAt >= EARNINGS_REFRESH_COOLDOWN_MS;
|
|
3520
|
+
if (isFresh && !negative || !canCalculate) return mapped;
|
|
3521
|
+
this.earningsCalculationAttempts.set(calculationKey, now);
|
|
3522
|
+
const earnings = await executionSdk.calculateOnchainEarnings(smartWallet);
|
|
3523
|
+
assertCurrent();
|
|
3524
|
+
this.earningsSnapshot = { key: key2, raw: earnings, at: Date.now() };
|
|
3422
3525
|
return mapEarnings(earnings, smartWallet);
|
|
3423
3526
|
})().finally(() => {
|
|
3424
3527
|
if (this.earningsRefreshes.get(key2) === pending)
|
|
@@ -3576,10 +3679,10 @@ import {
|
|
|
3576
3679
|
custom as custom4,
|
|
3577
3680
|
decodeEventLog as decodeEventLog2,
|
|
3578
3681
|
encodeEventTopics,
|
|
3579
|
-
encodeFunctionData as
|
|
3682
|
+
encodeFunctionData as encodeFunctionData3,
|
|
3580
3683
|
erc20Abi as erc20Abi5,
|
|
3581
|
-
erc4626Abi as
|
|
3582
|
-
getAddress as
|
|
3684
|
+
erc4626Abi as erc4626Abi3,
|
|
3685
|
+
getAddress as getAddress6,
|
|
3583
3686
|
isAddress as isAddress4,
|
|
3584
3687
|
parseUnits as parseUnits2
|
|
3585
3688
|
} from "viem";
|
|
@@ -4656,13 +4759,13 @@ async function remainingVaultBalance(client, wallet, asset, vaults, blockNumber)
|
|
|
4656
4759
|
import {
|
|
4657
4760
|
getAddress as getAddress3,
|
|
4658
4761
|
isAddressEqual,
|
|
4659
|
-
parseAbi as
|
|
4762
|
+
parseAbi as parseAbi3
|
|
4660
4763
|
} from "viem";
|
|
4661
4764
|
var YIELDSEEKER_FACTORY = "0x9c7410a0faCAC60850C46eE5b58B518daec95130";
|
|
4662
4765
|
var YIELDSEEKER_V1_IMPLEMENTATION = "0x32403455357589523435dB18b414057D7cE05051";
|
|
4663
4766
|
var YIELDSEEKER_V2_IMPLEMENTATION = "0x613Db9c924ADB7A7A90c11b02Cce20c913E3Fe50";
|
|
4664
4767
|
var ERC1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
|
4665
|
-
var walletAbi =
|
|
4768
|
+
var walletAbi = parseAbi3([
|
|
4666
4769
|
"function owner() view returns (address)",
|
|
4667
4770
|
"function ownerAgentIndex() view returns (uint256)",
|
|
4668
4771
|
"function baseAsset() view returns (address)",
|
|
@@ -4670,7 +4773,7 @@ var walletAbi = parseAbi4([
|
|
|
4670
4773
|
"function upgradeToLatest()",
|
|
4671
4774
|
"event Upgraded(address indexed implementation)"
|
|
4672
4775
|
]);
|
|
4673
|
-
var factoryAbi =
|
|
4776
|
+
var factoryAbi = parseAbi3([
|
|
4674
4777
|
"function userWallets(address owner, uint256 index) view returns (address)",
|
|
4675
4778
|
"function agentWalletImplementation() view returns (address)"
|
|
4676
4779
|
]);
|
|
@@ -5285,6 +5388,57 @@ async function submitYieldseekerSponsoredUpgrade(input) {
|
|
|
5285
5388
|
return submitSmart({ ...input, owner, rpcUrl, expiresAt: intent.expiresAt });
|
|
5286
5389
|
}
|
|
5287
5390
|
|
|
5391
|
+
// src/agents/yieldseeker/yieldseeker.vault-reads.ts
|
|
5392
|
+
import { decodeFunctionResult, encodeFunctionData as encodeFunctionData2, erc4626Abi as erc4626Abi2, getAddress as getAddress5, parseAbi as parseAbi4 } from "viem";
|
|
5393
|
+
var nativeAbi = parseAbi4([
|
|
5394
|
+
"function underlying() view returns (address)",
|
|
5395
|
+
"function baseToken() view returns (address)",
|
|
5396
|
+
"function UNDERLYING_ASSET_ADDRESS() view returns (address)",
|
|
5397
|
+
"function balanceOfUnderlying(address owner) returns (uint256)"
|
|
5398
|
+
]);
|
|
5399
|
+
function nativeYieldseekerProtocol(option) {
|
|
5400
|
+
switch (option.provider?.toLowerCase()) {
|
|
5401
|
+
case "moonwell":
|
|
5402
|
+
return "moonwell";
|
|
5403
|
+
case "compound":
|
|
5404
|
+
return "compound";
|
|
5405
|
+
case "aave":
|
|
5406
|
+
return "aave";
|
|
5407
|
+
default:
|
|
5408
|
+
return void 0;
|
|
5409
|
+
}
|
|
5410
|
+
}
|
|
5411
|
+
function yieldseekerUnderlyingCall(option) {
|
|
5412
|
+
const address = getAddress5(option.address);
|
|
5413
|
+
switch (nativeYieldseekerProtocol(option)) {
|
|
5414
|
+
case "moonwell":
|
|
5415
|
+
return { address, abi: nativeAbi, functionName: "underlying" };
|
|
5416
|
+
case "compound":
|
|
5417
|
+
return { address, abi: nativeAbi, functionName: "baseToken" };
|
|
5418
|
+
case "aave":
|
|
5419
|
+
return { address, abi: nativeAbi, functionName: "UNDERLYING_ASSET_ADDRESS" };
|
|
5420
|
+
default:
|
|
5421
|
+
return { address, abi: erc4626Abi2, functionName: "asset" };
|
|
5422
|
+
}
|
|
5423
|
+
}
|
|
5424
|
+
async function readNativeYieldseekerAssets(client, option, wallet, balance, blockNumber) {
|
|
5425
|
+
if (nativeYieldseekerProtocol(option) !== "moonwell") {
|
|
5426
|
+
return balance;
|
|
5427
|
+
}
|
|
5428
|
+
try {
|
|
5429
|
+
const { data } = await client.call({
|
|
5430
|
+
to: getAddress5(option.address),
|
|
5431
|
+
blockNumber,
|
|
5432
|
+
data: encodeFunctionData2({ abi: nativeAbi, functionName: "balanceOfUnderlying", args: [getAddress5(wallet)] })
|
|
5433
|
+
});
|
|
5434
|
+
if (!data) return void 0;
|
|
5435
|
+
const assets = decodeFunctionResult({ abi: nativeAbi, functionName: "balanceOfUnderlying", data });
|
|
5436
|
+
return assets > 0n ? assets : void 0;
|
|
5437
|
+
} catch {
|
|
5438
|
+
return void 0;
|
|
5439
|
+
}
|
|
5440
|
+
}
|
|
5441
|
+
|
|
5288
5442
|
// src/agents/yieldseeker/yieldseeker.agent.ts
|
|
5289
5443
|
var OWNEY_AGENT_NAME = "owney";
|
|
5290
5444
|
var OWNEY_AGENT_RULE_PRESET = "explorative";
|
|
@@ -5294,7 +5448,8 @@ var YIELDSEEKER_USERNAME_CREATE_ATTEMPTS = 3;
|
|
|
5294
5448
|
var YIELDSEEKER_VAULT_OPTIONS_CACHE_MS = 6e4;
|
|
5295
5449
|
var YIELDSEEKER_AGENT_LIST_CACHE_MS = 12e4;
|
|
5296
5450
|
var YIELDSEEKER_PORTFOLIO_CACHE_MS = 12e4;
|
|
5297
|
-
var
|
|
5451
|
+
var YIELDSEEKER_SETTLEMENT_BACKOFF_MS = [5e3, 1e4, 2e4, 4e4, 6e4];
|
|
5452
|
+
var YIELDSEEKER_SETTLEMENT_SNAPSHOT_WINDOW_MS = 10 * 6e4;
|
|
5298
5453
|
var YIELDSEEKER_SETTLEMENT_ACTIVITY_CACHE_MS = 15e3;
|
|
5299
5454
|
var YIELDSEEKER_READ_FAILURE_COOLDOWN_MS = 6e4;
|
|
5300
5455
|
var YIELDSEEKER_SETTLEMENT_WINDOW_MS = 5 * 6e4;
|
|
@@ -5381,6 +5536,7 @@ var YieldseekerAgent = class {
|
|
|
5381
5536
|
receiptClient;
|
|
5382
5537
|
// A client preparation hint only; the routing API still checks live identity.
|
|
5383
5538
|
withdrawalVersionCache = /* @__PURE__ */ new Map();
|
|
5539
|
+
pendingWalletStatus = /* @__PURE__ */ new Map();
|
|
5384
5540
|
pendingUpgrades = /* @__PURE__ */ new Map();
|
|
5385
5541
|
transactionExecutor;
|
|
5386
5542
|
unwindReceiptWaiter;
|
|
@@ -5397,6 +5553,7 @@ var YieldseekerAgent = class {
|
|
|
5397
5553
|
liquidityVersions = /* @__PURE__ */ new Map();
|
|
5398
5554
|
snapshotFailures = /* @__PURE__ */ new Map();
|
|
5399
5555
|
standardReadFailures = /* @__PURE__ */ new Map();
|
|
5556
|
+
endpointReadFailures = /* @__PURE__ */ new Map();
|
|
5400
5557
|
reconcileUntil = /* @__PURE__ */ new Map();
|
|
5401
5558
|
allocationRefresh = /* @__PURE__ */ new Set();
|
|
5402
5559
|
activityRefreshUntil = /* @__PURE__ */ new Map();
|
|
@@ -5404,6 +5561,8 @@ var YieldseekerAgent = class {
|
|
|
5404
5561
|
// settlement; keep slow reconciliation after the fast window expires.
|
|
5405
5562
|
snapshotMovements = /* @__PURE__ */ new Map();
|
|
5406
5563
|
movementEpochs = /* @__PURE__ */ new Map();
|
|
5564
|
+
// Forced checks issued by the current movement's settlement schedule.
|
|
5565
|
+
settlementChecks = /* @__PURE__ */ new Map();
|
|
5407
5566
|
vaultOptions = /* @__PURE__ */ new Map();
|
|
5408
5567
|
pendingVaultOptions = /* @__PURE__ */ new Map();
|
|
5409
5568
|
constructor(owneyApiKey, options = {}) {
|
|
@@ -5427,19 +5586,31 @@ var YieldseekerAgent = class {
|
|
|
5427
5586
|
async getWalletStatus(state, chainId, asset, useCachedV2 = false) {
|
|
5428
5587
|
this.assertChain(chainId);
|
|
5429
5588
|
this.assertAsset(asset);
|
|
5589
|
+
const generation = this.readGeneration;
|
|
5430
5590
|
const context = await this.findAgent(state, chainId, asset);
|
|
5591
|
+
this.assertSession(generation, chainId, asset);
|
|
5431
5592
|
if (!context) return null;
|
|
5432
5593
|
const key2 = `${chainId}:${state.walletAddress.toLowerCase()}:${context.wallet.walletAddress.toLowerCase()}:${asset}`;
|
|
5433
5594
|
const cached = this.withdrawalVersionCache.get(key2);
|
|
5434
5595
|
if (useCachedV2 && cached && Date.now() - cached.at < 15 * 6e4) return { ...cached.status };
|
|
5435
|
-
const
|
|
5596
|
+
const existing = this.pendingWalletStatus.get(key2);
|
|
5597
|
+
if (existing) return { ...await existing };
|
|
5598
|
+
const pending = readYieldseekerWalletStatus(
|
|
5436
5599
|
this.getReceiptClient(),
|
|
5437
|
-
|
|
5438
|
-
|
|
5600
|
+
getAddress6(context.wallet.walletAddress),
|
|
5601
|
+
getAddress6(state.walletAddress),
|
|
5439
5602
|
YIELDSEEKER_ASSET_METADATA[asset].address
|
|
5440
|
-
)
|
|
5441
|
-
|
|
5442
|
-
|
|
5603
|
+
).then((status) => {
|
|
5604
|
+
this.assertSession(generation, chainId, asset);
|
|
5605
|
+
if (this.pendingWalletStatus.get(key2) !== pending)
|
|
5606
|
+
throw new OwneyError("AGENT_API_ERROR", "Yieldseeker wallet verification changed. Please retry.", void 0, this.id);
|
|
5607
|
+
this.rememberWalletStatus(key2, status);
|
|
5608
|
+
return status;
|
|
5609
|
+
}).finally(() => {
|
|
5610
|
+
if (this.pendingWalletStatus.get(key2) === pending) this.pendingWalletStatus.delete(key2);
|
|
5611
|
+
});
|
|
5612
|
+
this.pendingWalletStatus.set(key2, pending);
|
|
5613
|
+
return { ...await pending };
|
|
5443
5614
|
}
|
|
5444
5615
|
rememberWalletStatus(key2, status) {
|
|
5445
5616
|
if (status.verified && status.version === "v2") {
|
|
@@ -5465,6 +5636,7 @@ var YieldseekerAgent = class {
|
|
|
5465
5636
|
const pendingKey2 = `owney.yieldseeker.upgrade:${before.owner.toLowerCase()}:${before.agentWallet.toLowerCase()}`;
|
|
5466
5637
|
const confirmUpgrade = async (hash2, receipt2) => {
|
|
5467
5638
|
this.withdrawalVersionCache.delete(versionKey);
|
|
5639
|
+
this.pendingWalletStatus.delete(versionKey);
|
|
5468
5640
|
const upgradeTopic = encodeEventTopics({ abi: YIELDSEEKER_UPGRADE_ABI, eventName: "Upgraded" })[0];
|
|
5469
5641
|
const lastUpgrade = receipt2?.logs.filter((log) => log.address.toLowerCase() === before.agentWallet.toLowerCase() && log.topics[0] === upgradeTopic).at(-1);
|
|
5470
5642
|
const expectedUpgrade = (() => {
|
|
@@ -5608,7 +5780,7 @@ var YieldseekerAgent = class {
|
|
|
5608
5780
|
{ version: before.version },
|
|
5609
5781
|
this.id
|
|
5610
5782
|
);
|
|
5611
|
-
const data =
|
|
5783
|
+
const data = encodeFunctionData3({ abi: YIELDSEEKER_UPGRADE_ABI, functionName: "upgradeToLatest" });
|
|
5612
5784
|
let txHash;
|
|
5613
5785
|
let receipt;
|
|
5614
5786
|
if (gasMode === "sponsored") {
|
|
@@ -5651,7 +5823,7 @@ var YieldseekerAgent = class {
|
|
|
5651
5823
|
let submittedHash;
|
|
5652
5824
|
try {
|
|
5653
5825
|
txHash = await this.submitTransaction(state, chainId, {
|
|
5654
|
-
from:
|
|
5826
|
+
from: getAddress6(state.walletAddress),
|
|
5655
5827
|
to: before.agentWallet,
|
|
5656
5828
|
data,
|
|
5657
5829
|
value: "0",
|
|
@@ -5685,6 +5857,7 @@ var YieldseekerAgent = class {
|
|
|
5685
5857
|
}
|
|
5686
5858
|
async disconnect() {
|
|
5687
5859
|
this.withdrawalVersionCache.clear();
|
|
5860
|
+
this.pendingWalletStatus.clear();
|
|
5688
5861
|
this.readGeneration += 1;
|
|
5689
5862
|
this.auth.clear();
|
|
5690
5863
|
for (const key2 of this.users.keys()) {
|
|
@@ -5702,11 +5875,13 @@ var YieldseekerAgent = class {
|
|
|
5702
5875
|
this.liquidityVersions.clear();
|
|
5703
5876
|
this.snapshotFailures.clear();
|
|
5704
5877
|
this.standardReadFailures.clear();
|
|
5878
|
+
this.endpointReadFailures.clear();
|
|
5705
5879
|
this.reconcileUntil.clear();
|
|
5706
5880
|
this.allocationRefresh.clear();
|
|
5707
5881
|
this.activityRefreshUntil.clear();
|
|
5708
5882
|
this.snapshotMovements.clear();
|
|
5709
5883
|
this.movementEpochs.clear();
|
|
5884
|
+
this.settlementChecks.clear();
|
|
5710
5885
|
}
|
|
5711
5886
|
async activateAgent(state, chainId, asset) {
|
|
5712
5887
|
this.assertChain(chainId);
|
|
@@ -5759,12 +5934,12 @@ var YieldseekerAgent = class {
|
|
|
5759
5934
|
} else {
|
|
5760
5935
|
try {
|
|
5761
5936
|
txHash = await this.submitTransaction(state, chainId, {
|
|
5762
|
-
from:
|
|
5937
|
+
from: getAddress6(state.walletAddress),
|
|
5763
5938
|
to: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
5764
|
-
data:
|
|
5939
|
+
data: encodeFunctionData3({
|
|
5765
5940
|
abi: erc20Abi5,
|
|
5766
5941
|
functionName: "transfer",
|
|
5767
|
-
args: [
|
|
5942
|
+
args: [getAddress6(context.wallet.walletAddress), BigInt(amount)]
|
|
5768
5943
|
}),
|
|
5769
5944
|
value: "0",
|
|
5770
5945
|
chainId
|
|
@@ -5827,7 +6002,10 @@ var YieldseekerAgent = class {
|
|
|
5827
6002
|
ownerTransferAttempted = true;
|
|
5828
6003
|
}, gasMode, onBeforeSubmission, assertOperation);
|
|
5829
6004
|
} catch (error) {
|
|
5830
|
-
if (error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED")
|
|
6005
|
+
if (error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED") {
|
|
6006
|
+
this.withdrawalVersionCache.clear();
|
|
6007
|
+
this.pendingWalletStatus.clear();
|
|
6008
|
+
}
|
|
5831
6009
|
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)))
|
|
5832
6010
|
notifyFunding(onProgress, {
|
|
5833
6011
|
...evidence,
|
|
@@ -5910,7 +6088,7 @@ var YieldseekerAgent = class {
|
|
|
5910
6088
|
);
|
|
5911
6089
|
const positionsRead = this.readWithdrawalPositions(state, chainId, context);
|
|
5912
6090
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
5913
|
-
const agentWallet =
|
|
6091
|
+
const agentWallet = getAddress6(context.wallet.walletAddress);
|
|
5914
6092
|
const readOwnerFunds = async (blockNumber) => {
|
|
5915
6093
|
const feeTracker = await withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5916
6094
|
address: agentWallet,
|
|
@@ -5930,7 +6108,7 @@ var YieldseekerAgent = class {
|
|
|
5930
6108
|
blockNumber
|
|
5931
6109
|
}), chainId, "eth_call", this.id),
|
|
5932
6110
|
withPaidRpcDiagnostics(() => this.getReceiptClient().readContract({
|
|
5933
|
-
address:
|
|
6111
|
+
address: getAddress6(feeTracker),
|
|
5934
6112
|
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
5935
6113
|
functionName: "getFeesOwed",
|
|
5936
6114
|
args: [agentWallet],
|
|
@@ -5972,7 +6150,7 @@ var YieldseekerAgent = class {
|
|
|
5972
6150
|
let active2 = true;
|
|
5973
6151
|
const checks = this.readVaultShares(values.map((position2) => position2.vaultOption), agentWallet, blockNumber).catch(() => []);
|
|
5974
6152
|
const reads = values.map(async (position2, index) => {
|
|
5975
|
-
const address =
|
|
6153
|
+
const address = getAddress6(position2.vaultOption.address);
|
|
5976
6154
|
try {
|
|
5977
6155
|
const group = (await checks).slice(index * 2, index * 2 + 2);
|
|
5978
6156
|
if (!active2 || group.length !== 2 || group.some((value) => value.status !== "success")) return null;
|
|
@@ -5980,13 +6158,27 @@ var YieldseekerAgent = class {
|
|
|
5980
6158
|
const [underlying, shares] = group.map((value) => value.result);
|
|
5981
6159
|
if (typeof underlying !== "string" || !isAddress4(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n) return null;
|
|
5982
6160
|
const verified = { ...position2, assetsRaw: shares.toString(), withdrawableAssetsRaw: "0" };
|
|
6161
|
+
if (shares > 0n && nativeYieldseekerProtocol(position2.vaultOption)) {
|
|
6162
|
+
const value = await readNativeYieldseekerAssets(
|
|
6163
|
+
this.getReceiptClient(),
|
|
6164
|
+
position2.vaultOption,
|
|
6165
|
+
agentWallet,
|
|
6166
|
+
shares,
|
|
6167
|
+
blockNumber
|
|
6168
|
+
);
|
|
6169
|
+
if (!active2 || value === void 0) return null;
|
|
6170
|
+
this.assertSession(generation, chainId, asset);
|
|
6171
|
+
verified.withdrawableAssetsRaw = value.toString();
|
|
6172
|
+
verifiedPositions.push(verified);
|
|
6173
|
+
return verified;
|
|
6174
|
+
}
|
|
5983
6175
|
if (shares > 0n) {
|
|
5984
6176
|
let positiveValue = false;
|
|
5985
6177
|
const valuations = await Promise.all(["convertToAssets", "previewRedeem"].map(async (functionName) => {
|
|
5986
6178
|
try {
|
|
5987
6179
|
const value = await this.getReceiptClient().readContract({
|
|
5988
6180
|
address,
|
|
5989
|
-
abi:
|
|
6181
|
+
abi: erc4626Abi3,
|
|
5990
6182
|
functionName,
|
|
5991
6183
|
args: [shares],
|
|
5992
6184
|
blockNumber
|
|
@@ -6227,7 +6419,7 @@ var YieldseekerAgent = class {
|
|
|
6227
6419
|
delta: maxSettlementBasis.delta - payout,
|
|
6228
6420
|
expectedNetDeposits: maxSettlementBasis.expectedNetDeposits - payout
|
|
6229
6421
|
} : this.snapshotMovement(state, chainId, context, -payout);
|
|
6230
|
-
const account =
|
|
6422
|
+
const account = getAddress6(state.walletAddress);
|
|
6231
6423
|
let receipt;
|
|
6232
6424
|
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" };
|
|
6233
6425
|
if (amount === void 0) base7.maxCompletion = {
|
|
@@ -6238,11 +6430,11 @@ var YieldseekerAgent = class {
|
|
|
6238
6430
|
};
|
|
6239
6431
|
if (base7.maxCompletion) notifyFunding(onProgress, base7);
|
|
6240
6432
|
let submissionEvidence = base7;
|
|
6241
|
-
const data = amount === void 0 ?
|
|
6433
|
+
const data = amount === void 0 ? encodeFunctionData3({
|
|
6242
6434
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
6243
6435
|
functionName: "withdrawAllAssetToUser",
|
|
6244
6436
|
args: [account, metadata.address]
|
|
6245
|
-
}) :
|
|
6437
|
+
}) : encodeFunctionData3({
|
|
6246
6438
|
abi: YIELDSEEKER_AGENT_WALLET_ABI,
|
|
6247
6439
|
functionName: "withdrawAssetToUser",
|
|
6248
6440
|
args: [account, metadata.address, payout]
|
|
@@ -6259,7 +6451,7 @@ var YieldseekerAgent = class {
|
|
|
6259
6451
|
refreshYieldseekerSignature: (rejected) => this.auth.refreshToken(state, chainId, rejected),
|
|
6260
6452
|
userId: context.user.userId,
|
|
6261
6453
|
agentId: context.agent.agentId,
|
|
6262
|
-
agentWallet:
|
|
6454
|
+
agentWallet: getAddress6(context.wallet.walletAddress),
|
|
6263
6455
|
asset: metadata.address,
|
|
6264
6456
|
callData: data,
|
|
6265
6457
|
onPending: (pending) => {
|
|
@@ -6281,7 +6473,7 @@ var YieldseekerAgent = class {
|
|
|
6281
6473
|
chainId,
|
|
6282
6474
|
{
|
|
6283
6475
|
from: account,
|
|
6284
|
-
to:
|
|
6476
|
+
to: getAddress6(context.wallet.walletAddress),
|
|
6285
6477
|
data,
|
|
6286
6478
|
value: "0",
|
|
6287
6479
|
chainId
|
|
@@ -6426,28 +6618,35 @@ var YieldseekerAgent = class {
|
|
|
6426
6618
|
);
|
|
6427
6619
|
if (!Array.isArray(response?.vaultPositions)) return unknown("CATALOG_INVALID");
|
|
6428
6620
|
const positions = this.validateWithdrawalPositions(response.vaultPositions, evidence.chainId, asset);
|
|
6429
|
-
const vaults = new
|
|
6430
|
-
|
|
6431
|
-
|
|
6621
|
+
const vaults = new Map(proof.vaults.map((address) => [address.toLowerCase(), { address }]));
|
|
6622
|
+
positions.forEach((p) => vaults.set(p.vaultOption.address.toLowerCase(), p.vaultOption));
|
|
6623
|
+
const needsInventory = !proof.inventoryComplete;
|
|
6624
|
+
if (needsInventory || [...vaults.values()].some((option) => !option.provider)) {
|
|
6625
|
+
const catalog = await this.loadVaultOptions(asset).catch(() => []);
|
|
6432
6626
|
const eligible = catalog.filter((o) => o.chainId === evidence.chainId && o.assetAddress?.toLowerCase() === metadata.address.toLowerCase() && isAddress4(o.address));
|
|
6433
|
-
if (!eligible.length) return unknown("INVENTORY_INCOMPLETE");
|
|
6434
|
-
|
|
6627
|
+
if (needsInventory && !eligible.length) return unknown("INVENTORY_INCOMPLETE");
|
|
6628
|
+
for (const option of eligible) {
|
|
6629
|
+
const key2 = option.address.toLowerCase();
|
|
6630
|
+
if (needsInventory || vaults.has(key2)) vaults.set(key2, option);
|
|
6631
|
+
}
|
|
6435
6632
|
}
|
|
6436
|
-
if (vaults.size > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT || [...vaults].some((a) => !isAddress4(a)))
|
|
6633
|
+
if (vaults.size > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT || [...vaults.keys()].some((a) => !isAddress4(a)))
|
|
6437
6634
|
return unknown("INVENTORY_INVALID");
|
|
6438
6635
|
reason = "VAULT_PROOF_UNAVAILABLE";
|
|
6439
6636
|
const blockNumber = BigInt(evidence.blockNumber);
|
|
6440
|
-
const
|
|
6441
|
-
const
|
|
6637
|
+
const options = [...vaults.values()];
|
|
6638
|
+
const addresses = options.map((option) => option.address);
|
|
6639
|
+
const shares = await this.readVaultShares(options, evidence.agentWallet, blockNumber);
|
|
6442
6640
|
const checks = await Promise.all(addresses.map(async (address, i) => {
|
|
6443
6641
|
const group = shares.slice(i * 2, i * 2 + 2);
|
|
6444
6642
|
if (group.length !== 2 || group.some((r) => r.status !== "success")) return "unavailable";
|
|
6445
6643
|
const [underlying, value] = group.map((r) => r.result);
|
|
6446
6644
|
if (typeof underlying !== "string" || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof value !== "bigint" || value < 0n) return "unavailable";
|
|
6447
6645
|
if (value === 0n) return "complete";
|
|
6646
|
+
if (nativeYieldseekerProtocol(options[i])) return "remaining";
|
|
6448
6647
|
const amounts = await Promise.allSettled(["convertToAssets", "previewRedeem"].map((functionName) => this.getReceiptClient().readContract({
|
|
6449
|
-
address:
|
|
6450
|
-
abi:
|
|
6648
|
+
address: getAddress6(address),
|
|
6649
|
+
abi: erc4626Abi3,
|
|
6451
6650
|
functionName,
|
|
6452
6651
|
args: [value],
|
|
6453
6652
|
blockNumber
|
|
@@ -6467,15 +6666,15 @@ var YieldseekerAgent = class {
|
|
|
6467
6666
|
}
|
|
6468
6667
|
}
|
|
6469
6668
|
async readVaultShares(options, wallet, blockNumber) {
|
|
6470
|
-
const owner =
|
|
6669
|
+
const owner = getAddress6(wallet);
|
|
6471
6670
|
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));
|
|
6472
6671
|
const results = await Promise.all(batches.map((batch) => this.getReceiptClient().multicall({
|
|
6473
6672
|
blockNumber,
|
|
6474
6673
|
allowFailure: true,
|
|
6475
6674
|
contracts: batch.flatMap((option) => {
|
|
6476
|
-
const address =
|
|
6675
|
+
const address = getAddress6(option.address);
|
|
6477
6676
|
return [
|
|
6478
|
-
|
|
6677
|
+
yieldseekerUnderlyingCall(option),
|
|
6479
6678
|
{ address, abi: erc20Abi5, functionName: "balanceOf", args: [owner] }
|
|
6480
6679
|
];
|
|
6481
6680
|
})
|
|
@@ -6554,7 +6753,7 @@ var YieldseekerAgent = class {
|
|
|
6554
6753
|
const read2 = async () => {
|
|
6555
6754
|
const client = this.getReceiptClient();
|
|
6556
6755
|
const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
|
|
6557
|
-
const wallet =
|
|
6756
|
+
const wallet = getAddress6(context.wallet.walletAddress);
|
|
6558
6757
|
assertReadCurrent();
|
|
6559
6758
|
if (typeof blockNumber !== "bigint") throw unavailable();
|
|
6560
6759
|
const indexedPositions = this.validateWithdrawalPositions(positions ?? [], chainId, context.asset);
|
|
@@ -6582,7 +6781,7 @@ var YieldseekerAgent = class {
|
|
|
6582
6781
|
if (typeof idle !== "bigint" || idle < 0n || !isAddress4(feeTracker) || /^0x0{40}$/i.test(feeTracker)) throw unavailable();
|
|
6583
6782
|
assertReadCurrent();
|
|
6584
6783
|
const fees = await client.readContract({
|
|
6585
|
-
address:
|
|
6784
|
+
address: getAddress6(feeTracker),
|
|
6586
6785
|
abi: YIELDSEEKER_FEE_TRACKER_ABI,
|
|
6587
6786
|
functionName: "getFeesOwed",
|
|
6588
6787
|
args: [wallet],
|
|
@@ -6648,11 +6847,21 @@ var YieldseekerAgent = class {
|
|
|
6648
6847
|
verified.push(position2);
|
|
6649
6848
|
knownProof = proof(false);
|
|
6650
6849
|
if (shares === 0n) return true;
|
|
6850
|
+
if (nativeYieldseekerProtocol(option)) {
|
|
6851
|
+
const value2 = await readNativeYieldseekerAssets(client, option, wallet, shares, blockNumber);
|
|
6852
|
+
assertReadCurrent();
|
|
6853
|
+
if (value2 === void 0) return false;
|
|
6854
|
+
position2.withdrawableAssetsRaw = value2.toString();
|
|
6855
|
+
position2.withdrawableAssetsBase = value2.toString();
|
|
6856
|
+
deployed += value2;
|
|
6857
|
+
knownProof = proof(false);
|
|
6858
|
+
return true;
|
|
6859
|
+
}
|
|
6651
6860
|
let preview;
|
|
6652
6861
|
try {
|
|
6653
6862
|
const value2 = await client.readContract({
|
|
6654
|
-
address:
|
|
6655
|
-
abi:
|
|
6863
|
+
address: getAddress6(option.address),
|
|
6864
|
+
abi: erc4626Abi3,
|
|
6656
6865
|
functionName: "previewRedeem",
|
|
6657
6866
|
args: [shares],
|
|
6658
6867
|
blockNumber
|
|
@@ -6665,8 +6874,8 @@ var YieldseekerAgent = class {
|
|
|
6665
6874
|
if (value === void 0 || value === 0n) {
|
|
6666
6875
|
try {
|
|
6667
6876
|
const converted = await client.readContract({
|
|
6668
|
-
address:
|
|
6669
|
-
abi:
|
|
6877
|
+
address: getAddress6(option.address),
|
|
6878
|
+
abi: erc4626Abi3,
|
|
6670
6879
|
functionName: "convertToAssets",
|
|
6671
6880
|
args: [shares],
|
|
6672
6881
|
blockNumber
|
|
@@ -6757,7 +6966,7 @@ var YieldseekerAgent = class {
|
|
|
6757
6966
|
const key2 = this.contextKey(state, evidence.chainId, evidence.token);
|
|
6758
6967
|
const generation = this.readGeneration, version = this.portfolioVersion(key2);
|
|
6759
6968
|
if (read2.readVersion !== version) return void 0;
|
|
6760
|
-
const wallet =
|
|
6969
|
+
const wallet = getAddress6(context.wallet.walletAddress);
|
|
6761
6970
|
const asset = YIELDSEEKER_ASSET_METADATA[evidence.token];
|
|
6762
6971
|
const client = this.getReceiptClient();
|
|
6763
6972
|
const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
|
|
@@ -6798,16 +7007,18 @@ var YieldseekerAgent = class {
|
|
|
6798
7007
|
if (failure && failure.retryAt > Date.now()) this.snapshotFailures.set(key2, failure);
|
|
6799
7008
|
this.allocationRefresh.add(key2);
|
|
6800
7009
|
}
|
|
6801
|
-
invalidateFundingRead(state, chainId, asset, refreshUntil = Date.now() +
|
|
7010
|
+
invalidateFundingRead(state, chainId, asset, refreshUntil = Date.now() + YIELDSEEKER_SETTLEMENT_SNAPSHOT_WINDOW_MS) {
|
|
6802
7011
|
this.assertAsset(asset);
|
|
6803
7012
|
const key2 = this.contextKey(state, chainId, asset);
|
|
6804
7013
|
const failure = this.snapshotFailures.get(key2);
|
|
6805
7014
|
this.advancePortfolioVersion(key2);
|
|
6806
7015
|
if (failure && failure.retryAt > Date.now()) this.snapshotFailures.set(key2, failure);
|
|
6807
7016
|
if (!this.snapshotMovements.has(key2)) this.snapshotMovements.set(key2, null);
|
|
6808
|
-
const until = Math.min(refreshUntil, Date.now() +
|
|
7017
|
+
const until = Math.min(refreshUntil, Date.now() + YIELDSEEKER_SETTLEMENT_SNAPSHOT_WINDOW_MS);
|
|
7018
|
+
const activityUntil = Math.min(refreshUntil, Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS);
|
|
7019
|
+
if (until > Date.now()) this.beginSettlement(key2, false);
|
|
6809
7020
|
this.reconcileUntil.set(key2, Math.max(this.reconcileUntil.get(key2) ?? 0, until));
|
|
6810
|
-
this.activityRefreshUntil.set(key2, Math.max(this.activityRefreshUntil.get(key2) ?? 0,
|
|
7021
|
+
this.activityRefreshUntil.set(key2, Math.max(this.activityRefreshUntil.get(key2) ?? 0, activityUntil));
|
|
6811
7022
|
}
|
|
6812
7023
|
endFundingBalanceRefresh(state, chainId, asset, read2) {
|
|
6813
7024
|
this.assertChain(chainId);
|
|
@@ -6838,7 +7049,7 @@ var YieldseekerAgent = class {
|
|
|
6838
7049
|
address: YIELDSEEKER_ASSET_METADATA[asset].address,
|
|
6839
7050
|
abi: erc20Abi5,
|
|
6840
7051
|
functionName: "balanceOf",
|
|
6841
|
-
args: [
|
|
7052
|
+
args: [getAddress6(context.wallet.walletAddress)]
|
|
6842
7053
|
}), chainId, "eth_call", this.id);
|
|
6843
7054
|
this.assertSession(generation, chainId, asset);
|
|
6844
7055
|
if (typeof idle !== "bigint" || idle < 0n) throw this.invalidResponse("wallet deposit balance");
|
|
@@ -7044,7 +7255,7 @@ var YieldseekerAgent = class {
|
|
|
7044
7255
|
this.users.set(key2, persisted);
|
|
7045
7256
|
return persisted;
|
|
7046
7257
|
}
|
|
7047
|
-
const walletAddress =
|
|
7258
|
+
const walletAddress = getAddress6(state.walletAddress);
|
|
7048
7259
|
const generation = this.readGeneration;
|
|
7049
7260
|
let user = null;
|
|
7050
7261
|
try {
|
|
@@ -7217,8 +7428,21 @@ var YieldseekerAgent = class {
|
|
|
7217
7428
|
portfolioVersion(contextKey) {
|
|
7218
7429
|
return this.portfolioVersions.get(contextKey) ?? 0;
|
|
7219
7430
|
}
|
|
7431
|
+
/** Snapshot cache lifetime: the settlement backoff while a movement is being
|
|
7432
|
+
* checked, otherwise the routine non-forced cadence. */
|
|
7220
7433
|
portfolioCacheMs(contextKey) {
|
|
7221
|
-
|
|
7434
|
+
if ((this.reconcileUntil.get(contextKey) ?? 0) <= Date.now()) return YIELDSEEKER_PORTFOLIO_CACHE_MS;
|
|
7435
|
+
const checks = this.settlementChecks.get(contextKey) ?? 0;
|
|
7436
|
+
return YIELDSEEKER_SETTLEMENT_BACKOFF_MS[Math.min(checks, YIELDSEEKER_SETTLEMENT_BACKOFF_MS.length - 1)];
|
|
7437
|
+
}
|
|
7438
|
+
/** A pending movement inside its ten-minute window forces snapshots. */
|
|
7439
|
+
settling(contextKey) {
|
|
7440
|
+
return this.snapshotMovements.has(contextKey) && (this.reconcileUntil.get(contextKey) ?? 0) > Date.now();
|
|
7441
|
+
}
|
|
7442
|
+
/** Start a new forced-check schedule, or keep the active one. */
|
|
7443
|
+
beginSettlement(contextKey, restart) {
|
|
7444
|
+
if (!restart && (this.reconcileUntil.get(contextKey) ?? 0) > Date.now()) return;
|
|
7445
|
+
this.settlementChecks.set(contextKey, 0);
|
|
7222
7446
|
}
|
|
7223
7447
|
snapshotMovement(state, chainId, context, delta) {
|
|
7224
7448
|
const key2 = this.contextKey(state, chainId, context.asset);
|
|
@@ -7244,13 +7468,15 @@ var YieldseekerAgent = class {
|
|
|
7244
7468
|
expectedNetDeposits: BigInt(snapshot.netDepositsBase) + delta
|
|
7245
7469
|
};
|
|
7246
7470
|
}
|
|
7247
|
-
advancePortfolioVersion(contextKey, { snapshotOnly = false } = {}) {
|
|
7471
|
+
advancePortfolioVersion(contextKey, { snapshotOnly = false, keepPositions = false } = {}) {
|
|
7248
7472
|
const version = this.portfolioVersion(contextKey) + 1;
|
|
7249
7473
|
this.portfolioVersions.set(contextKey, version);
|
|
7250
7474
|
if (!snapshotOnly) this.liquidityVersions.set(contextKey, version);
|
|
7251
7475
|
this.snapshotFailures.delete(contextKey);
|
|
7252
|
-
|
|
7476
|
+
const kinds = !snapshotOnly ? ["snapshot", "positions", "historic", "actions"] : keepPositions ? ["snapshot"] : ["snapshot", "positions"];
|
|
7477
|
+
for (const kind of kinds) {
|
|
7253
7478
|
const key2 = `${kind}:${contextKey}`;
|
|
7479
|
+
if (snapshotOnly && kind === "positions" && this.pendingReads.has(key2)) continue;
|
|
7254
7480
|
this.readCache.delete(key2);
|
|
7255
7481
|
this.pendingReads.delete(key2);
|
|
7256
7482
|
}
|
|
@@ -7295,7 +7521,7 @@ var YieldseekerAgent = class {
|
|
|
7295
7521
|
if ((!cached || cached.expiresAt <= Date.now()) && !this.pendingReads.has(key2)) {
|
|
7296
7522
|
const failure = this.snapshotFailures.get(contextKey);
|
|
7297
7523
|
if (failure && failure.retryAt > Date.now()) throw failure.error;
|
|
7298
|
-
this.advancePortfolioVersion(contextKey, { snapshotOnly: true });
|
|
7524
|
+
this.advancePortfolioVersion(contextKey, { snapshotOnly: true, keepPositions: this.settling(contextKey) });
|
|
7299
7525
|
}
|
|
7300
7526
|
const version = this.portfolioVersion(contextKey);
|
|
7301
7527
|
const generation = this.readGeneration;
|
|
@@ -7303,6 +7529,8 @@ var YieldseekerAgent = class {
|
|
|
7303
7529
|
version,
|
|
7304
7530
|
read: this.cachedRead(key2, this.portfolioCacheMs(contextKey), () => {
|
|
7305
7531
|
const allocationChanged = this.allocationRefresh.delete(contextKey);
|
|
7532
|
+
const settling = this.settling(contextKey);
|
|
7533
|
+
if (settling) this.settlementChecks.set(contextKey, (this.settlementChecks.get(contextKey) ?? 0) + 1);
|
|
7306
7534
|
return this.requestPortfolioSnapshot(
|
|
7307
7535
|
state,
|
|
7308
7536
|
chainId,
|
|
@@ -7310,7 +7538,7 @@ var YieldseekerAgent = class {
|
|
|
7310
7538
|
contextKey,
|
|
7311
7539
|
version,
|
|
7312
7540
|
generation,
|
|
7313
|
-
allocationChanged ||
|
|
7541
|
+
allocationChanged || settling
|
|
7314
7542
|
);
|
|
7315
7543
|
})
|
|
7316
7544
|
};
|
|
@@ -7380,7 +7608,7 @@ var YieldseekerAgent = class {
|
|
|
7380
7608
|
const [positions, historic, actions] = await Promise.all([
|
|
7381
7609
|
options.includePositions === false ? Promise.resolve({ vaultPositions: [] }) : this.cachedRead(
|
|
7382
7610
|
`positions:${contextKey}`,
|
|
7383
|
-
|
|
7611
|
+
YIELDSEEKER_PORTFOLIO_CACHE_MS,
|
|
7384
7612
|
async () => {
|
|
7385
7613
|
const response = await this.walletRequest(
|
|
7386
7614
|
state,
|
|
@@ -7436,7 +7664,8 @@ var YieldseekerAgent = class {
|
|
|
7436
7664
|
}
|
|
7437
7665
|
async refreshSnapshotAfterMovement(state, chainId, context, movement, settlement) {
|
|
7438
7666
|
const contextKey = this.contextKey(state, chainId, context.asset);
|
|
7439
|
-
this.
|
|
7667
|
+
this.beginSettlement(contextKey, true);
|
|
7668
|
+
this.reconcileUntil.set(contextKey, Date.now() + YIELDSEEKER_SETTLEMENT_SNAPSHOT_WINDOW_MS);
|
|
7440
7669
|
this.activityRefreshUntil.set(contextKey, Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS);
|
|
7441
7670
|
const epoch = this.movementEpochs.get(contextKey) ?? 0;
|
|
7442
7671
|
this.snapshotMovements.set(contextKey, settlement?.epoch === epoch ? settlement : null);
|
|
@@ -7444,9 +7673,11 @@ var YieldseekerAgent = class {
|
|
|
7444
7673
|
const version = this.advancePortfolioVersion(contextKey);
|
|
7445
7674
|
const generation = this.readGeneration;
|
|
7446
7675
|
try {
|
|
7676
|
+
const ttl = this.portfolioCacheMs(contextKey);
|
|
7677
|
+
this.settlementChecks.set(contextKey, 1);
|
|
7447
7678
|
await this.cachedRead(
|
|
7448
7679
|
`snapshot:${contextKey}`,
|
|
7449
|
-
|
|
7680
|
+
ttl,
|
|
7450
7681
|
() => this.requestPortfolioSnapshot(state, chainId, context, contextKey, version, generation, true)
|
|
7451
7682
|
);
|
|
7452
7683
|
} catch (error) {
|
|
@@ -7462,10 +7693,14 @@ var YieldseekerAgent = class {
|
|
|
7462
7693
|
async walletRequest(state, chainId, path, options = {}) {
|
|
7463
7694
|
const read2 = (options.method ?? "GET") === "GET";
|
|
7464
7695
|
const userKey = this.userKey(state, chainId);
|
|
7696
|
+
const endpointKey = `${userKey}:${path}`;
|
|
7465
7697
|
if (read2) {
|
|
7466
7698
|
const failure = this.standardReadFailures.get(userKey);
|
|
7467
7699
|
if (failure && failure.retryAt > Date.now()) throw failure.error;
|
|
7468
7700
|
if (failure) this.standardReadFailures.delete(userKey);
|
|
7701
|
+
const endpointFailure = this.endpointReadFailures.get(endpointKey);
|
|
7702
|
+
if (endpointFailure && endpointFailure.retryAt > Date.now()) throw endpointFailure.error;
|
|
7703
|
+
if (endpointFailure) this.endpointReadFailures.delete(endpointKey);
|
|
7469
7704
|
}
|
|
7470
7705
|
const generation = this.readGeneration;
|
|
7471
7706
|
try {
|
|
@@ -7479,6 +7714,12 @@ var YieldseekerAgent = class {
|
|
|
7479
7714
|
error: mapped
|
|
7480
7715
|
});
|
|
7481
7716
|
}
|
|
7717
|
+
if (read2 && this.readGeneration === generation && error instanceof YieldseekerApiError && (error.status === 0 || error.status === 408 || error.status >= 500 && error.status < 600)) {
|
|
7718
|
+
this.endpointReadFailures.set(endpointKey, {
|
|
7719
|
+
retryAt: Date.now() + YIELDSEEKER_READ_FAILURE_COOLDOWN_MS,
|
|
7720
|
+
error: mapped
|
|
7721
|
+
});
|
|
7722
|
+
}
|
|
7482
7723
|
throw mapped;
|
|
7483
7724
|
}
|
|
7484
7725
|
}
|
|
@@ -7536,7 +7777,7 @@ var YieldseekerAgent = class {
|
|
|
7536
7777
|
return hash3;
|
|
7537
7778
|
}
|
|
7538
7779
|
this.assertTransaction(transaction, state, chainId);
|
|
7539
|
-
const account =
|
|
7780
|
+
const account = getAddress6(state.walletAddress);
|
|
7540
7781
|
const walletClient = createWalletClient4({
|
|
7541
7782
|
account,
|
|
7542
7783
|
chain: base4,
|
|
@@ -7557,7 +7798,7 @@ var YieldseekerAgent = class {
|
|
|
7557
7798
|
const hash2 = await walletClient.sendTransaction({
|
|
7558
7799
|
account,
|
|
7559
7800
|
chain: base4,
|
|
7560
|
-
to:
|
|
7801
|
+
to: getAddress6(transaction.to),
|
|
7561
7802
|
data: transaction.data,
|
|
7562
7803
|
value: BigInt(transaction.value)
|
|
7563
7804
|
});
|
|
@@ -7661,7 +7902,7 @@ var YieldseekerAgent = class {
|
|
|
7661
7902
|
return receipt;
|
|
7662
7903
|
}
|
|
7663
7904
|
assertTransaction(transaction, state, chainId) {
|
|
7664
|
-
if (!transaction || typeof transaction.from !== "string" || !isAddress4(transaction.from) || typeof transaction.to !== "string" || !isAddress4(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 ||
|
|
7905
|
+
if (!transaction || typeof transaction.from !== "string" || !isAddress4(transaction.from) || typeof transaction.to !== "string" || !isAddress4(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 || getAddress6(transaction.from) !== getAddress6(state.walletAddress)) {
|
|
7665
7906
|
throw this.invalidResponse("transaction");
|
|
7666
7907
|
}
|
|
7667
7908
|
}
|
|
@@ -8878,7 +9119,7 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
8878
9119
|
const candidates = /* @__PURE__ */ new Map();
|
|
8879
9120
|
const toDate = (/* @__PURE__ */ new Date()).toISOString();
|
|
8880
9121
|
const fromDate = new Date(options.submittedAt - 5e3).toISOString();
|
|
8881
|
-
const
|
|
9122
|
+
const cursors2 = /* @__PURE__ */ new Set();
|
|
8882
9123
|
let cursor;
|
|
8883
9124
|
for (let pageNumber = 0; pageNumber < 10; pageNumber++) {
|
|
8884
9125
|
assertActive();
|
|
@@ -8904,8 +9145,8 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
8904
9145
|
const confirmed = inspected.flatMap((value) => value.status === "fulfilled" && value.value.transfer === "confirmed" && value.value.delivery === "confirmed" && (rawAmount(value.value.blockNumber) ?? 0n) > options.baselineBlock ? [value.value] : []);
|
|
8905
9146
|
return confirmed.length === 1 ? confirmed[0] : void 0;
|
|
8906
9147
|
}
|
|
8907
|
-
if (!page.nextCursor ||
|
|
8908
|
-
|
|
9148
|
+
if (!page.nextCursor || cursors2.has(page.nextCursor)) return;
|
|
9149
|
+
cursors2.add(page.nextCursor);
|
|
8909
9150
|
cursor = page.nextCursor;
|
|
8910
9151
|
}
|
|
8911
9152
|
};
|
|
@@ -9572,7 +9813,7 @@ function aggregateDailyEarnings(series, tokenSymbol) {
|
|
|
9572
9813
|
}
|
|
9573
9814
|
return [...byAsset.entries()].map(([asset, components]) => {
|
|
9574
9815
|
const dates = [...new Set(components.flatMap((points2) => points2.map((point) => point.date)))].sort();
|
|
9575
|
-
const
|
|
9816
|
+
const cursors2 = components.map((points2) => ({
|
|
9576
9817
|
index: 0,
|
|
9577
9818
|
// Providers can have different lifetime/window offsets and start dates.
|
|
9578
9819
|
// Remove each opening reading before combining their earned amounts.
|
|
@@ -9582,7 +9823,7 @@ function aggregateDailyEarnings(series, tokenSymbol) {
|
|
|
9582
9823
|
const points = dates.map((date) => {
|
|
9583
9824
|
let amount = 0;
|
|
9584
9825
|
components.forEach((component, index) => {
|
|
9585
|
-
const cursor =
|
|
9826
|
+
const cursor = cursors2[index];
|
|
9586
9827
|
while (cursor.index < component.length && component[cursor.index].date <= date) {
|
|
9587
9828
|
cursor.current = component[cursor.index++].amount;
|
|
9588
9829
|
}
|
|
@@ -9604,16 +9845,312 @@ import {
|
|
|
9604
9845
|
} from "viem";
|
|
9605
9846
|
import { base as base6, arbitrum as arbitrum3, mainnet as mainnet3 } from "viem/chains";
|
|
9606
9847
|
|
|
9848
|
+
// src/lib/token-batch-execution.ts
|
|
9849
|
+
import {
|
|
9850
|
+
keccak256 as keccak2562,
|
|
9851
|
+
parseTransaction
|
|
9852
|
+
} from "viem";
|
|
9853
|
+
async function executeTokenBatch(input, initial) {
|
|
9854
|
+
let body = initial, family, ended = false;
|
|
9855
|
+
const deadlineAt = Date.now() + 9e4, controller = new AbortController();
|
|
9856
|
+
let timer;
|
|
9857
|
+
const pending = () => new OwneyError(
|
|
9858
|
+
"DEPOSIT_IN_PROGRESS",
|
|
9859
|
+
"Deposit is awaiting confirmation. Its transaction is still being checked.",
|
|
9860
|
+
{
|
|
9861
|
+
sponsorPending: true,
|
|
9862
|
+
phase: body.serializedTransaction ? "deposit" : "approval",
|
|
9863
|
+
txHash: body.serializedTransaction ? keccak2562(body.serializedTransaction) : body.serializedPermitTransaction ? keccak2562(body.serializedPermitTransaction) : void 0,
|
|
9864
|
+
notSubmitted: false,
|
|
9865
|
+
safeToFallback: false
|
|
9866
|
+
}
|
|
9867
|
+
);
|
|
9868
|
+
const assertCurrent = () => {
|
|
9869
|
+
if (ended || Date.now() >= deadlineAt) throw pending();
|
|
9870
|
+
input.assertCurrent?.();
|
|
9871
|
+
};
|
|
9872
|
+
const timeout = new Promise((_, reject) => {
|
|
9873
|
+
timer = setTimeout(() => {
|
|
9874
|
+
ended = true;
|
|
9875
|
+
controller.abort();
|
|
9876
|
+
reject(pending());
|
|
9877
|
+
}, 9e4);
|
|
9878
|
+
});
|
|
9879
|
+
const bounded2 = async (work) => {
|
|
9880
|
+
const value = await Promise.race([work, timeout]);
|
|
9881
|
+
assertCurrent();
|
|
9882
|
+
return value;
|
|
9883
|
+
};
|
|
9884
|
+
const post = () => bounded2(
|
|
9885
|
+
postSponsorBatchTransfer({
|
|
9886
|
+
apiKey: input.apiKey,
|
|
9887
|
+
baseUrl: input.baseUrl,
|
|
9888
|
+
body,
|
|
9889
|
+
signal: controller.signal
|
|
9890
|
+
})
|
|
9891
|
+
);
|
|
9892
|
+
const replacements = { approval: 0, deposit: 0 };
|
|
9893
|
+
const unavailable = /* @__PURE__ */ new Set();
|
|
9894
|
+
const observedApprovals = /* @__PURE__ */ new Set();
|
|
9895
|
+
const checkpointed = /* @__PURE__ */ new Set();
|
|
9896
|
+
const accounted = /* @__PURE__ */ new Set();
|
|
9897
|
+
const remember = async (phase, bytes, hash2, predecessorHash) => {
|
|
9898
|
+
assertCurrent();
|
|
9899
|
+
if (keccak2562(bytes) !== hash2) throw pending();
|
|
9900
|
+
const tx = parseTransaction(bytes);
|
|
9901
|
+
const next = {
|
|
9902
|
+
...body,
|
|
9903
|
+
[phase === "deposit" ? "serializedTransaction" : "serializedPermitTransaction"]: bytes
|
|
9904
|
+
};
|
|
9905
|
+
family = await bounded2(
|
|
9906
|
+
saveTokenBatchAttempt(
|
|
9907
|
+
input,
|
|
9908
|
+
next,
|
|
9909
|
+
{
|
|
9910
|
+
phase,
|
|
9911
|
+
hash: hash2,
|
|
9912
|
+
serializedTransaction: bytes,
|
|
9913
|
+
nonce: String(tx.nonce),
|
|
9914
|
+
predecessorHash,
|
|
9915
|
+
preparedAt: Date.now(),
|
|
9916
|
+
submissionAttempted: false
|
|
9917
|
+
},
|
|
9918
|
+
assertCurrent
|
|
9919
|
+
)
|
|
9920
|
+
);
|
|
9921
|
+
body = { ...family.body, transfers: initial.transfers };
|
|
9922
|
+
};
|
|
9923
|
+
const reference = (hash2) => family ? {
|
|
9924
|
+
authorizationId: family.authorizationId,
|
|
9925
|
+
predecessorTxHash: family.attempts.find((a) => a.hash === hash2)?.predecessorHash
|
|
9926
|
+
} : void 0;
|
|
9927
|
+
const markSubmission = async (phase) => {
|
|
9928
|
+
const hash2 = phase === "deposit" ? family?.current.deposit : family?.current.approval;
|
|
9929
|
+
if (!family || !hash2) throw pending();
|
|
9930
|
+
if (phase === "deposit" && !checkpointed.has(hash2)) {
|
|
9931
|
+
try {
|
|
9932
|
+
await bounded2(
|
|
9933
|
+
Promise.resolve(input.onBeforeSubmission?.(hash2, reference(hash2)))
|
|
9934
|
+
);
|
|
9935
|
+
} catch (error) {
|
|
9936
|
+
if (error instanceof OwneyError && error.code === "DEPOSIT_CHECKPOINT_FAILED")
|
|
9937
|
+
throw new OwneyError(error.code, error.message, {
|
|
9938
|
+
...error.details,
|
|
9939
|
+
notSubmitted: !family.attempts.some(
|
|
9940
|
+
(a) => a.phase === "deposit" && a.submissionAttempted
|
|
9941
|
+
),
|
|
9942
|
+
safeToFallback: false
|
|
9943
|
+
});
|
|
9944
|
+
throw error;
|
|
9945
|
+
}
|
|
9946
|
+
checkpointed.add(hash2);
|
|
9947
|
+
}
|
|
9948
|
+
assertCurrent();
|
|
9949
|
+
const attempt = family.attempts.find((a) => a.hash === hash2);
|
|
9950
|
+
family = await bounded2(
|
|
9951
|
+
saveTokenBatchAttempt(
|
|
9952
|
+
input,
|
|
9953
|
+
body,
|
|
9954
|
+
{ ...attempt, submissionAttempted: true },
|
|
9955
|
+
assertCurrent
|
|
9956
|
+
)
|
|
9957
|
+
);
|
|
9958
|
+
assertCurrent();
|
|
9959
|
+
if (phase === "deposit")
|
|
9960
|
+
try {
|
|
9961
|
+
input.onSubmitted?.(hash2, reference(hash2));
|
|
9962
|
+
} catch {
|
|
9963
|
+
}
|
|
9964
|
+
assertCurrent();
|
|
9965
|
+
};
|
|
9966
|
+
const observe = async () => {
|
|
9967
|
+
if (!family) return;
|
|
9968
|
+
const observation = await bounded2(
|
|
9969
|
+
observeTokenBatchFamily({
|
|
9970
|
+
scope: input,
|
|
9971
|
+
family,
|
|
9972
|
+
pub: input.pub,
|
|
9973
|
+
deadlineAt,
|
|
9974
|
+
assertCurrent
|
|
9975
|
+
})
|
|
9976
|
+
);
|
|
9977
|
+
if (observation.status === "confirmed") {
|
|
9978
|
+
const winning = family.attempts.find(
|
|
9979
|
+
(a) => a.hash === observation.txHash
|
|
9980
|
+
);
|
|
9981
|
+
if (!accounted.has(observation.txHash)) {
|
|
9982
|
+
const accounting = new AbortController();
|
|
9983
|
+
const accountingTimer = setTimeout(() => accounting.abort(), 8e3);
|
|
9984
|
+
void postSponsorBatchTransfer({
|
|
9985
|
+
apiKey: input.apiKey,
|
|
9986
|
+
baseUrl: input.baseUrl,
|
|
9987
|
+
body: {
|
|
9988
|
+
...body,
|
|
9989
|
+
serializedTransaction: winning.serializedTransaction
|
|
9990
|
+
},
|
|
9991
|
+
signal: accounting.signal
|
|
9992
|
+
}).catch(() => {
|
|
9993
|
+
}).finally(() => clearTimeout(accountingTimer));
|
|
9994
|
+
}
|
|
9995
|
+
return observation.txHash;
|
|
9996
|
+
}
|
|
9997
|
+
if (observation.status === "failed" || observation.status === "expired")
|
|
9998
|
+
throw new OwneyError(
|
|
9999
|
+
"SPONSOR_REQUEST_FAILED",
|
|
10000
|
+
observation.status === "expired" ? "The deposit authorization expired without transferring funds. Start a new deposit." : "Sponsored deposit transactions reverted. Please retry.",
|
|
10001
|
+
{ notSubmitted: true, safeToFallback: false }
|
|
10002
|
+
);
|
|
10003
|
+
};
|
|
10004
|
+
try {
|
|
10005
|
+
assertCurrent();
|
|
10006
|
+
family = await bounded2(
|
|
10007
|
+
loadTokenBatchFamily(
|
|
10008
|
+
input,
|
|
10009
|
+
initial.serializedTransaction ? keccak2562(initial.serializedTransaction) : initial.serializedPermitTransaction ? keccak2562(initial.serializedPermitTransaction) : void 0
|
|
10010
|
+
)
|
|
10011
|
+
);
|
|
10012
|
+
if (family) body = { ...family.body, transfers: initial.transfers };
|
|
10013
|
+
else
|
|
10014
|
+
tokenBatchWrite(
|
|
10015
|
+
tokenBatchKey(input),
|
|
10016
|
+
JSON.stringify(tokenBatchStoredBody(body))
|
|
10017
|
+
);
|
|
10018
|
+
if (family) {
|
|
10019
|
+
const winner = await observe();
|
|
10020
|
+
if (winner) return winner;
|
|
10021
|
+
}
|
|
10022
|
+
if (family)
|
|
10023
|
+
for (const attempt of family.attempts)
|
|
10024
|
+
family = await bounded2(
|
|
10025
|
+
saveTokenBatchAttempt(input, body, attempt, assertCurrent)
|
|
10026
|
+
);
|
|
10027
|
+
let needsPost = true;
|
|
10028
|
+
while (true) {
|
|
10029
|
+
assertCurrent();
|
|
10030
|
+
const winner = await observe();
|
|
10031
|
+
if (winner) return winner;
|
|
10032
|
+
if (needsPost) {
|
|
10033
|
+
needsPost = false;
|
|
10034
|
+
const signedPhase = body.serializedTransaction ? "deposit" : body.serializedPermitTransaction ? "approval" : void 0;
|
|
10035
|
+
if (signedPhase) await markSubmission(signedPhase);
|
|
10036
|
+
let result;
|
|
10037
|
+
try {
|
|
10038
|
+
result = await post();
|
|
10039
|
+
} catch (error) {
|
|
10040
|
+
assertCurrent();
|
|
10041
|
+
if (!family) {
|
|
10042
|
+
tokenBatchClear(
|
|
10043
|
+
tokenBatchKey(input),
|
|
10044
|
+
JSON.stringify(tokenBatchStoredBody(body))
|
|
10045
|
+
);
|
|
10046
|
+
throw error;
|
|
10047
|
+
}
|
|
10048
|
+
}
|
|
10049
|
+
if (result?.serializedPermitTransaction && !body.serializedPermitTransaction) {
|
|
10050
|
+
if (!result.permitTxHash) throw pending();
|
|
10051
|
+
await remember(
|
|
10052
|
+
"approval",
|
|
10053
|
+
result.serializedPermitTransaction,
|
|
10054
|
+
result.permitTxHash
|
|
10055
|
+
);
|
|
10056
|
+
needsPost = true;
|
|
10057
|
+
continue;
|
|
10058
|
+
}
|
|
10059
|
+
if (result?.serializedTransaction && !body.serializedTransaction) {
|
|
10060
|
+
await remember(
|
|
10061
|
+
"deposit",
|
|
10062
|
+
result.serializedTransaction,
|
|
10063
|
+
result.txHash
|
|
10064
|
+
);
|
|
10065
|
+
needsPost = true;
|
|
10066
|
+
continue;
|
|
10067
|
+
}
|
|
10068
|
+
if (result && !result.status && !result.serializedTransaction && !result.serializedPermitTransaction && family?.current.deposit === result.txHash)
|
|
10069
|
+
accounted.add(result.txHash);
|
|
10070
|
+
const winner2 = await observe();
|
|
10071
|
+
if (winner2) return winner2;
|
|
10072
|
+
}
|
|
10073
|
+
const phase = body.serializedTransaction ? "deposit" : "approval";
|
|
10074
|
+
const current = family?.current[phase];
|
|
10075
|
+
if (!current) throw pending();
|
|
10076
|
+
if (phase === "approval" && !observedApprovals.has(current)) {
|
|
10077
|
+
const receipt = await bounded2(
|
|
10078
|
+
input.pub.getTransactionReceipt({ hash: current }).catch(() => void 0)
|
|
10079
|
+
);
|
|
10080
|
+
if (receipt?.status === "success" && receipt.transactionHash?.toLowerCase() === current.toLowerCase() && receipt.from?.toLowerCase() === family.relayer.toLowerCase() && receipt.to?.toLowerCase() === body.token.toLowerCase()) {
|
|
10081
|
+
observedApprovals.add(current);
|
|
10082
|
+
needsPost = true;
|
|
10083
|
+
continue;
|
|
10084
|
+
}
|
|
10085
|
+
}
|
|
10086
|
+
if (replacements[phase] < 2 && !unavailable.has(phase)) {
|
|
10087
|
+
let recovered;
|
|
10088
|
+
try {
|
|
10089
|
+
recovered = await bounded2(
|
|
10090
|
+
postSponsorBatchRecovery({
|
|
10091
|
+
apiKey: input.apiKey,
|
|
10092
|
+
baseUrl: input.baseUrl,
|
|
10093
|
+
body: { ...body, phase },
|
|
10094
|
+
signal: controller.signal
|
|
10095
|
+
})
|
|
10096
|
+
);
|
|
10097
|
+
} catch {
|
|
10098
|
+
assertCurrent();
|
|
10099
|
+
}
|
|
10100
|
+
assertCurrent();
|
|
10101
|
+
if (!recovered) unavailable.add(phase);
|
|
10102
|
+
else if (recovered.status === "prepared") {
|
|
10103
|
+
if (recovered.predecessorHash !== current) throw pending();
|
|
10104
|
+
await remember(
|
|
10105
|
+
phase,
|
|
10106
|
+
recovered.serializedTransaction,
|
|
10107
|
+
recovered.txHash,
|
|
10108
|
+
current
|
|
10109
|
+
);
|
|
10110
|
+
replacements[phase]++;
|
|
10111
|
+
needsPost = true;
|
|
10112
|
+
continue;
|
|
10113
|
+
} else if (phase === "approval" && (recovered.status === "approval-satisfied" || recovered.status === "included" && recovered.outcome === "success")) {
|
|
10114
|
+
needsPost = true;
|
|
10115
|
+
} else if (recovered.status === "included" && recovered.outcome === "reverted") {
|
|
10116
|
+
if (phase === "approval" && !family.attempts.some((a) => a.phase === "deposit") && family.attempts.filter((a) => a.phase === "approval").length === 1) {
|
|
10117
|
+
finishTokenBatchFamily(input, family, {
|
|
10118
|
+
kind: "failed",
|
|
10119
|
+
blockNumber: recovered.blockNumber
|
|
10120
|
+
});
|
|
10121
|
+
throw new OwneyError(
|
|
10122
|
+
"SPONSOR_REQUEST_FAILED",
|
|
10123
|
+
"Sponsored approval reverted. Please retry.",
|
|
10124
|
+
{ notSubmitted: true, safeToFallback: false }
|
|
10125
|
+
);
|
|
10126
|
+
}
|
|
10127
|
+
unavailable.add(phase);
|
|
10128
|
+
}
|
|
10129
|
+
}
|
|
10130
|
+
await bounded2(
|
|
10131
|
+
new Promise(
|
|
10132
|
+
(resolve) => setTimeout(
|
|
10133
|
+
resolve,
|
|
10134
|
+
Math.min(2e3, Math.max(0, deadlineAt - Date.now()))
|
|
10135
|
+
)
|
|
10136
|
+
)
|
|
10137
|
+
);
|
|
10138
|
+
}
|
|
10139
|
+
} finally {
|
|
10140
|
+
ended = true;
|
|
10141
|
+
if (timer) clearTimeout(timer);
|
|
10142
|
+
controller.abort();
|
|
10143
|
+
}
|
|
10144
|
+
}
|
|
10145
|
+
|
|
9607
10146
|
// src/lib/sponsored-token-batch.ts
|
|
9608
10147
|
import {
|
|
9609
10148
|
isAddressEqual as isAddressEqual2,
|
|
9610
|
-
encodeFunctionData as
|
|
9611
|
-
keccak256 as
|
|
9612
|
-
parseTransaction,
|
|
10149
|
+
encodeFunctionData as encodeFunctionData4,
|
|
10150
|
+
keccak256 as keccak2563,
|
|
10151
|
+
parseTransaction as parseTransaction2,
|
|
9613
10152
|
toBytes
|
|
9614
10153
|
} from "viem";
|
|
9615
|
-
var memory = /* @__PURE__ */ new Map();
|
|
9616
|
-
var settledRecords = /* @__PURE__ */ new Map();
|
|
9617
10154
|
var expiredRecords = /* @__PURE__ */ new Map();
|
|
9618
10155
|
var inflight = /* @__PURE__ */ new Map();
|
|
9619
10156
|
var planOf = (transfers) => JSON.stringify(
|
|
@@ -9622,18 +10159,7 @@ var planOf = (transfers) => JSON.stringify(
|
|
|
9622
10159
|
amount: BigInt(t.amount).toString()
|
|
9623
10160
|
}))
|
|
9624
10161
|
);
|
|
9625
|
-
var pendingKey = (i) => `owney.token-batch.v1:${
|
|
9626
|
-
function pendingTokenBatch(i) {
|
|
9627
|
-
try {
|
|
9628
|
-
const saved = read(pendingKey(i));
|
|
9629
|
-
if (!saved) return;
|
|
9630
|
-
const body = JSON.parse(saved);
|
|
9631
|
-
if (!body.serializedTransaction || body.chainId !== i.chainId || !isAddressEqual2(body.from, i.owner) || !isAddressEqual2(body.token, i.token) || !body.transfers?.length) return;
|
|
9632
|
-
return { txHash: keccak2562(body.serializedTransaction), transfers: body.transfers.map(({ to, amount }) => ({ to, amount })) };
|
|
9633
|
-
} catch {
|
|
9634
|
-
return;
|
|
9635
|
-
}
|
|
9636
|
-
}
|
|
10162
|
+
var pendingKey = (i) => `owney.token-batch.v1:${keccak2563(toBytes(i.apiKey))}:${i.baseUrl ?? "default"}:${i.chainId}:${i.owner.toLowerCase()}:${i.token.toLowerCase()}`;
|
|
9637
10163
|
async function releaseExpiredTokenBatch(i) {
|
|
9638
10164
|
const key2 = pendingKey(i);
|
|
9639
10165
|
let timer;
|
|
@@ -9642,7 +10168,7 @@ async function releaseExpiredTokenBatch(i) {
|
|
|
9642
10168
|
let active2 = read(key2);
|
|
9643
10169
|
if (active2 && i.expectedTxHash) {
|
|
9644
10170
|
const current = JSON.parse(active2);
|
|
9645
|
-
if (!current.serializedTransaction ||
|
|
10171
|
+
if (!current.serializedTransaction || keccak2563(current.serializedTransaction).toLowerCase() !== i.expectedTxHash.toLowerCase())
|
|
9646
10172
|
active2 = void 0;
|
|
9647
10173
|
}
|
|
9648
10174
|
const archiveKey = i.expectedTxHash ? `${key2}:expired:${i.expectedTxHash.toLowerCase()}` : void 0;
|
|
@@ -9650,10 +10176,10 @@ async function releaseExpiredTokenBatch(i) {
|
|
|
9650
10176
|
if (!saved) return;
|
|
9651
10177
|
const body = JSON.parse(saved);
|
|
9652
10178
|
if (!body.serializedTransaction || body.chainId !== i.chainId || !isAddressEqual2(body.from, i.owner) || !isAddressEqual2(body.token, i.token) || !body.transfers?.length) return;
|
|
9653
|
-
const txHash =
|
|
10179
|
+
const txHash = keccak2563(body.serializedTransaction);
|
|
9654
10180
|
if (i.expectedTxHash && txHash.toLowerCase() !== i.expectedTxHash.toLowerCase()) return;
|
|
9655
|
-
const transaction =
|
|
9656
|
-
if (transaction.chainId !== i.chainId || !transaction.to || !isAddressEqual2(transaction.to, BATCH_PERMIT2_ADDRESS) || (transaction.value ?? 0n) !== 0n || transaction.data !==
|
|
10181
|
+
const transaction = parseTransaction2(body.serializedTransaction);
|
|
10182
|
+
if (transaction.chainId !== i.chainId || !transaction.to || !isAddressEqual2(transaction.to, BATCH_PERMIT2_ADDRESS) || (transaction.value ?? 0n) !== 0n || transaction.data !== encodeFunctionData4({
|
|
9657
10183
|
abi: PERMIT2_BATCH_ABI,
|
|
9658
10184
|
functionName: "permitWitnessTransferFrom",
|
|
9659
10185
|
args: batchArgs(body)
|
|
@@ -9682,8 +10208,7 @@ async function releaseExpiredTokenBatch(i) {
|
|
|
9682
10208
|
}
|
|
9683
10209
|
} catch {
|
|
9684
10210
|
}
|
|
9685
|
-
if (
|
|
9686
|
-
else clear(key2, body);
|
|
10211
|
+
if (persisted) clear(key2, body);
|
|
9687
10212
|
}
|
|
9688
10213
|
return { txHash, transfers: body.transfers.map(({ to, amount }) => ({ to, amount })), blockNumber: block.number };
|
|
9689
10214
|
};
|
|
@@ -9701,37 +10226,17 @@ async function releaseExpiredTokenBatch(i) {
|
|
|
9701
10226
|
if (timer) clearTimeout(timer);
|
|
9702
10227
|
}
|
|
9703
10228
|
}
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
return value && settledRecords.get(key2) === value ? void 0 : value;
|
|
9707
|
-
}
|
|
9708
|
-
function storedBody(body) {
|
|
9709
|
-
return JSON.stringify({
|
|
9710
|
-
...body,
|
|
9711
|
-
transfers: body.transfers.map(({ to, amount }) => ({ to, amount }))
|
|
9712
|
-
});
|
|
9713
|
-
}
|
|
9714
|
-
function save(key2, body) {
|
|
9715
|
-
const value = storedBody(body);
|
|
9716
|
-
if (typeof window === "undefined") memory.set(key2, value);
|
|
9717
|
-
else window.localStorage.setItem(key2, value);
|
|
9718
|
-
settledRecords.delete(key2);
|
|
9719
|
-
}
|
|
10229
|
+
var read = tokenBatchRead;
|
|
10230
|
+
var storedBody = (body) => JSON.stringify(tokenBatchStoredBody(body));
|
|
9720
10231
|
function clear(key2, body) {
|
|
9721
|
-
|
|
9722
|
-
if (typeof window === "undefined") memory.delete(key2);
|
|
9723
|
-
else window.localStorage.removeItem(key2);
|
|
9724
|
-
settledRecords.delete(key2);
|
|
9725
|
-
} catch {
|
|
9726
|
-
settledRecords.set(key2, storedBody(body));
|
|
9727
|
-
}
|
|
10232
|
+
tokenBatchClear(key2, storedBody(body));
|
|
9728
10233
|
}
|
|
9729
10234
|
function sponsorTokenBatch(i) {
|
|
9730
10235
|
const key2 = pendingKey(i);
|
|
9731
10236
|
const plan = planOf(i.transfers);
|
|
9732
10237
|
const active2 = inflight.get(key2);
|
|
9733
10238
|
if (active2) {
|
|
9734
|
-
if (active2.plan !== plan
|
|
10239
|
+
if (active2.plan !== plan)
|
|
9735
10240
|
return Promise.reject(
|
|
9736
10241
|
new Error(
|
|
9737
10242
|
"A token deposit is already in progress. Wait for its result before depositing again."
|
|
@@ -9748,136 +10253,13 @@ async function execute(i, key2, plan) {
|
|
|
9748
10253
|
(t) => BigInt(t.amount) <= 0n || BigInt(t.amount) >= 1n << 256n
|
|
9749
10254
|
) || new Set(i.transfers.map((t) => t.to.toLowerCase())).size !== i.transfers.length)
|
|
9750
10255
|
throw new Error("Invalid token deposit shares.");
|
|
9751
|
-
const send =
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
const rejectedPreparation = (error) => !body.serializedTransaction && error instanceof OwneyError && typeof error.details?.statusCode === "number" && error.details.statusCode >= 400 && error.details.statusCode < 500 && ![408, 429].includes(error.details.statusCode);
|
|
9755
|
-
try {
|
|
9756
|
-
const post = () => postSponsorBatchTransfer({ apiKey: i.apiKey, baseUrl: i.baseUrl, body });
|
|
9757
|
-
const pendingError = (phase, txHash) => new OwneyError(
|
|
9758
|
-
"DEPOSIT_IN_PROGRESS",
|
|
9759
|
-
phase === "approval" ? "Deposit approval is awaiting confirmation. Retry the same amount to continue it." : "Deposit is awaiting confirmation. Its transaction is still being checked.",
|
|
9760
|
-
{ sponsorPending: true, phase, txHash, safeToFallback: false }
|
|
9761
|
-
);
|
|
9762
|
-
const assertReceipt = (receipt, phase) => {
|
|
9763
|
-
if (receipt.status !== "success") throw new OwneyError(
|
|
9764
|
-
"SPONSOR_REQUEST_FAILED",
|
|
9765
|
-
`Sponsored ${phase} transaction reverted. Please retry.`,
|
|
9766
|
-
{ notSubmitted: true, safeToFallback: false }
|
|
9767
|
-
);
|
|
9768
|
-
};
|
|
9769
|
-
const waitForConfirmation = async (phase, hash3) => {
|
|
9770
|
-
let receipt;
|
|
9771
|
-
try {
|
|
9772
|
-
receipt = await i.pub.waitForTransactionReceipt({ hash: hash3, confirmations: 1, timeout: 9e4, checkReplacement: false });
|
|
9773
|
-
} catch {
|
|
9774
|
-
receipt = await i.pub.getTransactionReceipt({ hash: hash3 }).catch(() => void 0);
|
|
9775
|
-
}
|
|
9776
|
-
if (!receipt) throw pendingError(phase, hash3);
|
|
9777
|
-
assertReceipt(receipt, phase);
|
|
9778
|
-
};
|
|
9779
|
-
const postSigned = async (phase, hash3) => {
|
|
9780
|
-
try {
|
|
9781
|
-
return await post();
|
|
9782
|
-
} catch (error) {
|
|
9783
|
-
if (rejectedPreparation(error) || error instanceof OwneyError && error.details?.notSubmitted === true) throw error;
|
|
9784
|
-
await waitForConfirmation(phase, hash3);
|
|
9785
|
-
return void 0;
|
|
9786
|
-
}
|
|
9787
|
-
};
|
|
9788
|
-
if (body.erc2612Permit && !body.serializedPermitTransaction) {
|
|
9789
|
-
const preparedPermit = await postSponsorBatchTransfer({
|
|
9790
|
-
apiKey: i.apiKey,
|
|
9791
|
-
baseUrl: i.baseUrl,
|
|
9792
|
-
body
|
|
9793
|
-
});
|
|
9794
|
-
if (!preparedPermit.serializedPermitTransaction || !preparedPermit.permitTxHash || keccak2562(preparedPermit.serializedPermitTransaction) !== preparedPermit.permitTxHash)
|
|
9795
|
-
throw new Error(
|
|
9796
|
-
"Sponsorship API did not return a valid prepared permit transaction."
|
|
9797
|
-
);
|
|
9798
|
-
body = {
|
|
9799
|
-
...body,
|
|
9800
|
-
serializedPermitTransaction: preparedPermit.serializedPermitTransaction
|
|
9801
|
-
};
|
|
9802
|
-
save(key2, body);
|
|
9803
|
-
}
|
|
9804
|
-
if (!body.serializedTransaction) {
|
|
9805
|
-
const permitHash = body.serializedPermitTransaction ? keccak2562(body.serializedPermitTransaction) : void 0;
|
|
9806
|
-
let prepared = permitHash ? await postSigned("approval", permitHash) : await post();
|
|
9807
|
-
if (prepared?.status === "pending") {
|
|
9808
|
-
if (!permitHash || prepared.phase !== "approval" || prepared.txHash !== permitHash)
|
|
9809
|
-
throw new Error("Sponsorship approval does not match the pending transaction.");
|
|
9810
|
-
await waitForConfirmation("approval", permitHash);
|
|
9811
|
-
prepared = void 0;
|
|
9812
|
-
}
|
|
9813
|
-
if (!prepared) prepared = await post();
|
|
9814
|
-
if (prepared.status === "pending" && permitHash && prepared.phase === "approval" && prepared.txHash === permitHash)
|
|
9815
|
-
throw pendingError("approval", permitHash);
|
|
9816
|
-
if (!prepared.serializedTransaction || keccak2562(prepared.serializedTransaction) !== prepared.txHash)
|
|
9817
|
-
throw new Error(
|
|
9818
|
-
"Sponsorship API did not return a valid prepared transaction."
|
|
9819
|
-
);
|
|
9820
|
-
body = {
|
|
9821
|
-
...body,
|
|
9822
|
-
serializedTransaction: prepared.serializedTransaction
|
|
9823
|
-
};
|
|
9824
|
-
save(key2, body);
|
|
9825
|
-
}
|
|
9826
|
-
await i.onBeforeSubmission?.(keccak2562(body.serializedTransaction));
|
|
9827
|
-
try {
|
|
9828
|
-
i.onSubmitted?.(keccak2562(body.serializedTransaction));
|
|
9829
|
-
} catch {
|
|
9830
|
-
}
|
|
9831
|
-
const hash2 = keccak2562(body.serializedTransaction);
|
|
9832
|
-
const existing = await i.pub.getTransactionReceipt({ hash: hash2 }).catch(() => void 0);
|
|
9833
|
-
if (existing) {
|
|
9834
|
-
assertReceipt(existing, "deposit");
|
|
9835
|
-
void post().catch(() => void 0);
|
|
9836
|
-
clear(key2, body);
|
|
9837
|
-
return hash2;
|
|
9838
|
-
}
|
|
9839
|
-
let result = await postSigned("deposit", hash2);
|
|
9840
|
-
if (result?.status === "pending" && result.phase === "approval" && body.serializedPermitTransaction && result.txHash === keccak2562(body.serializedPermitTransaction)) {
|
|
9841
|
-
await waitForConfirmation("approval", result.txHash);
|
|
9842
|
-
result = await postSigned("deposit", hash2);
|
|
9843
|
-
if (result?.status === "pending" && result.phase === "approval" && result.txHash === keccak2562(body.serializedPermitTransaction)) throw pendingError("approval", result.txHash);
|
|
9844
|
-
}
|
|
9845
|
-
if (result) {
|
|
9846
|
-
if (result.txHash !== hash2 || result.status === "pending" && result.phase !== "deposit")
|
|
9847
|
-
throw new Error("Sponsorship receipt does not match the pending transaction.");
|
|
9848
|
-
if (result.status === "pending") await waitForConfirmation("deposit", hash2);
|
|
9849
|
-
}
|
|
9850
|
-
if (!result || result.status === "pending") void post().catch(() => void 0);
|
|
9851
|
-
clear(key2, body);
|
|
9852
|
-
return hash2;
|
|
9853
|
-
} catch (error) {
|
|
9854
|
-
if (initial.serializedTransaction && error instanceof OwneyError && error.code === "DEPOSIT_CHECKPOINT_FAILED") {
|
|
9855
|
-
throw new OwneyError(
|
|
9856
|
-
error.code,
|
|
9857
|
-
"Could not save recovery of the existing deposit.",
|
|
9858
|
-
{ ...error.details, notSubmitted: false, safeToFallback: false }
|
|
9859
|
-
);
|
|
9860
|
-
}
|
|
9861
|
-
if (!body.serializedTransaction && !body.serializedPermitTransaction || rejectedPreparation(error) || error instanceof OwneyError && error.details?.notSubmitted === true)
|
|
9862
|
-
clear(key2, body);
|
|
9863
|
-
throw error;
|
|
9864
|
-
}
|
|
9865
|
-
};
|
|
9866
|
-
const saved = read(key2);
|
|
9867
|
-
if (i.resumeTxHash && pendingTokenBatch(i)?.txHash !== i.resumeTxHash)
|
|
9868
|
-
throw new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit changed. Check its status before depositing again.");
|
|
10256
|
+
const send = (body) => executeTokenBatch(i, body);
|
|
10257
|
+
const family = await loadTokenBatchFamily(i, i.resumeTxHash);
|
|
10258
|
+
const saved = family ? JSON.stringify(family.body) : read(key2);
|
|
9869
10259
|
if (saved) {
|
|
9870
10260
|
const previous = JSON.parse(saved);
|
|
9871
10261
|
if (previous.chainId !== i.chainId || !isAddressEqual2(previous.from, i.owner) || !isAddressEqual2(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
9872
|
-
throw new
|
|
9873
|
-
"Retry the previous token deposit and agent split first to reconcile its status."
|
|
9874
|
-
);
|
|
9875
|
-
if (await releaseExpiredTokenBatch({ ...i, expectedTxHash: previous.serializedTransaction ? keccak2562(previous.serializedTransaction) : void 0 }))
|
|
9876
|
-
throw new OwneyError(
|
|
9877
|
-
"SPONSOR_REQUEST_FAILED",
|
|
9878
|
-
"The deposit authorization expired without transferring funds. Start a new deposit.",
|
|
9879
|
-
{ notSubmitted: true, safeToFallback: false }
|
|
9880
|
-
);
|
|
10262
|
+
throw new OwneyError("DEPOSIT_IN_PROGRESS", "Retry the previous token deposit and agent split first to reconcile its status.");
|
|
9881
10263
|
i.onApproved?.();
|
|
9882
10264
|
return send({ ...previous, transfers: i.transfers });
|
|
9883
10265
|
}
|
|
@@ -9983,8 +10365,9 @@ function makeSponsoredTokenCallback(deps) {
|
|
|
9983
10365
|
pub,
|
|
9984
10366
|
wallet,
|
|
9985
10367
|
onApproved: deps.onApproved,
|
|
9986
|
-
onSubmitted: (hash2) => deps.onSubmitted?.(hash2, transfers),
|
|
9987
|
-
onBeforeSubmission: (hash2) => deps.onBeforeSubmission?.(hash2, transfers),
|
|
10368
|
+
onSubmitted: (hash2, reference) => deps.onSubmitted?.(hash2, transfers, reference),
|
|
10369
|
+
onBeforeSubmission: (hash2, reference) => deps.onBeforeSubmission?.(hash2, transfers, reference),
|
|
10370
|
+
assertCurrent: deps.assertCurrent,
|
|
9988
10371
|
resumeTxHash: deps.resumeTxHash
|
|
9989
10372
|
});
|
|
9990
10373
|
};
|
|
@@ -10071,7 +10454,7 @@ async function runAgentDepositBatch(chainId, legs, transfer) {
|
|
|
10071
10454
|
}
|
|
10072
10455
|
|
|
10073
10456
|
// src/lib/sponsored-calls-deposit.ts
|
|
10074
|
-
import { encodeFunctionData as
|
|
10457
|
+
import { encodeFunctionData as encodeFunctionData5, erc20Abi as erc20Abi6, toHex as toHex3 } from "viem";
|
|
10075
10458
|
var DEFAULT_POLL_INTERVAL_MS = 1500;
|
|
10076
10459
|
var DEFAULT_MAX_POLLS = 30;
|
|
10077
10460
|
async function paymasterSupported(provider, owner, chainId) {
|
|
@@ -10116,7 +10499,7 @@ function makeSponsoredCallsCallback(deps) {
|
|
|
10116
10499
|
const calls = transfers.map((transfer) => ({
|
|
10117
10500
|
to: token,
|
|
10118
10501
|
value: "0x0",
|
|
10119
|
-
data:
|
|
10502
|
+
data: encodeFunctionData5({
|
|
10120
10503
|
abi: erc20Abi6,
|
|
10121
10504
|
functionName: "transfer",
|
|
10122
10505
|
args: [transfer.to, BigInt(transfer.amount)]
|
|
@@ -10321,6 +10704,7 @@ var OwneySDK = class {
|
|
|
10321
10704
|
paymasterServiceUrl;
|
|
10322
10705
|
cachedSponsoredCallsCallbacks = /* @__PURE__ */ new Map();
|
|
10323
10706
|
agentReads = new AgentReads();
|
|
10707
|
+
depositFamilyReads = /* @__PURE__ */ new Map();
|
|
10324
10708
|
recoveredFundingReads = /* @__PURE__ */ new Set();
|
|
10325
10709
|
fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
|
|
10326
10710
|
activationChecks = /* @__PURE__ */ new Map();
|
|
@@ -10456,11 +10840,13 @@ var OwneySDK = class {
|
|
|
10456
10840
|
return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
|
|
10457
10841
|
}
|
|
10458
10842
|
/** Builds the default USDC batch callback for the connected wallet. */
|
|
10459
|
-
getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission) {
|
|
10460
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && this.cachedSponsoredCallback)
|
|
10843
|
+
getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission, assertSubmissionCurrent) {
|
|
10844
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && this.cachedSponsoredCallback)
|
|
10461
10845
|
return this.cachedSponsoredCallback;
|
|
10462
10846
|
const provider = this.requireConnectedProvider();
|
|
10463
|
-
const
|
|
10847
|
+
const connection = this.requireState();
|
|
10848
|
+
const owner = connection.walletAddress;
|
|
10849
|
+
const connectedChain = connection.chainId;
|
|
10464
10850
|
const callback = makeSponsoredTokenCallback({
|
|
10465
10851
|
apiKey: this.apiKey,
|
|
10466
10852
|
baseUrl: this.routingApiBaseUrl,
|
|
@@ -10469,6 +10855,10 @@ var OwneySDK = class {
|
|
|
10469
10855
|
onSubmitted,
|
|
10470
10856
|
onBeforeSubmission,
|
|
10471
10857
|
resumeTxHash,
|
|
10858
|
+
assertCurrent: () => {
|
|
10859
|
+
assertSubmissionCurrent?.();
|
|
10860
|
+
if (!this.state || this.state !== connection || this.state.chainId !== connectedChain || this.state.provider !== provider || this.state.walletAddress.toLowerCase() !== owner.toLowerCase()) throw new OwneyError("NOT_CONNECTED", "Wallet changed before deposit submission.");
|
|
10861
|
+
},
|
|
10472
10862
|
tokenAddressByChain: SPONSORED_USDC_BY_CHAIN,
|
|
10473
10863
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
10474
10864
|
// PublicClient/WalletClient param types — structurally identical at
|
|
@@ -10484,7 +10874,7 @@ var OwneySDK = class {
|
|
|
10484
10874
|
transport: custom5(provider)
|
|
10485
10875
|
})
|
|
10486
10876
|
});
|
|
10487
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission) this.cachedSponsoredCallback = callback;
|
|
10877
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent) this.cachedSponsoredCallback = callback;
|
|
10488
10878
|
return callback;
|
|
10489
10879
|
}
|
|
10490
10880
|
/** Builds the wallet-native sponsored calls callback for compatible paymasters. */
|
|
@@ -10513,11 +10903,13 @@ var OwneySDK = class {
|
|
|
10513
10903
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
10514
10904
|
* single-use batch authorization instead of an EIP-3009 authorization.
|
|
10515
10905
|
*/
|
|
10516
|
-
getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission) {
|
|
10517
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && this.cachedWethSponsoredCallback)
|
|
10906
|
+
getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission, assertSubmissionCurrent) {
|
|
10907
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && this.cachedWethSponsoredCallback)
|
|
10518
10908
|
return this.cachedWethSponsoredCallback;
|
|
10519
10909
|
const provider = this.requireConnectedProvider();
|
|
10520
|
-
const
|
|
10910
|
+
const connection = this.requireState();
|
|
10911
|
+
const owner = connection.walletAddress;
|
|
10912
|
+
const connectedChain = connection.chainId;
|
|
10521
10913
|
const callback = makeSponsoredTokenCallback({
|
|
10522
10914
|
apiKey: this.apiKey,
|
|
10523
10915
|
baseUrl: this.routingApiBaseUrl,
|
|
@@ -10526,6 +10918,10 @@ var OwneySDK = class {
|
|
|
10526
10918
|
onSubmitted,
|
|
10527
10919
|
onBeforeSubmission,
|
|
10528
10920
|
resumeTxHash,
|
|
10921
|
+
assertCurrent: () => {
|
|
10922
|
+
assertSubmissionCurrent?.();
|
|
10923
|
+
if (!this.state || this.state !== connection || this.state.chainId !== connectedChain || this.state.provider !== provider || this.state.walletAddress.toLowerCase() !== owner.toLowerCase()) throw new OwneyError("NOT_CONNECTED", "Wallet changed before deposit submission.");
|
|
10924
|
+
},
|
|
10529
10925
|
tokenAddressByChain: SPONSORED_WETH_BY_CHAIN,
|
|
10530
10926
|
// Casts work around viem's chain-narrowed Client vs the generic
|
|
10531
10927
|
// PublicClient/WalletClient param types — structurally identical at
|
|
@@ -10541,7 +10937,7 @@ var OwneySDK = class {
|
|
|
10541
10937
|
transport: custom5(provider)
|
|
10542
10938
|
})
|
|
10543
10939
|
});
|
|
10544
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission) this.cachedWethSponsoredCallback = callback;
|
|
10940
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent) this.cachedWethSponsoredCallback = callback;
|
|
10545
10941
|
return callback;
|
|
10546
10942
|
}
|
|
10547
10943
|
getAgent(agentId) {
|
|
@@ -10865,10 +11261,10 @@ var OwneySDK = class {
|
|
|
10865
11261
|
}
|
|
10866
11262
|
notifyFunding(options.onProgress, evidence);
|
|
10867
11263
|
} : void 0;
|
|
10868
|
-
const onSubmitted = onProgress ? (hash2, destinations) => {
|
|
11264
|
+
const onSubmitted = onProgress ? (hash2, destinations, depositAuthorization) => {
|
|
10869
11265
|
for (const destination of destinations) {
|
|
10870
11266
|
const evidence = prepared.get(destination.to.toLowerCase());
|
|
10871
|
-
if (evidence) notifyFunding(onProgress, { ...evidence, txHash: hash2, transfer: "pending" });
|
|
11267
|
+
if (evidence) notifyFunding(onProgress, { ...evidence, txHash: hash2, ...depositAuthorization ? { depositAuthorization } : {}, transfer: "pending" });
|
|
10872
11268
|
}
|
|
10873
11269
|
} : void 0;
|
|
10874
11270
|
const onCallsProgress = onProgress ? (callsId, destinations, transfer) => {
|
|
@@ -10910,7 +11306,7 @@ var OwneySDK = class {
|
|
|
10910
11306
|
});
|
|
10911
11307
|
await checkpoint2(evidence);
|
|
10912
11308
|
} : void 0;
|
|
10913
|
-
const checkpointHash = checkpointDestinations ? (hash2, destinations) => checkpointDestinations(destinations, { txHash: hash2 }) : void 0;
|
|
11309
|
+
const checkpointHash = checkpointDestinations ? (hash2, destinations, depositAuthorization) => checkpointDestinations(destinations, { txHash: hash2, depositAuthorization }) : void 0;
|
|
10914
11310
|
const checkpointCalls = checkpointDestinations ? (id, destinations) => checkpointDestinations(destinations, { callsId: id }) : void 0;
|
|
10915
11311
|
const checkpointPaid = checkpoint2 ? (evidence) => checkpoint2([evidence]) : void 0;
|
|
10916
11312
|
const customCallback = depositCallback && checkpointDestinations ? makeVerificationAwareDepositCallback(async (to, cid, value, verification) => {
|
|
@@ -10920,19 +11316,20 @@ var OwneySDK = class {
|
|
|
10920
11316
|
}) : depositCallback;
|
|
10921
11317
|
if (options.resumeTxHash && (depositCallback || this.paymasterServiceUrl || !/^0x[\da-f]{64}$/i.test(options.resumeTxHash)))
|
|
10922
11318
|
throw new OwneyError("DEPOSIT_IN_PROGRESS", "This deposit cannot resume the saved relayer transaction.");
|
|
10923
|
-
const effectiveCallback = customCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress, checkpointCalls) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash, checkpointHash) : this.getDefaultSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash, checkpointHash));
|
|
11319
|
+
const effectiveCallback = customCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress, checkpointCalls) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash, checkpointHash, options.assertSubmissionCurrent) : this.getDefaultSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash, checkpointHash, options.assertSubmissionCurrent));
|
|
10924
11320
|
try {
|
|
10925
11321
|
if (options.resumeTxHash) {
|
|
10926
11322
|
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
10927
|
-
const
|
|
11323
|
+
const family = token && await loadTokenBatchFamily({
|
|
10928
11324
|
apiKey: this.apiKey,
|
|
10929
11325
|
baseUrl: this.routingApiBaseUrl,
|
|
10930
11326
|
owner: state.walletAddress,
|
|
10931
11327
|
chainId,
|
|
10932
11328
|
token: token.address
|
|
10933
|
-
});
|
|
11329
|
+
}, options.resumeTxHash);
|
|
11330
|
+
const pending = family && { transfers: family.body.transfers };
|
|
10934
11331
|
const invalid2 = () => new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit does not match this request. Check its status before depositing again.");
|
|
10935
|
-
if (!pending || pending.
|
|
11332
|
+
if (!pending || pending.transfers.some((transfer) => !/^\d+$/.test(transfer.amount) || BigInt(transfer.amount) <= 0n) || pending.transfers.reduce((sum, transfer) => sum + BigInt(transfer.amount), 0n) !== BigInt(amount)) throw invalid2();
|
|
10936
11333
|
const candidates = agentId ? [this.getAgent(agentId)] : this.getEligibleAgents(chainId, asset);
|
|
10937
11334
|
const wallets = await Promise.all(candidates.map(async (agent) => {
|
|
10938
11335
|
this.validateAssetSupport(agent, chainId, asset);
|
|
@@ -11391,8 +11788,46 @@ var OwneySDK = class {
|
|
|
11391
11788
|
if (!token) throw new OwneyError("CHAIN_UNSUPPORTED", "Unsupported funding asset.");
|
|
11392
11789
|
const rpc2 = this.getPaidRpcClient(chainId);
|
|
11393
11790
|
let receipt;
|
|
11791
|
+
if (direction === "deposit" && !evidence.callsId && !evidence.userOpHash) {
|
|
11792
|
+
const scope = {
|
|
11793
|
+
apiKey: this.apiKey,
|
|
11794
|
+
baseUrl: this.routingApiBaseUrl,
|
|
11795
|
+
owner: state.walletAddress,
|
|
11796
|
+
chainId,
|
|
11797
|
+
token: token.address
|
|
11798
|
+
};
|
|
11799
|
+
const family = await loadTokenBatchFamily(scope, evidence.txHash, !evidence.depositAuthorization);
|
|
11800
|
+
assertCurrent();
|
|
11801
|
+
if (family) {
|
|
11802
|
+
if (evidence.depositAuthorization && evidence.depositAuthorization.authorizationId !== family.authorizationId || !family.body.transfers.some((t) => t.to.toLowerCase() === agentWallet.toLowerCase() && t.amount === evidence.intendedAmount))
|
|
11803
|
+
throw new OwneyError("DEPOSIT_IN_PROGRESS", "Deposit evidence does not match its signed authorization.");
|
|
11804
|
+
const familyKey = `${tokenBatchKey(scope)}:${family.authorizationId}`;
|
|
11805
|
+
let reading = this.depositFamilyReads.get(familyKey);
|
|
11806
|
+
if (!reading) {
|
|
11807
|
+
reading = observeTokenBatchFamily({ scope, family, pub: rpc2, deadlineAt: Date.now() + 8e3, assertCurrent }).finally(() => this.depositFamilyReads.delete(familyKey));
|
|
11808
|
+
this.depositFamilyReads.set(familyKey, reading);
|
|
11809
|
+
}
|
|
11810
|
+
const result = await reading;
|
|
11811
|
+
assertCurrent();
|
|
11812
|
+
evidence = { ...evidence, depositAuthorization: { authorizationId: family.authorizationId } };
|
|
11813
|
+
if (result.status === "confirmed") {
|
|
11814
|
+
receipt = result.receipt;
|
|
11815
|
+
evidence = { ...evidence, txHash: result.txHash };
|
|
11816
|
+
} else if (result.status === "expired" || result.status === "failed")
|
|
11817
|
+
return {
|
|
11818
|
+
...evidence,
|
|
11819
|
+
agentWallet,
|
|
11820
|
+
transfer: "failed",
|
|
11821
|
+
delivery: "not-applicable",
|
|
11822
|
+
registration: "not-applicable",
|
|
11823
|
+
confirmedAmount: "0",
|
|
11824
|
+
blockNumber: (result.status === "expired" ? result.blockNumber : result.receipts.reduce((n, r) => r.blockNumber > n ? r.blockNumber : n, 0n)).toString()
|
|
11825
|
+
};
|
|
11826
|
+
else return { ...evidence, agentWallet, transfer: "unknown", delivery: "not-applicable" };
|
|
11827
|
+
}
|
|
11828
|
+
}
|
|
11394
11829
|
try {
|
|
11395
|
-
receipt
|
|
11830
|
+
receipt ??= await rpc2.getTransactionReceipt({ hash: evidence.txHash });
|
|
11396
11831
|
} catch (error) {
|
|
11397
11832
|
if (direction === "deposit") {
|
|
11398
11833
|
const expired = await releaseExpiredTokenBatch({
|
|
@@ -11512,29 +11947,65 @@ var OwneySDK = class {
|
|
|
11512
11947
|
/** Whether a foreground deposit can resume the same signed batch, never a
|
|
11513
11948
|
* fresh authorization. The batch rail independently rechecks the exact plan. */
|
|
11514
11949
|
async canResumePendingDeposit(evidences, amount) {
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
if (
|
|
11531
|
-
const
|
|
11532
|
-
const
|
|
11533
|
-
|
|
11534
|
-
|
|
11950
|
+
try {
|
|
11951
|
+
if (this.paymasterServiceUrl || !evidences.length || !/^\d+$/.test(amount)) return false;
|
|
11952
|
+
const first = evidences[0];
|
|
11953
|
+
if (!first.txHash) return false;
|
|
11954
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(first.chainId);
|
|
11955
|
+
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === first.token);
|
|
11956
|
+
if (!token) return false;
|
|
11957
|
+
const family = await loadTokenBatchFamily({
|
|
11958
|
+
apiKey: this.apiKey,
|
|
11959
|
+
baseUrl: this.routingApiBaseUrl,
|
|
11960
|
+
owner: state.walletAddress,
|
|
11961
|
+
chainId,
|
|
11962
|
+
token: token.address
|
|
11963
|
+
}, first.txHash);
|
|
11964
|
+
const transfers = family?.body.transfers;
|
|
11965
|
+
if (!family || !transfers || transfers.length !== evidences.length || transfers.reduce((n, t) => n + BigInt(t.amount), 0n).toString() !== amount) return false;
|
|
11966
|
+
const destinations = /* @__PURE__ */ new Set();
|
|
11967
|
+
for (const e of evidences) {
|
|
11968
|
+
if (e.chainId !== chainId || e.token !== first.token || e.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || e.callsId || e.userOpHash || !family.attempts.some((a) => a.phase === "deposit" && a.hash.toLowerCase() === e.txHash?.toLowerCase()) || e.depositAuthorization && e.depositAuthorization.authorizationId !== family.authorizationId) return false;
|
|
11969
|
+
const agent = this.getAgent(e.agentId);
|
|
11970
|
+
const wallet = agent.getFundingWallet ? await agent.getFundingWallet(state, chainId, e.token) : (await agent.getUserProfile(state, chainId)).smartWallet;
|
|
11971
|
+
assertCurrent();
|
|
11972
|
+
if (!wallet || destinations.has(wallet.toLowerCase()) || e.agentWallet && e.agentWallet.toLowerCase() !== wallet.toLowerCase() || !transfers.some((t) => t.to.toLowerCase() === wallet.toLowerCase() && t.amount === e.intendedAmount)) return false;
|
|
11973
|
+
destinations.add(wallet.toLowerCase());
|
|
11974
|
+
}
|
|
11975
|
+
assertCurrent();
|
|
11976
|
+
return true;
|
|
11977
|
+
} catch {
|
|
11978
|
+
return false;
|
|
11979
|
+
}
|
|
11980
|
+
}
|
|
11981
|
+
/** Validate signed journal membership, never just a caller-supplied family ID. */
|
|
11982
|
+
async validateDepositReplacement(previous, next) {
|
|
11983
|
+
try {
|
|
11984
|
+
if (!previous.length || previous.length !== next.length || previous.some((e) => e.transfer === "confirmed" || !/^\d+$/.test(e.intendedAmount))) return;
|
|
11985
|
+
const amount = previous.reduce((sum, e) => sum + BigInt(e.intendedAmount), 0n).toString();
|
|
11986
|
+
if (!await this.canResumePendingDeposit(previous, amount) || !await this.canResumePendingDeposit(next, amount)) return;
|
|
11987
|
+
const { state, chainId, assertCurrent } = this.captureReadScope(previous[0].chainId);
|
|
11988
|
+
const token = SupportedAssets.find((t) => t.chainId === chainId && t.symbol === previous[0].token);
|
|
11989
|
+
const family = await loadTokenBatchFamily({
|
|
11990
|
+
apiKey: this.apiKey,
|
|
11991
|
+
baseUrl: this.routingApiBaseUrl,
|
|
11992
|
+
owner: state.walletAddress,
|
|
11993
|
+
chainId,
|
|
11994
|
+
token: token.address
|
|
11995
|
+
}, previous[0].txHash);
|
|
11996
|
+
assertCurrent();
|
|
11997
|
+
if (!family || family.terminal) return;
|
|
11998
|
+
const predecessorTxHash = previous[0].txHash, replacementTxHash = next[0].txHash;
|
|
11999
|
+
const replacement = family.attempts.find((a) => a.phase === "deposit" && a.hash.toLowerCase() === replacementTxHash.toLowerCase());
|
|
12000
|
+
if (!replacement || replacement.predecessorHash?.toLowerCase() !== predecessorTxHash.toLowerCase()) return;
|
|
12001
|
+
for (const p of previous) {
|
|
12002
|
+
const matches = next.filter((n2) => n2.agentId === p.agentId), n = matches[0];
|
|
12003
|
+
if (matches.length !== 1 || p.txHash?.toLowerCase() !== predecessorTxHash.toLowerCase() || n.txHash?.toLowerCase() !== replacementTxHash.toLowerCase() || p.intendedAmount !== n.intendedAmount || p.agentWallet?.toLowerCase() !== n.agentWallet?.toLowerCase() || n.depositAuthorization?.authorizationId !== family.authorizationId || n.depositAuthorization.predecessorTxHash?.toLowerCase() !== predecessorTxHash.toLowerCase()) return;
|
|
12004
|
+
}
|
|
12005
|
+
return { authorizationId: family.authorizationId, predecessorTxHash, replacementTxHash };
|
|
12006
|
+
} catch {
|
|
12007
|
+
return;
|
|
11535
12008
|
}
|
|
11536
|
-
assertCurrent();
|
|
11537
|
-
return true;
|
|
11538
12009
|
}
|
|
11539
12010
|
/** Optional post-payout display proof; receipt verification and the provider
|
|
11540
12011
|
* adapter must agree before attaching it to this exact balance object. */
|
|
@@ -11686,7 +12157,7 @@ var OwneySDK = class {
|
|
|
11686
12157
|
* quote's source token. A quote naming ETH would have all of them reading
|
|
11687
12158
|
* the wrong asset.
|
|
11688
12159
|
*/
|
|
11689
|
-
async wrapNativeSource(quote, walletAddress, onStage, direction) {
|
|
12160
|
+
async wrapNativeSource(quote, walletAddress, onStage, direction, assertCurrent) {
|
|
11690
12161
|
const wrapper = requireWrapper(quote);
|
|
11691
12162
|
const chainId = quote.src.chainId;
|
|
11692
12163
|
const chain2 = swapChain(chainId);
|
|
@@ -11698,7 +12169,10 @@ var OwneySDK = class {
|
|
|
11698
12169
|
const wallet = createWalletClient5({
|
|
11699
12170
|
account: walletAddress,
|
|
11700
12171
|
chain: chain2,
|
|
11701
|
-
transport: custom5(
|
|
12172
|
+
transport: custom5({ request: (args) => {
|
|
12173
|
+
assertCurrent?.();
|
|
12174
|
+
return provider.request(args);
|
|
12175
|
+
} })
|
|
11702
12176
|
});
|
|
11703
12177
|
onStage?.("wrapping");
|
|
11704
12178
|
const { wrapped } = await wrapNative(
|
|
@@ -11719,6 +12193,7 @@ var OwneySDK = class {
|
|
|
11719
12193
|
return fees.maxFeePerGas ?? publicClient.getGasPrice();
|
|
11720
12194
|
},
|
|
11721
12195
|
sendTransaction: async (tx, onSubmitted) => {
|
|
12196
|
+
assertCurrent?.();
|
|
11722
12197
|
const hash2 = await wallet.sendTransaction({
|
|
11723
12198
|
to: tx.to,
|
|
11724
12199
|
data: tx.data,
|
|
@@ -12006,13 +12481,19 @@ var OwneySDK = class {
|
|
|
12006
12481
|
* Native Base/Arbitrum USDC uses sponsored ERC-2612; configured paymaster
|
|
12007
12482
|
* wallet calls also cover approval without spending the received tokens.
|
|
12008
12483
|
*/
|
|
12009
|
-
async settleSwapDepositAmount(chainId, token, symbol2, received) {
|
|
12484
|
+
async settleSwapDepositAmount(chainId, token, symbol2, received, assertCurrent) {
|
|
12010
12485
|
if (symbol2 !== "WETH" && symbol2 !== "USDC" || this.paymasterServiceUrl || supportsSponsoredErc2612(chainId, token)) {
|
|
12011
12486
|
return received.toString();
|
|
12012
12487
|
}
|
|
12013
12488
|
const allowance = await this.readTokenPermit2Allowance(chainId, token);
|
|
12489
|
+
assertCurrent?.();
|
|
12014
12490
|
if (allowance < received) {
|
|
12015
|
-
await this.approvePermit2(
|
|
12491
|
+
await this.approvePermit2(
|
|
12492
|
+
symbol2,
|
|
12493
|
+
received,
|
|
12494
|
+
chainId,
|
|
12495
|
+
...assertCurrent ? [assertCurrent] : []
|
|
12496
|
+
);
|
|
12016
12497
|
}
|
|
12017
12498
|
const balance = await this.readTokenBalance(chainId, token);
|
|
12018
12499
|
return (balance < received ? balance : received).toString();
|
|
@@ -12043,7 +12524,7 @@ var OwneySDK = class {
|
|
|
12043
12524
|
* so the deposit half of `swapAndDeposit` can be exercised on its own.
|
|
12044
12525
|
*/
|
|
12045
12526
|
runSwap(quote, options) {
|
|
12046
|
-
return executeSwap(this.buildSwapDeps(quote), { quote, ...options });
|
|
12527
|
+
return executeSwap(this.buildSwapDeps(quote, options.assertCurrent), { quote, ...options });
|
|
12047
12528
|
}
|
|
12048
12529
|
/**
|
|
12049
12530
|
* The half of the swap deps that only ever talks to the routing API.
|
|
@@ -12331,41 +12812,65 @@ var OwneySDK = class {
|
|
|
12331
12812
|
*/
|
|
12332
12813
|
async swapAndDeposit(options) {
|
|
12333
12814
|
const state = this.requireState();
|
|
12815
|
+
options.assertCurrent?.();
|
|
12816
|
+
if (options.agentId) {
|
|
12817
|
+
this.validateAssetSupport(
|
|
12818
|
+
this.getAgent(options.agentId),
|
|
12819
|
+
options.to.chainId,
|
|
12820
|
+
options.to.symbol
|
|
12821
|
+
);
|
|
12822
|
+
}
|
|
12334
12823
|
const api = this.swapApi();
|
|
12335
12824
|
const quoted = await api.quote({
|
|
12336
12825
|
from: options.from,
|
|
12337
12826
|
to: options.to,
|
|
12338
12827
|
walletAddress: state.walletAddress
|
|
12339
12828
|
});
|
|
12829
|
+
options.assertCurrent?.();
|
|
12340
12830
|
const quote = quoted.requiresOnchainOrder ? await this.wrapNativeSource(
|
|
12341
12831
|
quoted,
|
|
12342
12832
|
state.walletAddress,
|
|
12343
|
-
options.onSwapProgress
|
|
12833
|
+
options.onSwapProgress,
|
|
12834
|
+
void 0,
|
|
12835
|
+
options.assertCurrent
|
|
12344
12836
|
) : quoted;
|
|
12837
|
+
options.assertCurrent?.();
|
|
12345
12838
|
await this.ensureSwapChain(quote.src.chainId);
|
|
12839
|
+
options.assertCurrent?.();
|
|
12346
12840
|
const swap = await this.runSwap(quote, {
|
|
12347
12841
|
walletAddress: state.walletAddress,
|
|
12842
|
+
...options.assertCurrent ? { assertCurrent: options.assertCurrent } : {},
|
|
12348
12843
|
...options.slippage === void 0 ? {} : { slippage: options.slippage },
|
|
12349
12844
|
...options.onSwapProgress ? { onStage: options.onSwapProgress } : {}
|
|
12350
12845
|
});
|
|
12351
12846
|
options.onSwapProgress?.("depositing");
|
|
12352
12847
|
try {
|
|
12848
|
+
options.assertCurrent?.();
|
|
12353
12849
|
await this.ensureSwapChain(quote.dst.chainId);
|
|
12850
|
+
options.assertCurrent?.();
|
|
12354
12851
|
await this.walletSeesBalance(
|
|
12355
12852
|
quote.dst.chainId,
|
|
12356
12853
|
quote.dst.address,
|
|
12357
12854
|
BigInt(swap.received)
|
|
12358
12855
|
);
|
|
12856
|
+
options.assertCurrent?.();
|
|
12359
12857
|
const amount = await this.settleSwapDepositAmount(
|
|
12360
12858
|
quote.dst.chainId,
|
|
12361
12859
|
quote.dst.address,
|
|
12362
12860
|
options.to.symbol,
|
|
12363
|
-
BigInt(swap.received)
|
|
12861
|
+
BigInt(swap.received),
|
|
12862
|
+
options.assertCurrent
|
|
12364
12863
|
);
|
|
12864
|
+
options.assertCurrent?.();
|
|
12865
|
+
await options.onBeforeDeposit?.();
|
|
12866
|
+
options.assertCurrent?.();
|
|
12365
12867
|
const deposit = await this.deposit({
|
|
12366
12868
|
amount,
|
|
12367
12869
|
asset: options.to.symbol,
|
|
12368
|
-
...options.agentId ? { agentId: options.agentId } : {}
|
|
12870
|
+
...options.agentId ? { agentId: options.agentId } : {},
|
|
12871
|
+
...options.onDepositProgress ? { onProgress: options.onDepositProgress } : {},
|
|
12872
|
+
...options.onBeforeDepositSubmission ? { onBeforeSubmission: options.onBeforeDepositSubmission } : {},
|
|
12873
|
+
...options.assertCurrent ? { assertSubmissionCurrent: options.assertCurrent } : {}
|
|
12369
12874
|
});
|
|
12370
12875
|
return { swap, deposit, deposited: amount };
|
|
12371
12876
|
} catch (error) {
|
|
@@ -13682,7 +14187,7 @@ var OwneySDK = class {
|
|
|
13682
14187
|
* @param expectedChainId Chain captured by the deposit that requested approval.
|
|
13683
14188
|
* @returns the approval transaction hash.
|
|
13684
14189
|
*/
|
|
13685
|
-
async approvePermit2(asset = "WETH", requiredAmount = 0n, expectedChainId) {
|
|
14190
|
+
async approvePermit2(asset = "WETH", requiredAmount = 0n, expectedChainId, assertCurrent) {
|
|
13686
14191
|
const state = this.requireState();
|
|
13687
14192
|
const chainId = expectedChainId ?? this.requireChainId();
|
|
13688
14193
|
this.getEligibleAgents(chainId, asset, { excludeDisabled: true });
|
|
@@ -13703,9 +14208,13 @@ var OwneySDK = class {
|
|
|
13703
14208
|
const wallet = createWalletClient5({
|
|
13704
14209
|
account: state.walletAddress,
|
|
13705
14210
|
chain: VIEM_CHAIN2[chainId],
|
|
13706
|
-
transport: custom5(
|
|
14211
|
+
transport: custom5({ request: (args) => {
|
|
14212
|
+
assertCurrent?.();
|
|
14213
|
+
return provider.request(args);
|
|
14214
|
+
} })
|
|
13707
14215
|
});
|
|
13708
14216
|
await ensureWalletOnChain(walletChainClient, wallet, chainId);
|
|
14217
|
+
assertCurrent?.();
|
|
13709
14218
|
const hash2 = await wallet.writeContract({
|
|
13710
14219
|
address: token,
|
|
13711
14220
|
abi: ERC20_ALLOWANCE_ABI,
|
|
@@ -13879,7 +14388,7 @@ var OwneySDK = class {
|
|
|
13879
14388
|
};
|
|
13880
14389
|
|
|
13881
14390
|
// src/agents/zyfai/zyfai.siwx.ts
|
|
13882
|
-
import { getAddress as
|
|
14391
|
+
import { getAddress as getAddress7 } from "viem";
|
|
13883
14392
|
import { SiweMessage as SiweMessage2 } from "siwe";
|
|
13884
14393
|
import { ZyfaiSDK as ZyfaiSDK2 } from "@zyfai/sdk";
|
|
13885
14394
|
|
|
@@ -14006,7 +14515,7 @@ function buildSIWXConfig(deps) {
|
|
|
14006
14515
|
issuedAt,
|
|
14007
14516
|
toString() {
|
|
14008
14517
|
return new SiweMessage2({
|
|
14009
|
-
address:
|
|
14518
|
+
address: getAddress7(accountAddress),
|
|
14010
14519
|
chainId: numericChainId(chainId),
|
|
14011
14520
|
domain,
|
|
14012
14521
|
uri,
|