@owney/sdk 0.7.32-beta.1 → 0.7.32-beta.4
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-YBMCUL5D.js → chunk-CW55W2WO.js} +83 -5
- package/dist/index.cjs +2309 -397
- package/dist/index.d.cts +374 -6
- package/dist/index.d.ts +374 -6
- package/dist/index.js +2221 -391
- package/dist/{token-batch-family-EKOLMSTD.js → token-batch-family-TUGZWETV.js} +7 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -8,10 +8,13 @@ import {
|
|
|
8
8
|
PERMIT2_BATCH_ABI,
|
|
9
9
|
batchArgs,
|
|
10
10
|
batchTypedData,
|
|
11
|
+
discardHashlessTokenBatch,
|
|
12
|
+
dropExpiredHashlessTokenBatch,
|
|
11
13
|
erc2612TypedData,
|
|
12
14
|
finishTokenBatchFamily,
|
|
13
15
|
loadTokenBatchFamily,
|
|
14
16
|
saveTokenBatchAttempt,
|
|
17
|
+
savedTokenBatchBody,
|
|
15
18
|
supportsSponsoredErc2612,
|
|
16
19
|
tokenBatchClear,
|
|
17
20
|
tokenBatchKey,
|
|
@@ -19,7 +22,7 @@ import {
|
|
|
19
22
|
tokenBatchStoredBody,
|
|
20
23
|
tokenBatchWrite,
|
|
21
24
|
validateTokenBatchAttempt
|
|
22
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-CW55W2WO.js";
|
|
23
26
|
|
|
24
27
|
// src/lib/funding-evidence.ts
|
|
25
28
|
import { decodeEventLog, decodeFunctionData, erc20Abi, parseAbi } from "viem";
|
|
@@ -356,25 +359,41 @@ async function observeTokenBatchFamily(input) {
|
|
|
356
359
|
}
|
|
357
360
|
|
|
358
361
|
// src/lib/swap/swap.observation.ts
|
|
359
|
-
var
|
|
360
|
-
var
|
|
361
|
-
|
|
362
|
+
var claims = /* @__PURE__ */ new Map();
|
|
363
|
+
var watches = /* @__PURE__ */ new Map();
|
|
364
|
+
var isOrderObserved = (key2) => claims.has(key2) || watches.has(key2);
|
|
365
|
+
function claimOrder(key2) {
|
|
366
|
+
claims.set(key2, (claims.get(key2) ?? 0) + 1);
|
|
367
|
+
let released = false;
|
|
368
|
+
return () => {
|
|
369
|
+
if (released) return;
|
|
370
|
+
released = true;
|
|
371
|
+
const left = (claims.get(key2) ?? 1) - 1;
|
|
372
|
+
if (left > 0) claims.set(key2, left);
|
|
373
|
+
else claims.delete(key2);
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
async function observeOrder(key2, run, options = {}) {
|
|
362
377
|
const busy = () => new OwneyError(
|
|
363
378
|
"SWAP_REQUEST_FAILED",
|
|
364
379
|
"This order is already being observed.",
|
|
365
380
|
{ orderHash: key2, watchActive: true, settlementUnconfirmed: true }
|
|
366
381
|
);
|
|
367
|
-
if (
|
|
368
|
-
|
|
382
|
+
if (options.wait) {
|
|
383
|
+
for (let other = watches.get(key2); other; other = watches.get(key2))
|
|
384
|
+
await other.catch(() => void 0);
|
|
385
|
+
} else if (watches.has(key2)) throw busy();
|
|
386
|
+
const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
|
|
387
|
+
const name = `owney.swap.observe:${key2}`;
|
|
388
|
+
const watch = !locks ? run() : options.wait ? locks.request(name, () => run()) : locks.request(name, { ifAvailable: true }, (lock) => {
|
|
389
|
+
if (!lock) throw busy();
|
|
390
|
+
return run();
|
|
391
|
+
});
|
|
392
|
+
watches.set(key2, watch);
|
|
369
393
|
try {
|
|
370
|
-
|
|
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();
|
|
394
|
+
return await watch;
|
|
376
395
|
} finally {
|
|
377
|
-
|
|
396
|
+
if (watches.get(key2) === watch) watches.delete(key2);
|
|
378
397
|
}
|
|
379
398
|
}
|
|
380
399
|
|
|
@@ -529,7 +548,7 @@ function parseLeg(item) {
|
|
|
529
548
|
const r = item;
|
|
530
549
|
if (typeof r.legKey !== "string" || !r.legKey) return null;
|
|
531
550
|
if (typeof r.status !== "string" || !r.status) return null;
|
|
532
|
-
|
|
551
|
+
const leg = {
|
|
533
552
|
legKey: r.legKey,
|
|
534
553
|
agentType: strOrNull(r.agentType),
|
|
535
554
|
status: r.status,
|
|
@@ -539,8 +558,11 @@ function parseLeg(item) {
|
|
|
539
558
|
confirmedAt: strOrNull(r.confirmedAt),
|
|
540
559
|
agentWallet: strOrNull(r.agentWallet),
|
|
541
560
|
providerIndexedAt: strOrNull(r.providerIndexedAt),
|
|
542
|
-
providerBookedAt: strOrNull(r.providerBookedAt)
|
|
561
|
+
providerBookedAt: strOrNull(r.providerBookedAt),
|
|
562
|
+
providerRef: strOrNull(r.providerRef)
|
|
543
563
|
};
|
|
564
|
+
if (r.closed === true) leg.closed = true;
|
|
565
|
+
return leg;
|
|
544
566
|
}
|
|
545
567
|
function parseRow(item) {
|
|
546
568
|
if (!item || typeof item !== "object") return null;
|
|
@@ -960,7 +982,7 @@ var SnapshotHistory = class {
|
|
|
960
982
|
};
|
|
961
983
|
|
|
962
984
|
// src/lib/funding-ledger.ts
|
|
963
|
-
var SDK_VERSION = "0.7.32-beta.
|
|
985
|
+
var SDK_VERSION = "0.7.32-beta.4";
|
|
964
986
|
var SCOPE2 = "funding-ledger";
|
|
965
987
|
var REQUEST_TIMEOUT_MS2 = 5e3;
|
|
966
988
|
var RETRY_DELAYS_MS = [1e3, 4e3, 16e3];
|
|
@@ -1210,13 +1232,213 @@ function buildReporter(config) {
|
|
|
1210
1232
|
} catch (err) {
|
|
1211
1233
|
debugLog(SCOPE2, `event failed: ${describeError2(err)}`);
|
|
1212
1234
|
}
|
|
1235
|
+
},
|
|
1236
|
+
settle(timeoutMs) {
|
|
1237
|
+
return new Promise((resolve) => {
|
|
1238
|
+
let deadline;
|
|
1239
|
+
const done = () => {
|
|
1240
|
+
if (deadline !== void 0) clearTimeout(deadline);
|
|
1241
|
+
resolve();
|
|
1242
|
+
};
|
|
1243
|
+
try {
|
|
1244
|
+
flush();
|
|
1245
|
+
deadline = setTimeout(done, Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 0);
|
|
1246
|
+
unrefTimer(deadline);
|
|
1247
|
+
chain2.then(done, done);
|
|
1248
|
+
} catch (err) {
|
|
1249
|
+
debugLog(SCOPE2, `settle failed: ${describeError2(err)}`);
|
|
1250
|
+
done();
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1213
1253
|
}
|
|
1214
1254
|
};
|
|
1215
1255
|
}
|
|
1216
1256
|
}
|
|
1217
1257
|
|
|
1258
|
+
// src/lib/zyfai-server-tasks.ts
|
|
1259
|
+
var SCOPE3 = "zyfai-server-tasks";
|
|
1260
|
+
var ZYFAI_SERVER_TASKS = 1;
|
|
1261
|
+
var ZYFAI_SERVER_TASKS_OFF_CODE = "ZYFAI_SERVER_TASKS_OFF";
|
|
1262
|
+
function zyfaiServerTasksEnabled(option) {
|
|
1263
|
+
if (typeof option !== "function") return option === true;
|
|
1264
|
+
try {
|
|
1265
|
+
return option() === true;
|
|
1266
|
+
} catch {
|
|
1267
|
+
return false;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
var ZYFAI_HANDOFF_FEATURE_OFF_CODES = /* @__PURE__ */ new Set([
|
|
1271
|
+
"FUNDING_TASK_KIND_DISABLED",
|
|
1272
|
+
"ZYFAI_NOT_CONFIGURED",
|
|
1273
|
+
"PROBE_ONLY",
|
|
1274
|
+
"HTTP_404",
|
|
1275
|
+
"HTTP_401",
|
|
1276
|
+
"HTTP_403",
|
|
1277
|
+
ZYFAI_SERVER_TASKS_OFF_CODE
|
|
1278
|
+
]);
|
|
1279
|
+
var ZYFAI_HANDOFF_NO_TASK_CODES = /* @__PURE__ */ new Set([
|
|
1280
|
+
...ZYFAI_HANDOFF_FEATURE_OFF_CODES,
|
|
1281
|
+
"ZYFAI_SESSION_MISMATCH",
|
|
1282
|
+
"NEEDS_USER_SESSION"
|
|
1283
|
+
]);
|
|
1284
|
+
var UNCHECKED_NO_TASK_CODES = /* @__PURE__ */ new Set([
|
|
1285
|
+
"HTTP_401",
|
|
1286
|
+
"HTTP_403",
|
|
1287
|
+
"HTTP_404",
|
|
1288
|
+
ZYFAI_SERVER_TASKS_OFF_CODE
|
|
1289
|
+
]);
|
|
1290
|
+
function provesNoServerTask(code, afterLostAnswer) {
|
|
1291
|
+
return ZYFAI_HANDOFF_NO_TASK_CODES.has(code) && !(afterLostAnswer && UNCHECKED_NO_TASK_CODES.has(code));
|
|
1292
|
+
}
|
|
1293
|
+
var ZYFAI_WITHDRAWAL_NOT_SENT_CODES = /* @__PURE__ */ new Set([
|
|
1294
|
+
"PREREQUISITE_FAILED",
|
|
1295
|
+
"PREREQUISITE_TIMEOUT",
|
|
1296
|
+
"SUPERSEDED",
|
|
1297
|
+
"ZYFAI_SESSION_MISMATCH",
|
|
1298
|
+
"ZYFAI_REDEMPTION_IN_FLIGHT",
|
|
1299
|
+
"NEEDS_USER_SESSION",
|
|
1300
|
+
"NEEDS_USER_SESSION_KEY",
|
|
1301
|
+
"ZYFAI_FORBIDDEN",
|
|
1302
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
1303
|
+
"WITHDRAWAL_IN_PROGRESS",
|
|
1304
|
+
// The task's own agent:zyfai leg was already failed or confirmed (P4-8).
|
|
1305
|
+
"LEG_CLOSED",
|
|
1306
|
+
"MAX_ATTEMPTS",
|
|
1307
|
+
"SECRET_UNREADABLE"
|
|
1308
|
+
]);
|
|
1309
|
+
var ZYFAI_REGISTRATION_NOT_SENT_CODES = /* @__PURE__ */ new Set([
|
|
1310
|
+
"TRANSFER_NOT_SEEN",
|
|
1311
|
+
"DEPOSIT_NOT_SENT",
|
|
1312
|
+
"DEPOSIT_REVERTED",
|
|
1313
|
+
"TRANSFER_MISMATCH",
|
|
1314
|
+
"NEEDS_USER_SESSION",
|
|
1315
|
+
"ZYFAI_REGISTRATION_REJECTED",
|
|
1316
|
+
"MAX_ATTEMPTS",
|
|
1317
|
+
"SECRET_UNREADABLE"
|
|
1318
|
+
]);
|
|
1319
|
+
var ZYFAI_TASK_LIFETIME_MS = {
|
|
1320
|
+
zyfai_withdrawal: 72e5,
|
|
1321
|
+
zyfai_deposit_registration: 36e5
|
|
1322
|
+
};
|
|
1323
|
+
var SESSION_MARGIN_MS = 3e5;
|
|
1324
|
+
var SESSION_SLACK_MS = 6e4;
|
|
1325
|
+
function sessionExp(session) {
|
|
1326
|
+
try {
|
|
1327
|
+
const parts = session.split(".");
|
|
1328
|
+
if (parts.length !== 3) return void 0;
|
|
1329
|
+
const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
1330
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
1331
|
+
const decoded = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8");
|
|
1332
|
+
const claims2 = JSON.parse(decoded);
|
|
1333
|
+
const exp = isRecord(claims2) ? claims2.exp : void 0;
|
|
1334
|
+
return typeof exp === "number" && Number.isFinite(exp) ? exp : void 0;
|
|
1335
|
+
} catch {
|
|
1336
|
+
return void 0;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
function zyfaiSessionOutlivesTask(session, kind, nowMs = Date.now()) {
|
|
1340
|
+
const lifetimeMs = Object.prototype.hasOwnProperty.call(ZYFAI_TASK_LIFETIME_MS, kind) ? ZYFAI_TASK_LIFETIME_MS[kind] : void 0;
|
|
1341
|
+
if (typeof session !== "string" || lifetimeMs === void 0) return false;
|
|
1342
|
+
const exp = sessionExp(session);
|
|
1343
|
+
return exp !== void 0 && exp * 1e3 > nowMs + lifetimeMs + SESSION_MARGIN_MS + SESSION_SLACK_MS;
|
|
1344
|
+
}
|
|
1345
|
+
var DEFAULT_TIMEOUT_MS = 25e3;
|
|
1346
|
+
var DECLINED_STATUSES = /* @__PURE__ */ new Set([400, 401, 403, 404, 409, 429]);
|
|
1347
|
+
var ID = /^.{1,128}$/s;
|
|
1348
|
+
var TX_HASH = /^0x[0-9a-fA-F]{64}$/;
|
|
1349
|
+
var unknown = () => ({ type: "unknown" });
|
|
1350
|
+
var isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
1351
|
+
function requestBody(body, session) {
|
|
1352
|
+
const out = {
|
|
1353
|
+
kind: body.kind,
|
|
1354
|
+
operationId: body.operationId,
|
|
1355
|
+
owner: body.owner,
|
|
1356
|
+
chainId: body.chainId,
|
|
1357
|
+
asset: body.asset,
|
|
1358
|
+
amount: body.amount,
|
|
1359
|
+
agentWallet: body.agentWallet,
|
|
1360
|
+
zyfaiSession: session
|
|
1361
|
+
};
|
|
1362
|
+
if (body.after !== void 0) out.after = body.after;
|
|
1363
|
+
if (body.runNow !== void 0) out.runNow = body.runNow;
|
|
1364
|
+
return out;
|
|
1365
|
+
}
|
|
1366
|
+
function taskResult(value) {
|
|
1367
|
+
const result = {};
|
|
1368
|
+
if (!isRecord(value)) return result;
|
|
1369
|
+
if (typeof value.providerWithdrawalId === "string" && ID.test(value.providerWithdrawalId))
|
|
1370
|
+
result.providerWithdrawalId = value.providerWithdrawalId;
|
|
1371
|
+
if (typeof value.providerDepositId === "string" && ID.test(value.providerDepositId))
|
|
1372
|
+
result.providerDepositId = value.providerDepositId;
|
|
1373
|
+
if (typeof value.txHash === "string" && TX_HASH.test(value.txHash)) result.txHash = value.txHash;
|
|
1374
|
+
if (value.asyncPending === true) result.asyncPending = true;
|
|
1375
|
+
if (value.handedOff === "reconciler") result.handedOff = "reconciler";
|
|
1376
|
+
if (value.lifecycle === "failed") result.lifecycle = "failed";
|
|
1377
|
+
return result;
|
|
1378
|
+
}
|
|
1379
|
+
function answerOf(payload) {
|
|
1380
|
+
if (!isRecord(payload) || payload.success !== true || !isRecord(payload.data)) return unknown();
|
|
1381
|
+
const data = payload.data;
|
|
1382
|
+
if (data.accepted === false)
|
|
1383
|
+
return typeof data.code === "string" && data.code ? { type: "declined", code: data.code } : unknown();
|
|
1384
|
+
if (data.accepted !== true || typeof data.taskId !== "string" || !data.taskId || typeof data.state !== "string" || !data.state) return unknown();
|
|
1385
|
+
return {
|
|
1386
|
+
type: "accepted",
|
|
1387
|
+
taskId: data.taskId,
|
|
1388
|
+
state: data.state,
|
|
1389
|
+
...typeof data.code === "string" && data.code ? { code: data.code } : {},
|
|
1390
|
+
result: taskResult(data.result)
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
async function handOffZyfaiTask(cfg, body, session) {
|
|
1394
|
+
if (cfg?.zyfaiServerTasks !== true) return { type: "declined", code: ZYFAI_SERVER_TASKS_OFF_CODE };
|
|
1395
|
+
let timer;
|
|
1396
|
+
try {
|
|
1397
|
+
const controller = new AbortController();
|
|
1398
|
+
const timeoutMs = typeof cfg.timeoutMs === "number" && Number.isFinite(cfg.timeoutMs) && cfg.timeoutMs > 0 ? cfg.timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
1399
|
+
const url = `${getRoutingApiBaseUrl(cfg.baseUrl).replace(/\/$/, "")}/api/v1/funding/zyfai-tasks`;
|
|
1400
|
+
const timedOut = new Promise((resolve) => {
|
|
1401
|
+
timer = setTimeout(() => {
|
|
1402
|
+
controller.abort();
|
|
1403
|
+
resolve("timeout");
|
|
1404
|
+
}, timeoutMs);
|
|
1405
|
+
timer.unref?.();
|
|
1406
|
+
});
|
|
1407
|
+
const exchange = (async () => {
|
|
1408
|
+
const res = await fetch(url, {
|
|
1409
|
+
method: "POST",
|
|
1410
|
+
headers: { "content-type": "application/json", "x-owney-api-key": cfg.apiKey },
|
|
1411
|
+
body: JSON.stringify(requestBody(body, session)),
|
|
1412
|
+
signal: controller.signal,
|
|
1413
|
+
// The body carries the session: never follow a redirect to another host.
|
|
1414
|
+
redirect: "error"
|
|
1415
|
+
});
|
|
1416
|
+
if (DECLINED_STATUSES.has(res.status)) return { type: "declined", code: `HTTP_${res.status}` };
|
|
1417
|
+
if (res.status !== 200) {
|
|
1418
|
+
debugLog(SCOPE3, `handoff answered ${res.status}`);
|
|
1419
|
+
return unknown();
|
|
1420
|
+
}
|
|
1421
|
+
return answerOf(await res.json());
|
|
1422
|
+
})();
|
|
1423
|
+
exchange.catch(() => {
|
|
1424
|
+
});
|
|
1425
|
+
const answer = await Promise.race([exchange, timedOut]);
|
|
1426
|
+
if (answer === "timeout") {
|
|
1427
|
+
debugLog(SCOPE3, "handoff timed out");
|
|
1428
|
+
return unknown();
|
|
1429
|
+
}
|
|
1430
|
+
debugLog(SCOPE3, `handoff ${answer.type}`);
|
|
1431
|
+
return answer;
|
|
1432
|
+
} catch {
|
|
1433
|
+
debugLog(SCOPE3, "handoff failed");
|
|
1434
|
+
return unknown();
|
|
1435
|
+
} finally {
|
|
1436
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1218
1440
|
// src/lib/withdrawal-reservation.ts
|
|
1219
|
-
var
|
|
1441
|
+
var SCOPE4 = "withdrawal-reservation";
|
|
1220
1442
|
var CLAIM_TIMEOUT_MS = 4e3;
|
|
1221
1443
|
var HEARTBEAT_INTERVAL_MS = 6e4;
|
|
1222
1444
|
var HOLDER_TIMEOUT_MS = 5e3;
|
|
@@ -1314,7 +1536,7 @@ function compact2(obj) {
|
|
|
1314
1536
|
async function claimWithdrawalReservation(cfg, input) {
|
|
1315
1537
|
const result = await claimOnce(cfg, input);
|
|
1316
1538
|
if (result.status === "unavailable" && result.httpStatus === 403 && input.supersedes) {
|
|
1317
|
-
debugLog(
|
|
1539
|
+
debugLog(SCOPE4, "claim 403 with supersedes: retrying without it");
|
|
1318
1540
|
const { supersedes: _dropped, ...rest } = input;
|
|
1319
1541
|
const retried = await claimOnce(cfg, rest);
|
|
1320
1542
|
return retried.status === "claimed" ? { ...retried, supersedesDropped: true } : retried;
|
|
@@ -1341,7 +1563,7 @@ async function claimOnce(cfg, input) {
|
|
|
1341
1563
|
res = await post(cfg, endpoint(cfg), body, CLAIM_TIMEOUT_MS);
|
|
1342
1564
|
} catch (err) {
|
|
1343
1565
|
const reason = err instanceof RequestTimeout ? "timeout" : "network";
|
|
1344
|
-
debugLog(
|
|
1566
|
+
debugLog(SCOPE4, `claim ${reason}: ${describeError3(err)}`);
|
|
1345
1567
|
return { status: "unavailable", reason };
|
|
1346
1568
|
}
|
|
1347
1569
|
if (!res) return { status: "unavailable", reason: "network" };
|
|
@@ -1357,7 +1579,7 @@ async function claimOnce(cfg, input) {
|
|
|
1357
1579
|
}
|
|
1358
1580
|
}
|
|
1359
1581
|
if (!res.ok) {
|
|
1360
|
-
debugLog(
|
|
1582
|
+
debugLog(SCOPE4, `claim ${res.status}`);
|
|
1361
1583
|
return { status: "unavailable", reason: "http", httpStatus: res.status };
|
|
1362
1584
|
}
|
|
1363
1585
|
const data = unwrap2(res.body);
|
|
@@ -1392,12 +1614,12 @@ async function holderRequest(cfg, operationId, action, body, retryDelays = []) {
|
|
|
1392
1614
|
if (res.ok) return "ok";
|
|
1393
1615
|
if (res.status === 404) return "gone";
|
|
1394
1616
|
if (res.status < 500 && res.status !== 429) {
|
|
1395
|
-
debugLog(
|
|
1617
|
+
debugLog(SCOPE4, `${action} rejected with ${res.status}`);
|
|
1396
1618
|
return "rejected";
|
|
1397
1619
|
}
|
|
1398
|
-
debugLog(
|
|
1620
|
+
debugLog(SCOPE4, `${action} ${res.status}`);
|
|
1399
1621
|
} catch (err) {
|
|
1400
|
-
debugLog(
|
|
1622
|
+
debugLog(SCOPE4, `${action} failed: ${describeError3(err)}`);
|
|
1401
1623
|
}
|
|
1402
1624
|
if (attempt < retryDelays.length) {
|
|
1403
1625
|
await new Promise((resolve) => unrefTimer2(setTimeout(resolve, retryDelays[attempt])));
|
|
@@ -1570,6 +1792,8 @@ function ledgerLegFromEvidence(e, kind) {
|
|
|
1570
1792
|
if (e.callsId && e.callsId.length <= 200) leg.callsId = e.callsId;
|
|
1571
1793
|
if (isUint(e.confirmedAmount)) leg.confirmedAmountRaw = e.confirmedAmount;
|
|
1572
1794
|
if (e.blockNumber && /^\d{1,19}$/.test(e.blockNumber)) leg.blockNumber = e.blockNumber;
|
|
1795
|
+
const providerRef = e.withdrawalRequest?.providerWithdrawalId || e.providerDepositId;
|
|
1796
|
+
if (typeof providerRef === "string" && providerRef.length >= 1 && providerRef.length <= 128) leg.providerRef = providerRef;
|
|
1573
1797
|
const failure = e.withdrawalRequest?.failure;
|
|
1574
1798
|
if (failure) leg.errorCode = failure.toUpperCase().replace(/-/g, "_");
|
|
1575
1799
|
return leg;
|
|
@@ -1597,6 +1821,12 @@ function withdrawalLedgerRail(e) {
|
|
|
1597
1821
|
return e.userOpHash ? "yieldseeker_userop" : e.callsId ? "yieldseeker_wallet_calls" : e.txHash ? "yieldseeker_paid" : void 0;
|
|
1598
1822
|
return void 0;
|
|
1599
1823
|
}
|
|
1824
|
+
function depositLedgerRail(e) {
|
|
1825
|
+
if (e.callsId) return "wallet_calls";
|
|
1826
|
+
if (e.depositAuthorization || e.serverBroadcast) return "permit2_batch";
|
|
1827
|
+
return void 0;
|
|
1828
|
+
}
|
|
1829
|
+
var FUNDING_DEPOSIT_RECOVERY_REPORT = 1;
|
|
1600
1830
|
var SWAP_LEDGER_RAILS = {
|
|
1601
1831
|
classic: "swap_classic",
|
|
1602
1832
|
fusion: "swap_fusion",
|
|
@@ -1664,6 +1894,12 @@ var TRANSFER_EVENT = getAbiItem({
|
|
|
1664
1894
|
abi: parseAbi2(["event Transfer(address indexed from, address indexed to, uint256 value)"]),
|
|
1665
1895
|
name: "Transfer"
|
|
1666
1896
|
});
|
|
1897
|
+
var acceptedChunk = /* @__PURE__ */ new Map();
|
|
1898
|
+
var acceptedChunkByClient = /* @__PURE__ */ new WeakMap();
|
|
1899
|
+
function providerKey(client) {
|
|
1900
|
+
const { chain: chain2, transport } = client;
|
|
1901
|
+
return typeof chain2?.id === "number" ? `${chain2.id}:${typeof transport?.url === "string" ? transport.url : ""}` : void 0;
|
|
1902
|
+
}
|
|
1667
1903
|
function parseBlock(value) {
|
|
1668
1904
|
if (value === void 0 || value === null) return void 0;
|
|
1669
1905
|
return typeof value === "string" && /^\d{1,19}$/.test(value) ? BigInt(value) : null;
|
|
@@ -1688,11 +1924,12 @@ async function scanWithdrawalPayouts(client, input) {
|
|
|
1688
1924
|
const candidates = [];
|
|
1689
1925
|
if (start > end) {
|
|
1690
1926
|
const kept = input.cursor ?? (start > 0n ? start - 1n : 0n);
|
|
1691
|
-
return { candidates, scannedTo: kept.toString() };
|
|
1927
|
+
return { candidates, scannedTo: kept.toString(), caughtUp: true };
|
|
1692
1928
|
}
|
|
1693
1929
|
const tokenAddress = input.token.toLowerCase();
|
|
1694
1930
|
const owner = input.owner.toLowerCase();
|
|
1695
|
-
|
|
1931
|
+
const key2 = providerKey(client);
|
|
1932
|
+
let size = (key2 !== void 0 ? acceptedChunk.get(key2) : acceptedChunkByClient.get(client)) ?? PAYOUT_SCAN_CHUNK_BLOCKS;
|
|
1696
1933
|
let from = start;
|
|
1697
1934
|
let scannedTo = start - 1n;
|
|
1698
1935
|
for (let requests = 0; from <= end && requests < MAX_CHUNKS_PER_SCAN; ) {
|
|
@@ -1707,12 +1944,21 @@ async function scanWithdrawalPayouts(client, input) {
|
|
|
1707
1944
|
toBlock: to
|
|
1708
1945
|
});
|
|
1709
1946
|
} catch (error) {
|
|
1710
|
-
if (
|
|
1711
|
-
|
|
1712
|
-
|
|
1947
|
+
if (!isRateLimited(error) && isRangeError(error)) {
|
|
1948
|
+
const requested = to - from + 1n;
|
|
1949
|
+
const allowed = allowedRange(error);
|
|
1950
|
+
const next = allowed !== void 0 && allowed < requested ? allowed : requested / 2n;
|
|
1951
|
+
if (next >= 1n && next < requested) {
|
|
1952
|
+
size = next;
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1713
1955
|
}
|
|
1714
1956
|
throw error;
|
|
1715
1957
|
}
|
|
1958
|
+
if (size < PAYOUT_SCAN_CHUNK_BLOCKS) {
|
|
1959
|
+
if (key2 !== void 0) acceptedChunk.set(key2, size);
|
|
1960
|
+
else acceptedChunkByClient.set(client, size);
|
|
1961
|
+
}
|
|
1716
1962
|
check();
|
|
1717
1963
|
requests++;
|
|
1718
1964
|
for (const log of logs) {
|
|
@@ -1730,7 +1976,8 @@ async function scanWithdrawalPayouts(client, input) {
|
|
|
1730
1976
|
scannedTo = to;
|
|
1731
1977
|
from = to + 1n;
|
|
1732
1978
|
}
|
|
1733
|
-
|
|
1979
|
+
const stalled = input.cursor !== void 0 && scannedTo <= input.cursor;
|
|
1980
|
+
return { candidates, scannedTo: (scannedTo < 0n ? 0n : scannedTo).toString(), caughtUp: from > end || stalled };
|
|
1734
1981
|
}
|
|
1735
1982
|
async function estimateBlockAt(client, latest, targetMs) {
|
|
1736
1983
|
const head = await client.getBlock({ blockNumber: latest });
|
|
@@ -1755,6 +2002,7 @@ function causeChain(error) {
|
|
|
1755
2002
|
chain2.push({
|
|
1756
2003
|
code: value.code,
|
|
1757
2004
|
status: value.status,
|
|
2005
|
+
data: value.data,
|
|
1758
2006
|
text: [value.message, value.details, value.shortMessage].filter((part) => typeof part === "string").join(" ")
|
|
1759
2007
|
});
|
|
1760
2008
|
current = value.cause;
|
|
@@ -1764,6 +2012,14 @@ function causeChain(error) {
|
|
|
1764
2012
|
function isRateLimited(error) {
|
|
1765
2013
|
return causeChain(error).some((entry) => entry.status === 429 || entry.code === 429 || /rate|too many requests/i.test(entry.text));
|
|
1766
2014
|
}
|
|
2015
|
+
function allowedRange(error) {
|
|
2016
|
+
for (const entry of causeChain(error)) {
|
|
2017
|
+
const hinted = entry.data && typeof entry.data === "object" ? entry.data.maxBlockRange : void 0;
|
|
2018
|
+
const value = typeof hinted === "number" ? hinted : Number(/"maxBlockRange"\s*:\s*(\d+)/.exec(entry.text)?.[1]);
|
|
2019
|
+
if (Number.isSafeInteger(value) && value >= 1) return BigInt(value);
|
|
2020
|
+
}
|
|
2021
|
+
return void 0;
|
|
2022
|
+
}
|
|
1767
2023
|
function isRangeError(error) {
|
|
1768
2024
|
return causeChain(error).some((entry) => entry.code === -32005 || /block range|range (is )?too (large|wide|big)|range limit|exceed(s|ed)? .*(range|limit|results)|too many (results|logs|blocks)|response size|query returned more than|max(imum)? .*blocks/i.test(entry.text));
|
|
1769
2025
|
}
|
|
@@ -1869,6 +2125,7 @@ async function getSponsorRelayerAddress(input) {
|
|
|
1869
2125
|
return parsed.data.relayer;
|
|
1870
2126
|
}
|
|
1871
2127
|
async function postSponsorBatchTransfer(input) {
|
|
2128
|
+
const flaggedPrepare = input.body.serverBroadcast === true && !input.body.serializedTransaction;
|
|
1872
2129
|
let res;
|
|
1873
2130
|
try {
|
|
1874
2131
|
res = await fetch(
|
|
@@ -1887,7 +2144,7 @@ async function postSponsorBatchTransfer(input) {
|
|
|
1887
2144
|
throw new OwneyError(
|
|
1888
2145
|
"SPONSOR_REQUEST_FAILED",
|
|
1889
2146
|
"Deposit status is unknown. Retry the same amount to check it.",
|
|
1890
|
-
{ safeToFallback: false }
|
|
2147
|
+
{ safeToFallback: false, ...flaggedPrepare ? { outcomeUnknown: true } : {} }
|
|
1891
2148
|
);
|
|
1892
2149
|
}
|
|
1893
2150
|
const parsed = await res.json().catch(() => null);
|
|
@@ -1898,7 +2155,13 @@ async function postSponsorBatchTransfer(input) {
|
|
|
1898
2155
|
{
|
|
1899
2156
|
statusCode: res.status,
|
|
1900
2157
|
safeToFallback: false,
|
|
1901
|
-
notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true
|
|
2158
|
+
notSubmitted: parsed?.notSubmitted === true || parsed?.error?.notSubmitted === true || parsed?.error?.details?.notSubmitted === true,
|
|
2159
|
+
// Another authorization of this operation is being sent by the
|
|
2160
|
+
// server (409, nothing signed).
|
|
2161
|
+
...parsed?.code === "DEPOSIT_ALREADY_SENDING" ? { code: "DEPOSIT_ALREADY_SENDING" } : {},
|
|
2162
|
+
// No definitive answer (5xx, an unreadable or malformed body): the
|
|
2163
|
+
// server may have accepted it. A 4xx with a body is a refusal.
|
|
2164
|
+
...flaggedPrepare && (res.status >= 500 || parsed === null || res.ok) ? { outcomeUnknown: true } : {}
|
|
1902
2165
|
}
|
|
1903
2166
|
);
|
|
1904
2167
|
}
|
|
@@ -1915,19 +2178,19 @@ async function postSponsorBatchRecovery(input) {
|
|
|
1915
2178
|
input.signal?.throwIfAborted();
|
|
1916
2179
|
if (!response.ok || payload?.success !== true) throw failure();
|
|
1917
2180
|
const value = payload.data;
|
|
1918
|
-
const { isAddress:
|
|
1919
|
-
const { validateTokenBatchAttempt: validateTokenBatchAttempt2 } = await import("./token-batch-family-
|
|
2181
|
+
const { isAddress: isAddress6, keccak256: keccak2564, parseTransaction: parseTransaction3, recoverTransactionAddress } = await import("viem");
|
|
2182
|
+
const { validateTokenBatchAttempt: validateTokenBatchAttempt2 } = await import("./token-batch-family-TUGZWETV.js");
|
|
1920
2183
|
const phase = input.body.phase;
|
|
1921
2184
|
const original = phase === "approval" ? input.body.serializedPermitTransaction : input.body.serializedTransaction;
|
|
1922
2185
|
if (!original) throw failure();
|
|
1923
2186
|
const hash2 = keccak2564(original), relayer = await recoverTransactionAddress({ serializedTransaction: original });
|
|
1924
|
-
const
|
|
1925
|
-
if (!value || value.protocol !== 1 || value.phase !== phase || !
|
|
2187
|
+
const validHash2 = (v) => typeof v === "string" && /^0x[\da-f]{64}$/i.test(v);
|
|
2188
|
+
if (!value || value.protocol !== 1 || value.phase !== phase || !validHash2(value.txHash) || !isAddress6(value.relayer) || value.relayer.toLowerCase() !== relayer.toLowerCase()) throw failure();
|
|
1926
2189
|
if (value.status !== "prepared" && value.txHash.toLowerCase() !== hash2.toLowerCase()) throw failure();
|
|
1927
2190
|
if (value.status === "unresolved") {
|
|
1928
2191
|
if (!["nonce-not-consumed", "authorization-used", "authorization-expired", "state-unavailable", "preparation-rejected", "relayer-changed"].includes(value.reason)) throw failure();
|
|
1929
2192
|
} else {
|
|
1930
|
-
if (!("blockNumber" in value) || typeof value.blockNumber !== "string" || !/^\d{1,78}$/.test(value.blockNumber) || BigInt(value.blockNumber) >= 1n << 256n || !
|
|
2193
|
+
if (!("blockNumber" in value) || typeof value.blockNumber !== "string" || !/^\d{1,78}$/.test(value.blockNumber) || BigInt(value.blockNumber) >= 1n << 256n || !validHash2(value.blockHash)) throw failure();
|
|
1931
2194
|
if (value.status === "prepared") {
|
|
1932
2195
|
if (value.predecessorHash?.toLowerCase() !== hash2.toLowerCase()) throw failure();
|
|
1933
2196
|
const tx = parseTransaction3(value.serializedTransaction), old = parseTransaction3(original);
|
|
@@ -2225,10 +2488,19 @@ var AgentReads = class {
|
|
|
2225
2488
|
}
|
|
2226
2489
|
};
|
|
2227
2490
|
|
|
2491
|
+
// src/agents/zyfai/zyfai.session-errors.ts
|
|
2492
|
+
var SESSION_REJECTED = /(?:^|: )Unauthorized: (?:(?:Invalid or expired user session|Missing user session)\b.*|Invalid API key)$/;
|
|
2493
|
+
function isZyfaiSessionRejected(message) {
|
|
2494
|
+
return SESSION_REJECTED.test(message);
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2228
2497
|
// src/agents/zyfai/zyfai.withdraw-submission.ts
|
|
2229
2498
|
var BELOW_MINIMUM = /^Idle balance (\d+(?:\.\d+)?) (USDC|WETH|ETH|EURC) is below the minimum withdrawal of (\d+(?:\.\d+)?) \2\.?$/;
|
|
2230
2499
|
var listenersByClient = /* @__PURE__ */ new WeakMap();
|
|
2231
|
-
|
|
2500
|
+
var ZYFAI_WITHDRAW_DISPATCH_CHECKPOINT = 1;
|
|
2501
|
+
var MAX_WITHDRAWAL_ID_LENGTH = 128;
|
|
2502
|
+
var WITHDRAWAL_PATHS = { get: "/users/withdraw/v2", post: "/users/partial-withdraw/v2" };
|
|
2503
|
+
function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent, beforeDispatch) {
|
|
2232
2504
|
const client = sdk.httpClient;
|
|
2233
2505
|
if (!client || typeof client.get !== "function" || typeof client.post !== "function")
|
|
2234
2506
|
return {
|
|
@@ -2248,7 +2520,7 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
2248
2520
|
const original = client[method];
|
|
2249
2521
|
client[method] = async function(...args) {
|
|
2250
2522
|
const [url, options] = args;
|
|
2251
|
-
const withdrawal =
|
|
2523
|
+
const withdrawal = url === WITHDRAWAL_PATHS[method];
|
|
2252
2524
|
const payload = method === "get" ? options?.params : options;
|
|
2253
2525
|
const matching = withdrawal ? [...observers].filter((observer) => observer.chainId === payload?.chainId && observer.token === payload?.tokenSymbol) : [];
|
|
2254
2526
|
for (const observer of matching) observer.assertCurrent?.();
|
|
@@ -2256,8 +2528,12 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
2256
2528
|
const request3 = {
|
|
2257
2529
|
attempted: true,
|
|
2258
2530
|
method: method === "get" ? "GET" : "POST",
|
|
2259
|
-
path: method
|
|
2531
|
+
path: WITHDRAWAL_PATHS[method]
|
|
2260
2532
|
};
|
|
2533
|
+
if (withdrawal) {
|
|
2534
|
+
for (const observer of matching) await observer.beforeDispatch?.({ ...request3 });
|
|
2535
|
+
for (const observer of matching) observer.assertCurrent?.();
|
|
2536
|
+
}
|
|
2261
2537
|
let belowMinimum2;
|
|
2262
2538
|
const notify2 = (outcome) => {
|
|
2263
2539
|
if (outcome === "rejected" && matching.length !== 1) return;
|
|
@@ -2291,7 +2567,13 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
2291
2567
|
] };
|
|
2292
2568
|
}
|
|
2293
2569
|
try {
|
|
2294
|
-
|
|
2570
|
+
const result = await original.apply(this, callArgs);
|
|
2571
|
+
if (withdrawal) {
|
|
2572
|
+
const id = result?.id;
|
|
2573
|
+
if (typeof id === "string" && id.length >= 1 && id.length <= MAX_WITHDRAWAL_ID_LENGTH)
|
|
2574
|
+
request3.providerWithdrawalId = id;
|
|
2575
|
+
}
|
|
2576
|
+
return result;
|
|
2295
2577
|
} catch (error) {
|
|
2296
2578
|
const message = error instanceof Error ? error.message : "";
|
|
2297
2579
|
const minimum = BELOW_MINIMUM.exec(message);
|
|
@@ -2299,7 +2581,7 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
2299
2581
|
const status = error?.response?.status;
|
|
2300
2582
|
if (request3.httpStatus === void 0 && typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599)
|
|
2301
2583
|
request3.httpStatus = status;
|
|
2302
|
-
request3.failure = message ===
|
|
2584
|
+
request3.failure = isZyfaiSessionRejected(message) && (request3.httpStatus === void 0 || request3.httpStatus === 401) ? "unauthorized" : message === "Forbidden: Access denied" ? "forbidden" : message === "Rate limit exceeded. Please try again later." ? "rate-limited" : request3.httpStatus === 400 && message === "No balance to withdraw" ? "insufficient-balance" : minimum ? "insufficient-balance" : /^User doesn't have any active session key\.?$/.test(message) ? "session-key-missing" : method === "post" && request3.httpStatus === 400 && message === "Partial withdrawal already in progress" ? "withdrawal-in-progress" : message === "Network error: Unable to reach the server" ? "network" : "other";
|
|
2303
2585
|
if (["unauthorized", "forbidden", "rate-limited", "insufficient-balance", "session-key-missing", "withdrawal-in-progress"].includes(request3.failure) && (request3.httpStatus === void 0 || request3.httpStatus >= 400 && request3.httpStatus < 500)) notify2("rejected");
|
|
2304
2586
|
throw error;
|
|
2305
2587
|
} finally {
|
|
@@ -2313,7 +2595,7 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
2313
2595
|
let rejected = false;
|
|
2314
2596
|
let request2 = { attempted: false };
|
|
2315
2597
|
let belowMinimum;
|
|
2316
|
-
const listener = { chainId, token, assertCurrent, notify: (outcome, evidence, detail) => {
|
|
2598
|
+
const listener = { chainId, token, assertCurrent, beforeDispatch, notify: (outcome, evidence, detail) => {
|
|
2317
2599
|
request2 = evidence;
|
|
2318
2600
|
if (outcome === "attempted") submitted = true;
|
|
2319
2601
|
else if (outcome === "rejected") {
|
|
@@ -2454,7 +2736,7 @@ async function submitZyfaiDeposit({ sdk, state, chain: chain2, amount, asset, sm
|
|
|
2454
2736
|
if (!token || !isAddress(owner) || !isAddress(smartWallet) || !/^\d+$/.test(amount) || BigInt(amount) <= 0n)
|
|
2455
2737
|
throw new OwneyError("AGENT_INVALID_RESPONSE", "Invalid Zyfai deposit transfer.", { chainId: chain2.id, asset }, "zyfai");
|
|
2456
2738
|
assertCurrent();
|
|
2457
|
-
const prepared = await sdk.
|
|
2739
|
+
const prepared = await sdk.prepareDeposit({ userAddress: owner, chainId: chain2.id, amount, asset });
|
|
2458
2740
|
assertCurrent();
|
|
2459
2741
|
const expectedData = encodeFunctionData({ abi: erc20Abi2, functionName: "transfer", args: [smartWallet, BigInt(amount)] });
|
|
2460
2742
|
const transfer = prepared?.transfer;
|
|
@@ -2536,6 +2818,13 @@ function tokenDecimals(symbol2, explicit) {
|
|
|
2536
2818
|
return explicit;
|
|
2537
2819
|
return DECIMALS_BY_SYMBOL[(symbol2 ?? "").toLowerCase()] ?? 6;
|
|
2538
2820
|
}
|
|
2821
|
+
function decodeDeltaAmount(raw2, decimals) {
|
|
2822
|
+
if (typeof raw2 !== "string") return void 0;
|
|
2823
|
+
if (/^0x[0-9a-f]+$/i.test(raw2)) return String(hexToDecimal(raw2, decimals));
|
|
2824
|
+
if (/^\d+$/.test(raw2))
|
|
2825
|
+
return String(hexToDecimal(BigInt(raw2).toString(16), decimals));
|
|
2826
|
+
return void 0;
|
|
2827
|
+
}
|
|
2539
2828
|
function mapWithdraw(raw2) {
|
|
2540
2829
|
return {
|
|
2541
2830
|
txHash: raw2.txHash,
|
|
@@ -2585,6 +2874,40 @@ function mapPendingAllocations(raw2) {
|
|
|
2585
2874
|
}
|
|
2586
2875
|
return pending.length > 0 ? pending : void 0;
|
|
2587
2876
|
}
|
|
2877
|
+
function mapPendingWithdrawals(raw2) {
|
|
2878
|
+
if (!Array.isArray(raw2)) return void 0;
|
|
2879
|
+
const pending = [];
|
|
2880
|
+
for (const entry of raw2) {
|
|
2881
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
2882
|
+
if (typeof entry.id !== "string" || entry.id === "") continue;
|
|
2883
|
+
if (entry.status !== "REQUESTED" && entry.status !== "CLAIMABLE") continue;
|
|
2884
|
+
if (typeof entry.chainId !== "number" || !Number.isFinite(entry.chainId)) continue;
|
|
2885
|
+
const asset = entry.token?.symbol;
|
|
2886
|
+
if (typeof asset !== "string" || asset === "" || typeof entry.amount !== "string") continue;
|
|
2887
|
+
let amountRaw;
|
|
2888
|
+
try {
|
|
2889
|
+
const amount = BigInt(/^0x/i.test(entry.amount) ? entry.amount : `0x${entry.amount}`);
|
|
2890
|
+
if (amount <= 0n) continue;
|
|
2891
|
+
amountRaw = amount.toString();
|
|
2892
|
+
} catch {
|
|
2893
|
+
continue;
|
|
2894
|
+
}
|
|
2895
|
+
pending.push({ id: entry.id, status: entry.status, chainId: entry.chainId, asset, amountRaw });
|
|
2896
|
+
}
|
|
2897
|
+
return pending.length > 0 ? pending : void 0;
|
|
2898
|
+
}
|
|
2899
|
+
function mapRecentAsyncWithdrawals(raw2) {
|
|
2900
|
+
if (!Array.isArray(raw2)) return void 0;
|
|
2901
|
+
const recent = [];
|
|
2902
|
+
for (const entry of raw2) {
|
|
2903
|
+
if (typeof entry !== "object" || entry === null) return void 0;
|
|
2904
|
+
if (entry.status === "FAILED") continue;
|
|
2905
|
+
const asset = entry.token?.symbol;
|
|
2906
|
+
if (typeof entry.id !== "string" || entry.id === "" || typeof entry.status !== "string" || entry.status === "" || typeof entry.chainId !== "number" || !Number.isFinite(entry.chainId) || typeof asset !== "string" || asset === "") return void 0;
|
|
2907
|
+
recent.push({ id: entry.id, status: entry.status, chainId: entry.chainId, asset });
|
|
2908
|
+
}
|
|
2909
|
+
return recent;
|
|
2910
|
+
}
|
|
2588
2911
|
function mapBalances(raw2, _chainId, smartWallet) {
|
|
2589
2912
|
const portfolio = raw2.portfolio;
|
|
2590
2913
|
if (!portfolio?.portfolioByChain || typeof portfolio.portfolioByChain !== "object")
|
|
@@ -2643,6 +2966,9 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
2643
2966
|
const pendingAllocations = mapPendingAllocations(
|
|
2644
2967
|
portfolio.staleBalances
|
|
2645
2968
|
);
|
|
2969
|
+
const asyncWithdrawals = portfolio.pendingAsyncWithdrawals;
|
|
2970
|
+
const pendingWithdrawals = mapPendingWithdrawals(asyncWithdrawals);
|
|
2971
|
+
const recentAsyncWithdrawals = mapRecentAsyncWithdrawals(asyncWithdrawals);
|
|
2646
2972
|
return {
|
|
2647
2973
|
smartWallet,
|
|
2648
2974
|
totalBalance: String(totalBalance),
|
|
@@ -2658,7 +2984,49 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
2658
2984
|
blockNumber: provenance.blockNumber === void 0 ? void 0 : String(provenance.blockNumber),
|
|
2659
2985
|
positionsStatus: Array.isArray(portfolio.positions) ? "available" : "unavailable"
|
|
2660
2986
|
})),
|
|
2661
|
-
...pendingAllocations ? { pendingAllocations } : {}
|
|
2987
|
+
...pendingAllocations ? { pendingAllocations } : {},
|
|
2988
|
+
...pendingWithdrawals ? { pendingWithdrawals } : {},
|
|
2989
|
+
...recentAsyncWithdrawals ? { recentAsyncWithdrawals } : {}
|
|
2990
|
+
};
|
|
2991
|
+
}
|
|
2992
|
+
var TX_HASH2 = /^0x[0-9a-fA-F]{64}$/;
|
|
2993
|
+
var validHash = (value) => typeof value === "string" && TX_HASH2.test(value) ? value : void 0;
|
|
2994
|
+
var validDate = (value) => typeof value === "string" && Number.isFinite(Date.parse(value)) ? value : void 0;
|
|
2995
|
+
function mapWithdrawalLifecycle(raw2) {
|
|
2996
|
+
const body = raw2;
|
|
2997
|
+
if (!body || typeof body !== "object" || typeof body.status !== "string" || typeof body.syncSettled !== "boolean")
|
|
2998
|
+
return null;
|
|
2999
|
+
const syncTxHash = validHash(body.syncTxHash);
|
|
3000
|
+
const estimatedClaimAt = validDate(body.estimatedClaimAt);
|
|
3001
|
+
const asyncLegs = [];
|
|
3002
|
+
for (const leg of Array.isArray(body.asyncLegs) ? body.asyncLegs : []) {
|
|
3003
|
+
if (!leg || typeof leg !== "object" || typeof leg.id !== "string" || leg.id === "" || typeof leg.status !== "string") continue;
|
|
3004
|
+
const legClaimAt = validDate(leg.estimatedClaimAt);
|
|
3005
|
+
const claimTxHash = validHash(leg.claimTxHash);
|
|
3006
|
+
asyncLegs.push({
|
|
3007
|
+
id: leg.id,
|
|
3008
|
+
status: leg.status,
|
|
3009
|
+
...legClaimAt ? { estimatedClaimAt: legClaimAt } : {},
|
|
3010
|
+
...claimTxHash ? { claimTxHash } : {}
|
|
3011
|
+
});
|
|
3012
|
+
}
|
|
3013
|
+
const failedAsyncIds = Array.isArray(body.failedAsyncIds) ? body.failedAsyncIds.filter((id) => typeof id === "string" && id !== "") : [];
|
|
3014
|
+
return {
|
|
3015
|
+
status: body.status,
|
|
3016
|
+
syncSettled: body.syncSettled,
|
|
3017
|
+
...syncTxHash ? { syncTxHash } : {},
|
|
3018
|
+
asyncLegs,
|
|
3019
|
+
failedAsyncIds,
|
|
3020
|
+
...estimatedClaimAt ? { estimatedClaimAt } : {},
|
|
3021
|
+
hasError: typeof body.lastError === "string" && body.lastError !== ""
|
|
3022
|
+
};
|
|
3023
|
+
}
|
|
3024
|
+
function mapDepositPlacement(raw2) {
|
|
3025
|
+
const body = raw2;
|
|
3026
|
+
if (!body || typeof body !== "object" || typeof body.status !== "string") return null;
|
|
3027
|
+
return {
|
|
3028
|
+
status: body.status,
|
|
3029
|
+
...typeof body.positionOutcome === "string" ? { positionOutcome: body.positionOutcome } : {}
|
|
2662
3030
|
};
|
|
2663
3031
|
}
|
|
2664
3032
|
function sumTokenValues(tokens) {
|
|
@@ -2822,12 +3190,24 @@ function mapEntries(rawEntries, chainId) {
|
|
|
2822
3190
|
const mappedAction = (entry.action ? actionMap[entry.action] : void 0) ?? entry.action;
|
|
2823
3191
|
const rawAmount2 = entry?.positions?.[0]?.amount;
|
|
2824
3192
|
const hasAmount = mappedAction !== "Withdraw" && typeof rawAmount2 === "string" && rawAmount2 !== "" && rawAmount2 !== "0x" && rawAmount2 !== "0x0" && rawAmount2 !== "0";
|
|
3193
|
+
const rawDelta = mappedAction === "Deposit" ? decodeDeltaAmount(
|
|
3194
|
+
entry?.positions?.[0]?.deltaAmount,
|
|
3195
|
+
tokenDecimals(entry?.positions?.[0]?.token_symbol)
|
|
3196
|
+
) : void 0;
|
|
2825
3197
|
const mappedPositions = positions.filter((p) => p.protocol_name).map((p) => ({
|
|
2826
3198
|
protocol: p.protocol_name,
|
|
2827
3199
|
pool: p.pool ?? void 0,
|
|
2828
3200
|
tokenSymbol: p.token_symbol ?? void 0,
|
|
2829
|
-
amount: mappedAction !== "Withdraw" && typeof p.amount === "string" && p.amount !== "" && p.amount !== "0x" ? String(hexToDecimal(p.amount, tokenDecimals(p.token_symbol))) : void 0
|
|
3201
|
+
amount: mappedAction !== "Withdraw" && typeof p.amount === "string" && p.amount !== "" && p.amount !== "0x" ? String(hexToDecimal(p.amount, tokenDecimals(p.token_symbol))) : void 0,
|
|
3202
|
+
deltaAmount: mappedAction !== "Withdraw" ? decodeDeltaAmount(p.deltaAmount, tokenDecimals(p.token_symbol)) : void 0
|
|
2830
3203
|
}));
|
|
3204
|
+
const reportedAmount = hasAmount ? String(
|
|
3205
|
+
hexToDecimal(
|
|
3206
|
+
rawAmount2,
|
|
3207
|
+
tokenDecimals(entry?.positions?.[0]?.token_symbol)
|
|
3208
|
+
)
|
|
3209
|
+
) : void 0;
|
|
3210
|
+
const transactionAmount = rawDelta === void 0 ? reportedAmount : Number(rawDelta) > 0 ? rawDelta : void 0;
|
|
2831
3211
|
return {
|
|
2832
3212
|
agent: "zyfai",
|
|
2833
3213
|
action: mappedAction,
|
|
@@ -2840,12 +3220,7 @@ function mapEntries(rawEntries, chainId) {
|
|
|
2840
3220
|
txHashes: entry.transactionHash ? [entry.transactionHash] : [],
|
|
2841
3221
|
chainId: entry.chainId,
|
|
2842
3222
|
tokenSymbol: entry?.positions?.[0]?.token_symbol,
|
|
2843
|
-
amount:
|
|
2844
|
-
hexToDecimal(
|
|
2845
|
-
rawAmount2,
|
|
2846
|
-
tokenDecimals(entry?.positions?.[0]?.token_symbol)
|
|
2847
|
-
)
|
|
2848
|
-
) : void 0
|
|
3223
|
+
amount: transactionAmount
|
|
2849
3224
|
}
|
|
2850
3225
|
],
|
|
2851
3226
|
rebalanceLog
|
|
@@ -3044,9 +3419,9 @@ var isJwtExpired = (token) => {
|
|
|
3044
3419
|
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
3045
3420
|
const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
|
|
3046
3421
|
const decoded = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8");
|
|
3047
|
-
const
|
|
3048
|
-
if (typeof
|
|
3049
|
-
return Date.now() / 1e3 >=
|
|
3422
|
+
const claims2 = JSON.parse(decoded);
|
|
3423
|
+
if (typeof claims2.exp !== "number") return false;
|
|
3424
|
+
return Date.now() / 1e3 >= claims2.exp - 30;
|
|
3050
3425
|
} catch {
|
|
3051
3426
|
return false;
|
|
3052
3427
|
}
|
|
@@ -3287,7 +3662,7 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
3287
3662
|
}
|
|
3288
3663
|
|
|
3289
3664
|
// src/lib/agent-accounts.ts
|
|
3290
|
-
var
|
|
3665
|
+
var SCOPE5 = "agent-accounts";
|
|
3291
3666
|
var REQUEST_TIMEOUT_MS3 = 5e3;
|
|
3292
3667
|
function compact3(obj) {
|
|
3293
3668
|
const out = {};
|
|
@@ -3357,7 +3732,7 @@ async function fetchAgentAccounts(input) {
|
|
|
3357
3732
|
signal
|
|
3358
3733
|
});
|
|
3359
3734
|
if (!res.ok) {
|
|
3360
|
-
debugLog(
|
|
3735
|
+
debugLog(SCOPE5, `fetch rejected with ${res.status}`);
|
|
3361
3736
|
return null;
|
|
3362
3737
|
}
|
|
3363
3738
|
const data = unwrap3(await res.json());
|
|
@@ -3372,7 +3747,7 @@ async function fetchAgentAccounts(input) {
|
|
|
3372
3747
|
cancel();
|
|
3373
3748
|
}
|
|
3374
3749
|
} catch (err) {
|
|
3375
|
-
debugLog(
|
|
3750
|
+
debugLog(SCOPE5, `fetch failed: ${describeError4(err)}`);
|
|
3376
3751
|
return null;
|
|
3377
3752
|
}
|
|
3378
3753
|
}
|
|
@@ -3403,7 +3778,7 @@ async function reportAgentAccount(input) {
|
|
|
3403
3778
|
signal
|
|
3404
3779
|
});
|
|
3405
3780
|
if (!res.ok) {
|
|
3406
|
-
debugLog(
|
|
3781
|
+
debugLog(SCOPE5, `report rejected with ${res.status}`);
|
|
3407
3782
|
return null;
|
|
3408
3783
|
}
|
|
3409
3784
|
const data = unwrap3(await res.json());
|
|
@@ -3415,7 +3790,7 @@ async function reportAgentAccount(input) {
|
|
|
3415
3790
|
cancel();
|
|
3416
3791
|
}
|
|
3417
3792
|
} catch (err) {
|
|
3418
|
-
debugLog(
|
|
3793
|
+
debugLog(SCOPE5, `report failed: ${describeError4(err)}`);
|
|
3419
3794
|
return null;
|
|
3420
3795
|
}
|
|
3421
3796
|
}
|
|
@@ -3576,6 +3951,17 @@ var ZYFAI_LEDGER_IDLE_CACHE_MS = 6e4;
|
|
|
3576
3951
|
var ZYFAI_LEDGER_REUSE_MS = 6e4;
|
|
3577
3952
|
var WITHDRAW_BALANCE_TIMEOUT_MS = 2e3;
|
|
3578
3953
|
var PAYOUT_SEARCH_HEAD_LIMIT_MS = 2e3;
|
|
3954
|
+
var WITHDRAW_HANDOFF_TIMEOUT_MS = 25e3;
|
|
3955
|
+
var PLANNED_HANDOFF_TIMEOUT_MS = 8e3;
|
|
3956
|
+
var PLANNED_HANDOFFS_LIMIT = 32;
|
|
3957
|
+
var REGISTRATION_HANDOFF_TIMEOUT_MS = 8e3;
|
|
3958
|
+
var REGISTRATION_NUDGE_TIMEOUT_MS = 25e3;
|
|
3959
|
+
var REGISTRATION_AMOUNT = /^[1-9][0-9]{0,77}$/;
|
|
3960
|
+
var TASK_CODE = /^[A-Z0-9_]{1,64}$/;
|
|
3961
|
+
var SERVER_LEG_CLEARED_CODE = "ZYFAI_LEG_CLEARED";
|
|
3962
|
+
var SESSION_ENDING_CODE = "ZYFAI_SESSION_ENDING";
|
|
3963
|
+
var SERVER_LEG_STATES_LIMIT = 2e3;
|
|
3964
|
+
var ADDRESS_PATTERN = /^0x[0-9a-fA-F]{40}$/;
|
|
3579
3965
|
function settleWithin(value, ms) {
|
|
3580
3966
|
let timer;
|
|
3581
3967
|
const limit = new Promise((resolve) => {
|
|
@@ -3585,6 +3971,28 @@ function settleWithin(value, ms) {
|
|
|
3585
3971
|
if (timer) clearTimeout(timer);
|
|
3586
3972
|
});
|
|
3587
3973
|
}
|
|
3974
|
+
function depositIdFromLog(logged) {
|
|
3975
|
+
const id = logged?.deposit?.id;
|
|
3976
|
+
return typeof id === "string" && id.length >= 1 && id.length <= 128 ? id : void 0;
|
|
3977
|
+
}
|
|
3978
|
+
function registrationOutcome(answer, knownTask) {
|
|
3979
|
+
if (answer.type === "unknown") return { kind: "unknown" };
|
|
3980
|
+
if (answer.type === "declined") {
|
|
3981
|
+
return !knownTask && provesNoServerTask(answer.code, true) ? { kind: "local" } : { kind: "unknown", code: answer.code };
|
|
3982
|
+
}
|
|
3983
|
+
if (answer.result.providerDepositId) return { kind: "confirmed", providerDepositId: answer.result.providerDepositId };
|
|
3984
|
+
if (answer.state === "failed" && answer.code && ZYFAI_REGISTRATION_NOT_SENT_CODES.has(answer.code)) return { kind: "local" };
|
|
3985
|
+
if (answer.state === "queued" || answer.state === "running") return { kind: "pending" };
|
|
3986
|
+
return { kind: "unknown", code: answer.code ?? answer.state.toUpperCase(), accepted: true };
|
|
3987
|
+
}
|
|
3988
|
+
function registrationOutcomeUnknown(code) {
|
|
3989
|
+
const error = new OwneyError(
|
|
3990
|
+
"AGENT_API_ERROR",
|
|
3991
|
+
"Zyfai registration by the server has no known outcome, so this browser does not register the deposit again.",
|
|
3992
|
+
{ status: 409, ...code && TASK_CODE.test(code) ? { reason: code } : {} }
|
|
3993
|
+
);
|
|
3994
|
+
return Object.assign(error, { status: 409 });
|
|
3995
|
+
}
|
|
3588
3996
|
function belowMinimumCeiling(rejection, token, decimals) {
|
|
3589
3997
|
if ((rejection.symbol === "ETH" ? "WETH" : rejection.symbol) !== token) return void 0;
|
|
3590
3998
|
const ceiling = (display) => {
|
|
@@ -3608,6 +4016,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3608
4016
|
referralSource;
|
|
3609
4017
|
accountReporting;
|
|
3610
4018
|
id = "zyfai";
|
|
4019
|
+
/** This adapter can hand its withdrawals to our server (part 4). */
|
|
4020
|
+
supportsServerTasks = true;
|
|
3611
4021
|
supportedChainIds = [8453, 42161, 1];
|
|
3612
4022
|
supportedAssets = [
|
|
3613
4023
|
{
|
|
@@ -3684,6 +4094,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3684
4094
|
/** A ledger read slower than this counts as "no ledger information" for that read. */
|
|
3685
4095
|
ledgerBudgetMs = LEDGER_READ_BUDGET_MS;
|
|
3686
4096
|
balanceSnapshots = /* @__PURE__ */ new WeakMap();
|
|
4097
|
+
/** operationId -> this session's plan-time handoff of that withdrawal (part 4),
|
|
4098
|
+
* read once by its nudge: the `after` legs and the planned share the task
|
|
4099
|
+
* was handed off with ("MAX" or digits). Who owns the leg is
|
|
4100
|
+
* `serverLegStates`, never this record. */
|
|
4101
|
+
plannedHandoffs = /* @__PURE__ */ new Map();
|
|
4102
|
+
/** operationId -> the server ownership of its Zyfai withdrawal leg (T9-I5).
|
|
4103
|
+
* The one source for "may this browser send it" (`mayWithdrawLocally`). */
|
|
4104
|
+
serverLegStates = /* @__PURE__ */ new Map();
|
|
4105
|
+
/** Deposit operations whose registration create was sent in this session:
|
|
4106
|
+
* a retry of one is always handed off again, whatever its session's
|
|
4107
|
+
* lifetime (F-I1(a)), so the server answers the task it may hold. */
|
|
4108
|
+
requestedRegistrations = /* @__PURE__ */ new Set();
|
|
3687
4109
|
/** A replaced wallet session gets its own vendor client. In-flight calls
|
|
3688
4110
|
* keep their original client; authentication must never change owners under
|
|
3689
4111
|
* an operation that is waiting for provider reads or an HTTP response. */
|
|
@@ -3894,10 +4316,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3894
4316
|
* Wraps `sdk.connectAccount` with a one-shot retry on auth errors. If the
|
|
3895
4317
|
* cached JWT is rejected by the backend (revoked, signing-key rotated, etc.)
|
|
3896
4318
|
* we drop the cache and try again — the second attempt has no cache entry,
|
|
3897
|
-
* so it falls through to a real SIWE prompt.
|
|
3898
|
-
*
|
|
3899
|
-
* JWT from a misconfigured API key; we
|
|
3900
|
-
* second attempt still fails the original
|
|
4319
|
+
* so it falls through to a real SIWE prompt. @zyfai/sdk 0.10.0 labels every
|
|
4320
|
+
* HTTP 401 as an invalid/expired or missing user session ("Unauthorized: …"),
|
|
4321
|
+
* which does not separate a revoked JWT from a misconfigured API key; we
|
|
4322
|
+
* retry once either way, and if the second attempt still fails the original
|
|
4323
|
+
* error propagates.
|
|
3901
4324
|
*/
|
|
3902
4325
|
async connectAccountWithRetry(provider, chainId, executionSdk = this.sdk) {
|
|
3903
4326
|
try {
|
|
@@ -3934,9 +4357,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3934
4357
|
}
|
|
3935
4358
|
}
|
|
3936
4359
|
/** A rejected execution session must not be reused by the next foreground attempt. */
|
|
3937
|
-
invalidateRejectedWithdrawalSession(error, state, chainId, authToken) {
|
|
4360
|
+
invalidateRejectedWithdrawalSession(error, state, chainId, authToken, httpStatus) {
|
|
3938
4361
|
const message = error instanceof Error ? error.message : "";
|
|
3939
|
-
if (
|
|
4362
|
+
if (!isZyfaiSessionRejected(message) || httpStatus !== void 0 && httpStatus !== 401) return;
|
|
4363
|
+
this.dropWithdrawalSession(state, chainId, authToken);
|
|
4364
|
+
}
|
|
4365
|
+
/** Drops the execution session a withdrawal used (Zyfai rejected it), unless
|
|
4366
|
+
* it has already changed. The next foreground connection signs in again. */
|
|
4367
|
+
dropWithdrawalSession(state, chainId, authToken) {
|
|
3940
4368
|
const internals = this.sdk;
|
|
3941
4369
|
if (typeof authToken !== "string" || internals.httpClient.authToken !== authToken || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== chainId) return;
|
|
3942
4370
|
clearSession(state.walletAddress, chainId);
|
|
@@ -4544,7 +4972,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4544
4972
|
await this.ensureSessionKey(ownerAddress, chainId, redeployed.address);
|
|
4545
4973
|
return redeployed.address;
|
|
4546
4974
|
}
|
|
4547
|
-
async deposit(state, chainId, amount, asset, depositCallback, onProgress, onBeforeSubmission) {
|
|
4975
|
+
async deposit(state, chainId, amount, asset, depositCallback, onProgress, onBeforeSubmission, context) {
|
|
4548
4976
|
const validChainId = isValidChainId(chainId);
|
|
4549
4977
|
const owner = state.walletAddress;
|
|
4550
4978
|
const provider = state.provider;
|
|
@@ -4559,6 +4987,12 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4559
4987
|
assertCurrent();
|
|
4560
4988
|
const smartWallet = await this.ensureWalletDeployed(owner, validChainId);
|
|
4561
4989
|
assertCurrent();
|
|
4990
|
+
let server;
|
|
4991
|
+
if (this.accountReporting.owneyApiKey && typeof context?.operationId === "string" && context.operationId && this.serverTasksOn(this.requestedRegistrations.has(context.operationId))) {
|
|
4992
|
+
server = await this.handOffDepositRegistration(owner, validChainId, asset, amount, smartWallet, context.operationId);
|
|
4993
|
+
assertCurrent();
|
|
4994
|
+
}
|
|
4995
|
+
const mark = server ?? {};
|
|
4562
4996
|
notifyFunding(onProgress, {
|
|
4563
4997
|
agentId: this.id,
|
|
4564
4998
|
chainId,
|
|
@@ -4568,7 +5002,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4568
5002
|
intendedAmount: amount,
|
|
4569
5003
|
transfer: "pending",
|
|
4570
5004
|
registration: "pending",
|
|
4571
|
-
delivery: "not-applicable"
|
|
5005
|
+
delivery: "not-applicable",
|
|
5006
|
+
...mark
|
|
4572
5007
|
});
|
|
4573
5008
|
const txHash = depositCallback ? await depositCallback(smartWallet, validChainId, amount) : await submitZyfaiDeposit({
|
|
4574
5009
|
sdk: executionSdk,
|
|
@@ -4587,7 +5022,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4587
5022
|
intendedAmount: amount,
|
|
4588
5023
|
transfer: "pending",
|
|
4589
5024
|
registration: "pending",
|
|
4590
|
-
delivery: "not-applicable"
|
|
5025
|
+
delivery: "not-applicable",
|
|
5026
|
+
...mark
|
|
4591
5027
|
}) : void 0
|
|
4592
5028
|
});
|
|
4593
5029
|
let evidence = {
|
|
@@ -4600,7 +5036,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4600
5036
|
txHash,
|
|
4601
5037
|
transfer: "pending",
|
|
4602
5038
|
registration: "pending",
|
|
4603
|
-
delivery: "not-applicable"
|
|
5039
|
+
delivery: "not-applicable",
|
|
5040
|
+
...mark
|
|
4604
5041
|
};
|
|
4605
5042
|
notifyFunding(onProgress, evidence);
|
|
4606
5043
|
try {
|
|
@@ -4620,11 +5057,32 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4620
5057
|
throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
|
|
4621
5058
|
if (evidence.transfer !== "confirmed") return { txHash, smartWallet, amount, evidence };
|
|
4622
5059
|
this.ledgerReads.clear();
|
|
5060
|
+
if (server) {
|
|
5061
|
+
let outcome;
|
|
5062
|
+
try {
|
|
5063
|
+
assertCurrent();
|
|
5064
|
+
outcome = await this.nudgeDepositRegistration(evidence, this.executionSession());
|
|
5065
|
+
} catch {
|
|
5066
|
+
outcome = { kind: "unknown" };
|
|
5067
|
+
}
|
|
5068
|
+
if (outcome.kind !== "local") {
|
|
5069
|
+
evidence = outcome.kind === "confirmed" ? { ...evidence, registration: "confirmed", providerDepositId: outcome.providerDepositId, serverTask: true } : {
|
|
5070
|
+
...evidence,
|
|
5071
|
+
registration: outcome.kind,
|
|
5072
|
+
...outcome.kind === "pending" || outcome.accepted ? { serverTask: true } : {}
|
|
5073
|
+
};
|
|
5074
|
+
if (outcome.kind !== "confirmed") this.notePendingRegistration(owner, validChainId, txHash, Date.now(), asset);
|
|
5075
|
+
notifyFunding(onProgress, evidence);
|
|
5076
|
+
return { txHash, smartWallet, amount, evidence };
|
|
5077
|
+
}
|
|
5078
|
+
evidence = { ...evidence, serverTask: false };
|
|
5079
|
+
}
|
|
4623
5080
|
try {
|
|
4624
5081
|
assertCurrent();
|
|
4625
5082
|
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
4626
|
-
await (tokenAddress ? executionSdk.logDeposit(validChainId, txHash, amount, tokenAddress) : executionSdk.logDeposit(validChainId, txHash, amount));
|
|
4627
|
-
|
|
5083
|
+
const logged = await (tokenAddress ? executionSdk.logDeposit(validChainId, txHash, amount, tokenAddress) : executionSdk.logDeposit(validChainId, txHash, amount));
|
|
5084
|
+
const providerDepositId = depositIdFromLog(logged);
|
|
5085
|
+
evidence = { ...evidence, registration: "confirmed", ...providerDepositId ? { providerDepositId } : {} };
|
|
4628
5086
|
} catch {
|
|
4629
5087
|
evidence = { ...evidence, registration: "unknown" };
|
|
4630
5088
|
this.notePendingRegistration(owner, validChainId, txHash, Date.now(), asset);
|
|
@@ -4744,10 +5202,69 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4744
5202
|
throw unavailable();
|
|
4745
5203
|
}
|
|
4746
5204
|
}
|
|
4747
|
-
|
|
5205
|
+
/**
|
|
5206
|
+
* Settles Zyfai's own wallet prompts before another agent's owner signature:
|
|
5207
|
+
* the SIWE login and, for a legacy wallet the login reported without a
|
|
5208
|
+
* session key, that key (attempted once per session, like activation). Reads
|
|
5209
|
+
* only the login's cached flags; never withdraws. A rejected prompt throws;
|
|
5210
|
+
* any other session-key failure is left to `withdraw`'s own recovery.
|
|
5211
|
+
*/
|
|
5212
|
+
async prepareWithdrawal(state, chainId, _token) {
|
|
5213
|
+
const validChainId = isValidChainId(chainId);
|
|
5214
|
+
await this.ensureConnected(state, validChainId);
|
|
5215
|
+
await this.ensureWithdrawalSessionKey(state.walletAddress, validChainId);
|
|
5216
|
+
}
|
|
5217
|
+
/** The session key a legacy wallet's login reported missing, created once per
|
|
5218
|
+
* session (one prompt). A rejected prompt throws; any other failure is left
|
|
5219
|
+
* to the withdrawal itself. */
|
|
5220
|
+
async ensureWithdrawalSessionKey(owner, validChainId) {
|
|
5221
|
+
const internals = this.sdk;
|
|
5222
|
+
const key2 = `${owner.toLowerCase()}:${validChainId}`;
|
|
5223
|
+
if (internals.isPredeployed !== false || internals.hasActiveSessionKey !== false || this.sessionKeyAttempts.has(key2)) return;
|
|
5224
|
+
this.sessionKeyAttempts.add(key2);
|
|
5225
|
+
try {
|
|
5226
|
+
await this.sdk.createSessionKey(owner, validChainId);
|
|
5227
|
+
} catch (error) {
|
|
5228
|
+
const code = error?.code;
|
|
5229
|
+
if (code === 4001 || code === "ACTION_REJECTED" || error instanceof Error && /user (rejected|denied)|request rejected|action rejected/i.test(error.message)) {
|
|
5230
|
+
this.sessionKeyAttempts.delete(key2);
|
|
5231
|
+
throw error;
|
|
5232
|
+
}
|
|
5233
|
+
console.warn(
|
|
5234
|
+
"[zyfai] session key before withdrawal skipped (non-fatal):",
|
|
5235
|
+
error instanceof Error ? error.message : String(error)
|
|
5236
|
+
);
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
5239
|
+
async withdraw(state, chainId, token, amount, onProgress, _gasMode, onBeforeSubmission, assertOperation, context) {
|
|
4748
5240
|
if (amount !== void 0 && (typeof amount !== "string" || !/^[0-9]+$/.test(amount) || BigInt(amount) <= 0n))
|
|
4749
5241
|
throw new OwneyError("WITHDRAW_INVALID_AMOUNT", "Withdrawal amount must be a positive integer in token base units.", void 0, this.id);
|
|
4750
5242
|
const validChainId = isValidChainId(chainId);
|
|
5243
|
+
if (this.accountReporting.owneyApiKey && typeof context?.operationId === "string" && context.operationId && // T10-I1: a leg whose mark was cleared is never handed off again.
|
|
5244
|
+
this.serverLegStates.get(context.operationId) !== "cleared" && // F-I2(iii): the opt-in, read now, unless the leg was already handed
|
|
5245
|
+
// off (planned, requested or accepted).
|
|
5246
|
+
this.serverTasksOn(context.planned === true || this.serverLegStates.has(context.operationId))) {
|
|
5247
|
+
const served = await this.withdrawThroughServer(
|
|
5248
|
+
state,
|
|
5249
|
+
validChainId,
|
|
5250
|
+
token,
|
|
5251
|
+
amount,
|
|
5252
|
+
onProgress,
|
|
5253
|
+
onBeforeSubmission,
|
|
5254
|
+
assertOperation,
|
|
5255
|
+
{ operationId: context.operationId, planned: context.planned === true }
|
|
5256
|
+
);
|
|
5257
|
+
if (served) return served;
|
|
5258
|
+
}
|
|
5259
|
+
if (typeof context?.operationId === "string" && context.operationId && !this.mayWithdrawLocally(context.operationId))
|
|
5260
|
+
this.serverLegUnresolved(onProgress, {
|
|
5261
|
+
agentId: this.id,
|
|
5262
|
+
chainId,
|
|
5263
|
+
token,
|
|
5264
|
+
walletAddress: state.walletAddress,
|
|
5265
|
+
intendedAmount: amount ?? "MAX",
|
|
5266
|
+
registration: "not-applicable"
|
|
5267
|
+
});
|
|
4751
5268
|
try {
|
|
4752
5269
|
await this.ensureConnected(state, validChainId);
|
|
4753
5270
|
} catch (error) {
|
|
@@ -4784,15 +5301,35 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4784
5301
|
assertCurrent();
|
|
4785
5302
|
assertOperation?.();
|
|
4786
5303
|
};
|
|
4787
|
-
let
|
|
5304
|
+
let headRead = Promise.resolve(void 0);
|
|
5305
|
+
let settledHead;
|
|
5306
|
+
const searchHead = () => settledHead ??= settleWithin(headRead, PAYOUT_SEARCH_HEAD_LIMIT_MS);
|
|
5307
|
+
const checkpoint2 = onBeforeSubmission ? async (request2) => {
|
|
5308
|
+
const head = await searchHead();
|
|
5309
|
+
const agentWallet = this.smartWalletCache.get(`${owner.toLowerCase()}_${validChainId}`);
|
|
5310
|
+
await onBeforeSubmission({
|
|
5311
|
+
agentId: this.id,
|
|
5312
|
+
chainId,
|
|
5313
|
+
token,
|
|
5314
|
+
walletAddress: state.walletAddress,
|
|
5315
|
+
intendedAmount: amount ?? "MAX",
|
|
5316
|
+
transfer: "pending",
|
|
5317
|
+
registration: "not-applicable",
|
|
5318
|
+
delivery: "pending",
|
|
5319
|
+
withdrawalRequest: request2,
|
|
5320
|
+
...agentWallet ? { agentWallet } : {},
|
|
5321
|
+
...head !== void 0 ? { payoutSearch: { fromBlock: head.toString() } } : {}
|
|
5322
|
+
});
|
|
5323
|
+
} : void 0;
|
|
5324
|
+
let observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertSubmission, checkpoint2);
|
|
4788
5325
|
const authToken = internals.httpClient?.authToken;
|
|
4789
5326
|
let raw2;
|
|
4790
|
-
let headRead = Promise.resolve(void 0);
|
|
4791
5327
|
try {
|
|
4792
5328
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
4793
5329
|
try {
|
|
4794
5330
|
assertCurrent();
|
|
4795
5331
|
headRead = this.readPayoutSearchHead(validChainId);
|
|
5332
|
+
settledHead = void 0;
|
|
4796
5333
|
raw2 = await executionSdk.withdrawFunds(owner, validChainId, amount, token);
|
|
4797
5334
|
break;
|
|
4798
5335
|
} catch (error) {
|
|
@@ -4821,7 +5358,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4821
5358
|
assertSubmission();
|
|
4822
5359
|
await this.recoverWithdrawalSession(state, validChainId, token, owner);
|
|
4823
5360
|
observation.stop();
|
|
4824
|
-
observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertSubmission);
|
|
5361
|
+
observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertSubmission, checkpoint2);
|
|
4825
5362
|
}
|
|
4826
5363
|
}
|
|
4827
5364
|
if (!raw2) throw new OwneyError(
|
|
@@ -4832,7 +5369,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4832
5369
|
);
|
|
4833
5370
|
} catch (error) {
|
|
4834
5371
|
if (staleSessionError && observation.attempted() === false) error = staleSessionError;
|
|
4835
|
-
this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken);
|
|
5372
|
+
this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken, observation.request()?.httpStatus);
|
|
4836
5373
|
if (observation.attempted() !== void 0) {
|
|
4837
5374
|
const failed = observation.attempted() === false || observation.rejected();
|
|
4838
5375
|
notifyFunding(onProgress, {
|
|
@@ -4886,7 +5423,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4886
5423
|
if (!raw2.txHash) {
|
|
4887
5424
|
const acceptedAt = Date.now();
|
|
4888
5425
|
const agentWallet = this.smartWalletCache.get(`${owner.toLowerCase()}_${validChainId}`);
|
|
4889
|
-
const head = await
|
|
5426
|
+
const head = await searchHead();
|
|
4890
5427
|
evidence = {
|
|
4891
5428
|
...evidence,
|
|
4892
5429
|
...agentWallet ? { agentWallet } : {},
|
|
@@ -4898,7 +5435,456 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4898
5435
|
this.ledgerReads.clear();
|
|
4899
5436
|
return { ...mapWithdraw(raw2), evidence };
|
|
4900
5437
|
}
|
|
4901
|
-
/**
|
|
5438
|
+
/**
|
|
5439
|
+
* Hands a planned withdrawal leg to our server at plan time (part 4), before
|
|
5440
|
+
* the earlier wallet-approval legs (`after`) are signed. The server sends it
|
|
5441
|
+
* once they are accepted; the browser nudges it at that point
|
|
5442
|
+
* (`withdraw(…, { operationId, planned: true })`). Uses the current execution
|
|
5443
|
+
* client's session, after `ensureConnected`. Never throws, never prompts
|
|
5444
|
+
* beyond that connection, and never withdraws locally.
|
|
5445
|
+
*/
|
|
5446
|
+
async handOffPlannedWithdrawal(state, chainId, token, amount, ctx) {
|
|
5447
|
+
const { owneyApiKey, routingApiBaseUrl } = this.accountReporting;
|
|
5448
|
+
if (!this.serverTasksOn(this.serverLegStates.has(ctx.operationId)))
|
|
5449
|
+
return { type: "declined", code: ZYFAI_SERVER_TASKS_OFF_CODE };
|
|
5450
|
+
if (this.serverLegStates.get(ctx.operationId) === "cleared") return { type: "declined", code: SERVER_LEG_CLEARED_CODE };
|
|
5451
|
+
const noSession = { type: "declined", code: "NO_SESSION" };
|
|
5452
|
+
if (!owneyApiKey) return noSession;
|
|
5453
|
+
let validChainId;
|
|
5454
|
+
let session;
|
|
5455
|
+
let agentWallet;
|
|
5456
|
+
try {
|
|
5457
|
+
validChainId = isValidChainId(chainId);
|
|
5458
|
+
await this.ensureConnected(state, validChainId);
|
|
5459
|
+
session = this.executionSession();
|
|
5460
|
+
if (!session) return noSession;
|
|
5461
|
+
if (this.serverLegStates.get(ctx.operationId) === void 0 && !zyfaiSessionOutlivesTask(session, "zyfai_withdrawal")) return { type: "declined", code: SESSION_ENDING_CODE };
|
|
5462
|
+
agentWallet = await this.handoffAgentWallet(state.walletAddress, validChainId);
|
|
5463
|
+
} catch {
|
|
5464
|
+
return noSession;
|
|
5465
|
+
}
|
|
5466
|
+
if (!agentWallet) return { type: "declined", code: "NO_AGENT_WALLET" };
|
|
5467
|
+
try {
|
|
5468
|
+
ctx.beforeRequest?.();
|
|
5469
|
+
} catch {
|
|
5470
|
+
}
|
|
5471
|
+
const afterLostAnswer = this.serverLegStates.get(ctx.operationId) === "requested";
|
|
5472
|
+
this.setServerLegState(ctx.operationId, "requested");
|
|
5473
|
+
let answer = await handOffZyfaiTask(
|
|
5474
|
+
{ apiKey: owneyApiKey, baseUrl: routingApiBaseUrl, timeoutMs: PLANNED_HANDOFF_TIMEOUT_MS, zyfaiServerTasks: true },
|
|
5475
|
+
{
|
|
5476
|
+
kind: "zyfai_withdrawal",
|
|
5477
|
+
operationId: ctx.operationId,
|
|
5478
|
+
owner: state.walletAddress,
|
|
5479
|
+
chainId: validChainId,
|
|
5480
|
+
asset: token,
|
|
5481
|
+
amount,
|
|
5482
|
+
agentWallet,
|
|
5483
|
+
after: ctx.after
|
|
5484
|
+
},
|
|
5485
|
+
session
|
|
5486
|
+
);
|
|
5487
|
+
if (answer.type === "declined" && ZYFAI_HANDOFF_NO_TASK_CODES.has(answer.code) && !provesNoServerTask(answer.code, afterLostAnswer)) answer = { type: "unknown" };
|
|
5488
|
+
if (answer.type === "accepted" || answer.type === "declined" && answer.code === "LEG_ALREADY_SENT")
|
|
5489
|
+
this.setServerLegState(ctx.operationId, "accepted");
|
|
5490
|
+
if (answer.type === "declined") {
|
|
5491
|
+
if (answer.code !== "LEG_ALREADY_SENT" && this.serverLegStates.get(ctx.operationId) === "accepted")
|
|
5492
|
+
return { type: "unknown" };
|
|
5493
|
+
if (provesNoServerTask(answer.code, afterLostAnswer)) this.clearServerLeg(ctx.operationId, "no-task-decline");
|
|
5494
|
+
return answer;
|
|
5495
|
+
}
|
|
5496
|
+
this.plannedHandoffs.delete(ctx.operationId);
|
|
5497
|
+
this.plannedHandoffs.set(ctx.operationId, { after: [...ctx.after], amount });
|
|
5498
|
+
while (this.plannedHandoffs.size > PLANNED_HANDOFFS_LIMIT)
|
|
5499
|
+
this.plannedHandoffs.delete(this.plannedHandoffs.keys().next().value);
|
|
5500
|
+
return answer;
|
|
5501
|
+
}
|
|
5502
|
+
/**
|
|
5503
|
+
* F-I2(iii): whether the server path may run. The caller's opt-in is read
|
|
5504
|
+
* now (a function is called) as an operation's first handoff would start.
|
|
5505
|
+
* An operation this session already handed off (`handedOff`: a planned leg,
|
|
5506
|
+
* a leg requested or accepted, a registration already sent) keeps going
|
|
5507
|
+
* through the server even after the opt-in turns off: its nudges, re-runs
|
|
5508
|
+
* and retries, so the server answers the task it may hold and the browser
|
|
5509
|
+
* never sends it too. Without any opt-in (P4-0) nothing was handed off.
|
|
5510
|
+
*/
|
|
5511
|
+
serverTasksOn(handedOff = false) {
|
|
5512
|
+
const option = this.accountReporting.zyfaiServerTasks;
|
|
5513
|
+
if (handedOff) return option === true || typeof option === "function";
|
|
5514
|
+
return zyfaiServerTasksEnabled(option);
|
|
5515
|
+
}
|
|
5516
|
+
/** True when this browser may send the operation's Zyfai withdrawal leg
|
|
5517
|
+
* itself: it never handed it off, or the server proved it has no task. */
|
|
5518
|
+
mayWithdrawLocally(operationId) {
|
|
5519
|
+
const current = this.serverLegStates.get(operationId);
|
|
5520
|
+
return current === void 0 || current === "cleared";
|
|
5521
|
+
}
|
|
5522
|
+
/** Moves the leg's server ownership forward: absent -> requested ->
|
|
5523
|
+
* accepted. "accepted" is sticky, and "cleared" is final (such a leg is
|
|
5524
|
+
* never handed off again). Clearing goes through `clearServerLeg`. */
|
|
5525
|
+
setServerLegState(operationId, next) {
|
|
5526
|
+
const current = this.serverLegStates.get(operationId);
|
|
5527
|
+
if (current === "accepted" || current === "cleared" || current === next) return;
|
|
5528
|
+
this.serverLegStates.set(operationId, next);
|
|
5529
|
+
while (this.serverLegStates.size > SERVER_LEG_STATES_LIMIT)
|
|
5530
|
+
this.serverLegStates.delete(this.serverLegStates.keys().next().value);
|
|
5531
|
+
}
|
|
5532
|
+
/** Clears the leg on a proof that the server has no task: a no-task decline
|
|
5533
|
+
* after a request, or nothing ever requested (T8-R4b). Never from accepted.
|
|
5534
|
+
* Returns whether the leg is cleared; the caller then emits
|
|
5535
|
+
* `serverTask: false` and may send locally (T10-I1, T9-I5). */
|
|
5536
|
+
clearServerLeg(operationId, proof) {
|
|
5537
|
+
const current = this.serverLegStates.get(operationId);
|
|
5538
|
+
if (current === "cleared") return true;
|
|
5539
|
+
if (proof === "no-task-decline" ? current !== "requested" : current !== void 0) return false;
|
|
5540
|
+
this.serverLegStates.set(operationId, "cleared");
|
|
5541
|
+
while (this.serverLegStates.size > SERVER_LEG_STATES_LIMIT)
|
|
5542
|
+
this.serverLegStates.delete(this.serverLegStates.keys().next().value);
|
|
5543
|
+
return true;
|
|
5544
|
+
}
|
|
5545
|
+
/** T9-I5: the server may hold this leg's task, so this browser never sends
|
|
5546
|
+
* it. The leg is reported failed with `serverTask: "unknown"` (never
|
|
5547
|
+
* cleared; a re-run hands it off again), and `error`, or an unresolved-leg
|
|
5548
|
+
* error, is thrown. */
|
|
5549
|
+
serverLegUnresolved(onProgress, base7, error) {
|
|
5550
|
+
notifyFunding(onProgress, {
|
|
5551
|
+
...base7,
|
|
5552
|
+
transfer: "failed",
|
|
5553
|
+
delivery: "failed",
|
|
5554
|
+
withdrawalRequest: { attempted: false },
|
|
5555
|
+
serverTask: "unknown"
|
|
5556
|
+
});
|
|
5557
|
+
throw error ?? new OwneyError(
|
|
5558
|
+
"WITHDRAW_FAILED",
|
|
5559
|
+
"The server may still send this Zyfai withdrawal, so this browser does not send it.",
|
|
5560
|
+
{ reason: "ZYFAI_SERVER_LEG_UNRESOLVED", serverTask: true, rejectedBeforeSubmission: true },
|
|
5561
|
+
this.id
|
|
5562
|
+
);
|
|
5563
|
+
}
|
|
5564
|
+
/** The current execution client's session. Never logged or returned. */
|
|
5565
|
+
executionSession() {
|
|
5566
|
+
const token = this.sdk.httpClient?.authToken;
|
|
5567
|
+
return typeof token === "string" && token ? token : void 0;
|
|
5568
|
+
}
|
|
5569
|
+
/** The owner's Zyfai wallet for a handoff (read-only), or undefined. */
|
|
5570
|
+
async handoffAgentWallet(owner, chainId) {
|
|
5571
|
+
if (!ADDRESS_PATTERN.test(owner)) return void 0;
|
|
5572
|
+
try {
|
|
5573
|
+
const wallet = await this.readSmartWallet(owner, chainId);
|
|
5574
|
+
return typeof wallet === "string" && ADDRESS_PATTERN.test(wallet) ? wallet : void 0;
|
|
5575
|
+
} catch {
|
|
5576
|
+
return void 0;
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
/**
|
|
5580
|
+
* Part 4: hands this withdrawal to our server, or nudges the task a plan-time
|
|
5581
|
+
* handoff created (`planned`). The server is the one sender from then on:
|
|
5582
|
+
* this never calls Zyfai's withdraw route, and never sends again after an
|
|
5583
|
+
* ambiguous answer. Returns undefined only when nothing was handed off, or
|
|
5584
|
+
* when the server proved no task exists (a decline in
|
|
5585
|
+
* `ZYFAI_HANDOFF_NO_TASK_CODES`, rulings P4-11, T7-R1, T8-C1 and F-I1(b);
|
|
5586
|
+
* after a lost answer only the server's own declines, F-I2(i)); today's
|
|
5587
|
+
* local path then runs, after the handoff's mark was cleared (T8-C2).
|
|
5588
|
+
*/
|
|
5589
|
+
async withdrawThroughServer(state, chainId, token, requestedAmount, onProgress, onBeforeSubmission, assertOperation, context) {
|
|
5590
|
+
const owner = state.walletAddress;
|
|
5591
|
+
const operationId = context.operationId;
|
|
5592
|
+
const plan = context.planned ? this.plannedHandoffs.get(operationId) : void 0;
|
|
5593
|
+
if (context.planned) this.plannedHandoffs.delete(operationId);
|
|
5594
|
+
if (context.planned && this.serverLegStates.get(operationId) === void 0)
|
|
5595
|
+
this.setServerLegState(operationId, "accepted");
|
|
5596
|
+
const planLost = context.planned && this.serverLegStates.get(operationId) === "requested";
|
|
5597
|
+
const serverOwned = () => context.planned || this.serverLegStates.get(operationId) === "accepted";
|
|
5598
|
+
const amount = plan?.amount === "MAX" ? void 0 : plan && /^[1-9][0-9]*$/.test(plan.amount) ? plan.amount : requestedAmount;
|
|
5599
|
+
const route = amount ? { attempted: true, method: "POST", path: "/users/partial-withdraw/v2" } : { attempted: true, method: "GET", path: "/users/withdraw/v2" };
|
|
5600
|
+
const base7 = {
|
|
5601
|
+
agentId: this.id,
|
|
5602
|
+
chainId,
|
|
5603
|
+
token,
|
|
5604
|
+
walletAddress: owner,
|
|
5605
|
+
intendedAmount: amount ?? "MAX",
|
|
5606
|
+
registration: "not-applicable"
|
|
5607
|
+
};
|
|
5608
|
+
let head;
|
|
5609
|
+
const searchHead = () => head ??= settleWithin(this.readPayoutSearchHead(chainId), PAYOUT_SEARCH_HEAD_LIMIT_MS);
|
|
5610
|
+
const accept = async (serverTask, agentWallet2, txHash, providerWithdrawalId) => {
|
|
5611
|
+
const withdrawalRequest = { ...route, ...providerWithdrawalId ? { providerWithdrawalId } : {} };
|
|
5612
|
+
let evidence;
|
|
5613
|
+
if (txHash) {
|
|
5614
|
+
evidence = { ...base7, txHash, transfer: "pending", delivery: "pending", withdrawalRequest, serverTask };
|
|
5615
|
+
} else {
|
|
5616
|
+
const acceptedAt = Date.now();
|
|
5617
|
+
const fromBlock = await searchHead();
|
|
5618
|
+
evidence = {
|
|
5619
|
+
...base7,
|
|
5620
|
+
transfer: "pending",
|
|
5621
|
+
delivery: "pending",
|
|
5622
|
+
...agentWallet2 ? { agentWallet: agentWallet2 } : {},
|
|
5623
|
+
withdrawalRequest: { ...withdrawalRequest, acceptedAt },
|
|
5624
|
+
...fromBlock !== void 0 ? { payoutSearch: { fromBlock: fromBlock.toString() } } : {},
|
|
5625
|
+
serverTask
|
|
5626
|
+
};
|
|
5627
|
+
}
|
|
5628
|
+
notifyFunding(onProgress, evidence);
|
|
5629
|
+
this.ledgerReads.clear();
|
|
5630
|
+
return { ...mapWithdraw({
|
|
5631
|
+
success: true,
|
|
5632
|
+
message: "Withdrawal handed to the server",
|
|
5633
|
+
txHash,
|
|
5634
|
+
type: amount ? "partial" : "full",
|
|
5635
|
+
amount: amount || "all"
|
|
5636
|
+
}), evidence };
|
|
5637
|
+
};
|
|
5638
|
+
try {
|
|
5639
|
+
await this.ensureConnected(state, chainId);
|
|
5640
|
+
} catch (error) {
|
|
5641
|
+
if (serverOwned()) return accept("unknown", void 0);
|
|
5642
|
+
if (!this.mayWithdrawLocally(operationId)) this.serverLegUnresolved(onProgress, base7, error);
|
|
5643
|
+
notifyFunding(onProgress, { ...base7, transfer: "failed", delivery: "failed" });
|
|
5644
|
+
throw error;
|
|
5645
|
+
}
|
|
5646
|
+
const generation = this.connectionGeneration;
|
|
5647
|
+
const internals = this.sdk;
|
|
5648
|
+
const session = this.executionSession();
|
|
5649
|
+
if (session && !context.planned && this.serverLegStates.get(operationId) === void 0 && !zyfaiSessionOutlivesTask(session, "zyfai_withdrawal")) return void 0;
|
|
5650
|
+
const agentWallet = session ? await this.handoffAgentWallet(owner, chainId) : void 0;
|
|
5651
|
+
if (!session || !agentWallet) {
|
|
5652
|
+
if (serverOwned()) return accept("unknown", agentWallet);
|
|
5653
|
+
if (!this.mayWithdrawLocally(operationId)) this.serverLegUnresolved(onProgress, base7);
|
|
5654
|
+
return void 0;
|
|
5655
|
+
}
|
|
5656
|
+
const assertSubmission = () => {
|
|
5657
|
+
const account = internals.walletClient?.account?.address;
|
|
5658
|
+
if (generation !== this.connectionGeneration || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId || state.chainId !== null && state.chainId !== chainId || account && account.toLowerCase() !== owner.toLowerCase())
|
|
5659
|
+
throw new OwneyError(
|
|
5660
|
+
"NOT_CONNECTED",
|
|
5661
|
+
"Wallet changed before Zyfai withdrawal submission.",
|
|
5662
|
+
{ phase: "prepare", rejectedBeforeSubmission: true },
|
|
5663
|
+
this.id
|
|
5664
|
+
);
|
|
5665
|
+
assertOperation?.();
|
|
5666
|
+
};
|
|
5667
|
+
try {
|
|
5668
|
+
if (!context.planned) await this.ensureWithdrawalSessionKey(owner, chainId);
|
|
5669
|
+
assertSubmission();
|
|
5670
|
+
const fromBlock = await searchHead();
|
|
5671
|
+
await onBeforeSubmission?.({
|
|
5672
|
+
...base7,
|
|
5673
|
+
transfer: "pending",
|
|
5674
|
+
delivery: "pending",
|
|
5675
|
+
withdrawalRequest: { ...route },
|
|
5676
|
+
agentWallet,
|
|
5677
|
+
...fromBlock !== void 0 ? { payoutSearch: { fromBlock: fromBlock.toString() } } : {},
|
|
5678
|
+
serverTask: true
|
|
5679
|
+
});
|
|
5680
|
+
assertSubmission();
|
|
5681
|
+
} catch (error) {
|
|
5682
|
+
if (serverOwned()) return accept("unknown", agentWallet);
|
|
5683
|
+
if (!this.clearServerLeg(operationId, "never-requested")) this.serverLegUnresolved(onProgress, base7, error);
|
|
5684
|
+
notifyFunding(onProgress, {
|
|
5685
|
+
...base7,
|
|
5686
|
+
transfer: "failed",
|
|
5687
|
+
delivery: "failed",
|
|
5688
|
+
withdrawalRequest: { attempted: false },
|
|
5689
|
+
serverTask: false
|
|
5690
|
+
});
|
|
5691
|
+
throw error;
|
|
5692
|
+
}
|
|
5693
|
+
const cfg = {
|
|
5694
|
+
apiKey: this.accountReporting.owneyApiKey,
|
|
5695
|
+
baseUrl: this.accountReporting.routingApiBaseUrl,
|
|
5696
|
+
timeoutMs: WITHDRAW_HANDOFF_TIMEOUT_MS,
|
|
5697
|
+
zyfaiServerTasks: true
|
|
5698
|
+
};
|
|
5699
|
+
const body = {
|
|
5700
|
+
kind: "zyfai_withdrawal",
|
|
5701
|
+
operationId: context.operationId,
|
|
5702
|
+
owner,
|
|
5703
|
+
chainId,
|
|
5704
|
+
asset: token,
|
|
5705
|
+
amount: amount ?? "MAX",
|
|
5706
|
+
agentWallet,
|
|
5707
|
+
...plan?.after.length ? { after: plan.after } : {},
|
|
5708
|
+
...context.planned ? { runNow: true } : {}
|
|
5709
|
+
};
|
|
5710
|
+
const requestedBefore = this.serverLegStates.get(operationId) === "requested";
|
|
5711
|
+
let afterUnknown = planLost;
|
|
5712
|
+
this.setServerLegState(operationId, "requested");
|
|
5713
|
+
let answer = await handOffZyfaiTask(cfg, body, session);
|
|
5714
|
+
if (answer.type === "unknown" && !context.planned) {
|
|
5715
|
+
afterUnknown = true;
|
|
5716
|
+
answer = await handOffZyfaiTask(cfg, body, session);
|
|
5717
|
+
}
|
|
5718
|
+
if (answer.type === "declined" && ZYFAI_HANDOFF_NO_TASK_CODES.has(answer.code) && !provesNoServerTask(answer.code, requestedBefore || afterUnknown)) answer = { type: "unknown" };
|
|
5719
|
+
if (answer.type === "accepted" || answer.type === "declined" && answer.code === "LEG_ALREADY_SENT")
|
|
5720
|
+
this.setServerLegState(operationId, "accepted");
|
|
5721
|
+
if (answer.type === "declined") {
|
|
5722
|
+
if (ZYFAI_HANDOFF_NO_TASK_CODES.has(answer.code) && this.clearServerLeg(operationId, "no-task-decline")) {
|
|
5723
|
+
const cleared = { ...base7, transfer: "pending", delivery: "pending", serverTask: false };
|
|
5724
|
+
notifyFunding(onProgress, cleared);
|
|
5725
|
+
try {
|
|
5726
|
+
await onBeforeSubmission?.({ ...cleared, withdrawalRequest: { attempted: false } });
|
|
5727
|
+
} catch (error) {
|
|
5728
|
+
notifyFunding(onProgress, { ...cleared, transfer: "failed", delivery: "failed", withdrawalRequest: { attempted: false } });
|
|
5729
|
+
throw error;
|
|
5730
|
+
}
|
|
5731
|
+
return void 0;
|
|
5732
|
+
}
|
|
5733
|
+
if (this.serverLegStates.get(operationId) === "accepted" || afterUnknown) return accept("unknown", agentWallet);
|
|
5734
|
+
return this.serverWithdrawalNotSent(answer.code, state, chainId, token, session, onProgress, base7, route, "unknown");
|
|
5735
|
+
}
|
|
5736
|
+
if (answer.type === "unknown") return accept("unknown", agentWallet);
|
|
5737
|
+
if (answer.state === "failed" && answer.code && ZYFAI_WITHDRAWAL_NOT_SENT_CODES.has(answer.code))
|
|
5738
|
+
return this.serverWithdrawalNotSent(answer.code, state, chainId, token, session, onProgress, base7, route, true);
|
|
5739
|
+
if (answer.state === "failed" || answer.state === "expired") return accept("unknown", agentWallet);
|
|
5740
|
+
return accept(true, agentWallet, answer.result.txHash, answer.result.providerWithdrawalId);
|
|
5741
|
+
}
|
|
5742
|
+
/** A server answer proving the withdrawal was never sent: today's matching
|
|
5743
|
+
* error, with evidence transfer and delivery `failed`. "Already in progress"
|
|
5744
|
+
* keeps the hold: failure `withdrawal-in-progress`, never rejectedBeforeSubmission.
|
|
5745
|
+
* `serverTask`: true for a task that ended unsent (the server owns the leg's
|
|
5746
|
+
* close), "unknown" for a declined handoff whose code does not prove no task
|
|
5747
|
+
* (never false: such a leg is never cleared, ruling T9-I3). */
|
|
5748
|
+
serverWithdrawalNotSent(code, state, chainId, token, session, onProgress, base7, route, serverTask) {
|
|
5749
|
+
let failure;
|
|
5750
|
+
let error;
|
|
5751
|
+
const sessionUnavailable = () => new OwneyError(
|
|
5752
|
+
"AGENT_AUTH_FAILED",
|
|
5753
|
+
"Zyfai has no active withdrawal session. Its withdrawal was not submitted.",
|
|
5754
|
+
{ reason: "ZYFAI_SESSION_UNAVAILABLE", phase: "prepare", chainId, token, serverTask: true, rejectedBeforeSubmission: true },
|
|
5755
|
+
this.id
|
|
5756
|
+
);
|
|
5757
|
+
switch (code) {
|
|
5758
|
+
case "NEEDS_USER_SESSION":
|
|
5759
|
+
failure = "unauthorized";
|
|
5760
|
+
error = sessionUnavailable();
|
|
5761
|
+
this.dropWithdrawalSession(state, chainId, session);
|
|
5762
|
+
break;
|
|
5763
|
+
case "NEEDS_USER_SESSION_KEY":
|
|
5764
|
+
failure = "session-key-missing";
|
|
5765
|
+
error = sessionUnavailable();
|
|
5766
|
+
this.rearmWithdrawalSessionKey(state.walletAddress, chainId);
|
|
5767
|
+
break;
|
|
5768
|
+
case "WITHDRAW_INSUFFICIENT_BALANCE":
|
|
5769
|
+
failure = "insufficient-balance";
|
|
5770
|
+
error = new OwneyError(
|
|
5771
|
+
"WITHDRAW_INSUFFICIENT_BALANCE",
|
|
5772
|
+
"No funds remain in Zyfai for this asset.",
|
|
5773
|
+
{ available: "0", chainId, token, rejectedBeforeSubmission: true },
|
|
5774
|
+
this.id
|
|
5775
|
+
);
|
|
5776
|
+
break;
|
|
5777
|
+
case "WITHDRAWAL_IN_PROGRESS":
|
|
5778
|
+
case "ZYFAI_REDEMPTION_IN_FLIGHT":
|
|
5779
|
+
failure = "withdrawal-in-progress";
|
|
5780
|
+
error = new OwneyError(
|
|
5781
|
+
"WITHDRAW_IN_PROGRESS",
|
|
5782
|
+
"A Zyfai withdrawal is already in progress.",
|
|
5783
|
+
{ reason: code, chainId, token, serverTask: true },
|
|
5784
|
+
this.id
|
|
5785
|
+
);
|
|
5786
|
+
break;
|
|
5787
|
+
default:
|
|
5788
|
+
failure = code === "ZYFAI_FORBIDDEN" ? "forbidden" : "other";
|
|
5789
|
+
error = new OwneyError(
|
|
5790
|
+
"WITHDRAW_FAILED",
|
|
5791
|
+
"The server did not send the Zyfai withdrawal.",
|
|
5792
|
+
{ reason: code, serverTask: true, rejectedBeforeSubmission: true },
|
|
5793
|
+
this.id
|
|
5794
|
+
);
|
|
5795
|
+
}
|
|
5796
|
+
notifyFunding(onProgress, {
|
|
5797
|
+
...base7,
|
|
5798
|
+
transfer: "failed",
|
|
5799
|
+
delivery: "failed",
|
|
5800
|
+
serverTask,
|
|
5801
|
+
withdrawalRequest: { ...route, failure }
|
|
5802
|
+
});
|
|
5803
|
+
throw error;
|
|
5804
|
+
}
|
|
5805
|
+
/** Zyfai has no session key for this wallet: for a legacy (not pre-deployed)
|
|
5806
|
+
* wallet, forget the login's "active" flag and this session's attempt, so the
|
|
5807
|
+
* next `ensureWithdrawalSessionKey` prompts once. Sends and prompts nothing. */
|
|
5808
|
+
rearmWithdrawalSessionKey(owner, chainId) {
|
|
5809
|
+
const internals = this.sdk;
|
|
5810
|
+
if (internals.isPredeployed !== false || this.connectedAddress?.toLowerCase() !== owner.toLowerCase() || this.connectedChainId !== chainId) return;
|
|
5811
|
+
internals.hasActiveSessionKey = false;
|
|
5812
|
+
this.sessionKeyAttempts.delete(`${owner.toLowerCase()}:${chainId}`);
|
|
5813
|
+
}
|
|
5814
|
+
/** The routing API's handoff config. Only called with the opt-in and the key. */
|
|
5815
|
+
registrationHandoffConfig(timeoutMs) {
|
|
5816
|
+
return {
|
|
5817
|
+
apiKey: this.accountReporting.owneyApiKey,
|
|
5818
|
+
baseUrl: this.accountReporting.routingApiBaseUrl,
|
|
5819
|
+
timeoutMs,
|
|
5820
|
+
zyfaiServerTasks: true
|
|
5821
|
+
};
|
|
5822
|
+
}
|
|
5823
|
+
/**
|
|
5824
|
+
* Part 4: hands a deposit's registration to our server before its transfer.
|
|
5825
|
+
* Undefined when nothing was handed off, or when the server proved it has no
|
|
5826
|
+
* task (a decline in `ZYFAI_HANDOFF_NO_TASK_CODES`, rulings P4-11, T7-R1 and
|
|
5827
|
+
* T8-F2; for a retried create only the server's own declines, F-I2(i)):
|
|
5828
|
+
* today's local registration then runs. Any other answer means the
|
|
5829
|
+
* server holds the registration, or may hold it (an unknown answer, or
|
|
5830
|
+
* another decline), so the browser never registers it except by the
|
|
5831
|
+
* not-sent fallback. Never throws; never logs or returns the session.
|
|
5832
|
+
*/
|
|
5833
|
+
async handOffDepositRegistration(owner, chainId, asset, amount, agentWallet, operationId) {
|
|
5834
|
+
const session = this.executionSession();
|
|
5835
|
+
if (!session || !ADDRESS_PATTERN.test(owner) || !ADDRESS_PATTERN.test(agentWallet) || !REGISTRATION_AMOUNT.test(amount) || asset !== "USDC" && asset !== "WETH") return void 0;
|
|
5836
|
+
const retried = this.requestedRegistrations.has(operationId);
|
|
5837
|
+
if (!retried) {
|
|
5838
|
+
if (!zyfaiSessionOutlivesTask(session, "zyfai_deposit_registration")) return void 0;
|
|
5839
|
+
this.requestedRegistrations.add(operationId);
|
|
5840
|
+
while (this.requestedRegistrations.size > SERVER_LEG_STATES_LIMIT)
|
|
5841
|
+
this.requestedRegistrations.delete(this.requestedRegistrations.values().next().value);
|
|
5842
|
+
}
|
|
5843
|
+
const answer = await handOffZyfaiTask(
|
|
5844
|
+
this.registrationHandoffConfig(REGISTRATION_HANDOFF_TIMEOUT_MS),
|
|
5845
|
+
{ kind: "zyfai_deposit_registration", operationId, owner, chainId, asset, amount, agentWallet },
|
|
5846
|
+
session
|
|
5847
|
+
);
|
|
5848
|
+
if (answer.type === "declined" && provesNoServerTask(answer.code, retried)) return void 0;
|
|
5849
|
+
if (answer.type === "accepted") return { serverTask: true, serverTaskOperationId: operationId };
|
|
5850
|
+
const lost = answer.type === "unknown" || ZYFAI_HANDOFF_NO_TASK_CODES.has(answer.code);
|
|
5851
|
+
return { serverTask: "unknown", serverTaskOperationId: operationId, serverTaskCreate: lost ? "lost" : "declined" };
|
|
5852
|
+
}
|
|
5853
|
+
/**
|
|
5854
|
+
* Nudges the server's registration task of this deposit evidence (with
|
|
5855
|
+
* `serverTask`) and reads what its answer proves. Without the opt-in (read
|
|
5856
|
+
* now, unless this session sent the registration's create, F-I2(iii)), the
|
|
5857
|
+
* key, a session or the task's identity no nudge is sent and the outcome is
|
|
5858
|
+
* unknown. Never throws; never registers locally.
|
|
5859
|
+
*/
|
|
5860
|
+
async nudgeDepositRegistration(evidence, session) {
|
|
5861
|
+
const { owneyApiKey } = this.accountReporting;
|
|
5862
|
+
const operationId = evidence.serverTaskOperationId;
|
|
5863
|
+
const agentWallet = evidence.agentWallet;
|
|
5864
|
+
const chainId = evidence.chainId;
|
|
5865
|
+
if (typeof operationId !== "string" || !operationId || !this.serverTasksOn(this.requestedRegistrations.has(operationId)) || !owneyApiKey || !session || typeof agentWallet !== "string" || !ADDRESS_PATTERN.test(agentWallet) || !ADDRESS_PATTERN.test(evidence.walletAddress) || !REGISTRATION_AMOUNT.test(evidence.intendedAmount) || evidence.token !== "USDC" && evidence.token !== "WETH" || chainId !== 1 && chainId !== 8453 && chainId !== 42161)
|
|
5866
|
+
return { kind: "unknown" };
|
|
5867
|
+
const answer = await handOffZyfaiTask(
|
|
5868
|
+
this.registrationHandoffConfig(REGISTRATION_NUDGE_TIMEOUT_MS),
|
|
5869
|
+
{
|
|
5870
|
+
kind: "zyfai_deposit_registration",
|
|
5871
|
+
operationId,
|
|
5872
|
+
owner: evidence.walletAddress,
|
|
5873
|
+
chainId,
|
|
5874
|
+
asset: evidence.token,
|
|
5875
|
+
amount: evidence.intendedAmount,
|
|
5876
|
+
agentWallet,
|
|
5877
|
+
runNow: true
|
|
5878
|
+
},
|
|
5879
|
+
session
|
|
5880
|
+
);
|
|
5881
|
+
return registrationOutcome(answer, evidence.serverTask === true || evidence.serverTaskCreate !== "lost");
|
|
5882
|
+
}
|
|
5883
|
+
/** Registration recovery never signs, transfers, activates, or refreshes authentication.
|
|
5884
|
+
* For a deposit the server registers (`serverTask`), it nudges the server's
|
|
5885
|
+
* task instead: still pending gives a transient error, and an unknown or
|
|
5886
|
+
* expired outcome the status-409 error. It registers locally only when the
|
|
5887
|
+
* server proved it never sent the registration (ruling A4). */
|
|
4902
5888
|
async retryDepositRegistration(state, evidence) {
|
|
4903
5889
|
const executionSdk = this.sdk;
|
|
4904
5890
|
const internals = executionSdk;
|
|
@@ -4909,11 +5895,49 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4909
5895
|
throw new OwneyError("AGENT_API_ERROR", "Deposit transfer must be verified before registration recovery.");
|
|
4910
5896
|
}
|
|
4911
5897
|
const cid = isValidChainId(evidence.chainId);
|
|
5898
|
+
let cleared = {};
|
|
5899
|
+
if (evidence.serverTask === true || evidence.serverTask === "unknown") {
|
|
5900
|
+
const outcome = await this.nudgeDepositRegistration(evidence, internals.httpClient.authToken ?? void 0);
|
|
5901
|
+
if (outcome.kind === "confirmed") {
|
|
5902
|
+
this.pendingRegistrations.get(`${evidence.walletAddress.toLowerCase()}:${cid}`)?.delete(evidence.txHash.toLowerCase());
|
|
5903
|
+
this.ledgerReads.clear();
|
|
5904
|
+
return { ...evidence, registration: "confirmed", providerDepositId: outcome.providerDepositId, serverTask: true };
|
|
5905
|
+
}
|
|
5906
|
+
if (outcome.kind === "pending")
|
|
5907
|
+
throw new OwneyError("AGENT_API_ERROR", "Zyfai registration by the server is temporarily unavailable.");
|
|
5908
|
+
if (outcome.kind === "unknown") throw registrationOutcomeUnknown(outcome.code);
|
|
5909
|
+
cleared = { serverTask: false };
|
|
5910
|
+
}
|
|
4912
5911
|
const tokenAddress = evidence.token === "WETH" ? WETH_ADDRESS_BY_CHAIN[cid] : void 0;
|
|
4913
|
-
await executionSdk.logDeposit(cid, evidence.txHash, evidence.intendedAmount, tokenAddress);
|
|
5912
|
+
const logged = await executionSdk.logDeposit(cid, evidence.txHash, evidence.intendedAmount, tokenAddress);
|
|
5913
|
+
const providerDepositId = depositIdFromLog(logged);
|
|
4914
5914
|
this.pendingRegistrations.get(`${evidence.walletAddress.toLowerCase()}:${cid}`)?.delete(evidence.txHash.toLowerCase());
|
|
4915
5915
|
this.ledgerReads.clear();
|
|
4916
|
-
return { ...evidence, registration: "confirmed" };
|
|
5916
|
+
return { ...evidence, ...cleared, registration: "confirmed", ...providerDepositId ? { providerDepositId } : {} };
|
|
5917
|
+
}
|
|
5918
|
+
/** Reads a deposit's placement status by its id. Never signs, prompts or authenticates:
|
|
5919
|
+
* it uses only a session the vendor client already holds, and any failure is `null`. */
|
|
5920
|
+
async getDepositPlacement(state, depositId) {
|
|
5921
|
+
const internals = this.sdk;
|
|
5922
|
+
const client = internals.httpClient;
|
|
5923
|
+
if (!client?.authToken || typeof client.get !== "function" || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase()) return null;
|
|
5924
|
+
try {
|
|
5925
|
+
return mapDepositPlacement(await client.get(`/users/deposits/${encodeURIComponent(depositId)}/v2`));
|
|
5926
|
+
} catch {
|
|
5927
|
+
return null;
|
|
5928
|
+
}
|
|
5929
|
+
}
|
|
5930
|
+
/** Reads a withdrawal's lifecycle by its id. Never signs, prompts or authenticates:
|
|
5931
|
+
* it uses only a session the vendor client already holds, and any failure is `null`. */
|
|
5932
|
+
async getWithdrawalLifecycle(state, withdrawalId) {
|
|
5933
|
+
const internals = this.sdk;
|
|
5934
|
+
const client = internals.httpClient;
|
|
5935
|
+
if (!client?.authToken || typeof client.get !== "function" || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase()) return null;
|
|
5936
|
+
try {
|
|
5937
|
+
return mapWithdrawalLifecycle(await client.get(`/users/withdrawals/${encodeURIComponent(withdrawalId)}`));
|
|
5938
|
+
} catch {
|
|
5939
|
+
return null;
|
|
5940
|
+
}
|
|
4917
5941
|
}
|
|
4918
5942
|
// --- IAgent: Portfolio reads ---
|
|
4919
5943
|
async getFundingWallet(state, chainId) {
|
|
@@ -5143,16 +6167,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
5143
6167
|
}
|
|
5144
6168
|
const now = Date.now();
|
|
5145
6169
|
const pending = [...this.pendingRegistrations.get(scope)?.values() ?? []].map((entry) => entry.at);
|
|
5146
|
-
const
|
|
6170
|
+
const active = zyfaiHoldActive({
|
|
5147
6171
|
movements,
|
|
5148
6172
|
pendingRegistrations: pending,
|
|
5149
6173
|
lastCheckTimestamp: raw2?.data?.lastCheckTimestamp,
|
|
5150
6174
|
lastLogDate: raw2?.data?.lastLogDate?.[String(chainId)],
|
|
5151
6175
|
now
|
|
5152
6176
|
});
|
|
5153
|
-
if (
|
|
6177
|
+
if (active) this.holdActiveScopes.add(scope);
|
|
5154
6178
|
else this.holdActiveScopes.delete(scope);
|
|
5155
|
-
if (!
|
|
6179
|
+
if (!active) {
|
|
5156
6180
|
this.lastGoodEarnings.set(key2, raw2);
|
|
5157
6181
|
return { value: raw2, active: false };
|
|
5158
6182
|
}
|
|
@@ -6135,7 +7159,7 @@ function actionEntry(action) {
|
|
|
6135
7159
|
rebalanceLog: []
|
|
6136
7160
|
};
|
|
6137
7161
|
}
|
|
6138
|
-
var
|
|
7162
|
+
var TX_HASH3 = /^0x[a-fA-F0-9]{64}$/;
|
|
6139
7163
|
function allocationLegs(value, context, requireAmount) {
|
|
6140
7164
|
if (!Array.isArray(value)) return void 0;
|
|
6141
7165
|
const legs = [];
|
|
@@ -6164,7 +7188,7 @@ function allocationEntry(action, context) {
|
|
|
6164
7188
|
if (action.actionType.toLowerCase() !== "autoseek") return void 0;
|
|
6165
7189
|
const hash2 = action.details?.depositTransactionHash;
|
|
6166
7190
|
const withdrawHash = action.details?.withdrawTransactionHash;
|
|
6167
|
-
if (typeof hash2 !== "string" || !
|
|
7191
|
+
if (typeof hash2 !== "string" || !TX_HASH3.test(hash2)) return null;
|
|
6168
7192
|
const deposits = allocationLegs(action.details?.deposits, context, true);
|
|
6169
7193
|
const withdrawals = action.details?.withdrawals == null ? [] : allocationLegs(action.details.withdrawals, context, false);
|
|
6170
7194
|
if (!deposits?.length || !withdrawals) return null;
|
|
@@ -6177,7 +7201,7 @@ function allocationEntry(action, context) {
|
|
|
6177
7201
|
}));
|
|
6178
7202
|
const amountRaw = deposits.reduce((total, leg) => total + leg.rawAmount, 0n);
|
|
6179
7203
|
const rebalance = withdrawals.length > 0;
|
|
6180
|
-
const hashes = rebalance && typeof withdrawHash === "string" &&
|
|
7204
|
+
const hashes = rebalance && typeof withdrawHash === "string" && TX_HASH3.test(withdrawHash) && withdrawHash.toLowerCase() !== hash2.toLowerCase() ? [hash2, withdrawHash] : [hash2];
|
|
6181
7205
|
const source = withdrawals.length === 1 ? withdrawals[0] : {
|
|
6182
7206
|
protocol: [...new Set(withdrawals.map((leg) => leg.protocol))].join(" + "),
|
|
6183
7207
|
pool: withdrawals.map((leg) => leg.pool).filter(Boolean).join(" + ") || void 0
|
|
@@ -6569,6 +7593,7 @@ function operationHash(operation) {
|
|
|
6569
7593
|
}
|
|
6570
7594
|
});
|
|
6571
7595
|
}
|
|
7596
|
+
var YIELDSEEKER_WITHDRAW_SERVER_SUBMIT = 1;
|
|
6572
7597
|
function sponsorError(message, safeToFallback, details = {}) {
|
|
6573
7598
|
return new OwneyError(
|
|
6574
7599
|
"SPONSOR_REQUEST_FAILED",
|
|
@@ -6591,6 +7616,22 @@ function assertIntentFresh(expiresAt) {
|
|
|
6591
7616
|
{ failureCode: "SPONSOR_INTENT_EXPIRED" }
|
|
6592
7617
|
);
|
|
6593
7618
|
}
|
|
7619
|
+
var PAYMASTER_EXPIRY_MARGIN_SECONDS = 30;
|
|
7620
|
+
function paymasterValidUntil(paymasterAndData) {
|
|
7621
|
+
if (paymasterAndData.length < 2 + 64) return void 0;
|
|
7622
|
+
const validUntil = Number.parseInt(paymasterAndData.slice(42, 54), 16);
|
|
7623
|
+
const now = Date.now() / 1e3;
|
|
7624
|
+
return validUntil > now - 86400 && validUntil < now + 86400 ? validUntil : void 0;
|
|
7625
|
+
}
|
|
7626
|
+
function assertPaymasterFresh(paymasterAndData) {
|
|
7627
|
+
const validUntil = paymasterValidUntil(paymasterAndData);
|
|
7628
|
+
if (validUntil !== void 0 && Date.now() / 1e3 + PAYMASTER_EXPIRY_MARGIN_SECONDS >= validUntil)
|
|
7629
|
+
throw sponsorError(
|
|
7630
|
+
"The withdrawal sponsorship expired while waiting for the signature. Please try again.",
|
|
7631
|
+
true,
|
|
7632
|
+
{ failureCode: "SPONSOR_INTENT_EXPIRED" }
|
|
7633
|
+
);
|
|
7634
|
+
}
|
|
6594
7635
|
function providerReason(value) {
|
|
6595
7636
|
if (typeof value !== "string") return void 0;
|
|
6596
7637
|
return value.replace(/https?:\/\/\S+/gi, "[url]").replace(/0x[0-9a-fA-F]{40,}/g, "[hex]").replace(/[A-Za-z0-9_+=\/-]{48,}/g, "[token]").slice(0, 300);
|
|
@@ -6672,6 +7713,12 @@ async function checkpoint(callback, pending) {
|
|
|
6672
7713
|
);
|
|
6673
7714
|
}
|
|
6674
7715
|
}
|
|
7716
|
+
function reportAccepted(onAccepted, accepted) {
|
|
7717
|
+
try {
|
|
7718
|
+
onAccepted?.(accepted);
|
|
7719
|
+
} catch {
|
|
7720
|
+
}
|
|
7721
|
+
}
|
|
6675
7722
|
function finalPaymasterFields(value) {
|
|
6676
7723
|
const result = {};
|
|
6677
7724
|
for (const key2 of [
|
|
@@ -6750,15 +7797,57 @@ async function requestIntent(input) {
|
|
|
6750
7797
|
url.searchParams.set("intent", intent.intent);
|
|
6751
7798
|
return { intent, rpcUrl: url.toString() };
|
|
6752
7799
|
}
|
|
7800
|
+
var RPC_TIMEOUT_MS = 15e3;
|
|
7801
|
+
var SERVER_SUBMIT_SEND_TIMEOUT_MS = 25e3;
|
|
7802
|
+
function postRpc(url, method, params, timeoutMs) {
|
|
7803
|
+
return fetch(url, {
|
|
7804
|
+
method: "POST",
|
|
7805
|
+
headers: { "content-type": "application/json" },
|
|
7806
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
7807
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
7808
|
+
});
|
|
7809
|
+
}
|
|
7810
|
+
function rpcRejection(status, payload, method, phase) {
|
|
7811
|
+
const rejection = payload?.error;
|
|
7812
|
+
const diagnostics = rpcDiagnostics(rejection?.data);
|
|
7813
|
+
const executionReverted = rejection?.code === -32521 || diagnostics.failureCode === "SPONSOR_EXECUTION_REVERTED";
|
|
7814
|
+
const details = {
|
|
7815
|
+
statusCode: status,
|
|
7816
|
+
rpcCode: rejection?.code,
|
|
7817
|
+
rpcMethod: method,
|
|
7818
|
+
phase,
|
|
7819
|
+
providerReason: providerReason(rejection?.data?.providerReason) ?? providerReason(rejection?.message),
|
|
7820
|
+
failureCode: "RPC_REJECTED",
|
|
7821
|
+
...diagnostics
|
|
7822
|
+
};
|
|
7823
|
+
if (phase === "prepare" && executionReverted)
|
|
7824
|
+
return new OwneyError(
|
|
7825
|
+
"WITHDRAW_FAILED",
|
|
7826
|
+
"Yieldseeker withdrawal simulation reverted before submission.",
|
|
7827
|
+
{
|
|
7828
|
+
...details,
|
|
7829
|
+
failureCode: "SPONSOR_EXECUTION_REVERTED",
|
|
7830
|
+
safeToFallback: false,
|
|
7831
|
+
rejectedBeforeSubmission: true
|
|
7832
|
+
},
|
|
7833
|
+
"yieldseeker"
|
|
7834
|
+
);
|
|
7835
|
+
const definitiveRejection = phase === "send" && !executionReverted && status < 500 && !!rejection && (rejection.data?.rejectedBeforeSubmission === true || [-32501, -32502, -32503, -32504, -32505, -32507].includes(rejection.code ?? 0) || // AA3x: the paymaster failed validation (e.g. AA32 expired), so the
|
|
7836
|
+
// bundler never accepted it. AA2x stays ambiguous (AA25 can mean it ran).
|
|
7837
|
+
/\bAA3\d\b/.test(rejection.message ?? "") || /maxFeePerGas.*(?:too low|lower than|less than|must be at least)/i.test(rejection.message ?? ""));
|
|
7838
|
+
return sponsorError(
|
|
7839
|
+
phase !== "prepare" && !definitiveRejection ? "Sponsored withdrawal submission status is unknown." : "Withdrawal sponsorship was declined before submission.",
|
|
7840
|
+
phase === "prepare" || definitiveRejection,
|
|
7841
|
+
{
|
|
7842
|
+
...details,
|
|
7843
|
+
...phase === "prepare" || definitiveRejection ? { rejectedBeforeSubmission: true } : {}
|
|
7844
|
+
}
|
|
7845
|
+
);
|
|
7846
|
+
}
|
|
6753
7847
|
async function rpc(url, method, params, phase) {
|
|
6754
7848
|
let response;
|
|
6755
7849
|
try {
|
|
6756
|
-
response = await
|
|
6757
|
-
method: "POST",
|
|
6758
|
-
headers: { "content-type": "application/json" },
|
|
6759
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
6760
|
-
signal: AbortSignal.timeout(15e3)
|
|
6761
|
-
});
|
|
7850
|
+
response = await postRpc(url, method, params, RPC_TIMEOUT_MS);
|
|
6762
7851
|
} catch {
|
|
6763
7852
|
throw sponsorError(
|
|
6764
7853
|
"Withdrawal sponsorship request status is unknown.",
|
|
@@ -6772,43 +7861,37 @@ async function rpc(url, method, params, phase) {
|
|
|
6772
7861
|
);
|
|
6773
7862
|
}
|
|
6774
7863
|
const payload = await response.json().catch(() => null);
|
|
6775
|
-
if (!response.ok || payload?.error || payload?.result === void 0)
|
|
6776
|
-
|
|
6777
|
-
const diagnostics = rpcDiagnostics(rejection?.data);
|
|
6778
|
-
const executionReverted = rejection?.code === -32521 || diagnostics.failureCode === "SPONSOR_EXECUTION_REVERTED";
|
|
6779
|
-
const details = {
|
|
6780
|
-
statusCode: response.status,
|
|
6781
|
-
rpcCode: rejection?.code,
|
|
6782
|
-
rpcMethod: method,
|
|
6783
|
-
phase,
|
|
6784
|
-
providerReason: providerReason(rejection?.data?.providerReason) ?? providerReason(rejection?.message),
|
|
6785
|
-
failureCode: "RPC_REJECTED",
|
|
6786
|
-
...diagnostics
|
|
6787
|
-
};
|
|
6788
|
-
if (phase === "prepare" && executionReverted)
|
|
6789
|
-
throw new OwneyError(
|
|
6790
|
-
"WITHDRAW_FAILED",
|
|
6791
|
-
"Yieldseeker withdrawal simulation reverted before submission.",
|
|
6792
|
-
{
|
|
6793
|
-
...details,
|
|
6794
|
-
failureCode: "SPONSOR_EXECUTION_REVERTED",
|
|
6795
|
-
safeToFallback: false,
|
|
6796
|
-
rejectedBeforeSubmission: true
|
|
6797
|
-
},
|
|
6798
|
-
"yieldseeker"
|
|
6799
|
-
);
|
|
6800
|
-
const definitiveRejection = phase === "send" && !executionReverted && response.status < 500 && !!rejection && (rejection.data?.rejectedBeforeSubmission === true || [-32501, -32502, -32503, -32504, -32505, -32507].includes(rejection.code ?? 0) || /maxFeePerGas.*(?:too low|lower than|less than|must be at least)/i.test(rejection.message ?? ""));
|
|
6801
|
-
throw sponsorError(
|
|
6802
|
-
phase !== "prepare" && !definitiveRejection ? "Sponsored withdrawal submission status is unknown." : "Withdrawal sponsorship was declined before submission.",
|
|
6803
|
-
phase === "prepare" || definitiveRejection,
|
|
6804
|
-
{
|
|
6805
|
-
...details,
|
|
6806
|
-
...phase === "prepare" || definitiveRejection ? { rejectedBeforeSubmission: true } : {}
|
|
6807
|
-
}
|
|
6808
|
-
);
|
|
6809
|
-
}
|
|
7864
|
+
if (!response.ok || payload?.error || payload?.result === void 0)
|
|
7865
|
+
throw rpcRejection(response.status, payload, method, phase);
|
|
6810
7866
|
return payload.result;
|
|
6811
7867
|
}
|
|
7868
|
+
async function sendSignedUserOperation(rpcUrl, operation, operationId) {
|
|
7869
|
+
const method = "eth_sendUserOperation";
|
|
7870
|
+
const params = [operation, ENTRY_POINT_V06];
|
|
7871
|
+
if (!operationId) return { type: "accepted", hash: await rpc(rpcUrl, method, params, "send"), serverSubmit: false };
|
|
7872
|
+
const url = new URL(rpcUrl);
|
|
7873
|
+
url.searchParams.set("operationId", operationId);
|
|
7874
|
+
url.searchParams.set("serverSubmit", "1");
|
|
7875
|
+
let response;
|
|
7876
|
+
try {
|
|
7877
|
+
response = await postRpc(url.toString(), method, params, SERVER_SUBMIT_SEND_TIMEOUT_MS);
|
|
7878
|
+
} catch {
|
|
7879
|
+
return { type: "pending", serverSubmit: "unknown" };
|
|
7880
|
+
}
|
|
7881
|
+
const payload = await response.json().catch(() => null);
|
|
7882
|
+
if (!payload) return { type: "pending", serverSubmit: "unknown" };
|
|
7883
|
+
if (response.ok && !payload.error && payload.result !== void 0)
|
|
7884
|
+
return {
|
|
7885
|
+
type: "accepted",
|
|
7886
|
+
hash: payload.result,
|
|
7887
|
+
serverSubmit: payload.owney?.serverSubmit === true && payload.owney.state === "accepted"
|
|
7888
|
+
};
|
|
7889
|
+
const rejection = rpcRejection(response.status, payload, method, "send");
|
|
7890
|
+
if (rejection.details?.rejectedBeforeSubmission === true) throw rejection;
|
|
7891
|
+
if (payload.error?.data?.serverSubmit === true) return { type: "pending", serverSubmit: true };
|
|
7892
|
+
if (response.status >= 500) return { type: "pending", serverSubmit: "unknown" };
|
|
7893
|
+
throw rejection;
|
|
7894
|
+
}
|
|
6812
7895
|
async function waitForTransaction(client, txHash) {
|
|
6813
7896
|
const receipt = await client.waitForTransactionReceipt({ hash: txHash, confirmations: 1 });
|
|
6814
7897
|
if (receipt.status !== "success")
|
|
@@ -6890,6 +7973,7 @@ async function submitEoa(input) {
|
|
|
6890
7973
|
operation = { ...operation, signature };
|
|
6891
7974
|
await assertOwnerSession(input.state, input.owner);
|
|
6892
7975
|
assertIntentFresh(input.expiresAt);
|
|
7976
|
+
assertPaymasterFresh(operation.paymasterAndData);
|
|
6893
7977
|
const userOperation = {
|
|
6894
7978
|
sender: operation.sender,
|
|
6895
7979
|
nonce: operation.nonce,
|
|
@@ -6906,18 +7990,19 @@ async function submitEoa(input) {
|
|
|
6906
7990
|
input.assertCurrent?.();
|
|
6907
7991
|
await checkpoint(input.onBeforeSubmission, { userOpHash, userOperation });
|
|
6908
7992
|
input.assertCurrent?.();
|
|
6909
|
-
const
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
);
|
|
7993
|
+
const sent = await sendSignedUserOperation(input.rpcUrl, operation, input.operationId);
|
|
7994
|
+
const serverSubmit = sent.type === "pending" ? sent.serverSubmit : sent.serverSubmit ? true : void 0;
|
|
7995
|
+
if (serverSubmit) savePending(input.onPending, { userOpHash, serverSubmit });
|
|
7996
|
+
if (sent.type === "accepted") {
|
|
7997
|
+
const submittedHash = sent.hash;
|
|
7998
|
+
if (!isHash(submittedHash) || submittedHash.toLowerCase() !== userOpHash.toLowerCase())
|
|
7999
|
+
throw sponsorError(
|
|
8000
|
+
"Bundler returned a different UserOperation hash.",
|
|
8001
|
+
false,
|
|
8002
|
+
{ userOpHash, submittedHash, phase: "send", failureCode: "BUNDLER_HASH_MISMATCH" }
|
|
8003
|
+
);
|
|
8004
|
+
reportAccepted(input.onAccepted, { userOpHash });
|
|
8005
|
+
}
|
|
6921
8006
|
const deadline = Date.now() + OBSERVATION_WINDOW_MS;
|
|
6922
8007
|
let lastReadError;
|
|
6923
8008
|
for (let poll = 0; poll < 40 && Date.now() < deadline; poll++) {
|
|
@@ -6950,6 +8035,7 @@ async function submitEoa(input) {
|
|
|
6950
8035
|
userOpHash,
|
|
6951
8036
|
phase: "observe",
|
|
6952
8037
|
failureCode: "RECEIPT_UNRESOLVED",
|
|
8038
|
+
...serverSubmit ? { serverSubmit } : {},
|
|
6953
8039
|
...lastReadError instanceof OwneyError ? { lastObservationError: lastReadError.details } : {}
|
|
6954
8040
|
}
|
|
6955
8041
|
);
|
|
@@ -7010,6 +8096,7 @@ async function submitSmart(input) {
|
|
|
7010
8096
|
{ callsId: requestedId, phase: "send", failureCode: "WALLET_SEND_UNRESOLVED" }
|
|
7011
8097
|
);
|
|
7012
8098
|
savePending(input.onPending, { callsId });
|
|
8099
|
+
reportAccepted(input.onAccepted, { callsId });
|
|
7013
8100
|
const deadline = Date.now() + OBSERVATION_WINDOW_MS;
|
|
7014
8101
|
for (let poll = 0; poll < 40 && Date.now() < deadline; poll++) {
|
|
7015
8102
|
let status;
|
|
@@ -7303,6 +8390,9 @@ var YieldseekerAgent = class {
|
|
|
7303
8390
|
endpointReadFailures = /* @__PURE__ */ new Map();
|
|
7304
8391
|
reconcileUntil = /* @__PURE__ */ new Map();
|
|
7305
8392
|
allocationRefresh = /* @__PURE__ */ new Set();
|
|
8393
|
+
/** Contexts whose next permitted snapshot read is forced once: a funding
|
|
8394
|
+
* invalidation with a tx hash (receipt recovery, a re-armed refresh). */
|
|
8395
|
+
forceNextSnapshot = /* @__PURE__ */ new Set();
|
|
7306
8396
|
activityRefreshUntil = /* @__PURE__ */ new Map();
|
|
7307
8397
|
// null means a confirmed movement has no reliable baseline. Do not guess
|
|
7308
8398
|
// settlement; keep slow reconciliation after the fast window expires.
|
|
@@ -7709,6 +8799,7 @@ var YieldseekerAgent = class {
|
|
|
7709
8799
|
this.endpointReadFailures.clear();
|
|
7710
8800
|
this.reconcileUntil.clear();
|
|
7711
8801
|
this.allocationRefresh.clear();
|
|
8802
|
+
this.forceNextSnapshot.clear();
|
|
7712
8803
|
this.activityRefreshUntil.clear();
|
|
7713
8804
|
this.snapshotMovements.clear();
|
|
7714
8805
|
this.movementEpochs.clear();
|
|
@@ -7822,7 +8913,7 @@ var YieldseekerAgent = class {
|
|
|
7822
8913
|
notifyFunding(onProgress, evidence);
|
|
7823
8914
|
return { txHash, smartWallet: context.wallet.walletAddress, amount, evidence };
|
|
7824
8915
|
}
|
|
7825
|
-
async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto", onBeforeSubmission, assertOperation) {
|
|
8916
|
+
async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto", onBeforeSubmission, assertOperation, submission) {
|
|
7826
8917
|
let ownerTransferAttempted = false;
|
|
7827
8918
|
let evidence = {
|
|
7828
8919
|
agentId: this.id,
|
|
@@ -7835,12 +8926,23 @@ var YieldseekerAgent = class {
|
|
|
7835
8926
|
delivery: "pending"
|
|
7836
8927
|
};
|
|
7837
8928
|
try {
|
|
7838
|
-
return await this.withdrawInternal(
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
8929
|
+
return await this.withdrawInternal(
|
|
8930
|
+
state,
|
|
8931
|
+
chainId,
|
|
8932
|
+
asset,
|
|
8933
|
+
amount,
|
|
8934
|
+
(next) => {
|
|
8935
|
+
evidence = { ...evidence, ...next };
|
|
8936
|
+
notifyFunding(onProgress, evidence);
|
|
8937
|
+
},
|
|
8938
|
+
() => {
|
|
8939
|
+
ownerTransferAttempted = true;
|
|
8940
|
+
},
|
|
8941
|
+
gasMode,
|
|
8942
|
+
onBeforeSubmission,
|
|
8943
|
+
assertOperation,
|
|
8944
|
+
submission?.operationId
|
|
8945
|
+
);
|
|
7844
8946
|
} catch (error) {
|
|
7845
8947
|
if (error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED") {
|
|
7846
8948
|
this.withdrawalVersionCache.clear();
|
|
@@ -7856,7 +8958,7 @@ var YieldseekerAgent = class {
|
|
|
7856
8958
|
throw error;
|
|
7857
8959
|
}
|
|
7858
8960
|
}
|
|
7859
|
-
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted, gasMode = "auto", onBeforeSubmission, assertOperation) {
|
|
8961
|
+
async withdrawInternal(state, chainId, asset, amount, onProgress, onOwnerTransferAttempted, gasMode = "auto", onBeforeSubmission, assertOperation, operationId) {
|
|
7860
8962
|
this.assertChain(chainId);
|
|
7861
8963
|
this.assertAsset(asset);
|
|
7862
8964
|
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
@@ -7988,13 +9090,13 @@ var YieldseekerAgent = class {
|
|
|
7988
9090
|
const verifyKnownPositions = async (blockNumber, values = [...knownPositions.values()], timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
|
|
7989
9091
|
const verifiedPositions = [];
|
|
7990
9092
|
if (timeoutMs <= 0) return { verified: false, positions: verifiedPositions };
|
|
7991
|
-
let
|
|
9093
|
+
let active = true;
|
|
7992
9094
|
const checks = this.readVaultShares(values.map((position2) => position2.vaultOption), agentWallet, blockNumber).catch(() => []);
|
|
7993
9095
|
const reads = values.map(async (position2, index) => {
|
|
7994
9096
|
const address = getAddress6(position2.vaultOption.address);
|
|
7995
9097
|
try {
|
|
7996
9098
|
const group = (await checks).slice(index * 2, index * 2 + 2);
|
|
7997
|
-
if (!
|
|
9099
|
+
if (!active || group.length !== 2 || group.some((value) => value.status !== "success")) return null;
|
|
7998
9100
|
this.assertSession(generation, chainId, asset);
|
|
7999
9101
|
const [underlying, shares] = group.map((value) => value.result);
|
|
8000
9102
|
if (typeof underlying !== "string" || !isAddress4(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n) return null;
|
|
@@ -8007,7 +9109,7 @@ var YieldseekerAgent = class {
|
|
|
8007
9109
|
shares,
|
|
8008
9110
|
blockNumber
|
|
8009
9111
|
);
|
|
8010
|
-
if (!
|
|
9112
|
+
if (!active || value === void 0) return null;
|
|
8011
9113
|
this.assertSession(generation, chainId, asset);
|
|
8012
9114
|
verified.withdrawableAssetsRaw = value.toString();
|
|
8013
9115
|
verifiedPositions.push(verified);
|
|
@@ -8024,7 +9126,7 @@ var YieldseekerAgent = class {
|
|
|
8024
9126
|
args: [shares],
|
|
8025
9127
|
blockNumber
|
|
8026
9128
|
});
|
|
8027
|
-
if (!
|
|
9129
|
+
if (!active || typeof value !== "bigint" || value < 0n) return null;
|
|
8028
9130
|
this.assertSession(generation, chainId, asset);
|
|
8029
9131
|
if (value > 0n && !positiveValue) {
|
|
8030
9132
|
positiveValue = true;
|
|
@@ -8052,19 +9154,20 @@ var YieldseekerAgent = class {
|
|
|
8052
9154
|
}
|
|
8053
9155
|
});
|
|
8054
9156
|
const result = await boundedVerification(Promise.all(reads), timeoutMs);
|
|
8055
|
-
|
|
9157
|
+
active = false;
|
|
8056
9158
|
this.assertSession(generation, chainId, asset);
|
|
8057
9159
|
return {
|
|
8058
9160
|
verified: result !== void 0 && result.every((position2) => position2 !== null),
|
|
8059
9161
|
positions: [...verifiedPositions]
|
|
8060
9162
|
};
|
|
8061
9163
|
};
|
|
8062
|
-
const refreshPositions = async () => {
|
|
9164
|
+
const refreshPositions = async (promptless = false) => {
|
|
8063
9165
|
this.assertSession(generation, chainId, asset);
|
|
8064
9166
|
const response = await this.walletRequest(
|
|
8065
9167
|
state,
|
|
8066
9168
|
chainId,
|
|
8067
|
-
this.agentPath(context, "vault-positions")
|
|
9169
|
+
this.agentPath(context, "vault-positions"),
|
|
9170
|
+
{ promptless }
|
|
8068
9171
|
);
|
|
8069
9172
|
this.assertSession(generation, chainId, asset);
|
|
8070
9173
|
if (!Array.isArray(response?.vaultPositions)) throw this.invalidResponse("vault positions");
|
|
@@ -8295,6 +9398,8 @@ var YieldseekerAgent = class {
|
|
|
8295
9398
|
agentWallet: getAddress6(context.wallet.walletAddress),
|
|
8296
9399
|
asset: metadata.address,
|
|
8297
9400
|
callData: data,
|
|
9401
|
+
// The EOA send opts in to the server-owned send with it.
|
|
9402
|
+
operationId,
|
|
8298
9403
|
onPending: (pending) => {
|
|
8299
9404
|
submissionEvidence = { ...submissionEvidence, ...pending };
|
|
8300
9405
|
notifyFunding(onProgress, submissionEvidence);
|
|
@@ -8303,6 +9408,11 @@ var YieldseekerAgent = class {
|
|
|
8303
9408
|
assertOperation?.();
|
|
8304
9409
|
await onBeforeSubmission?.({ ...base7, ...pending });
|
|
8305
9410
|
onOwnerTransferAttempted?.();
|
|
9411
|
+
},
|
|
9412
|
+
// The owner's signed operation was accepted: another agent may start.
|
|
9413
|
+
onAccepted: (accepted) => {
|
|
9414
|
+
submissionEvidence = { ...submissionEvidence, ...accepted, ownerAuthorization: "accepted" };
|
|
9415
|
+
notifyFunding(onProgress, submissionEvidence);
|
|
8306
9416
|
}
|
|
8307
9417
|
});
|
|
8308
9418
|
txHash = submitted.txHash;
|
|
@@ -8319,7 +9429,10 @@ var YieldseekerAgent = class {
|
|
|
8319
9429
|
value: "0",
|
|
8320
9430
|
chainId
|
|
8321
9431
|
},
|
|
8322
|
-
(hash2) =>
|
|
9432
|
+
(hash2) => {
|
|
9433
|
+
submissionEvidence = { ...submissionEvidence, txHash: hash2, ownerAuthorization: "accepted" };
|
|
9434
|
+
notifyFunding(onProgress, submissionEvidence);
|
|
9435
|
+
},
|
|
8323
9436
|
(value) => {
|
|
8324
9437
|
receipt = value;
|
|
8325
9438
|
},
|
|
@@ -8350,7 +9463,9 @@ var YieldseekerAgent = class {
|
|
|
8350
9463
|
const verificationDeadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
|
|
8351
9464
|
const knownAtPayout = new Set(knownPositions.keys());
|
|
8352
9465
|
const [catalogVerified, knownProof] = await Promise.all([
|
|
8353
|
-
|
|
9466
|
+
// Never a sign-in prompt here: a refused or missing cached sign-in
|
|
9467
|
+
// leaves the catalog unverified, the existing unverified-MAX outcome.
|
|
9468
|
+
boundedVerification(refreshPositions(true).then((values) => {
|
|
8354
9469
|
rememberPositions(values);
|
|
8355
9470
|
return true;
|
|
8356
9471
|
}).catch(() => false)),
|
|
@@ -8395,7 +9510,8 @@ var YieldseekerAgent = class {
|
|
|
8395
9510
|
);
|
|
8396
9511
|
const confirmedAmount = evidence.confirmedAmount && /^\d+$/.test(evidence.confirmedAmount) ? BigInt(evidence.confirmedAmount) : void 0;
|
|
8397
9512
|
const feeIncrease = ownerFunds.fees - initialFunds.fees;
|
|
8398
|
-
const
|
|
9513
|
+
const feeShortfall = requested - payout;
|
|
9514
|
+
const feeAdjustment = amount !== void 0 && receipt && initialFunds.blockNumber !== void 0 && latestUnwindBlock !== void 0 && initialFunds.blockNumber <= latestUnwindBlock && receipt.blockNumber >= latestUnwindBlock && evidence.transfer === "confirmed" && evidence.delivery === "confirmed" && confirmedAmount === payout && feeIncrease > 0n && feeIncrease <= maxFee && feeShortfall > 0n && feeShortfall <= feeIncrease ? { grossAmount: requested.toString(), feeAmount: feeShortfall.toString() } : void 0;
|
|
8399
9515
|
return {
|
|
8400
9516
|
txHash,
|
|
8401
9517
|
type: amount === void 0 && !completion ? "full" : "partial",
|
|
@@ -8457,14 +9573,14 @@ var YieldseekerAgent = class {
|
|
|
8457
9573
|
}
|
|
8458
9574
|
/** Recheck MAX without re-unwinding, paying out, or requesting a signature. */
|
|
8459
9575
|
async verifyWithdrawalCompletion(state, evidence) {
|
|
8460
|
-
const
|
|
9576
|
+
const unknown2 = (reason2) => ({ status: "unavailable", reason: reason2 });
|
|
8461
9577
|
const proof = evidence.maxCompletion;
|
|
8462
|
-
if (evidence.agentId !== this.id || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || evidence.chainId !== 8453 || !proof || !evidence.agentWallet || !isAddress4(evidence.agentWallet) || !evidence.blockNumber || !/^\d+$/.test(evidence.blockNumber) || evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || !this.isTransactionHash(evidence.txHash)) return
|
|
9578
|
+
if (evidence.agentId !== this.id || evidence.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || evidence.chainId !== 8453 || !proof || !evidence.agentWallet || !isAddress4(evidence.agentWallet) || !evidence.blockNumber || !/^\d+$/.test(evidence.blockNumber) || evidence.transfer !== "confirmed" || evidence.delivery !== "confirmed" || !this.isTransactionHash(evidence.txHash)) return unknown2("PAYOUT_PROOF_UNAVAILABLE");
|
|
8463
9579
|
this.assertAsset(evidence.token);
|
|
8464
9580
|
const asset = evidence.token;
|
|
8465
9581
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
8466
9582
|
const signature = this.auth.peekToken(state, evidence.chainId);
|
|
8467
|
-
if (!signature) return
|
|
9583
|
+
if (!signature) return unknown2("AUTHENTICATION_REQUIRED");
|
|
8468
9584
|
let reason = "CATALOG_UNAVAILABLE";
|
|
8469
9585
|
try {
|
|
8470
9586
|
return await bounded(async () => {
|
|
@@ -8472,7 +9588,7 @@ var YieldseekerAgent = class {
|
|
|
8472
9588
|
`/users/${encodeURIComponent(proof.userId)}/agents/${encodeURIComponent(proof.agentId)}/vault-positions`,
|
|
8473
9589
|
{ signature }
|
|
8474
9590
|
);
|
|
8475
|
-
if (!Array.isArray(response?.vaultPositions)) return
|
|
9591
|
+
if (!Array.isArray(response?.vaultPositions)) return unknown2("CATALOG_INVALID");
|
|
8476
9592
|
const positions = this.validateWithdrawalPositions(response.vaultPositions, evidence.chainId, asset);
|
|
8477
9593
|
const vaults = new Map(proof.vaults.map((address) => [address.toLowerCase(), { address }]));
|
|
8478
9594
|
positions.forEach((p) => vaults.set(p.vaultOption.address.toLowerCase(), p.vaultOption));
|
|
@@ -8480,14 +9596,14 @@ var YieldseekerAgent = class {
|
|
|
8480
9596
|
if (needsInventory || [...vaults.values()].some((option) => !option.provider)) {
|
|
8481
9597
|
const catalog = await this.loadVaultOptions(asset).catch(() => []);
|
|
8482
9598
|
const eligible = catalog.filter((o) => o.chainId === evidence.chainId && o.assetAddress?.toLowerCase() === metadata.address.toLowerCase() && isAddress4(o.address));
|
|
8483
|
-
if (needsInventory && !eligible.length) return
|
|
9599
|
+
if (needsInventory && !eligible.length) return unknown2("INVENTORY_INCOMPLETE");
|
|
8484
9600
|
for (const option of eligible) {
|
|
8485
9601
|
const key2 = option.address.toLowerCase();
|
|
8486
9602
|
if (needsInventory || vaults.has(key2)) vaults.set(key2, option);
|
|
8487
9603
|
}
|
|
8488
9604
|
}
|
|
8489
9605
|
if (vaults.size > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT || [...vaults.keys()].some((a) => !isAddress4(a)))
|
|
8490
|
-
return
|
|
9606
|
+
return unknown2("INVENTORY_INVALID");
|
|
8491
9607
|
reason = "VAULT_PROOF_UNAVAILABLE";
|
|
8492
9608
|
const blockNumber = BigInt(evidence.blockNumber);
|
|
8493
9609
|
const options = [...vaults.values()];
|
|
@@ -8518,7 +9634,7 @@ var YieldseekerAgent = class {
|
|
|
8518
9634
|
};
|
|
8519
9635
|
}, 8e3, () => new Error("MAX proof read timed out"));
|
|
8520
9636
|
} catch {
|
|
8521
|
-
return
|
|
9637
|
+
return unknown2(reason);
|
|
8522
9638
|
}
|
|
8523
9639
|
}
|
|
8524
9640
|
async readVaultShares(options, wallet, blockNumber) {
|
|
@@ -8599,12 +9715,12 @@ var YieldseekerAgent = class {
|
|
|
8599
9715
|
{ asset: context.asset, reason: "YIELDSEEKER_LIQUIDITY_UNVERIFIED" },
|
|
8600
9716
|
this.id
|
|
8601
9717
|
);
|
|
8602
|
-
let
|
|
9718
|
+
let active = true;
|
|
8603
9719
|
let knownProof;
|
|
8604
9720
|
const hasUnverifiedIndexedFunds = (proof) => proof.indexedPositions.some((position2) => !proof.resolvedVaults.includes(position2.vaultOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) > 0n);
|
|
8605
9721
|
const assertReadCurrent = () => {
|
|
8606
9722
|
this.assertSession(generation, chainId, context.asset);
|
|
8607
|
-
if (!
|
|
9723
|
+
if (!active || (this.liquidityVersions.get(contextKey) ?? 0) !== version) throw unavailable();
|
|
8608
9724
|
};
|
|
8609
9725
|
const read2 = async () => {
|
|
8610
9726
|
const client = this.getReceiptClient();
|
|
@@ -8793,7 +9909,7 @@ var YieldseekerAgent = class {
|
|
|
8793
9909
|
if (error instanceof OwneyError) throw error;
|
|
8794
9910
|
throw unavailable();
|
|
8795
9911
|
} finally {
|
|
8796
|
-
|
|
9912
|
+
active = false;
|
|
8797
9913
|
if (timer) clearTimeout(timer);
|
|
8798
9914
|
}
|
|
8799
9915
|
}
|
|
@@ -8878,6 +9994,7 @@ var YieldseekerAgent = class {
|
|
|
8878
9994
|
this.assertAsset(asset);
|
|
8879
9995
|
const until = Math.min(refreshUntil, Date.now() + YIELDSEEKER_SETTLEMENT_SNAPSHOT_WINDOW_MS);
|
|
8880
9996
|
const activityUntil = Math.min(refreshUntil, Date.now() + YIELDSEEKER_SETTLEMENT_WINDOW_MS);
|
|
9997
|
+
const hash2 = typeof txHash === "string" && /^0x[0-9a-fA-F]{64}$/.test(txHash);
|
|
8881
9998
|
this.invalidateSlot(state, chainId, asset, (key2) => {
|
|
8882
9999
|
const failure = this.snapshotFailures.get(key2);
|
|
8883
10000
|
this.advancePortfolioVersion(key2);
|
|
@@ -8889,6 +10006,7 @@ var YieldseekerAgent = class {
|
|
|
8889
10006
|
}
|
|
8890
10007
|
this.reconcileUntil.set(key2, Math.max(this.reconcileUntil.get(key2) ?? 0, until));
|
|
8891
10008
|
this.activityRefreshUntil.set(key2, Math.max(this.activityRefreshUntil.get(key2) ?? 0, activityUntil));
|
|
10009
|
+
if (hash2) this.forceNextSnapshot.add(key2);
|
|
8892
10010
|
});
|
|
8893
10011
|
}
|
|
8894
10012
|
endFundingBalanceRefresh(state, chainId, asset, read2) {
|
|
@@ -9724,7 +10842,7 @@ var YieldseekerAgent = class {
|
|
|
9724
10842
|
return this.walletRequest(
|
|
9725
10843
|
state,
|
|
9726
10844
|
chainId,
|
|
9727
|
-
|
|
10845
|
+
this.snapshotPath(context, forceRefresh)
|
|
9728
10846
|
).then((response) => {
|
|
9729
10847
|
if (!response?.agentSnapshot) {
|
|
9730
10848
|
throw this.invalidResponse("agent snapshot");
|
|
@@ -9754,6 +10872,16 @@ var YieldseekerAgent = class {
|
|
|
9754
10872
|
throw error;
|
|
9755
10873
|
});
|
|
9756
10874
|
}
|
|
10875
|
+
snapshotPath(context, forceRefresh) {
|
|
10876
|
+
return `${this.agentPath(context, "snapshot")}${query(forceRefresh ? { shouldForceRefresh: true } : { shouldOnlyUseRecentValue: true, shouldAllowStaleOnError: true })}`;
|
|
10877
|
+
}
|
|
10878
|
+
/** True while `walletRequest` would refuse a read of `path`: this user's
|
|
10879
|
+
* rate-limit cooldown or that endpoint's outage cooldown. */
|
|
10880
|
+
readCoolingDown(state, chainId, path) {
|
|
10881
|
+
const userKey = this.userKey(state, chainId);
|
|
10882
|
+
const now = Date.now();
|
|
10883
|
+
return (this.standardReadFailures.get(userKey)?.retryAt ?? 0) > now || (this.endpointReadFailures.get(`${userKey}:${path}`)?.retryAt ?? 0) > now;
|
|
10884
|
+
}
|
|
9757
10885
|
portfolioSnapshot(state, chainId, context, contextKey) {
|
|
9758
10886
|
const key2 = `snapshot:${contextKey}`;
|
|
9759
10887
|
const cached = this.readCache.get(key2);
|
|
@@ -9772,6 +10900,7 @@ var YieldseekerAgent = class {
|
|
|
9772
10900
|
const settling = inWindow && !this.deferredToLedger(contextKey);
|
|
9773
10901
|
if (inWindow) this.settlementChecks.set(contextKey, (this.settlementChecks.get(contextKey) ?? 0) + 1);
|
|
9774
10902
|
const ready = this.readyForces.delete(contextKey) && this.noteReadyForce(contextKey);
|
|
10903
|
+
const movementForce = this.forceNextSnapshot.has(contextKey) && !this.readCoolingDown(state, chainId, this.snapshotPath(context, true)) && this.forceNextSnapshot.delete(contextKey);
|
|
9775
10904
|
return this.requestPortfolioSnapshot(
|
|
9776
10905
|
state,
|
|
9777
10906
|
chainId,
|
|
@@ -9779,7 +10908,7 @@ var YieldseekerAgent = class {
|
|
|
9779
10908
|
contextKey,
|
|
9780
10909
|
version,
|
|
9781
10910
|
generation,
|
|
9782
|
-
allocationChanged || settling || ready
|
|
10911
|
+
allocationChanged || settling || ready || movementForce
|
|
9783
10912
|
);
|
|
9784
10913
|
})
|
|
9785
10914
|
};
|
|
@@ -10229,7 +11358,7 @@ var YieldseekerAgent = class {
|
|
|
10229
11358
|
}
|
|
10230
11359
|
const ttl = this.portfolioCacheMs(contextKey);
|
|
10231
11360
|
this.settlementChecks.set(contextKey, 1);
|
|
10232
|
-
const force =
|
|
11361
|
+
const force = true;
|
|
10233
11362
|
await this.cachedRead(
|
|
10234
11363
|
`snapshot:${contextKey}`,
|
|
10235
11364
|
ttl,
|
|
@@ -10284,13 +11413,22 @@ var YieldseekerAgent = class {
|
|
|
10284
11413
|
options.assertCurrent?.();
|
|
10285
11414
|
return this.api.request(path, { method: options.method, body: options.body, signature: signature2 });
|
|
10286
11415
|
};
|
|
10287
|
-
|
|
11416
|
+
const cached = options.promptless ? this.auth.peekToken(state, chainId) : void 0;
|
|
11417
|
+
if (options.promptless && !cached)
|
|
11418
|
+
throw new OwneyError(
|
|
11419
|
+
"AGENT_AUTH_FAILED",
|
|
11420
|
+
"No Yieldseeker sign-in is available without a prompt.",
|
|
11421
|
+
{ reason: "YIELDSEEKER_SIGN_IN_REQUIRED" },
|
|
11422
|
+
this.id
|
|
11423
|
+
);
|
|
11424
|
+
let signature = cached ?? await this.auth.getToken(state, chainId);
|
|
10288
11425
|
try {
|
|
10289
11426
|
return await request2(signature);
|
|
10290
11427
|
} catch (error) {
|
|
10291
11428
|
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
10292
11429
|
if (error.providerCode === "NO_USER") throw error;
|
|
10293
11430
|
if (!error.isAuthenticationError) throw error;
|
|
11431
|
+
if (options.promptless) throw error;
|
|
10294
11432
|
signature = await this.auth.refreshToken(state, chainId, signature);
|
|
10295
11433
|
try {
|
|
10296
11434
|
return await request2(signature);
|
|
@@ -10518,6 +11656,16 @@ var YieldseekerAgent = class {
|
|
|
10518
11656
|
};
|
|
10519
11657
|
|
|
10520
11658
|
// src/lib/swap/swap-api.ts
|
|
11659
|
+
var FUSION_SECRET_SERVER_PUBLISH = 1;
|
|
11660
|
+
var FUSION_SECRET_HANDOFF_TIMEOUT_MS = 5e3;
|
|
11661
|
+
function handoffFailure(error) {
|
|
11662
|
+
const statusCode2 = error instanceof OwneyError && typeof error.details?.statusCode === "number" ? error.details.statusCode : void 0;
|
|
11663
|
+
return new OwneyError(
|
|
11664
|
+
error instanceof OwneyError ? error.code : "SWAP_REQUEST_FAILED",
|
|
11665
|
+
"The swap secret handoff did not complete.",
|
|
11666
|
+
statusCode2 === void 0 ? {} : { statusCode: statusCode2 }
|
|
11667
|
+
);
|
|
11668
|
+
}
|
|
10521
11669
|
async function request(baseUrl, apiKey, path, init) {
|
|
10522
11670
|
const url = `${baseUrl}/api/v1/swap${path}`;
|
|
10523
11671
|
const controller = new AbortController();
|
|
@@ -10530,7 +11678,8 @@ async function request(baseUrl, apiKey, path, init) {
|
|
|
10530
11678
|
"Content-Type": "application/json",
|
|
10531
11679
|
"x-owney-api-key": apiKey
|
|
10532
11680
|
},
|
|
10533
|
-
...init ? { body: JSON.stringify(init.body) } : {}
|
|
11681
|
+
...init ? { body: JSON.stringify(init.body) } : {},
|
|
11682
|
+
...init?.redirect ? { redirect: init.redirect } : {}
|
|
10534
11683
|
});
|
|
10535
11684
|
if (!res.ok) {
|
|
10536
11685
|
const text = await res.text().catch(() => "");
|
|
@@ -10563,7 +11712,7 @@ async function request(baseUrl, apiKey, path, init) {
|
|
|
10563
11712
|
);
|
|
10564
11713
|
}
|
|
10565
11714
|
return json.data;
|
|
10566
|
-
}, 2e4, () => new OwneyError(
|
|
11715
|
+
}, init?.timeoutMs ?? 2e4, () => new OwneyError(
|
|
10567
11716
|
"SWAP_REQUEST_FAILED",
|
|
10568
11717
|
"Swap request timed out. Its outcome may still need confirmation.",
|
|
10569
11718
|
{ requestTimedOut: true, phase: path === "/order" ? "submit" : "read" }
|
|
@@ -10647,8 +11796,9 @@ function createSwapApi(baseUrl, apiKey) {
|
|
|
10647
11796
|
/**
|
|
10648
11797
|
* Builds a Fusion+ order server-side and returns EIP-712 typed data.
|
|
10649
11798
|
*
|
|
10650
|
-
* Only HASHES go over the wire. The preimages
|
|
10651
|
-
* see
|
|
11799
|
+
* Only HASHES go over the wire. The preimages leave the browser only
|
|
11800
|
+
* through `handoffSecrets`, and only with the caller's opt-in — see
|
|
11801
|
+
* swap.secrets.
|
|
10652
11802
|
*/
|
|
10653
11803
|
buildOrder: (params) => request(baseUrl, apiKey, "/order/build", {
|
|
10654
11804
|
method: "POST",
|
|
@@ -10675,6 +11825,23 @@ function createSwapApi(baseUrl, apiKey) {
|
|
|
10675
11825
|
method: "POST",
|
|
10676
11826
|
body: { orderHash, secret }
|
|
10677
11827
|
}),
|
|
11828
|
+
/**
|
|
11829
|
+
* Hands a signed Fusion+ order's secrets to our routing API, which checks
|
|
11830
|
+
* them against the order and publishes each one only once 1inch reports
|
|
11831
|
+
* its destination escrow ready (part 5). The browser keeps publishing too;
|
|
11832
|
+
* a double publish is a no-op upstream.
|
|
11833
|
+
*
|
|
11834
|
+
* Only our own API: a redirect is refused, since the body carries the
|
|
11835
|
+
* preimages. Bounded to 5 s.
|
|
11836
|
+
*/
|
|
11837
|
+
handoffSecrets: (body) => request(baseUrl, apiKey, "/order/secrets", {
|
|
11838
|
+
method: "POST",
|
|
11839
|
+
body,
|
|
11840
|
+
redirect: "error",
|
|
11841
|
+
timeoutMs: FUSION_SECRET_HANDOFF_TIMEOUT_MS
|
|
11842
|
+
}).catch((error) => {
|
|
11843
|
+
throw handoffFailure(error);
|
|
11844
|
+
}),
|
|
10678
11845
|
/**
|
|
10679
11846
|
* @param chainId present only for a SAME-CHAIN order. That product keys its
|
|
10680
11847
|
* orders per chain and the Fusion+ endpoint does not know them, so asking
|
|
@@ -10858,14 +12025,14 @@ function swapChain(chainId) {
|
|
|
10858
12025
|
}
|
|
10859
12026
|
|
|
10860
12027
|
// src/lib/swap/swap.arrival.ts
|
|
10861
|
-
var
|
|
12028
|
+
var DEFAULT_TIMEOUT_MS2 = 18e4;
|
|
10862
12029
|
var DEFAULT_POLL_MS = 4e3;
|
|
10863
12030
|
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
10864
12031
|
async function awaitBalanceIncrease(options) {
|
|
10865
12032
|
const {
|
|
10866
12033
|
readBalance,
|
|
10867
12034
|
baseline,
|
|
10868
|
-
timeoutMs =
|
|
12035
|
+
timeoutMs = DEFAULT_TIMEOUT_MS2,
|
|
10869
12036
|
pollMs = DEFAULT_POLL_MS
|
|
10870
12037
|
} = options;
|
|
10871
12038
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -10959,14 +12126,14 @@ var isSwapTerminal = (status) => SWAP_TERMINAL_STATUSES.includes(status);
|
|
|
10959
12126
|
var DEFAULT_POLL_MS2 = 5e3;
|
|
10960
12127
|
var MAX_BACKOFF_MS = 3e4;
|
|
10961
12128
|
var backoffFor = (failures, base7) => Math.min(base7 * 2 ** Math.min(failures, 5), MAX_BACKOFF_MS);
|
|
10962
|
-
var
|
|
12129
|
+
var DEFAULT_TIMEOUT_MS3 = 15 * 60 * 1e3;
|
|
10963
12130
|
async function runFusionOrderInternal(deps, options) {
|
|
10964
12131
|
const {
|
|
10965
12132
|
orderHash,
|
|
10966
12133
|
secrets,
|
|
10967
12134
|
onStage,
|
|
10968
12135
|
pollIntervalMs = DEFAULT_POLL_MS2,
|
|
10969
|
-
timeoutMs =
|
|
12136
|
+
timeoutMs = DEFAULT_TIMEOUT_MS3
|
|
10970
12137
|
} = options;
|
|
10971
12138
|
const deadline = deps.now() + timeoutMs;
|
|
10972
12139
|
let partial = options.hasPartialFill === true;
|
|
@@ -11047,14 +12214,14 @@ async function runFusionOrderInternal(deps, options) {
|
|
|
11047
12214
|
}
|
|
11048
12215
|
}
|
|
11049
12216
|
function runFusionOrder(deps, options) {
|
|
11050
|
-
return observeOrder(options.orderHash, () => runFusionOrderInternal(deps, options));
|
|
12217
|
+
return observeOrder(options.orderHash, () => runFusionOrderInternal(deps, options), { wait: options.waitForWatcher === true });
|
|
11051
12218
|
}
|
|
11052
12219
|
|
|
11053
12220
|
// src/lib/swap/swap.same-chain-runner.ts
|
|
11054
12221
|
var DEFAULT_POLL_MS3 = 5e3;
|
|
11055
12222
|
var MAX_BACKOFF_MS2 = 3e4;
|
|
11056
12223
|
var backoffFor2 = (failures, base7) => Math.min(base7 * 2 ** Math.min(failures, 5), MAX_BACKOFF_MS2);
|
|
11057
|
-
var
|
|
12224
|
+
var DEFAULT_TIMEOUT_MS4 = 3 * 60 * 1e3;
|
|
11058
12225
|
function explain(status) {
|
|
11059
12226
|
switch (status) {
|
|
11060
12227
|
case "expired":
|
|
@@ -11078,7 +12245,7 @@ async function runSameChainOrderInternal(deps, options) {
|
|
|
11078
12245
|
orderHash,
|
|
11079
12246
|
onStage,
|
|
11080
12247
|
pollIntervalMs = DEFAULT_POLL_MS3,
|
|
11081
|
-
timeoutMs =
|
|
12248
|
+
timeoutMs = DEFAULT_TIMEOUT_MS4
|
|
11082
12249
|
} = options;
|
|
11083
12250
|
const deadline = deps.now() + timeoutMs;
|
|
11084
12251
|
let partial = options.hasPartialFill === true;
|
|
@@ -11131,7 +12298,7 @@ async function runSameChainOrderInternal(deps, options) {
|
|
|
11131
12298
|
}
|
|
11132
12299
|
}
|
|
11133
12300
|
function runSameChainOrder(deps, options) {
|
|
11134
|
-
return observeOrder(options.orderHash, () => runSameChainOrderInternal(deps, options));
|
|
12301
|
+
return observeOrder(options.orderHash, () => runSameChainOrderInternal(deps, options), { wait: options.waitForWatcher === true });
|
|
11135
12302
|
}
|
|
11136
12303
|
|
|
11137
12304
|
// src/lib/swap/swap.secret-store.ts
|
|
@@ -11156,6 +12323,21 @@ function saveOrder(order) {
|
|
|
11156
12323
|
return false;
|
|
11157
12324
|
}
|
|
11158
12325
|
}
|
|
12326
|
+
function loadOrder(orderHash) {
|
|
12327
|
+
const store = storage5();
|
|
12328
|
+
if (!store) return null;
|
|
12329
|
+
try {
|
|
12330
|
+
const raw2 = store.getItem(keyFor2(orderHash));
|
|
12331
|
+
if (!raw2) return null;
|
|
12332
|
+
const parsed = JSON.parse(raw2);
|
|
12333
|
+
if (!Array.isArray(parsed.secrets) || parsed.secrets.length === 0) {
|
|
12334
|
+
return null;
|
|
12335
|
+
}
|
|
12336
|
+
return parsed;
|
|
12337
|
+
} catch {
|
|
12338
|
+
return null;
|
|
12339
|
+
}
|
|
12340
|
+
}
|
|
11159
12341
|
function clearOrder(orderHash) {
|
|
11160
12342
|
const store = storage5();
|
|
11161
12343
|
if (!store) return;
|
|
@@ -11234,10 +12416,10 @@ async function affordableAmount(deps, quoted, reserve = 0n) {
|
|
|
11234
12416
|
}
|
|
11235
12417
|
async function executeSwap(deps, options) {
|
|
11236
12418
|
const { quote, walletAddress, onStage } = options;
|
|
11237
|
-
let
|
|
12419
|
+
let active = true;
|
|
11238
12420
|
const original = deps;
|
|
11239
12421
|
const assertActive = () => {
|
|
11240
|
-
if (!
|
|
12422
|
+
if (!active) throw new OwneyError("SWAP_REQUEST_FAILED", "This swap attempt is no longer active.");
|
|
11241
12423
|
};
|
|
11242
12424
|
const call = async (work, write = false) => {
|
|
11243
12425
|
assertActive();
|
|
@@ -11298,7 +12480,7 @@ async function executeSwap(deps, options) {
|
|
|
11298
12480
|
}
|
|
11299
12481
|
return { received: arrived.toString(), ...result };
|
|
11300
12482
|
} finally {
|
|
11301
|
-
|
|
12483
|
+
active = false;
|
|
11302
12484
|
}
|
|
11303
12485
|
}
|
|
11304
12486
|
async function runClassic(deps, options) {
|
|
@@ -11417,6 +12599,7 @@ async function runSameChainFusion(deps, options, walletAddress) {
|
|
|
11417
12599
|
}
|
|
11418
12600
|
await runSameChainOrder(deps.sameChainRunner ?? deps.runner, {
|
|
11419
12601
|
orderHash: built.orderHash,
|
|
12602
|
+
waitForWatcher: true,
|
|
11420
12603
|
...onStage ? { onStage } : {}
|
|
11421
12604
|
});
|
|
11422
12605
|
return { orderHash: built.orderHash, spent: amount.toString() };
|
|
@@ -11485,67 +12668,118 @@ async function runFusion(deps, options, walletAddress) {
|
|
|
11485
12668
|
...direction ? { direction } : {},
|
|
11486
12669
|
...permit ? { permit } : {}
|
|
11487
12670
|
});
|
|
11488
|
-
const
|
|
11489
|
-
orderHash: built.orderHash,
|
|
11490
|
-
walletAddress,
|
|
11491
|
-
secrets,
|
|
11492
|
-
srcChainId: quote.src.chainId,
|
|
11493
|
-
srcSymbol: quote.src.symbol,
|
|
11494
|
-
dstChainId: quote.dst.chainId,
|
|
11495
|
-
dstSymbol: quote.dst.symbol,
|
|
11496
|
-
amount: amount.toString(),
|
|
11497
|
-
createdAt: Date.now()
|
|
11498
|
-
});
|
|
11499
|
-
debugLog("owney-sdk", "swap: order built", { orderHash: built.orderHash });
|
|
11500
|
-
let signature;
|
|
12671
|
+
const release = claimOrder(built.orderHash);
|
|
11501
12672
|
try {
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
12673
|
+
const stored = {
|
|
12674
|
+
orderHash: built.orderHash,
|
|
12675
|
+
walletAddress,
|
|
12676
|
+
secrets,
|
|
12677
|
+
srcChainId: quote.src.chainId,
|
|
12678
|
+
srcSymbol: quote.src.symbol,
|
|
12679
|
+
dstChainId: quote.dst.chainId,
|
|
12680
|
+
dstSymbol: quote.dst.symbol,
|
|
12681
|
+
amount: amount.toString(),
|
|
12682
|
+
createdAt: Date.now(),
|
|
12683
|
+
...options.pendingDeposit ? { pendingDeposit: withinOrderMinimum(options.pendingDeposit, built.dstAmountMin) } : {}
|
|
12684
|
+
};
|
|
12685
|
+
const durable = saveOrder(stored);
|
|
12686
|
+
debugLog("owney-sdk", "swap: order built", { orderHash: built.orderHash });
|
|
12687
|
+
let signature;
|
|
12688
|
+
try {
|
|
12689
|
+
onStage?.("signing");
|
|
12690
|
+
await deps.ensureChain(quote.src.chainId);
|
|
12691
|
+
signature = await awaitWalletPrompt(() => deps.signTypedData(built.typedData), "sign the swap");
|
|
12692
|
+
options.assertCurrent?.();
|
|
12693
|
+
options.onOrderPrepared?.(built.orderHash, { spent: amount.toString(), expected: built.dstAmount });
|
|
12694
|
+
} catch (error) {
|
|
12695
|
+
clearOrder(built.orderHash);
|
|
12696
|
+
throw error;
|
|
12697
|
+
}
|
|
12698
|
+
if (options.secretHandoff && deps.api.handoffSecrets) {
|
|
12699
|
+
if (await tryHandoff(deps, options.secretHandoff, built, quote, secretHashes, secrets))
|
|
12700
|
+
saveOrder({ ...stored, serverSecrets: true });
|
|
12701
|
+
}
|
|
12702
|
+
debugLog("owney-sdk", "swap: signed, submitting to relayer");
|
|
12703
|
+
try {
|
|
12704
|
+
await deps.api.submitOrder({
|
|
12705
|
+
srcChainId: quote.src.chainId,
|
|
12706
|
+
// The ORDER STRUCT, not the typed-data envelope we just signed. Sending
|
|
12707
|
+
// the envelope here gets a bare 500 from the relayer.
|
|
12708
|
+
order: built.order,
|
|
12709
|
+
signature,
|
|
12710
|
+
quoteId: built.quoteId,
|
|
12711
|
+
// Single-fill orders must NOT carry secretHashes — the relayer rejects
|
|
12712
|
+
// them with SECRET_HASHES_NOT_REQUIRED. The one hash is already inside the
|
|
12713
|
+
// order's hashlock, so repeating it here is redundant, and only a
|
|
12714
|
+
// multi-fill order (a Merkle tree of hashes) needs them listed.
|
|
12715
|
+
...secretHashes.length > 1 ? { secretHashes } : {},
|
|
12716
|
+
...built.extension ? { extension: built.extension } : {}
|
|
12717
|
+
});
|
|
12718
|
+
} catch (error) {
|
|
12719
|
+
throw submissionUnknown(error, built.orderHash);
|
|
12720
|
+
}
|
|
12721
|
+
debugLog("owney-sdk", "swap: order submitted, polling escrows");
|
|
12722
|
+
try {
|
|
12723
|
+
options.onOrderSubmitted?.(built.orderHash, { spent: amount.toString(), expected: built.dstAmount, durable });
|
|
12724
|
+
} catch {
|
|
12725
|
+
}
|
|
12726
|
+
try {
|
|
12727
|
+
await runFusionOrder(deps.runner, {
|
|
12728
|
+
orderHash: built.orderHash,
|
|
12729
|
+
// This run owns the order: wait out a watcher another tab started.
|
|
12730
|
+
waitForWatcher: true,
|
|
12731
|
+
secrets,
|
|
12732
|
+
...onStage ? { onStage } : {}
|
|
12733
|
+
});
|
|
12734
|
+
} catch (error) {
|
|
12735
|
+
if (error instanceof OwneyError && error.code === "SWAP_ORDER_REFUNDED" && error.details?.outcome === "refunded") {
|
|
12736
|
+
clearOrder(built.orderHash);
|
|
12737
|
+
}
|
|
12738
|
+
throw error;
|
|
12739
|
+
}
|
|
11508
12740
|
clearOrder(built.orderHash);
|
|
11509
|
-
|
|
11510
|
-
}
|
|
11511
|
-
|
|
12741
|
+
return { orderHash: built.orderHash, spent: amount.toString() };
|
|
12742
|
+
} finally {
|
|
12743
|
+
release();
|
|
12744
|
+
}
|
|
12745
|
+
}
|
|
12746
|
+
var HANDOFF_DECLINE_CODES = /* @__PURE__ */ new Set([
|
|
12747
|
+
"FUNDING_TASK_KIND_DISABLED",
|
|
12748
|
+
"SECRETS_INVALID",
|
|
12749
|
+
"SECRET_HASH_MISMATCH",
|
|
12750
|
+
"ORDER_UNREADABLE",
|
|
12751
|
+
"ORDER_HASH_MISMATCH",
|
|
12752
|
+
"HASHLOCK_MISMATCH",
|
|
12753
|
+
"MAKER_MISMATCH",
|
|
12754
|
+
"CHAIN_MISMATCH",
|
|
12755
|
+
"OPERATION_INVALID",
|
|
12756
|
+
"ORDER_CONFLICT"
|
|
12757
|
+
]);
|
|
12758
|
+
async function tryHandoff(deps, handoff, built, quote, secretHashes, secrets) {
|
|
11512
12759
|
try {
|
|
11513
|
-
await deps.api.
|
|
12760
|
+
const answer = await bounded(() => deps.api.handoffSecrets({
|
|
12761
|
+
operation: handoff.operation,
|
|
12762
|
+
orderHash: built.orderHash,
|
|
11514
12763
|
srcChainId: quote.src.chainId,
|
|
11515
|
-
|
|
11516
|
-
// the envelope here gets a bare 500 from the relayer.
|
|
12764
|
+
dstChainId: quote.dst.chainId,
|
|
11517
12765
|
order: built.order,
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
...built.extension ? { extension: built.extension } : {}
|
|
11526
|
-
});
|
|
11527
|
-
} catch (error) {
|
|
11528
|
-
throw submissionUnknown(error, built.orderHash);
|
|
11529
|
-
}
|
|
11530
|
-
debugLog("owney-sdk", "swap: order submitted, polling escrows");
|
|
11531
|
-
try {
|
|
11532
|
-
options.onOrderSubmitted?.(built.orderHash, { spent: amount.toString(), expected: built.dstAmount, durable });
|
|
11533
|
-
} catch {
|
|
11534
|
-
}
|
|
11535
|
-
try {
|
|
11536
|
-
await runFusionOrder(deps.runner, {
|
|
11537
|
-
orderHash: built.orderHash,
|
|
11538
|
-
secrets,
|
|
11539
|
-
...onStage ? { onStage } : {}
|
|
11540
|
-
});
|
|
11541
|
-
} catch (error) {
|
|
11542
|
-
if (error instanceof OwneyError && error.code === "SWAP_ORDER_REFUNDED" && error.details?.outcome === "refunded") {
|
|
11543
|
-
clearOrder(built.orderHash);
|
|
12766
|
+
extension: built.extension,
|
|
12767
|
+
secretHashes,
|
|
12768
|
+
fusionSecrets: secrets
|
|
12769
|
+
}), FUSION_SECRET_HANDOFF_TIMEOUT_MS, () => new Error("secret handoff timed out"));
|
|
12770
|
+
if (answer?.accepted === true) {
|
|
12771
|
+
debugLog("owney-sdk", "swap: secrets handed to the server", { orderHash: built.orderHash });
|
|
12772
|
+
return true;
|
|
11544
12773
|
}
|
|
11545
|
-
|
|
12774
|
+
const code = typeof answer?.code === "string" && HANDOFF_DECLINE_CODES.has(answer.code) ? answer.code : "OTHER";
|
|
12775
|
+
debugLog("owney-sdk", "swap: secret handoff declined, the browser publishes", { code });
|
|
12776
|
+
} catch {
|
|
12777
|
+
debugLog("owney-sdk", "swap: secret handoff unavailable, the browser publishes");
|
|
11546
12778
|
}
|
|
11547
|
-
|
|
11548
|
-
|
|
12779
|
+
return false;
|
|
12780
|
+
}
|
|
12781
|
+
function withinOrderMinimum(deposit, orderMinimum) {
|
|
12782
|
+
return typeof orderMinimum === "string" && /^\d+$/.test(orderMinimum) && /^\d+$/.test(deposit.received) && BigInt(orderMinimum) < BigInt(deposit.received) ? { ...deposit, received: orderMinimum } : deposit;
|
|
11549
12783
|
}
|
|
11550
12784
|
function submissionUnknown(error, orderHash) {
|
|
11551
12785
|
return new OwneyError(
|
|
@@ -11568,7 +12802,7 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
11568
12802
|
const payouts = new Map(Object.entries(options.results).map(([id, result]) => [id, { result, evidence: result.evidence }]));
|
|
11569
12803
|
const timeoutMs = Math.max(0, options.timeoutMs ?? 18e4);
|
|
11570
12804
|
const deadline = options.deadline ?? Date.now() + timeoutMs;
|
|
11571
|
-
let
|
|
12805
|
+
let active = true;
|
|
11572
12806
|
let timer;
|
|
11573
12807
|
const incomplete = (reason, agentId) => new OwneyError(
|
|
11574
12808
|
"WITHDRAW_SWAP_INCOMPLETE",
|
|
@@ -11586,7 +12820,7 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
11586
12820
|
);
|
|
11587
12821
|
const assertActive = () => {
|
|
11588
12822
|
options.assertCurrent();
|
|
11589
|
-
if (!
|
|
12823
|
+
if (!active || Date.now() > deadline) throw incomplete("PAYOUT_CONFIRMATION_TIMEOUT");
|
|
11590
12824
|
};
|
|
11591
12825
|
const pause = async () => {
|
|
11592
12826
|
assertActive();
|
|
@@ -11763,7 +12997,7 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
11763
12997
|
timer = setTimeout(() => reject(incomplete("PAYOUT_CONFIRMATION_TIMEOUT")), Math.max(0, deadline - Date.now()));
|
|
11764
12998
|
})]);
|
|
11765
12999
|
} finally {
|
|
11766
|
-
|
|
13000
|
+
active = false;
|
|
11767
13001
|
if (timer) clearTimeout(timer);
|
|
11768
13002
|
}
|
|
11769
13003
|
}
|
|
@@ -12331,7 +13565,8 @@ import {
|
|
|
12331
13565
|
createPublicClient as createPublicClient4,
|
|
12332
13566
|
createWalletClient as createWalletClient5,
|
|
12333
13567
|
custom as custom5,
|
|
12334
|
-
erc20Abi as erc20Abi7
|
|
13568
|
+
erc20Abi as erc20Abi7,
|
|
13569
|
+
isAddress as isAddress5
|
|
12335
13570
|
} from "viem";
|
|
12336
13571
|
import { base as base6, arbitrum as arbitrum3, mainnet as mainnet3 } from "viem/chains";
|
|
12337
13572
|
|
|
@@ -12340,21 +13575,42 @@ import {
|
|
|
12340
13575
|
keccak256 as keccak2562,
|
|
12341
13576
|
parseTransaction
|
|
12342
13577
|
} from "viem";
|
|
13578
|
+
var SPONSORED_DEPOSIT_SERVER_BROADCAST = 1;
|
|
12343
13579
|
async function executeTokenBatch(input, initial) {
|
|
12344
13580
|
let body = initial, family, ended = false;
|
|
12345
13581
|
const deadlineAt = Date.now() + 9e4, controller = new AbortController();
|
|
12346
13582
|
let timer;
|
|
12347
|
-
const
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
13583
|
+
const serverSent = /* @__PURE__ */ new Set();
|
|
13584
|
+
let accepted;
|
|
13585
|
+
let maybeAccepted = !!input.resumeSaved, alreadySending = false;
|
|
13586
|
+
const verifiedFailures = /* @__PURE__ */ new WeakSet();
|
|
13587
|
+
const pending = () => {
|
|
13588
|
+
const deposit = body.serializedTransaction ? keccak2562(body.serializedTransaction) : accepted;
|
|
13589
|
+
const unknown2 = !deposit && maybeAccepted;
|
|
13590
|
+
const deadline = /^\d{1,15}$/.test(body.deadline) ? Number(body.deadline) : void 0;
|
|
13591
|
+
return new OwneyError(
|
|
13592
|
+
"DEPOSIT_IN_PROGRESS",
|
|
13593
|
+
"Deposit is awaiting confirmation. Its transaction is still being checked.",
|
|
13594
|
+
{
|
|
13595
|
+
sponsorPending: true,
|
|
13596
|
+
phase: deposit || unknown2 ? "deposit" : "approval",
|
|
13597
|
+
txHash: deposit ?? (body.serializedPermitTransaction && !unknown2 ? keccak2562(body.serializedPermitTransaction) : void 0),
|
|
13598
|
+
notSubmitted: false,
|
|
13599
|
+
safeToFallback: false,
|
|
13600
|
+
// Only while the current deposit is the one the server will send.
|
|
13601
|
+
...deposit && serverSent.has(deposit) ? { serverBroadcast: true } : unknown2 ? { serverBroadcast: "unknown" } : {},
|
|
13602
|
+
...unknown2 && alreadySending ? { code: "DEPOSIT_ALREADY_SENDING" } : {},
|
|
13603
|
+
// The Permit2 deadline (seconds): after it no deposit of this
|
|
13604
|
+
// authorization can execute, which bounds an unknown outcome. Never
|
|
13605
|
+
// after a 409 DEPOSIT_ALREADY_SENDING: this refused authorization is
|
|
13606
|
+
// not the deposit being sent.
|
|
13607
|
+
...unknown2 && !alreadySending && deadline !== void 0 ? { authorizationDeadline: deadline } : {},
|
|
13608
|
+
// Its Permit2 nonce: with the deadline, the chain shows whether any
|
|
13609
|
+
// deposit of this authorization executed.
|
|
13610
|
+
...unknown2 && !alreadySending || deposit && serverSent.has(deposit) ? { permit2Nonce: body.nonce } : {}
|
|
13611
|
+
}
|
|
13612
|
+
);
|
|
13613
|
+
};
|
|
12358
13614
|
const assertCurrent = () => {
|
|
12359
13615
|
if (ended || Date.now() >= deadlineAt) throw pending();
|
|
12360
13616
|
input.assertCurrent?.();
|
|
@@ -12379,20 +13635,52 @@ async function executeTokenBatch(input, initial) {
|
|
|
12379
13635
|
}
|
|
12380
13636
|
return operationId ? { ...value, operationId } : value;
|
|
12381
13637
|
};
|
|
12382
|
-
const
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
13638
|
+
const withServerBroadcast = (value) => {
|
|
13639
|
+
const request2 = withOperationId(value);
|
|
13640
|
+
return request2.operationId ? { ...request2, serverBroadcast: true } : request2;
|
|
13641
|
+
};
|
|
13642
|
+
const post2 = async () => {
|
|
13643
|
+
const request2 = withServerBroadcast(body);
|
|
13644
|
+
const flaggedPrepare = request2.serverBroadcast === true && !request2.serializedTransaction && !(request2.erc2612Permit && !request2.serializedPermitTransaction);
|
|
13645
|
+
let result;
|
|
13646
|
+
try {
|
|
13647
|
+
result = await Promise.race([
|
|
13648
|
+
postSponsorBatchTransfer({
|
|
13649
|
+
apiKey: input.apiKey,
|
|
13650
|
+
baseUrl: input.baseUrl,
|
|
13651
|
+
body: request2,
|
|
13652
|
+
signal: controller.signal
|
|
13653
|
+
}),
|
|
13654
|
+
timeout
|
|
13655
|
+
]);
|
|
13656
|
+
} catch (error) {
|
|
13657
|
+
const details = error instanceof OwneyError ? error.details : void 0;
|
|
13658
|
+
const sending = details?.code === "DEPOSIT_ALREADY_SENDING";
|
|
13659
|
+
if (flaggedPrepare && (ended || !(error instanceof OwneyError) || details?.outcomeUnknown === true || sending)) {
|
|
13660
|
+
maybeAccepted = true;
|
|
13661
|
+
alreadySending = sending;
|
|
13662
|
+
}
|
|
13663
|
+
throw error;
|
|
13664
|
+
}
|
|
13665
|
+
maybeAccepted = false;
|
|
13666
|
+
alreadySending = false;
|
|
13667
|
+
if (result.serverBroadcast === true && result.serializedTransaction && !body.serializedTransaction) {
|
|
13668
|
+
serverSent.add(result.txHash);
|
|
13669
|
+
accepted = result.txHash;
|
|
13670
|
+
try {
|
|
13671
|
+
input.onSubmitted?.(result.txHash, reference(result.txHash), true);
|
|
13672
|
+
} catch {
|
|
13673
|
+
}
|
|
13674
|
+
}
|
|
13675
|
+
assertCurrent();
|
|
13676
|
+
return result;
|
|
13677
|
+
};
|
|
12390
13678
|
const replacements = { approval: 0, deposit: 0 };
|
|
12391
13679
|
const unavailable = /* @__PURE__ */ new Set();
|
|
12392
13680
|
const observedApprovals = /* @__PURE__ */ new Set();
|
|
12393
13681
|
const checkpointed = /* @__PURE__ */ new Set();
|
|
12394
13682
|
const accounted = /* @__PURE__ */ new Set();
|
|
12395
|
-
const remember = async (phase, bytes, hash2, predecessorHash) => {
|
|
13683
|
+
const remember = async (phase, bytes, hash2, predecessorHash, submissionAttempted = false) => {
|
|
12396
13684
|
assertCurrent();
|
|
12397
13685
|
if (keccak2562(bytes) !== hash2) throw pending();
|
|
12398
13686
|
const tx = parseTransaction(bytes);
|
|
@@ -12411,7 +13699,7 @@ async function executeTokenBatch(input, initial) {
|
|
|
12411
13699
|
nonce: String(tx.nonce),
|
|
12412
13700
|
predecessorHash,
|
|
12413
13701
|
preparedAt: Date.now(),
|
|
12414
|
-
submissionAttempted
|
|
13702
|
+
submissionAttempted
|
|
12415
13703
|
},
|
|
12416
13704
|
assertCurrent
|
|
12417
13705
|
)
|
|
@@ -12425,13 +13713,18 @@ async function executeTokenBatch(input, initial) {
|
|
|
12425
13713
|
const markSubmission = async (phase) => {
|
|
12426
13714
|
const hash2 = phase === "deposit" ? family?.current.deposit : family?.current.approval;
|
|
12427
13715
|
if (!family || !hash2) throw pending();
|
|
13716
|
+
const flag = serverSent.has(hash2) ? [true] : [];
|
|
12428
13717
|
if (phase === "deposit" && !checkpointed.has(hash2)) {
|
|
12429
13718
|
try {
|
|
12430
13719
|
await bounded2(
|
|
12431
|
-
Promise.resolve(
|
|
13720
|
+
Promise.resolve(
|
|
13721
|
+
input.onBeforeSubmission?.(hash2, reference(hash2), ...flag)
|
|
13722
|
+
)
|
|
12432
13723
|
);
|
|
13724
|
+
checkpointed.add(hash2);
|
|
12433
13725
|
} catch (error) {
|
|
12434
|
-
if (
|
|
13726
|
+
if (serverSent.has(hash2)) {
|
|
13727
|
+
} else if (error instanceof OwneyError && error.code === "DEPOSIT_CHECKPOINT_FAILED")
|
|
12435
13728
|
throw new OwneyError(error.code, error.message, {
|
|
12436
13729
|
...error.details,
|
|
12437
13730
|
notSubmitted: !family.attempts.some(
|
|
@@ -12439,9 +13732,8 @@ async function executeTokenBatch(input, initial) {
|
|
|
12439
13732
|
),
|
|
12440
13733
|
safeToFallback: false
|
|
12441
13734
|
});
|
|
12442
|
-
throw error;
|
|
13735
|
+
else throw error;
|
|
12443
13736
|
}
|
|
12444
|
-
checkpointed.add(hash2);
|
|
12445
13737
|
}
|
|
12446
13738
|
assertCurrent();
|
|
12447
13739
|
const attempt = family.attempts.find((a) => a.hash === hash2);
|
|
@@ -12456,7 +13748,7 @@ async function executeTokenBatch(input, initial) {
|
|
|
12456
13748
|
assertCurrent();
|
|
12457
13749
|
if (phase === "deposit")
|
|
12458
13750
|
try {
|
|
12459
|
-
input.onSubmitted?.(hash2, reference(hash2));
|
|
13751
|
+
input.onSubmitted?.(hash2, reference(hash2), ...flag);
|
|
12460
13752
|
} catch {
|
|
12461
13753
|
}
|
|
12462
13754
|
assertCurrent();
|
|
@@ -12492,12 +13784,15 @@ async function executeTokenBatch(input, initial) {
|
|
|
12492
13784
|
}
|
|
12493
13785
|
return observation.txHash;
|
|
12494
13786
|
}
|
|
12495
|
-
if (observation.status === "failed" || observation.status === "expired")
|
|
12496
|
-
|
|
13787
|
+
if (observation.status === "failed" || observation.status === "expired") {
|
|
13788
|
+
const failure = new OwneyError(
|
|
12497
13789
|
"SPONSOR_REQUEST_FAILED",
|
|
12498
13790
|
observation.status === "expired" ? "The deposit authorization expired without transferring funds. Start a new deposit." : "Sponsored deposit transactions reverted. Please retry.",
|
|
12499
13791
|
{ notSubmitted: true, safeToFallback: false }
|
|
12500
13792
|
);
|
|
13793
|
+
verifiedFailures.add(failure);
|
|
13794
|
+
throw failure;
|
|
13795
|
+
}
|
|
12501
13796
|
};
|
|
12502
13797
|
try {
|
|
12503
13798
|
assertCurrent();
|
|
@@ -12513,6 +13808,14 @@ async function executeTokenBatch(input, initial) {
|
|
|
12513
13808
|
tokenBatchKey(input),
|
|
12514
13809
|
JSON.stringify(tokenBatchStoredBody(body))
|
|
12515
13810
|
);
|
|
13811
|
+
if (/^\d{1,78}$/.test(body.nonce) && /^\d{1,15}$/.test(body.deadline))
|
|
13812
|
+
try {
|
|
13813
|
+
input.onAuthorization?.({
|
|
13814
|
+
permit2Nonce: body.nonce,
|
|
13815
|
+
authorizationDeadline: Number(body.deadline)
|
|
13816
|
+
});
|
|
13817
|
+
} catch {
|
|
13818
|
+
}
|
|
12516
13819
|
if (family) {
|
|
12517
13820
|
const winner = await observe();
|
|
12518
13821
|
if (winner) return winner;
|
|
@@ -12536,6 +13839,20 @@ async function executeTokenBatch(input, initial) {
|
|
|
12536
13839
|
result = await post2();
|
|
12537
13840
|
} catch (error) {
|
|
12538
13841
|
assertCurrent();
|
|
13842
|
+
if (maybeAccepted && alreadySending) {
|
|
13843
|
+
if (family)
|
|
13844
|
+
finishTokenBatchFamily(input, family, {
|
|
13845
|
+
kind: "failed",
|
|
13846
|
+
blockNumber: "0"
|
|
13847
|
+
});
|
|
13848
|
+
else
|
|
13849
|
+
tokenBatchClear(
|
|
13850
|
+
tokenBatchKey(input),
|
|
13851
|
+
JSON.stringify(tokenBatchStoredBody(body))
|
|
13852
|
+
);
|
|
13853
|
+
throw pending();
|
|
13854
|
+
}
|
|
13855
|
+
if (maybeAccepted && !family) throw pending();
|
|
12539
13856
|
if (!family) {
|
|
12540
13857
|
tokenBatchClear(
|
|
12541
13858
|
tokenBatchKey(input),
|
|
@@ -12558,7 +13875,9 @@ async function executeTokenBatch(input, initial) {
|
|
|
12558
13875
|
await remember(
|
|
12559
13876
|
"deposit",
|
|
12560
13877
|
result.serializedTransaction,
|
|
12561
|
-
result.txHash
|
|
13878
|
+
result.txHash,
|
|
13879
|
+
void 0,
|
|
13880
|
+
serverSent.has(result.txHash)
|
|
12562
13881
|
);
|
|
12563
13882
|
needsPost = true;
|
|
12564
13883
|
continue;
|
|
@@ -12634,6 +13953,10 @@ async function executeTokenBatch(input, initial) {
|
|
|
12634
13953
|
)
|
|
12635
13954
|
);
|
|
12636
13955
|
}
|
|
13956
|
+
} catch (error) {
|
|
13957
|
+
if ((serverSent.size || maybeAccepted) && !(error && typeof error === "object" && verifiedFailures.has(error)))
|
|
13958
|
+
throw pending();
|
|
13959
|
+
throw error;
|
|
12637
13960
|
} finally {
|
|
12638
13961
|
ended = true;
|
|
12639
13962
|
if (timer) clearTimeout(timer);
|
|
@@ -12663,14 +13986,14 @@ async function releaseExpiredTokenBatch(i) {
|
|
|
12663
13986
|
let timer;
|
|
12664
13987
|
let expired = false;
|
|
12665
13988
|
const check = async () => {
|
|
12666
|
-
let
|
|
12667
|
-
if (
|
|
12668
|
-
const current = JSON.parse(
|
|
13989
|
+
let active = read(key2);
|
|
13990
|
+
if (active && i.expectedTxHash) {
|
|
13991
|
+
const current = JSON.parse(active);
|
|
12669
13992
|
if (!current.serializedTransaction || keccak2563(current.serializedTransaction).toLowerCase() !== i.expectedTxHash.toLowerCase())
|
|
12670
|
-
|
|
13993
|
+
active = void 0;
|
|
12671
13994
|
}
|
|
12672
13995
|
const archiveKey = i.expectedTxHash ? `${key2}:expired:${i.expectedTxHash.toLowerCase()}` : void 0;
|
|
12673
|
-
const saved =
|
|
13996
|
+
const saved = active ?? (archiveKey && (expiredRecords.get(archiveKey) ?? (typeof window === "undefined" ? void 0 : window.localStorage.getItem(archiveKey))));
|
|
12674
13997
|
if (!saved) return;
|
|
12675
13998
|
const body = JSON.parse(saved);
|
|
12676
13999
|
if (!body.serializedTransaction || body.chainId !== i.chainId || !isAddressEqual2(body.from, i.owner) || !isAddressEqual2(body.token, i.token) || !body.transfers?.length) return;
|
|
@@ -12694,8 +14017,8 @@ async function releaseExpiredTokenBatch(i) {
|
|
|
12694
14017
|
args: [i.owner, nonce >> 8n],
|
|
12695
14018
|
blockNumber: block.number
|
|
12696
14019
|
});
|
|
12697
|
-
if (expired || typeof bitmap !== "bigint" || bitmap < 0n || bitmap > MAX_UINT256 || (bitmap & 1n << (nonce & 255n)) !== 0n ||
|
|
12698
|
-
if (
|
|
14020
|
+
if (expired || typeof bitmap !== "bigint" || bitmap < 0n || bitmap > MAX_UINT256 || (bitmap & 1n << (nonce & 255n)) !== 0n || active && read(key2) !== active) return;
|
|
14021
|
+
if (active) {
|
|
12699
14022
|
const terminalKey = `${key2}:expired:${txHash.toLowerCase()}`;
|
|
12700
14023
|
expiredRecords.set(terminalKey, saved);
|
|
12701
14024
|
let persisted = typeof window === "undefined";
|
|
@@ -12732,15 +14055,15 @@ function clear(key2, body) {
|
|
|
12732
14055
|
function sponsorTokenBatch(i) {
|
|
12733
14056
|
const key2 = pendingKey(i);
|
|
12734
14057
|
const plan = planOf(i.transfers);
|
|
12735
|
-
const
|
|
12736
|
-
if (
|
|
12737
|
-
if (
|
|
14058
|
+
const active = inflight.get(key2);
|
|
14059
|
+
if (active) {
|
|
14060
|
+
if (active.plan !== plan)
|
|
12738
14061
|
return Promise.reject(
|
|
12739
14062
|
new Error(
|
|
12740
14063
|
"A token deposit is already in progress. Wait for its result before depositing again."
|
|
12741
14064
|
)
|
|
12742
14065
|
);
|
|
12743
|
-
return
|
|
14066
|
+
return active.promise;
|
|
12744
14067
|
}
|
|
12745
14068
|
const promise = execute(i, key2, plan).finally(() => inflight.delete(key2));
|
|
12746
14069
|
inflight.set(key2, { plan, promise });
|
|
@@ -12752,8 +14075,15 @@ async function execute(i, key2, plan) {
|
|
|
12752
14075
|
) || new Set(i.transfers.map((t) => t.to.toLowerCase())).size !== i.transfers.length)
|
|
12753
14076
|
throw new Error("Invalid token deposit shares.");
|
|
12754
14077
|
const send = (body) => executeTokenBatch(i, body);
|
|
14078
|
+
if (!i.resumeTxHash) await dropExpiredHashlessTokenBatch(i, i.pub);
|
|
12755
14079
|
const family = await loadTokenBatchFamily(i, i.resumeTxHash);
|
|
12756
14080
|
const saved = family ? JSON.stringify(family.body) : read(key2);
|
|
14081
|
+
if (!saved && i.resumeSaved)
|
|
14082
|
+
throw new OwneyError(
|
|
14083
|
+
"DEPOSIT_IN_PROGRESS",
|
|
14084
|
+
"The saved deposit is no longer available. Check its status before depositing again.",
|
|
14085
|
+
{ notSubmitted: false, safeToFallback: false }
|
|
14086
|
+
);
|
|
12757
14087
|
if (saved) {
|
|
12758
14088
|
const previous = JSON.parse(saved);
|
|
12759
14089
|
if (previous.chainId !== i.chainId || !isAddressEqual2(previous.from, i.owner) || !isAddressEqual2(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
@@ -12863,10 +14193,13 @@ function makeSponsoredTokenCallback(deps) {
|
|
|
12863
14193
|
pub,
|
|
12864
14194
|
wallet,
|
|
12865
14195
|
onApproved: deps.onApproved,
|
|
12866
|
-
|
|
12867
|
-
|
|
14196
|
+
// Rest arguments keep the callbacks' arity: the flag appears only when set.
|
|
14197
|
+
onSubmitted: (hash2, ...rest) => deps.onSubmitted?.(hash2, transfers, ...rest),
|
|
14198
|
+
onBeforeSubmission: (hash2, ...rest) => deps.onBeforeSubmission?.(hash2, transfers, ...rest),
|
|
12868
14199
|
assertCurrent: deps.assertCurrent,
|
|
12869
14200
|
resumeTxHash: deps.resumeTxHash,
|
|
14201
|
+
...deps.resumeSaved ? { resumeSaved: true } : {},
|
|
14202
|
+
...deps.onAuthorization ? { onAuthorization: deps.onAuthorization } : {},
|
|
12870
14203
|
operationId: deps.operationId
|
|
12871
14204
|
});
|
|
12872
14205
|
};
|
|
@@ -13173,6 +14506,33 @@ function sponsoredTokensFor(asset) {
|
|
|
13173
14506
|
function shouldFallbackToUserPaid(error, asset, appCallback) {
|
|
13174
14507
|
return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
|
|
13175
14508
|
}
|
|
14509
|
+
function serverBroadcastOf(error) {
|
|
14510
|
+
const flag = (value) => {
|
|
14511
|
+
const details = value?.details;
|
|
14512
|
+
return details?.serverBroadcast === true || details?.serverBroadcast === "unknown" ? details.serverBroadcast : void 0;
|
|
14513
|
+
};
|
|
14514
|
+
return flag(error) ?? flag(error?.details?.cause);
|
|
14515
|
+
}
|
|
14516
|
+
function authorizationDeadlineOf(error) {
|
|
14517
|
+
const read2 = (value) => {
|
|
14518
|
+
const deadline = value?.details?.authorizationDeadline;
|
|
14519
|
+
return typeof deadline === "number" && Number.isSafeInteger(deadline) && deadline > 0 ? deadline : void 0;
|
|
14520
|
+
};
|
|
14521
|
+
return read2(error) ?? read2(error?.details?.cause);
|
|
14522
|
+
}
|
|
14523
|
+
function alreadySendingOf(error) {
|
|
14524
|
+
const code = (value) => value?.details?.code;
|
|
14525
|
+
return code(error) === "DEPOSIT_ALREADY_SENDING" || code(error?.details?.cause) === "DEPOSIT_ALREADY_SENDING";
|
|
14526
|
+
}
|
|
14527
|
+
function permit2NonceOf(error) {
|
|
14528
|
+
const read2 = (value) => {
|
|
14529
|
+
const nonce = value?.details?.permit2Nonce;
|
|
14530
|
+
return typeof nonce === "string" && /^\d{1,78}$/.test(nonce) ? nonce : void 0;
|
|
14531
|
+
};
|
|
14532
|
+
return read2(error) ?? read2(error?.details?.cause);
|
|
14533
|
+
}
|
|
14534
|
+
var serverMayOwn = (leg) => leg !== void 0 && leg.settled !== null;
|
|
14535
|
+
var SERVER_HANDOFF_WAIT_MS = 1e4;
|
|
13176
14536
|
var OwneySDK = class {
|
|
13177
14537
|
agents = /* @__PURE__ */ new Map();
|
|
13178
14538
|
activeAgents = /* @__PURE__ */ new Set();
|
|
@@ -13201,13 +14561,21 @@ var OwneySDK = class {
|
|
|
13201
14561
|
ledger;
|
|
13202
14562
|
reservationMode;
|
|
13203
14563
|
reservationTransport;
|
|
14564
|
+
/** The caller's Zyfai server tasks opt-in: `true`, a function read each
|
|
14565
|
+
* time a handoff would start (F-I2(iii)), or undefined (off). */
|
|
14566
|
+
zyfaiServerTasks;
|
|
14567
|
+
/** The caller opted in to the Fusion+ secret handoff (`fusionSecretHandoff: true`). */
|
|
14568
|
+
fusionSecretHandoff;
|
|
13204
14569
|
cachedSponsoredCallback = null;
|
|
13205
14570
|
cachedWethSponsoredCallback = null;
|
|
13206
14571
|
paymasterServiceUrl;
|
|
13207
14572
|
cachedSponsoredCallsCallbacks = /* @__PURE__ */ new Map();
|
|
13208
14573
|
agentReads = new AgentReads();
|
|
13209
14574
|
depositFamilyReads = /* @__PURE__ */ new Map();
|
|
13210
|
-
|
|
14575
|
+
/** Movements (owner, chain, agent, token, tx hash) whose balance refresh this
|
|
14576
|
+
* session started: true when it armed the refresh, false when its deadline
|
|
14577
|
+
* had passed (it only dropped the cache and forced one snapshot read). */
|
|
14578
|
+
recoveredFundingReads = /* @__PURE__ */ new Map();
|
|
13211
14579
|
fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
|
|
13212
14580
|
activationChecks = /* @__PURE__ */ new Map();
|
|
13213
14581
|
// Protect the SDK entry point as well as the mini-app hook. This owns only
|
|
@@ -13245,6 +14613,8 @@ var OwneySDK = class {
|
|
|
13245
14613
|
});
|
|
13246
14614
|
this.reservationMode = config.withdrawalReservation === "fail-closed" || config.withdrawalReservation === "off" ? config.withdrawalReservation : "fail-open";
|
|
13247
14615
|
this.reservationTransport = { apiKey: config.apiKey, baseUrl: config.routingApiBaseUrl, appName: config.appName };
|
|
14616
|
+
this.zyfaiServerTasks = config.zyfaiServerTasks === true || typeof config.zyfaiServerTasks === "function" ? config.zyfaiServerTasks : void 0;
|
|
14617
|
+
this.fusionSecretHandoff = config.fusionSecretHandoff === true;
|
|
13248
14618
|
}
|
|
13249
14619
|
/** Finishes a funding-ledger operation on success and adds its reference to
|
|
13250
14620
|
* the result. The result object is copied, never mutated or replaced.
|
|
@@ -13310,16 +14680,34 @@ var OwneySDK = class {
|
|
|
13310
14680
|
return void 0;
|
|
13311
14681
|
}
|
|
13312
14682
|
}
|
|
14683
|
+
/**
|
|
14684
|
+
* Part 5: the swap's ledger identity, for the executor's Fusion+ secret
|
|
14685
|
+
* handoff. Only with the `fusionSecretHandoff` opt-in and an open operation;
|
|
14686
|
+
* otherwise undefined, and the executor sends nothing. Owner and token are
|
|
14687
|
+
* lowercased, as the ledger reports them.
|
|
14688
|
+
*/
|
|
14689
|
+
secretHandoffFor(op, input) {
|
|
14690
|
+
if (!this.fusionSecretHandoff || !op || !input || input.kind !== "swap_deposit" && input.kind !== "withdraw_swap")
|
|
14691
|
+
return void 0;
|
|
14692
|
+
return { operation: {
|
|
14693
|
+
clientOperationId: op.operationId,
|
|
14694
|
+
kind: input.kind,
|
|
14695
|
+
owner: input.owner.toLowerCase(),
|
|
14696
|
+
chainId: input.chainId,
|
|
14697
|
+
token: input.token.toLowerCase()
|
|
14698
|
+
} };
|
|
14699
|
+
}
|
|
13313
14700
|
closeSwapLedger(op) {
|
|
13314
14701
|
if (op && this.openSwapLedgers.get(op.operationId) === op) this.openSwapLedgers.delete(op.operationId);
|
|
13315
14702
|
}
|
|
13316
|
-
/** Reports one agent's withdrawal evidence as its leg
|
|
13317
|
-
|
|
14703
|
+
/** Reports one agent's withdrawal evidence as its leg (with `errorCode`, when
|
|
14704
|
+
* given). Never throws. */
|
|
14705
|
+
ledgerWithdrawalLeg(op, evidence, errorCode) {
|
|
13318
14706
|
if (!op) return;
|
|
13319
14707
|
try {
|
|
13320
14708
|
const leg = ledgerLegFromEvidence(evidence, "withdraw");
|
|
13321
14709
|
const rail = withdrawalLedgerRail(evidence);
|
|
13322
|
-
if (leg) op.leg(rail ? { ...leg, rail } : leg);
|
|
14710
|
+
if (leg) op.leg({ ...rail ? { ...leg, rail } : leg, ...errorCode ? { errorCode } : {} });
|
|
13323
14711
|
if (leg?.status === "confirmed") this.ledgerProviderSamples(op, evidence, "withdraw");
|
|
13324
14712
|
} catch {
|
|
13325
14713
|
}
|
|
@@ -13554,8 +14942,8 @@ var OwneySDK = class {
|
|
|
13554
14942
|
return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
|
|
13555
14943
|
}
|
|
13556
14944
|
/** Builds the default USDC batch callback for the connected wallet. */
|
|
13557
|
-
getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission, assertSubmissionCurrent, operationId) {
|
|
13558
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId && this.cachedSponsoredCallback)
|
|
14945
|
+
getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission, assertSubmissionCurrent, operationId, resumeSaved, onAuthorization) {
|
|
14946
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId && !resumeSaved && !onAuthorization && this.cachedSponsoredCallback)
|
|
13559
14947
|
return this.cachedSponsoredCallback;
|
|
13560
14948
|
const provider = this.requireConnectedProvider();
|
|
13561
14949
|
const connection = this.requireState();
|
|
@@ -13569,6 +14957,8 @@ var OwneySDK = class {
|
|
|
13569
14957
|
onSubmitted,
|
|
13570
14958
|
onBeforeSubmission,
|
|
13571
14959
|
resumeTxHash,
|
|
14960
|
+
...resumeSaved ? { resumeSaved: true } : {},
|
|
14961
|
+
...onAuthorization ? { onAuthorization } : {},
|
|
13572
14962
|
operationId,
|
|
13573
14963
|
assertCurrent: () => {
|
|
13574
14964
|
assertSubmissionCurrent?.();
|
|
@@ -13589,7 +14979,7 @@ var OwneySDK = class {
|
|
|
13589
14979
|
transport: custom5(provider)
|
|
13590
14980
|
})
|
|
13591
14981
|
});
|
|
13592
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId) this.cachedSponsoredCallback = callback;
|
|
14982
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId && !resumeSaved && !onAuthorization) this.cachedSponsoredCallback = callback;
|
|
13593
14983
|
return callback;
|
|
13594
14984
|
}
|
|
13595
14985
|
/** Builds the wallet-native sponsored calls callback for compatible paymasters. */
|
|
@@ -13618,8 +15008,8 @@ var OwneySDK = class {
|
|
|
13618
15008
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
13619
15009
|
* single-use batch authorization instead of an EIP-3009 authorization.
|
|
13620
15010
|
*/
|
|
13621
|
-
getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission, assertSubmissionCurrent, operationId) {
|
|
13622
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId && this.cachedWethSponsoredCallback)
|
|
15011
|
+
getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission, assertSubmissionCurrent, operationId, resumeSaved, onAuthorization) {
|
|
15012
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId && !resumeSaved && !onAuthorization && this.cachedWethSponsoredCallback)
|
|
13623
15013
|
return this.cachedWethSponsoredCallback;
|
|
13624
15014
|
const provider = this.requireConnectedProvider();
|
|
13625
15015
|
const connection = this.requireState();
|
|
@@ -13633,6 +15023,8 @@ var OwneySDK = class {
|
|
|
13633
15023
|
onSubmitted,
|
|
13634
15024
|
onBeforeSubmission,
|
|
13635
15025
|
resumeTxHash,
|
|
15026
|
+
...resumeSaved ? { resumeSaved: true } : {},
|
|
15027
|
+
...onAuthorization ? { onAuthorization } : {},
|
|
13636
15028
|
operationId,
|
|
13637
15029
|
assertCurrent: () => {
|
|
13638
15030
|
assertSubmissionCurrent?.();
|
|
@@ -13653,7 +15045,7 @@ var OwneySDK = class {
|
|
|
13653
15045
|
transport: custom5(provider)
|
|
13654
15046
|
})
|
|
13655
15047
|
});
|
|
13656
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId) this.cachedWethSponsoredCallback = callback;
|
|
15048
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId && !resumeSaved && !onAuthorization) this.cachedWethSponsoredCallback = callback;
|
|
13657
15049
|
return callback;
|
|
13658
15050
|
}
|
|
13659
15051
|
getAgent(agentId) {
|
|
@@ -13667,12 +15059,12 @@ var OwneySDK = class {
|
|
|
13667
15059
|
return [...this.getActiveAgents().keys()].sort();
|
|
13668
15060
|
}
|
|
13669
15061
|
getActiveAgents() {
|
|
13670
|
-
const
|
|
15062
|
+
const active = /* @__PURE__ */ new Map();
|
|
13671
15063
|
for (const id of this.activeAgents) {
|
|
13672
15064
|
const agent = this.agents.get(id);
|
|
13673
|
-
if (agent)
|
|
15065
|
+
if (agent) active.set(id, agent);
|
|
13674
15066
|
}
|
|
13675
|
-
return
|
|
15067
|
+
return active;
|
|
13676
15068
|
}
|
|
13677
15069
|
/**
|
|
13678
15070
|
* Read the org's agent policy once and reuse it. fetchOrgAgentConfig never
|
|
@@ -13771,7 +15163,13 @@ var OwneySDK = class {
|
|
|
13771
15163
|
this.routingApiBaseUrl
|
|
13772
15164
|
) : this.rpcUrls,
|
|
13773
15165
|
this.referralSource,
|
|
13774
|
-
{
|
|
15166
|
+
{
|
|
15167
|
+
owneyApiKey: this.apiKey,
|
|
15168
|
+
routingApiBaseUrl: this.routingApiBaseUrl,
|
|
15169
|
+
// Only an explicit opt-in lets the agent hand the Zyfai session to
|
|
15170
|
+
// Owney; a function is passed as it is and read per handoff.
|
|
15171
|
+
...this.zyfaiServerTasks !== void 0 ? { zyfaiServerTasks: this.zyfaiServerTasks } : {}
|
|
15172
|
+
}
|
|
13775
15173
|
);
|
|
13776
15174
|
}
|
|
13777
15175
|
if (agentId === "yieldseeker") {
|
|
@@ -13993,10 +15391,21 @@ var OwneySDK = class {
|
|
|
13993
15391
|
} catch {
|
|
13994
15392
|
}
|
|
13995
15393
|
};
|
|
13996
|
-
|
|
15394
|
+
let permit;
|
|
15395
|
+
const onAuthorization = (value) => {
|
|
15396
|
+
permit = value;
|
|
15397
|
+
};
|
|
15398
|
+
const permitFields = () => permit ? { permit2Nonce: permit.permit2Nonce, authorizationDeadline: permit.authorizationDeadline } : {};
|
|
15399
|
+
const onSubmitted = (hash2, destinations, depositAuthorization, serverBroadcast) => {
|
|
13997
15400
|
for (const destination of destinations) {
|
|
13998
15401
|
const evidence = prepared.get(destination.to.toLowerCase());
|
|
13999
|
-
if (evidence) notifyFunding(onProgress, {
|
|
15402
|
+
if (evidence) notifyFunding(onProgress, {
|
|
15403
|
+
...evidence,
|
|
15404
|
+
txHash: hash2,
|
|
15405
|
+
...depositAuthorization ? { depositAuthorization } : {},
|
|
15406
|
+
...serverBroadcast ? { serverBroadcast, ...permitFields() } : {},
|
|
15407
|
+
transfer: "pending"
|
|
15408
|
+
});
|
|
14000
15409
|
}
|
|
14001
15410
|
};
|
|
14002
15411
|
const onCallsProgress = (callsId, destinations, transfer) => {
|
|
@@ -14005,6 +15414,7 @@ var OwneySDK = class {
|
|
|
14005
15414
|
if (evidence) notifyFunding(onProgress, { ...evidence, callsId, transfer });
|
|
14006
15415
|
}
|
|
14007
15416
|
};
|
|
15417
|
+
const serverTaskContext = (agent) => this.zyfaiServerTasks !== void 0 && agent.supportsServerTasks === true && ledgerOp ? { operationId: ledgerOp.operationId } : void 0;
|
|
14008
15418
|
const ledgerDone = (result) => this.ledgerSucceeded(ledgerOp, result);
|
|
14009
15419
|
const ledgerFailed = (error) => this.ledgerFailed(ledgerOp, error);
|
|
14010
15420
|
if (BigInt(amount) === 0n) {
|
|
@@ -14040,7 +15450,7 @@ var OwneySDK = class {
|
|
|
14040
15450
|
});
|
|
14041
15451
|
await checkpoint2(evidence);
|
|
14042
15452
|
} : void 0;
|
|
14043
|
-
const checkpointHash = checkpointDestinations ? (hash2, destinations, depositAuthorization) => checkpointDestinations(destinations, { txHash: hash2, depositAuthorization }) : void 0;
|
|
15453
|
+
const checkpointHash = checkpointDestinations ? (hash2, destinations, depositAuthorization, serverBroadcast) => checkpointDestinations(destinations, { txHash: hash2, depositAuthorization, ...serverBroadcast ? { serverBroadcast, ...permitFields() } : {} }) : void 0;
|
|
14044
15454
|
const checkpointCalls = checkpointDestinations ? (id, destinations) => checkpointDestinations(destinations, { callsId: id }) : void 0;
|
|
14045
15455
|
const checkpointPaid = checkpoint2 ? (evidence) => checkpoint2([evidence]) : void 0;
|
|
14046
15456
|
const customCallback = depositCallback && checkpointDestinations ? makeVerificationAwareDepositCallback(async (to, cid, value, verification) => {
|
|
@@ -14048,9 +15458,10 @@ var OwneySDK = class {
|
|
|
14048
15458
|
await checkpointDestinations([{ to, amount: value }], {});
|
|
14049
15459
|
return depositCallback(to, cid, value);
|
|
14050
15460
|
}) : depositCallback;
|
|
14051
|
-
if (options.resumeTxHash && (depositCallback || this.paymasterServiceUrl || !/^0x[\da-f]{64}$/i.test(options.resumeTxHash)))
|
|
15461
|
+
if (options.resumeTxHash && (depositCallback || this.paymasterServiceUrl || !/^0x[\da-f]{64}$/i.test(options.resumeTxHash)) || options.resumeSavedBatch && (options.resumeTxHash || depositCallback || this.paymasterServiceUrl))
|
|
14052
15462
|
throw new OwneyError("DEPOSIT_IN_PROGRESS", "This deposit cannot resume the saved relayer transaction.");
|
|
14053
|
-
const effectiveCallback = customCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress, checkpointCalls) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash, checkpointHash, options.assertSubmissionCurrent, ledgerOperationId) : this.getDefaultSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash, checkpointHash, options.assertSubmissionCurrent, ledgerOperationId));
|
|
15463
|
+
const effectiveCallback = customCallback ?? (this.paymasterServiceUrl ? this.getDefaultSponsoredCallsCallback(asset, onApproved, onSubmitted, onCallsProgress, checkpointCalls) : asset === "WETH" ? this.getDefaultWethSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash, checkpointHash, options.assertSubmissionCurrent, ledgerOperationId, options.resumeSavedBatch, onAuthorization) : this.getDefaultSponsoredCallback(onApproved, onSubmitted, options.resumeTxHash, checkpointHash, options.assertSubmissionCurrent, ledgerOperationId, options.resumeSavedBatch, onAuthorization));
|
|
15464
|
+
let resumedDeadline;
|
|
14054
15465
|
try {
|
|
14055
15466
|
const ledgerToken = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
14056
15467
|
ledgerOp = this.startLedger({
|
|
@@ -14065,16 +15476,19 @@ var OwneySDK = class {
|
|
|
14065
15476
|
} catch {
|
|
14066
15477
|
}
|
|
14067
15478
|
try {
|
|
14068
|
-
if (options.resumeTxHash) {
|
|
15479
|
+
if (options.resumeTxHash || options.resumeSavedBatch) {
|
|
14069
15480
|
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
14070
|
-
const
|
|
15481
|
+
const scope = token && {
|
|
14071
15482
|
apiKey: this.apiKey,
|
|
14072
15483
|
baseUrl: this.routingApiBaseUrl,
|
|
14073
15484
|
owner: state.walletAddress,
|
|
14074
15485
|
chainId,
|
|
14075
15486
|
token: token.address
|
|
14076
|
-
}
|
|
14077
|
-
const
|
|
15487
|
+
};
|
|
15488
|
+
const family = scope && (options.resumeTxHash ? await loadTokenBatchFamily(scope, options.resumeTxHash) : { body: await savedTokenBatchBody(scope, this.getPaidRpcClient(chainId)) });
|
|
15489
|
+
if (options.resumeSavedBatch && family?.body && /^\d{1,15}$/.test(family.body.deadline))
|
|
15490
|
+
resumedDeadline = Number(family.body.deadline);
|
|
15491
|
+
const pending = family?.body && { transfers: family.body.transfers };
|
|
14078
15492
|
const invalid2 = () => new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit does not match this request. Check its status before depositing again.");
|
|
14079
15493
|
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();
|
|
14080
15494
|
const candidates = agentId ? [this.getAgent(agentId)] : this.getEligibleAgents(chainId, asset);
|
|
@@ -14094,12 +15508,10 @@ var OwneySDK = class {
|
|
|
14094
15508
|
return {
|
|
14095
15509
|
id: agent.id,
|
|
14096
15510
|
amount: transfer.amount,
|
|
14097
|
-
run: (callback) => withFailureReporting(
|
|
14098
|
-
|
|
14099
|
-
agent.
|
|
14100
|
-
|
|
14101
|
-
this.routingApiBaseUrl
|
|
14102
|
-
)
|
|
15511
|
+
run: (callback) => withFailureReporting(this.apiKey, agent.id, () => {
|
|
15512
|
+
const context = serverTaskContext(agent);
|
|
15513
|
+
return context ? agent.deposit(state, chainId, transfer.amount, asset, callback, onProgress, void 0, context) : agent.deposit(state, chainId, transfer.amount, asset, callback, onProgress);
|
|
15514
|
+
}, this.routingApiBaseUrl)
|
|
14103
15515
|
};
|
|
14104
15516
|
});
|
|
14105
15517
|
const batchTransfer2 = getDepositBatchTransfer(effectiveCallback);
|
|
@@ -14138,7 +15550,8 @@ var OwneySDK = class {
|
|
|
14138
15550
|
onProgress,
|
|
14139
15551
|
() => paidFallbackAgents.add(agent.id),
|
|
14140
15552
|
!!options.resumeTxHash,
|
|
14141
|
-
checkpointPaid
|
|
15553
|
+
checkpointPaid,
|
|
15554
|
+
serverTaskContext(agent)
|
|
14142
15555
|
));
|
|
14143
15556
|
}
|
|
14144
15557
|
const totalAmount = BigInt(amount);
|
|
@@ -14200,7 +15613,10 @@ var OwneySDK = class {
|
|
|
14200
15613
|
run: (callback) => withFailureReporting(
|
|
14201
15614
|
this.apiKey,
|
|
14202
15615
|
agent.id,
|
|
14203
|
-
() =>
|
|
15616
|
+
() => {
|
|
15617
|
+
const context = serverTaskContext(agent);
|
|
15618
|
+
return context ? agent.deposit(state, chainId, amount2, asset, callback, onProgress, void 0, context) : agent.deposit(state, chainId, amount2, asset, callback, onProgress);
|
|
15619
|
+
},
|
|
14204
15620
|
this.routingApiBaseUrl
|
|
14205
15621
|
)
|
|
14206
15622
|
})),
|
|
@@ -14241,7 +15657,8 @@ var OwneySDK = class {
|
|
|
14241
15657
|
onProgress,
|
|
14242
15658
|
() => paidFallbackAgents.add(agent.id),
|
|
14243
15659
|
!!options.resumeTxHash,
|
|
14244
|
-
checkpointPaid
|
|
15660
|
+
checkpointPaid,
|
|
15661
|
+
serverTaskContext(agent)
|
|
14245
15662
|
);
|
|
14246
15663
|
} catch (error) {
|
|
14247
15664
|
if (Object.keys(agentResults).length === 0) throw error;
|
|
@@ -14261,7 +15678,22 @@ var OwneySDK = class {
|
|
|
14261
15678
|
}
|
|
14262
15679
|
return ledgerDone({ agentResults });
|
|
14263
15680
|
} catch (error) {
|
|
14264
|
-
if (
|
|
15681
|
+
if (options.resumeSavedBatch && !serverBroadcastOf(error))
|
|
15682
|
+
error = new OwneyError(
|
|
15683
|
+
"DEPOSIT_IN_PROGRESS",
|
|
15684
|
+
"Deposit is awaiting confirmation. Its transaction is still being checked.",
|
|
15685
|
+
{
|
|
15686
|
+
sponsorPending: true,
|
|
15687
|
+
phase: "deposit",
|
|
15688
|
+
notSubmitted: false,
|
|
15689
|
+
safeToFallback: false,
|
|
15690
|
+
serverBroadcast: "unknown",
|
|
15691
|
+
...resumedDeadline !== void 0 ? { authorizationDeadline: resumedDeadline } : {},
|
|
15692
|
+
cause: error
|
|
15693
|
+
}
|
|
15694
|
+
);
|
|
15695
|
+
const serverMaySend = serverBroadcastOf(error);
|
|
15696
|
+
if (checkpointFailure && !serverMaySend && !(error instanceof OwneyError && error.details?.notSubmitted === false)) {
|
|
14265
15697
|
error = error instanceof OwneyError && error.code === "DEPOSIT_PARTIAL_FAILURE" ? new OwneyError(
|
|
14266
15698
|
error.code,
|
|
14267
15699
|
"Some deposits completed before another agent could be submitted.",
|
|
@@ -14279,6 +15711,19 @@ var OwneySDK = class {
|
|
|
14279
15711
|
const rejected = error instanceof OwneyError && error.details?.notSubmitted === true;
|
|
14280
15712
|
if (!rejected && paidFallbackAgents.has(id) || evidence?.transfer === "confirmed" || evidence?.transfer === "failed") continue;
|
|
14281
15713
|
if (!rejected && (evidence?.txHash || evidence?.userOpHash || evidence?.callsId)) continue;
|
|
15714
|
+
if (serverMaySend) {
|
|
15715
|
+
const refused = alreadySendingOf(error);
|
|
15716
|
+
const deadline = refused ? void 0 : authorizationDeadlineOf(error) ?? permit?.authorizationDeadline;
|
|
15717
|
+
const nonce = refused ? void 0 : permit2NonceOf(error) ?? permit?.permit2Nonce;
|
|
15718
|
+
if (evidence) notifyFunding(onProgress, {
|
|
15719
|
+
...evidence,
|
|
15720
|
+
serverBroadcast: evidence.serverBroadcast ?? "unknown",
|
|
15721
|
+
...deadline !== void 0 && !evidence.authorizationDeadline ? { authorizationDeadline: deadline } : {},
|
|
15722
|
+
...nonce !== void 0 && !evidence.permit2Nonce ? { permit2Nonce: nonce } : {},
|
|
15723
|
+
transfer: "pending"
|
|
15724
|
+
});
|
|
15725
|
+
continue;
|
|
15726
|
+
}
|
|
14282
15727
|
notifyFunding(onProgress, { ...evidence ?? {
|
|
14283
15728
|
agentId: id,
|
|
14284
15729
|
chainId,
|
|
@@ -14325,11 +15770,11 @@ var OwneySDK = class {
|
|
|
14325
15770
|
* propagates unchanged. Scoped per agent invocation so one agent's failure
|
|
14326
15771
|
* doesn't force a retry of sibling agents in a multi-agent split.
|
|
14327
15772
|
*/
|
|
14328
|
-
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress, onPaidFallback, resumeOnly = false, onBeforeSubmission) {
|
|
15773
|
+
async depositWithFallback(agent, state, chainId, amount, asset, effectiveCallback, appCallback, onProgress, onPaidFallback, resumeOnly = false, onBeforeSubmission, serverTaskContext) {
|
|
14329
15774
|
const attempt = (callback) => withFailureReporting(
|
|
14330
15775
|
this.apiKey,
|
|
14331
15776
|
agent.id,
|
|
14332
|
-
() => onBeforeSubmission ? agent.deposit(state, chainId, amount, asset, callback, onProgress, onBeforeSubmission) : onProgress ? agent.deposit(state, chainId, amount, asset, callback, onProgress) : agent.deposit(state, chainId, amount, asset, callback),
|
|
15777
|
+
() => serverTaskContext ? agent.deposit(state, chainId, amount, asset, callback, onProgress, onBeforeSubmission, serverTaskContext) : onBeforeSubmission ? agent.deposit(state, chainId, amount, asset, callback, onProgress, onBeforeSubmission) : onProgress ? agent.deposit(state, chainId, amount, asset, callback, onProgress) : agent.deposit(state, chainId, amount, asset, callback),
|
|
14333
15778
|
this.routingApiBaseUrl
|
|
14334
15779
|
);
|
|
14335
15780
|
let approvalAttempted = false;
|
|
@@ -14626,11 +16071,36 @@ var OwneySDK = class {
|
|
|
14626
16071
|
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${evidence.agentId}:${evidence.token}:${evidence.txHash}`;
|
|
14627
16072
|
if (options.refreshBalance !== false && (verified.transfer === "confirmed" || verified.transfer === "failed" && verified.confirmedAmount === "0") && !this.recoveredFundingReads.has(key2)) {
|
|
14628
16073
|
agent.invalidateFundingRead?.(state, chainId, evidence.token, options.balanceRefreshUntil, evidence.txHash);
|
|
14629
|
-
|
|
14630
|
-
|
|
16074
|
+
const until = options.balanceRefreshUntil;
|
|
16075
|
+
this.noteFundingRefresh(key2, until === void 0 || until > Date.now());
|
|
14631
16076
|
}
|
|
14632
16077
|
return verified;
|
|
14633
16078
|
}
|
|
16079
|
+
noteFundingRefresh(key2, armed) {
|
|
16080
|
+
this.recoveredFundingReads.delete(key2);
|
|
16081
|
+
this.recoveredFundingReads.set(key2, armed);
|
|
16082
|
+
if (this.recoveredFundingReads.size > 256) this.recoveredFundingReads.delete(this.recoveredFundingReads.keys().next().value);
|
|
16083
|
+
}
|
|
16084
|
+
/**
|
|
16085
|
+
* Re-arms an agent's balance refresh after a confirmed deposit or withdrawal
|
|
16086
|
+
* the caller already verified (e.g. after a page reload, which loses the
|
|
16087
|
+
* refresh the SDK started in memory). Same refresh as receipt recovery
|
|
16088
|
+
* (`inspectFunding`), without a receipt read, and shared with it: once per
|
|
16089
|
+
* movement (`txHash`) in this SDK session, unless that recovery came after
|
|
16090
|
+
* its own deadline and so armed nothing. `until` bounds it (the agent caps
|
|
16091
|
+
* it further). Returns false when the agent has no such refresh or `until`
|
|
16092
|
+
* has passed; throws when not connected or on another chain. Never prompts.
|
|
16093
|
+
*/
|
|
16094
|
+
refreshAgentBalanceAfterMovement(input) {
|
|
16095
|
+
const { state, chainId } = this.captureReadScope(input.chainId);
|
|
16096
|
+
const agent = this.getAgent(input.agentId);
|
|
16097
|
+
if (typeof agent.invalidateFundingRead !== "function" || !Number.isFinite(input.until) || input.until <= Date.now()) return false;
|
|
16098
|
+
const key2 = input.txHash ? `${state.walletAddress.toLowerCase()}:${chainId}:${input.agentId}:${input.token}:${input.txHash}` : void 0;
|
|
16099
|
+
if (key2 && this.recoveredFundingReads.get(key2) === true) return true;
|
|
16100
|
+
agent.invalidateFundingRead(state, chainId, input.token, input.until, input.txHash);
|
|
16101
|
+
if (key2) this.noteFundingRefresh(key2, true);
|
|
16102
|
+
return true;
|
|
16103
|
+
}
|
|
14634
16104
|
/**
|
|
14635
16105
|
* Lists on-chain payout candidates for a withdrawal the provider accepted
|
|
14636
16106
|
* without a hash: ERC-20 `Transfer` logs on the evidence's token that reach
|
|
@@ -14752,22 +16222,25 @@ var OwneySDK = class {
|
|
|
14752
16222
|
try {
|
|
14753
16223
|
if (this.paymasterServiceUrl || !evidences.length || !/^\d+$/.test(amount)) return false;
|
|
14754
16224
|
const first = evidences[0];
|
|
14755
|
-
|
|
16225
|
+
const saved = !first.txHash && evidences.every((e) => !e.txHash && (e.serverBroadcast === "unknown" || e.serverBroadcast === true) && typeof e.authorizationDeadline === "number");
|
|
16226
|
+
if (!first.txHash && !saved) return false;
|
|
14756
16227
|
const { state, chainId, assertCurrent } = this.captureReadScope(first.chainId);
|
|
14757
16228
|
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === first.token);
|
|
14758
16229
|
if (!token) return false;
|
|
14759
|
-
const
|
|
16230
|
+
const scope = {
|
|
14760
16231
|
apiKey: this.apiKey,
|
|
14761
16232
|
baseUrl: this.routingApiBaseUrl,
|
|
14762
16233
|
owner: state.walletAddress,
|
|
14763
16234
|
chainId,
|
|
14764
16235
|
token: token.address
|
|
14765
|
-
}
|
|
14766
|
-
const
|
|
14767
|
-
|
|
16236
|
+
};
|
|
16237
|
+
const family = saved ? void 0 : await loadTokenBatchFamily(scope, first.txHash);
|
|
16238
|
+
const savedBody = saved ? await savedTokenBatchBody(scope, this.getPaidRpcClient(chainId)) : void 0;
|
|
16239
|
+
const transfers = saved ? savedBody?.transfers : family?.body.transfers;
|
|
16240
|
+
if (!family && !savedBody || !transfers || transfers.length !== evidences.length || transfers.reduce((n, t) => n + BigInt(t.amount), 0n).toString() !== amount) return false;
|
|
14768
16241
|
const destinations = /* @__PURE__ */ new Set();
|
|
14769
16242
|
for (const e of evidences) {
|
|
14770
|
-
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;
|
|
16243
|
+
if (e.chainId !== chainId || e.token !== first.token || e.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || e.callsId || e.userOpHash || (family ? !family.attempts.some((a) => a.phase === "deposit" && a.hash.toLowerCase() === e.txHash?.toLowerCase()) || e.depositAuthorization && e.depositAuthorization.authorizationId !== family.authorizationId : String(e.authorizationDeadline) !== savedBody.deadline)) return false;
|
|
14771
16244
|
const agent = this.getAgent(e.agentId);
|
|
14772
16245
|
const wallet = agent.getFundingWallet ? await agent.getFundingWallet(state, chainId, e.token) : (await agent.getUserProfile(state, chainId)).smartWallet;
|
|
14773
16246
|
assertCurrent();
|
|
@@ -14780,6 +16253,62 @@ var OwneySDK = class {
|
|
|
14780
16253
|
return false;
|
|
14781
16254
|
}
|
|
14782
16255
|
}
|
|
16256
|
+
/** Whether a Permit2 deposit authorization can still execute and whether it
|
|
16257
|
+
* did: `expired` when the finalized block is more than 300 s past
|
|
16258
|
+
* `deadline` (chain time), `used` when the owner's Permit2 nonce bit is set
|
|
16259
|
+
* at that block. Null when anything cannot be read. Never throws. */
|
|
16260
|
+
async checkDepositAuthorization(input) {
|
|
16261
|
+
try {
|
|
16262
|
+
const { chainId, owner, permit2Nonce, deadline } = input;
|
|
16263
|
+
if (!SupportedAssets.some((asset) => asset.chainId === chainId) || !isAddress5(owner) || typeof permit2Nonce !== "string" || !/^\d{1,78}$/.test(permit2Nonce) || !Number.isSafeInteger(deadline) || deadline <= 0) return null;
|
|
16264
|
+
const nonce = BigInt(permit2Nonce);
|
|
16265
|
+
if (nonce >= 1n << 256n) return null;
|
|
16266
|
+
const pub = this.getPaidRpcClient(chainId);
|
|
16267
|
+
const block = await pub.getBlock({ blockTag: "finalized" });
|
|
16268
|
+
if (typeof block?.timestamp !== "bigint" || typeof block.number !== "bigint") return null;
|
|
16269
|
+
const bitmap = await pub.readContract({
|
|
16270
|
+
address: BATCH_PERMIT2_ADDRESS,
|
|
16271
|
+
abi: PERMIT2_BATCH_ABI,
|
|
16272
|
+
functionName: "nonceBitmap",
|
|
16273
|
+
args: [owner, nonce >> 8n],
|
|
16274
|
+
blockNumber: block.number
|
|
16275
|
+
});
|
|
16276
|
+
if (typeof bitmap !== "bigint" || bitmap < 0n) return null;
|
|
16277
|
+
return { expired: block.timestamp > BigInt(deadline) + 300n, used: (bitmap & 1n << (nonce & 255n)) !== 0n };
|
|
16278
|
+
} catch {
|
|
16279
|
+
return null;
|
|
16280
|
+
}
|
|
16281
|
+
}
|
|
16282
|
+
/** Drops the saved signed body of a hashless deposit the server may have
|
|
16283
|
+
* been sending (`serverBroadcast` 'unknown'/true, no hash) once the app has
|
|
16284
|
+
* settled it another way: by the ledger's hash or its expired deadline.
|
|
16285
|
+
* Only the body with this Permit2 deadline and these exact shares is
|
|
16286
|
+
* dropped, so it never resumes or blocks a different deposit. Never throws. */
|
|
16287
|
+
async discardSavedDepositBatch(evidences) {
|
|
16288
|
+
try {
|
|
16289
|
+
if (this.paymasterServiceUrl || !evidences.length) return false;
|
|
16290
|
+
const first = evidences[0];
|
|
16291
|
+
const deadline = first.authorizationDeadline;
|
|
16292
|
+
if (typeof deadline !== "number" || !Number.isSafeInteger(deadline) || deadline <= 0) return false;
|
|
16293
|
+
const { state, chainId } = this.captureReadScope(first.chainId);
|
|
16294
|
+
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === first.token);
|
|
16295
|
+
if (!token) return false;
|
|
16296
|
+
if (evidences.some((e) => e.chainId !== chainId || e.token !== first.token || e.authorizationDeadline !== deadline || e.walletAddress.toLowerCase() !== state.walletAddress.toLowerCase() || !e.agentWallet || e.serverBroadcast !== "unknown" && e.serverBroadcast !== true)) return false;
|
|
16297
|
+
return await discardHashlessTokenBatch(
|
|
16298
|
+
{
|
|
16299
|
+
apiKey: this.apiKey,
|
|
16300
|
+
baseUrl: this.routingApiBaseUrl,
|
|
16301
|
+
owner: state.walletAddress,
|
|
16302
|
+
chainId,
|
|
16303
|
+
token: token.address
|
|
16304
|
+
},
|
|
16305
|
+
String(deadline),
|
|
16306
|
+
(body) => evidences.every((e) => body.transfers.some((t) => t.to.toLowerCase() === e.agentWallet.toLowerCase() && t.amount === e.intendedAmount))
|
|
16307
|
+
);
|
|
16308
|
+
} catch {
|
|
16309
|
+
return false;
|
|
16310
|
+
}
|
|
16311
|
+
}
|
|
14783
16312
|
/** Validate signed journal membership, never just a caller-supplied family ID. */
|
|
14784
16313
|
async validateDepositReplacement(previous, next) {
|
|
14785
16314
|
try {
|
|
@@ -14872,6 +16401,22 @@ var OwneySDK = class {
|
|
|
14872
16401
|
if (verified.agentId !== "zyfai") return verified;
|
|
14873
16402
|
return this.getAgent("zyfai").retryDepositRegistration(state, verified);
|
|
14874
16403
|
}
|
|
16404
|
+
/** A Zyfai withdrawal's lifecycle by the id recorded on its evidence. Never signs,
|
|
16405
|
+
* prompts or authenticates; `null` when unavailable or not this wallet's Zyfai withdrawal. */
|
|
16406
|
+
async getZyfaiWithdrawal(evidence) {
|
|
16407
|
+
const state = this.state;
|
|
16408
|
+
const id = evidence.withdrawalRequest?.providerWithdrawalId;
|
|
16409
|
+
if (evidence.agentId !== "zyfai" || !id || !state || state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase()) return null;
|
|
16410
|
+
return this.getAgent("zyfai").getWithdrawalLifecycle(state, id);
|
|
16411
|
+
}
|
|
16412
|
+
/** A Zyfai deposit's placement status by the id recorded on its evidence. Never signs,
|
|
16413
|
+
* prompts or authenticates; `null` when unavailable or not this wallet's Zyfai deposit. */
|
|
16414
|
+
async getZyfaiDeposit(evidence) {
|
|
16415
|
+
const state = this.state;
|
|
16416
|
+
const id = evidence.providerDepositId;
|
|
16417
|
+
if (evidence.agentId !== "zyfai" || !id || !state || state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase()) return null;
|
|
16418
|
+
return this.getAgent("zyfai").getDepositPlacement(state, id);
|
|
16419
|
+
}
|
|
14875
16420
|
// --- Fund operations ---
|
|
14876
16421
|
/** On-chain version and ownership status for this owner's Yieldseeker agent. */
|
|
14877
16422
|
async getYieldseekerWalletStatus(asset) {
|
|
@@ -15454,6 +16999,17 @@ var OwneySDK = class {
|
|
|
15454
16999
|
onStage: (stage) => options.onStage(order.orderHash, stage)
|
|
15455
17000
|
} : {}
|
|
15456
17001
|
});
|
|
17002
|
+
const deposit = order.pendingDeposit;
|
|
17003
|
+
if (deposit && typeof deposit.received === "string" && typeof deposit.symbol === "string" && typeof deposit.chainId === "number" && loadOrder(order.orderHash))
|
|
17004
|
+
savePendingDeposit({
|
|
17005
|
+
id: `${deposit.chainId}-${deposit.symbol}-${order.orderHash}`,
|
|
17006
|
+
received: deposit.received,
|
|
17007
|
+
symbol: deposit.symbol,
|
|
17008
|
+
chainId: deposit.chainId,
|
|
17009
|
+
createdAt: Date.now(),
|
|
17010
|
+
...typeof deposit.agentId === "string" ? { agentId: deposit.agentId } : {},
|
|
17011
|
+
...typeof deposit.operationId === "string" ? { operationId: deposit.operationId } : {}
|
|
17012
|
+
});
|
|
15457
17013
|
clearOrder(order.orderHash);
|
|
15458
17014
|
updateWithdrawSwap(order.orderHash, { status: "landed" });
|
|
15459
17015
|
return { orderHash: order.orderHash, status: "completed" };
|
|
@@ -15624,14 +17180,16 @@ var OwneySDK = class {
|
|
|
15624
17180
|
options.to.symbol
|
|
15625
17181
|
);
|
|
15626
17182
|
}
|
|
15627
|
-
|
|
17183
|
+
let ledgerInput;
|
|
17184
|
+
const ledgerOp = this.openSwapLedger(() => ledgerInput = {
|
|
15628
17185
|
operationId: options.operationId,
|
|
15629
17186
|
kind: "swap_deposit",
|
|
15630
17187
|
owner: state.walletAddress,
|
|
15631
17188
|
chainId: options.to.chainId,
|
|
15632
17189
|
token: SupportedAssets.find((value) => value.chainId === options.to.chainId && value.symbol === options.to.symbol)?.address ?? "",
|
|
15633
17190
|
asset: options.to.symbol.toLowerCase()
|
|
15634
|
-
})
|
|
17191
|
+
});
|
|
17192
|
+
const secretHandoff = this.secretHandoffFor(ledgerOp, ledgerInput);
|
|
15635
17193
|
try {
|
|
15636
17194
|
const api = this.swapApi();
|
|
15637
17195
|
const quoted = await api.quote({
|
|
@@ -15659,6 +17217,17 @@ var OwneySDK = class {
|
|
|
15659
17217
|
...options.assertCurrent ? { assertCurrent: options.assertCurrent } : {},
|
|
15660
17218
|
...options.slippage === void 0 ? {} : { slippage: options.slippage },
|
|
15661
17219
|
...options.onSwapProgress ? { onStage: options.onSwapProgress } : {},
|
|
17220
|
+
// Stored with a cross-chain order, so a resume that finishes the swap
|
|
17221
|
+
// can still lead to the deposit (ROUT-625). The guaranteed minimum,
|
|
17222
|
+
// never the quoted amount.
|
|
17223
|
+
pendingDeposit: {
|
|
17224
|
+
received: quote.dstAmountMin,
|
|
17225
|
+
symbol: options.to.symbol,
|
|
17226
|
+
chainId: quote.dst.chainId,
|
|
17227
|
+
...options.agentId ? { agentId: options.agentId } : {},
|
|
17228
|
+
...ledgerOp ? { operationId: ledgerOp.operationId } : {}
|
|
17229
|
+
},
|
|
17230
|
+
...secretHandoff ? { secretHandoff } : {},
|
|
15662
17231
|
...ledgerOp ? { onOrderSubmitted: (orderHash) => {
|
|
15663
17232
|
swapIds.orderHash = orderHash;
|
|
15664
17233
|
this.ledgerSwap(ledgerOp, quote, { status: "submitted", orderHash });
|
|
@@ -15678,6 +17247,7 @@ var OwneySDK = class {
|
|
|
15678
17247
|
receivedDstAmountRaw: swap.received
|
|
15679
17248
|
});
|
|
15680
17249
|
options.onSwapProgress?.("depositing");
|
|
17250
|
+
const backgroundDepositId = swap.orderHash ? `${quote.dst.chainId}-${options.to.symbol}-${swap.orderHash}` : void 0;
|
|
15681
17251
|
try {
|
|
15682
17252
|
options.assertCurrent?.();
|
|
15683
17253
|
await this.ensureSwapChain(quote.dst.chainId);
|
|
@@ -15707,10 +17277,13 @@ var OwneySDK = class {
|
|
|
15707
17277
|
...options.assertCurrent ? { assertSubmissionCurrent: options.assertCurrent } : {},
|
|
15708
17278
|
...ledgerOp ? { operationId: ledgerOp.operationId } : {}
|
|
15709
17279
|
});
|
|
17280
|
+
if (backgroundDepositId) clearPendingDeposit(backgroundDepositId);
|
|
15710
17281
|
return this.ledgerSucceeded(ledgerOp, { swap, deposit, deposited: amount });
|
|
15711
17282
|
} catch (error) {
|
|
15712
17283
|
const pending = {
|
|
15713
|
-
|
|
17284
|
+
// Keyed by the order when there is one, so it replaces any record a
|
|
17285
|
+
// background resume wrote for the same order rather than doubling it.
|
|
17286
|
+
id: backgroundDepositId ?? `${quote.dst.chainId}-${options.to.symbol}-${Date.now()}`,
|
|
15714
17287
|
received: swap.received,
|
|
15715
17288
|
symbol: options.to.symbol,
|
|
15716
17289
|
chainId: quote.dst.chainId,
|
|
@@ -15866,7 +17439,7 @@ var OwneySDK = class {
|
|
|
15866
17439
|
const state = this.requireState();
|
|
15867
17440
|
const activeChainId = this.requireChainId();
|
|
15868
17441
|
const revision = this.connectionRevision;
|
|
15869
|
-
let
|
|
17442
|
+
let active = true;
|
|
15870
17443
|
let record2;
|
|
15871
17444
|
let ledgerOp;
|
|
15872
17445
|
let hadDurableRecord = false;
|
|
@@ -15874,7 +17447,7 @@ var OwneySDK = class {
|
|
|
15874
17447
|
let confirmationDeadline = Infinity;
|
|
15875
17448
|
const assertCurrent = () => {
|
|
15876
17449
|
options.assertCurrent?.();
|
|
15877
|
-
if (!
|
|
17450
|
+
if (!active) throw new OwneyError("WITHDRAW_SWAP_INCOMPLETE", "This withdrawal attempt is no longer active.", { swapSubmitted: false });
|
|
15878
17451
|
if (this.state !== state || this.connectionRevision !== revision || state.chainId !== activeChainId)
|
|
15879
17452
|
throw new OwneyError("NOT_CONNECTED", "Wallet changed during withdrawal and swap.");
|
|
15880
17453
|
};
|
|
@@ -15919,9 +17492,10 @@ var OwneySDK = class {
|
|
|
15919
17492
|
const saved = options.recoveryId ? listWithdrawSwaps(Date.now(), true).find((row) => row.orderHash === options.recoveryId) : void 0;
|
|
15920
17493
|
if (options.recoveryId && (!saved?.recovery?.results || saved.recovery.phase !== "ready" || saved.walletAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || saved.srcChainId !== activeChainId || saved.srcSymbol !== asset.symbol || saved.dstChainId !== options.to.chainId || saved.dstSymbol !== options.to.symbol))
|
|
15921
17494
|
throw new OwneyError("WITHDRAW_SWAP_INCOMPLETE", "This withdrawal is not ready to continue.");
|
|
17495
|
+
let ledgerInput;
|
|
15922
17496
|
ledgerOp = this.openSwapLedger(() => {
|
|
15923
17497
|
const requestedRaw = saved ? saved.recovery.requested : options.amount === void 0 ? void 0 : parseUnits4(options.amount, asset.decimals).toString();
|
|
15924
|
-
return {
|
|
17498
|
+
return ledgerInput = {
|
|
15925
17499
|
operationId: saved?.operationId ?? options.operationId,
|
|
15926
17500
|
kind: "withdraw_swap",
|
|
15927
17501
|
owner: state.walletAddress,
|
|
@@ -15932,6 +17506,7 @@ var OwneySDK = class {
|
|
|
15932
17506
|
...requestedRaw === void 0 ? {} : { requestedAmountRaw: requestedRaw }
|
|
15933
17507
|
};
|
|
15934
17508
|
});
|
|
17509
|
+
const secretHandoff = this.secretHandoffFor(ledgerOp, ledgerInput);
|
|
15935
17510
|
const prepareTimeout = () => new OwneyError(
|
|
15936
17511
|
"WITHDRAW_SWAP_INCOMPLETE",
|
|
15937
17512
|
"Could not read the source wallet. No new withdrawal was submitted.",
|
|
@@ -15991,7 +17566,7 @@ var OwneySDK = class {
|
|
|
15991
17566
|
evidence.set(next.agentId, next);
|
|
15992
17567
|
if (record2?.recovery) {
|
|
15993
17568
|
record2.recovery.evidence[next.agentId] = next;
|
|
15994
|
-
if (
|
|
17569
|
+
if (active) saveWithdrawSwap(record2);
|
|
15995
17570
|
else {
|
|
15996
17571
|
const latest = listWithdrawSwaps(Date.now(), true).find((row) => row.recovery?.attemptId === record2.recovery.attemptId);
|
|
15997
17572
|
if (latest?.recovery) updateWithdrawSwap(latest.orderHash, { recovery: {
|
|
@@ -16107,6 +17682,7 @@ var OwneySDK = class {
|
|
|
16107
17682
|
quote,
|
|
16108
17683
|
walletAddress: state.walletAddress,
|
|
16109
17684
|
direction: "withdraw",
|
|
17685
|
+
...secretHandoff ? { secretHandoff } : {},
|
|
16110
17686
|
...options.slippage === void 0 ? {} : { slippage: options.slippage },
|
|
16111
17687
|
...options.onSwapProgress ? { onStage: options.onSwapProgress } : {},
|
|
16112
17688
|
onOrderPrepared: (orderHash, details) => {
|
|
@@ -16195,7 +17771,7 @@ var OwneySDK = class {
|
|
|
16195
17771
|
this.ledgerFailed(ledgerOp, error);
|
|
16196
17772
|
throw error;
|
|
16197
17773
|
} finally {
|
|
16198
|
-
|
|
17774
|
+
active = false;
|
|
16199
17775
|
releaseSubmission();
|
|
16200
17776
|
this.closeSwapLedger(ledgerOp);
|
|
16201
17777
|
}
|
|
@@ -16243,11 +17819,13 @@ var OwneySDK = class {
|
|
|
16243
17819
|
});
|
|
16244
17820
|
} catch {
|
|
16245
17821
|
}
|
|
16246
|
-
const ledgerLeg = (evidence) => this.ledgerWithdrawalLeg(ledgerOp, evidence);
|
|
17822
|
+
const ledgerLeg = (evidence, errorCode) => this.ledgerWithdrawalLeg(ledgerOp, evidence, errorCode);
|
|
16247
17823
|
let hold;
|
|
16248
17824
|
let reservationRejected = false;
|
|
16249
17825
|
let innerStarted = false;
|
|
16250
17826
|
const reservationLegs = /* @__PURE__ */ new Map();
|
|
17827
|
+
const plannedLegs = /* @__PURE__ */ new Map();
|
|
17828
|
+
const serverLegs = /* @__PURE__ */ new Map();
|
|
16251
17829
|
const reservation = {
|
|
16252
17830
|
dispatching: (id) => hold?.dispatch(id),
|
|
16253
17831
|
settled: (id, leg) => {
|
|
@@ -16257,11 +17835,22 @@ var OwneySDK = class {
|
|
|
16257
17835
|
const settleHold = () => {
|
|
16258
17836
|
if (!hold) return;
|
|
16259
17837
|
hold.stopHeartbeat();
|
|
16260
|
-
|
|
17838
|
+
const serverOwned = new Set(ids.filter((id) => !attempted.has(id) && serverMayOwn(serverLegs.get(id))));
|
|
17839
|
+
this.settleWithdrawalReservation(hold, ids, attempted, reservationLegs, serverOwned);
|
|
16261
17840
|
};
|
|
16262
17841
|
const runAgents = () => {
|
|
16263
17842
|
innerStarted = true;
|
|
16264
|
-
const inner = this.withdrawFromAgents(
|
|
17843
|
+
const inner = this.withdrawFromAgents(
|
|
17844
|
+
options,
|
|
17845
|
+
state,
|
|
17846
|
+
chainId,
|
|
17847
|
+
attempted,
|
|
17848
|
+
ledgerLeg,
|
|
17849
|
+
reservation,
|
|
17850
|
+
plannedLegs,
|
|
17851
|
+
(timeoutMs) => ledgerOp?.settle?.(timeoutMs),
|
|
17852
|
+
serverLegs
|
|
17853
|
+
);
|
|
16265
17854
|
void inner.then(settleHold, settleHold);
|
|
16266
17855
|
return inner;
|
|
16267
17856
|
};
|
|
@@ -16285,7 +17874,9 @@ var OwneySDK = class {
|
|
|
16285
17874
|
} catch (error) {
|
|
16286
17875
|
if (!reservationRejected) {
|
|
16287
17876
|
for (const id of ids) if (!attempted.has(id)) {
|
|
16288
|
-
|
|
17877
|
+
if (serverMayOwn(serverLegs.get(id))) continue;
|
|
17878
|
+
const planned = plannedLegs.get(id);
|
|
17879
|
+
notifyFunding(options.onProgress, planned ? { ...planned, transfer: "failed", delivery: "failed" } : {
|
|
16289
17880
|
agentId: id,
|
|
16290
17881
|
chainId,
|
|
16291
17882
|
token: options.asset,
|
|
@@ -16407,12 +17998,13 @@ var OwneySDK = class {
|
|
|
16407
17998
|
* - a throw without submission evidence: `not_submitted`;
|
|
16408
17999
|
* - Zyfai "withdrawal already in progress": kept, dispatch `provider_in_progress`.
|
|
16409
18000
|
*/
|
|
16410
|
-
settleWithdrawalReservation(hold, ids, attempted, legs) {
|
|
18001
|
+
settleWithdrawalReservation(hold, ids, attempted, legs, keep) {
|
|
16411
18002
|
try {
|
|
16412
18003
|
if ([...attempted].some((id) => !legs.has(id))) return;
|
|
16413
18004
|
const releases = /* @__PURE__ */ new Map();
|
|
16414
18005
|
for (const id of new Set(ids)) {
|
|
16415
18006
|
if (!RESERVATION_AGENTS.includes(id)) continue;
|
|
18007
|
+
if (keep?.has(id)) continue;
|
|
16416
18008
|
const leg = legs.get(id);
|
|
16417
18009
|
const outcome = attempted.has(id) && leg ? reservationLegOutcome(leg) : "not_submitted";
|
|
16418
18010
|
if (outcome === "keep") continue;
|
|
@@ -16443,6 +18035,31 @@ var OwneySDK = class {
|
|
|
16443
18035
|
} catch {
|
|
16444
18036
|
}
|
|
16445
18037
|
}
|
|
18038
|
+
/**
|
|
18039
|
+
* Reports a withdrawal step the app closed as never sent (a reload before its
|
|
18040
|
+
* request was saved) as a failed ledger leg with `NOT_SUBMITTED`, so other
|
|
18041
|
+
* devices and later recovery stop treating it as in flight. Refuses any step
|
|
18042
|
+
* with a hash or a saved request. Never touches the hold; never throws.
|
|
18043
|
+
*/
|
|
18044
|
+
async reportWithdrawalNotSubmitted(operationId, evidence) {
|
|
18045
|
+
try {
|
|
18046
|
+
if (typeof operationId !== "string" || !operationId || !evidence || evidence.transfer !== "failed" || evidence.txHash || evidence.userOpHash || evidence.callsId || evidence.withdrawalRequest?.attempted === true) return;
|
|
18047
|
+
const leg = ledgerLegFromEvidence(evidence, "withdraw");
|
|
18048
|
+
if (leg?.status !== "failed") return;
|
|
18049
|
+
const token = SupportedAssets.find((value) => value.chainId === evidence.chainId && value.symbol === evidence.token);
|
|
18050
|
+
const op = this.ledger.resume({
|
|
18051
|
+
operationId,
|
|
18052
|
+
kind: "withdraw",
|
|
18053
|
+
owner: evidence.walletAddress,
|
|
18054
|
+
chainId: evidence.chainId,
|
|
18055
|
+
token: token?.address ?? "",
|
|
18056
|
+
asset: String(evidence.token).toLowerCase()
|
|
18057
|
+
});
|
|
18058
|
+
op.leg({ ...leg, errorCode: "NOT_SUBMITTED" });
|
|
18059
|
+
op.finish({});
|
|
18060
|
+
} catch {
|
|
18061
|
+
}
|
|
18062
|
+
}
|
|
16446
18063
|
/**
|
|
16447
18064
|
* Reports a withdrawal leg that was proven after its call ended (a hashless
|
|
16448
18065
|
* Zyfai payout found on chain or in history) to the funding ledger, then
|
|
@@ -16492,7 +18109,36 @@ var OwneySDK = class {
|
|
|
16492
18109
|
} catch {
|
|
16493
18110
|
}
|
|
16494
18111
|
}
|
|
16495
|
-
|
|
18112
|
+
/**
|
|
18113
|
+
* Reports a deposit leg that the app's recovery verified on chain after its
|
|
18114
|
+
* call ended to the funding ledger. Only evidence whose transfer is confirmed
|
|
18115
|
+
* with a transaction hash is reported; anything else is ignored. The ledger
|
|
18116
|
+
* PATCHes the existing operation (creating it only on a 404) and the server
|
|
18117
|
+
* re-verifies the hash. Deposits hold no reservation, so nothing is released.
|
|
18118
|
+
* Never throws.
|
|
18119
|
+
*/
|
|
18120
|
+
async reportDepositRecovery(operationId, evidence) {
|
|
18121
|
+
try {
|
|
18122
|
+
if (typeof operationId !== "string" || !operationId || !evidence || evidence.transfer !== "confirmed") return;
|
|
18123
|
+
const leg = ledgerLegFromEvidence(evidence, "deposit");
|
|
18124
|
+
if (leg?.status !== "confirmed" || !leg.txHash) return;
|
|
18125
|
+
const rail = depositLedgerRail(evidence);
|
|
18126
|
+
const token = SupportedAssets.find((value) => value.chainId === evidence.chainId && value.symbol === evidence.token);
|
|
18127
|
+
const op = this.ledger.resume({
|
|
18128
|
+
operationId,
|
|
18129
|
+
kind: "deposit",
|
|
18130
|
+
owner: evidence.walletAddress,
|
|
18131
|
+
chainId: evidence.chainId,
|
|
18132
|
+
token: token?.address ?? "",
|
|
18133
|
+
asset: String(evidence.token).toLowerCase()
|
|
18134
|
+
});
|
|
18135
|
+
op.leg(rail ? { ...leg, rail } : leg);
|
|
18136
|
+
op.finish({});
|
|
18137
|
+
this.ledgerProviderSamples(op, evidence, "deposit");
|
|
18138
|
+
} catch {
|
|
18139
|
+
}
|
|
18140
|
+
}
|
|
18141
|
+
async withdrawFromAgents(options, state, chainId, attempted, ledgerLeg, reservation, plannedLegs, settleLedger, serverLegs = /* @__PURE__ */ new Map()) {
|
|
16496
18142
|
const { asset, amount, agentId, onAgentResult } = options;
|
|
16497
18143
|
const notifyAgentResult = (id, result) => {
|
|
16498
18144
|
try {
|
|
@@ -16538,7 +18184,22 @@ var OwneySDK = class {
|
|
|
16538
18184
|
{ asset, amount, agentErrors: agentErrors2, agentFailures, pendingSubmissions: pending }
|
|
16539
18185
|
);
|
|
16540
18186
|
};
|
|
16541
|
-
const
|
|
18187
|
+
const awaitServerHandoffs = async () => {
|
|
18188
|
+
const pending = [...serverLegs.values()].filter((leg) => leg.settled === void 0).map((leg) => leg.answer);
|
|
18189
|
+
if (!pending.length) return;
|
|
18190
|
+
let timer;
|
|
18191
|
+
await Promise.race([
|
|
18192
|
+
Promise.all(pending),
|
|
18193
|
+
new Promise((resolve) => {
|
|
18194
|
+
timer = setTimeout(resolve, SERVER_HANDOFF_WAIT_MS);
|
|
18195
|
+
})
|
|
18196
|
+
]);
|
|
18197
|
+
clearTimeout(timer);
|
|
18198
|
+
};
|
|
18199
|
+
const submit = async (agent, requested2, onAccepted) => {
|
|
18200
|
+
const plannedTask = serverLegs.get(agent.id);
|
|
18201
|
+
const serverTask = plannedTask ? await plannedTask.answer : void 0;
|
|
18202
|
+
if (serverTask) await settleLedger?.(3e3);
|
|
16542
18203
|
if (this.state !== state || this.state.chainId !== chainId)
|
|
16543
18204
|
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed before withdrawal submission.");
|
|
16544
18205
|
attempted.add(agent.id);
|
|
@@ -16560,6 +18221,7 @@ var OwneySDK = class {
|
|
|
16560
18221
|
submissionEvidence.set(agent.id, evidence);
|
|
16561
18222
|
notifyFunding(options.onProgress, evidence);
|
|
16562
18223
|
ledgerLeg?.(evidence);
|
|
18224
|
+
if (onAccepted && evidence.ownerAuthorization === "accepted" && evidence.transfer !== "failed" && (evidence.userOpHash || evidence.callsId || evidence.txHash)) onAccepted();
|
|
16563
18225
|
};
|
|
16564
18226
|
progress(evidence);
|
|
16565
18227
|
adapterReported = false;
|
|
@@ -16574,14 +18236,26 @@ var OwneySDK = class {
|
|
|
16574
18236
|
reservation?.dispatching(agent.id);
|
|
16575
18237
|
} catch {
|
|
16576
18238
|
}
|
|
16577
|
-
const
|
|
18239
|
+
const context = typeof options.operationId === "string" ? { operationId: options.operationId, ...serverTask ? { planned: true } : {} } : void 0;
|
|
18240
|
+
const onBeforeSubmission = options.onBeforeSubmission ? (next) => options.onBeforeSubmission({ ...evidence, ...next }) : void 0;
|
|
18241
|
+
const result = context ? await agent.withdraw(
|
|
18242
|
+
state,
|
|
18243
|
+
chainId,
|
|
18244
|
+
token,
|
|
18245
|
+
requested2,
|
|
18246
|
+
progress,
|
|
18247
|
+
options.gasMode ?? "auto",
|
|
18248
|
+
onBeforeSubmission,
|
|
18249
|
+
options.assertCurrent,
|
|
18250
|
+
context
|
|
18251
|
+
) : options.onBeforeSubmission || options.assertCurrent ? await agent.withdraw(
|
|
16578
18252
|
state,
|
|
16579
18253
|
chainId,
|
|
16580
18254
|
token,
|
|
16581
18255
|
requested2,
|
|
16582
18256
|
progress,
|
|
16583
18257
|
options.gasMode ?? "auto",
|
|
16584
|
-
|
|
18258
|
+
onBeforeSubmission,
|
|
16585
18259
|
options.assertCurrent
|
|
16586
18260
|
) : await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
|
|
16587
18261
|
settled({
|
|
@@ -16592,6 +18266,10 @@ var OwneySDK = class {
|
|
|
16592
18266
|
} catch (error) {
|
|
16593
18267
|
if (!adapterReported && !evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
16594
18268
|
progress({ ...evidence, transfer: "failed", delivery: "failed" });
|
|
18269
|
+
else if (error instanceof OwneyError && error.details?.rejectedBeforeSubmission === true && evidence.transfer !== "confirmed")
|
|
18270
|
+
progress({ ...evidence, transfer: "failed", delivery: "failed" });
|
|
18271
|
+
if (serverLegs.size && requested2 === void 0 && agent.withdrawalRequiresWalletApproval && error instanceof OwneyError && error.code === "WITHDRAW_INSUFFICIENT_BALANCE" && error.details?.available === "0" && evidence.transfer === "failed" && !evidence.txHash && !evidence.userOpHash && !evidence.callsId)
|
|
18272
|
+
ledgerLeg?.(evidence, "WITHDRAW_INSUFFICIENT_BALANCE");
|
|
16595
18273
|
settled({ settled: "threw", evidence });
|
|
16596
18274
|
throw error;
|
|
16597
18275
|
}
|
|
@@ -16617,40 +18295,151 @@ var OwneySDK = class {
|
|
|
16617
18295
|
notifyAgentResult(agent.id, result);
|
|
16618
18296
|
return result;
|
|
16619
18297
|
}
|
|
18298
|
+
const dispatch = async (agent, requested2) => {
|
|
18299
|
+
let accept;
|
|
18300
|
+
const accepted = new Promise((resolve) => {
|
|
18301
|
+
accept = () => resolve(true);
|
|
18302
|
+
});
|
|
18303
|
+
const outcome = submit(agent, requested2, agent.withdrawalRequiresWalletApproval ? accept : void 0);
|
|
18304
|
+
const handedOver = await Promise.race([accepted, outcome.then(() => false, () => false)]);
|
|
18305
|
+
return { outcome, handedOver };
|
|
18306
|
+
};
|
|
18307
|
+
const prepareLaterAgents = async (agents) => {
|
|
18308
|
+
if (!agents.some((agent) => agent.withdrawalRequiresWalletApproval)) return;
|
|
18309
|
+
for (const agent of agents) {
|
|
18310
|
+
if (agent.withdrawalRequiresWalletApproval || typeof agent.prepareWithdrawal !== "function") continue;
|
|
18311
|
+
try {
|
|
18312
|
+
await agent.prepareWithdrawal(state, chainId, token);
|
|
18313
|
+
} catch (error) {
|
|
18314
|
+
if (this.isUserRejectedWithdrawal(error)) throw error;
|
|
18315
|
+
console.warn(`withdraw preparation failed for agent "${agent.id}":`, error);
|
|
18316
|
+
}
|
|
18317
|
+
}
|
|
18318
|
+
};
|
|
18319
|
+
const recordPlan = (legs) => {
|
|
18320
|
+
if (legs.length < 2) return;
|
|
18321
|
+
const handOffs = [];
|
|
18322
|
+
for (const [index, [agent, intendedAmount]] of legs.entries()) {
|
|
18323
|
+
const planned = {
|
|
18324
|
+
agentId: agent.id,
|
|
18325
|
+
chainId,
|
|
18326
|
+
token,
|
|
18327
|
+
walletAddress: state.walletAddress,
|
|
18328
|
+
intendedAmount,
|
|
18329
|
+
transfer: "pending",
|
|
18330
|
+
registration: "not-applicable",
|
|
18331
|
+
delivery: "pending",
|
|
18332
|
+
planned: true
|
|
18333
|
+
};
|
|
18334
|
+
plannedLegs?.set(planned.agentId, planned);
|
|
18335
|
+
notifyFunding(options.onProgress, planned);
|
|
18336
|
+
const after = legs.slice(0, index).filter(([earlier]) => earlier.withdrawalRequiresWalletApproval).map(([earlier]) => `agent:${earlier.id}`);
|
|
18337
|
+
if (agent.supportsServerTasks === true && typeof agent.handOffPlannedWithdrawal === "function" && after.length && zyfaiServerTasksEnabled(this.zyfaiServerTasks)) handOffs.push([agent, planned, after]);
|
|
18338
|
+
}
|
|
18339
|
+
const operationId = options.operationId;
|
|
18340
|
+
if (typeof operationId !== "string" || !operationId) return;
|
|
18341
|
+
for (const [agent, planned, after] of handOffs) {
|
|
18342
|
+
const leg = { answer: Promise.resolve(void 0), share: planned.intendedAmount };
|
|
18343
|
+
leg.answer = (async () => {
|
|
18344
|
+
let serverTask;
|
|
18345
|
+
let noTask = false;
|
|
18346
|
+
try {
|
|
18347
|
+
const answer = await agent.handOffPlannedWithdrawal(
|
|
18348
|
+
state,
|
|
18349
|
+
chainId,
|
|
18350
|
+
token,
|
|
18351
|
+
planned.intendedAmount,
|
|
18352
|
+
{
|
|
18353
|
+
operationId,
|
|
18354
|
+
after,
|
|
18355
|
+
// T10-I1: saved before the request leaves, as the dispatch
|
|
18356
|
+
// handoff's checkpoint is, so a reload while the server
|
|
18357
|
+
// answers finds a leg the server may own.
|
|
18358
|
+
beforeRequest: () => notifyFunding(options.onProgress, { ...planned, serverTask: "unknown" })
|
|
18359
|
+
}
|
|
18360
|
+
);
|
|
18361
|
+
serverTask = answer.type === "accepted" ? true : answer.type === "unknown" || answer.code === "LEG_ALREADY_SENT" ? "unknown" : void 0;
|
|
18362
|
+
noTask = answer.type === "declined" && ZYFAI_HANDOFF_NO_TASK_CODES.has(answer.code);
|
|
18363
|
+
} catch {
|
|
18364
|
+
serverTask = "unknown";
|
|
18365
|
+
}
|
|
18366
|
+
leg.settled = serverTask ?? null;
|
|
18367
|
+
if (serverTask) {
|
|
18368
|
+
const marked = { ...planned, serverTask };
|
|
18369
|
+
plannedLegs?.set(planned.agentId, marked);
|
|
18370
|
+
notifyFunding(options.onProgress, marked);
|
|
18371
|
+
} else if (noTask) {
|
|
18372
|
+
notifyFunding(options.onProgress, { ...planned, serverTask: false });
|
|
18373
|
+
}
|
|
18374
|
+
return serverTask;
|
|
18375
|
+
})();
|
|
18376
|
+
serverLegs.set(planned.agentId, leg);
|
|
18377
|
+
}
|
|
18378
|
+
};
|
|
16620
18379
|
const eligibleAgents = this.getEligibleAgents(chainId, asset).filter((agent) => !options.excludeAgentIds?.includes(agent.id));
|
|
16621
18380
|
const withdrawalAgents = this.orderAgentsForWithdrawal(eligibleAgents);
|
|
16622
18381
|
if (!amount) {
|
|
16623
18382
|
const results2 = {};
|
|
16624
18383
|
const agentErrors2 = {};
|
|
16625
18384
|
const incompleteWithdrawals = {};
|
|
16626
|
-
|
|
16627
|
-
|
|
16628
|
-
results2[agent.id] = await submit(agent);
|
|
16629
|
-
notifyAgentResult(agent.id, results2[agent.id]);
|
|
16630
|
-
} catch (err) {
|
|
18385
|
+
const failed2 = (agent, err, handedOver) => {
|
|
18386
|
+
if (!handedOver) {
|
|
16631
18387
|
if (err instanceof OwneyError && err.code === "SPONSOR_REQUEST_FAILED" && err.details?.safeToFallback === true) throw err;
|
|
16632
18388
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
16633
|
-
|
|
16634
|
-
|
|
16635
|
-
|
|
16636
|
-
|
|
16637
|
-
|
|
16638
|
-
|
|
16639
|
-
|
|
16640
|
-
|
|
18389
|
+
}
|
|
18390
|
+
if (err instanceof OwneyError && err.code === "WITHDRAW_INSUFFICIENT_BALANCE" && err.details?.available === "0" && submissionEvidence.get(agent.id)?.transfer === "failed") return;
|
|
18391
|
+
if (agent.id === "yieldseeker" && err instanceof OwneyError && err.code === "WITHDRAW_PARTIAL_FAILURE" && err.details?.reason === "YIELDSEEKER_MAX_INCOMPLETE") {
|
|
18392
|
+
incompleteWithdrawals[agent.id] = { positionsStatus: err.details.positionsStatus };
|
|
18393
|
+
const evidence = submissionEvidence.get(agent.id);
|
|
18394
|
+
if (evidence?.agentId === agent.id && evidence.chainId === chainId && evidence.token === token && evidence.walletAddress.toLowerCase() === state.walletAddress.toLowerCase() && evidence.intendedAmount === "MAX" && evidence.transfer === "confirmed" && evidence.delivery === "confirmed" && evidence.txHash && /^0x[\da-f]{64}$/i.test(evidence.txHash) && evidence.confirmedAmount && /^\d+$/.test(evidence.confirmedAmount) && BigInt(evidence.confirmedAmount) > 0n) {
|
|
18395
|
+
results2[agent.id] = { txHash: evidence.txHash, type: "partial", amount: evidence.confirmedAmount, evidence };
|
|
18396
|
+
notifyAgentResult(agent.id, results2[agent.id]);
|
|
18397
|
+
}
|
|
18398
|
+
}
|
|
18399
|
+
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
18400
|
+
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
18401
|
+
recordAgentFailure(agent.id, err);
|
|
18402
|
+
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
18403
|
+
void reportAgentFailure(
|
|
18404
|
+
this.apiKey,
|
|
18405
|
+
agent.id,
|
|
18406
|
+
code,
|
|
18407
|
+
this.routingApiBaseUrl
|
|
18408
|
+
);
|
|
18409
|
+
};
|
|
18410
|
+
const handedOverLegs2 = [];
|
|
18411
|
+
const settleHandedOver2 = async () => {
|
|
18412
|
+
for (const { agent, outcome } of handedOverLegs2.splice(0)) {
|
|
18413
|
+
try {
|
|
18414
|
+
results2[agent.id] = await outcome;
|
|
18415
|
+
notifyAgentResult(agent.id, results2[agent.id]);
|
|
18416
|
+
} catch (err) {
|
|
18417
|
+
failed2(agent, err, true);
|
|
18418
|
+
}
|
|
18419
|
+
}
|
|
18420
|
+
};
|
|
18421
|
+
await prepareLaterAgents(withdrawalAgents);
|
|
18422
|
+
recordPlan(withdrawalAgents.map((agent) => [agent, "MAX"]));
|
|
18423
|
+
try {
|
|
18424
|
+
for (const agent of withdrawalAgents) {
|
|
18425
|
+
const leg = await dispatch(agent);
|
|
18426
|
+
if (leg.handedOver) {
|
|
18427
|
+
handedOverLegs2.push({ agent, outcome: leg.outcome });
|
|
18428
|
+
continue;
|
|
18429
|
+
}
|
|
18430
|
+
try {
|
|
18431
|
+
results2[agent.id] = await leg.outcome;
|
|
18432
|
+
notifyAgentResult(agent.id, results2[agent.id]);
|
|
18433
|
+
} catch (err) {
|
|
18434
|
+
failed2(agent, err, false);
|
|
16641
18435
|
}
|
|
16642
|
-
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
16643
|
-
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
16644
|
-
recordAgentFailure(agent.id, err);
|
|
16645
|
-
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
16646
|
-
void reportAgentFailure(
|
|
16647
|
-
this.apiKey,
|
|
16648
|
-
agent.id,
|
|
16649
|
-
code,
|
|
16650
|
-
this.routingApiBaseUrl
|
|
16651
|
-
);
|
|
16652
18436
|
}
|
|
18437
|
+
} catch (error) {
|
|
18438
|
+
if (serverLegs.size) await awaitServerHandoffs();
|
|
18439
|
+
await settleHandedOver2();
|
|
18440
|
+
throw error;
|
|
16653
18441
|
}
|
|
18442
|
+
await settleHandedOver2();
|
|
16654
18443
|
if (Object.keys(results2).length === 0) {
|
|
16655
18444
|
throwUnconfirmed(agentErrors2);
|
|
16656
18445
|
if (Object.keys(agentErrors2).length === 0)
|
|
@@ -16795,32 +18584,65 @@ var OwneySDK = class {
|
|
|
16795
18584
|
const agentErrors = {
|
|
16796
18585
|
...aggregated.agentErrors ?? {}
|
|
16797
18586
|
};
|
|
16798
|
-
|
|
16799
|
-
|
|
16800
|
-
assertPlan();
|
|
16801
|
-
if (p.planned <= 0n) continue;
|
|
16802
|
-
try {
|
|
16803
|
-
results[p.agent.id] = await submit(p.agent, p.planned.toString());
|
|
16804
|
-
notifyAgentResult(p.agent.id, results[p.agent.id]);
|
|
16805
|
-
} catch (err) {
|
|
18587
|
+
const failed = (p, index, err, handedOver) => {
|
|
18588
|
+
if (!handedOver) {
|
|
16806
18589
|
if (err instanceof OwneyError && err.code === "SPONSOR_REQUEST_FAILED" && err.details?.safeToFallback === true) throw err;
|
|
16807
18590
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
16808
|
-
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
16809
|
-
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
16810
|
-
recordAgentFailure(p.agent.id, err);
|
|
16811
|
-
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
16812
|
-
void reportAgentFailure(
|
|
16813
|
-
this.apiKey,
|
|
16814
|
-
p.agent.id,
|
|
16815
|
-
code,
|
|
16816
|
-
this.routingApiBaseUrl
|
|
16817
|
-
);
|
|
16818
|
-
const failedAmount = p.planned;
|
|
16819
|
-
p.planned = 0n;
|
|
16820
|
-
if (submissionEvidence.get(p.agent.id)?.transfer === "failed")
|
|
16821
|
-
redistributeShare(orderedPlans, i, failedAmount);
|
|
16822
18591
|
}
|
|
18592
|
+
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
18593
|
+
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
18594
|
+
recordAgentFailure(p.agent.id, err);
|
|
18595
|
+
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
18596
|
+
void reportAgentFailure(
|
|
18597
|
+
this.apiKey,
|
|
18598
|
+
p.agent.id,
|
|
18599
|
+
code,
|
|
18600
|
+
this.routingApiBaseUrl
|
|
18601
|
+
);
|
|
18602
|
+
const failedAmount = p.planned;
|
|
18603
|
+
p.planned = 0n;
|
|
18604
|
+
if (!handedOver && submissionEvidence.get(p.agent.id)?.transfer === "failed")
|
|
18605
|
+
redistributeShare(orderedPlans, index, failedAmount);
|
|
18606
|
+
};
|
|
18607
|
+
const handedOverLegs = [];
|
|
18608
|
+
const settleHandedOver = async () => {
|
|
18609
|
+
for (const { plan, index, outcome } of handedOverLegs.splice(0)) {
|
|
18610
|
+
try {
|
|
18611
|
+
results[plan.agent.id] = await outcome;
|
|
18612
|
+
notifyAgentResult(plan.agent.id, results[plan.agent.id]);
|
|
18613
|
+
} catch (err) {
|
|
18614
|
+
failed(plan, index, err, true);
|
|
18615
|
+
}
|
|
18616
|
+
}
|
|
18617
|
+
};
|
|
18618
|
+
const dispatchedPlans = orderedPlans.filter((plan) => plan.planned > 0n);
|
|
18619
|
+
await prepareLaterAgents(dispatchedPlans.map((plan) => plan.agent));
|
|
18620
|
+
recordPlan(dispatchedPlans.map((plan) => [plan.agent, plan.planned.toString()]));
|
|
18621
|
+
try {
|
|
18622
|
+
for (let i = 0; i < orderedPlans.length; i++) {
|
|
18623
|
+
const p = orderedPlans[i];
|
|
18624
|
+
assertPlan();
|
|
18625
|
+
if (p.planned <= 0n) continue;
|
|
18626
|
+
const serverLeg = serverLegs.get(p.agent.id);
|
|
18627
|
+
if (serverLeg && await serverLeg.answer && /^[1-9][0-9]*$/.test(serverLeg.share)) p.planned = BigInt(serverLeg.share);
|
|
18628
|
+
const leg = await dispatch(p.agent, p.planned.toString());
|
|
18629
|
+
if (leg.handedOver) {
|
|
18630
|
+
handedOverLegs.push({ plan: p, index: i, outcome: leg.outcome });
|
|
18631
|
+
continue;
|
|
18632
|
+
}
|
|
18633
|
+
try {
|
|
18634
|
+
results[p.agent.id] = await leg.outcome;
|
|
18635
|
+
notifyAgentResult(p.agent.id, results[p.agent.id]);
|
|
18636
|
+
} catch (err) {
|
|
18637
|
+
failed(p, i, err, false);
|
|
18638
|
+
}
|
|
18639
|
+
}
|
|
18640
|
+
} catch (error) {
|
|
18641
|
+
if (serverLegs.size) await awaitServerHandoffs();
|
|
18642
|
+
await settleHandedOver();
|
|
18643
|
+
throw error;
|
|
16823
18644
|
}
|
|
18645
|
+
await settleHandedOver();
|
|
16824
18646
|
if (Object.keys(results).length === 0) {
|
|
16825
18647
|
throwUnconfirmed(agentErrors);
|
|
16826
18648
|
throw new OwneyError(
|
|
@@ -17756,12 +19578,20 @@ function createOwneySIWX(config) {
|
|
|
17756
19578
|
export {
|
|
17757
19579
|
AgentChainIncompatibleError,
|
|
17758
19580
|
AgentNotFoundError,
|
|
19581
|
+
FUNDING_DEPOSIT_RECOVERY_REPORT,
|
|
19582
|
+
FUSION_SECRET_SERVER_PUBLISH,
|
|
17759
19583
|
InvalidHistoryCursorError,
|
|
17760
19584
|
NotConnectedError,
|
|
17761
19585
|
OwneyError,
|
|
17762
19586
|
OwneySDK,
|
|
17763
19587
|
SDK_VERSION,
|
|
19588
|
+
SPONSORED_DEPOSIT_SERVER_BROADCAST,
|
|
19589
|
+
YIELDSEEKER_WITHDRAW_SERVER_SUBMIT,
|
|
17764
19590
|
YieldseekerAgent,
|
|
19591
|
+
ZYFAI_REGISTRATION_NOT_SENT_CODES,
|
|
19592
|
+
ZYFAI_SERVER_TASKS,
|
|
19593
|
+
ZYFAI_WITHDRAWAL_NOT_SENT_CODES,
|
|
19594
|
+
ZYFAI_WITHDRAW_DISPATCH_CHECKPOINT,
|
|
17765
19595
|
clearWithdrawSwap,
|
|
17766
19596
|
createOwneySIWX,
|
|
17767
19597
|
listPendingDeposits,
|