@owney/sdk 0.7.32-beta.2 → 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 +2282 -417
- package/dist/index.d.cts +358 -6
- package/dist/index.d.ts +358 -6
- package/dist/index.js +2194 -411
- 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,7 +1792,7 @@ 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;
|
|
1573
|
-
const providerRef = e.withdrawalRequest?.providerWithdrawalId;
|
|
1795
|
+
const providerRef = e.withdrawalRequest?.providerWithdrawalId || e.providerDepositId;
|
|
1574
1796
|
if (typeof providerRef === "string" && providerRef.length >= 1 && providerRef.length <= 128) leg.providerRef = providerRef;
|
|
1575
1797
|
const failure = e.withdrawalRequest?.failure;
|
|
1576
1798
|
if (failure) leg.errorCode = failure.toUpperCase().replace(/-/g, "_");
|
|
@@ -1599,6 +1821,12 @@ function withdrawalLedgerRail(e) {
|
|
|
1599
1821
|
return e.userOpHash ? "yieldseeker_userop" : e.callsId ? "yieldseeker_wallet_calls" : e.txHash ? "yieldseeker_paid" : void 0;
|
|
1600
1822
|
return void 0;
|
|
1601
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;
|
|
1602
1830
|
var SWAP_LEDGER_RAILS = {
|
|
1603
1831
|
classic: "swap_classic",
|
|
1604
1832
|
fusion: "swap_fusion",
|
|
@@ -1666,6 +1894,12 @@ var TRANSFER_EVENT = getAbiItem({
|
|
|
1666
1894
|
abi: parseAbi2(["event Transfer(address indexed from, address indexed to, uint256 value)"]),
|
|
1667
1895
|
name: "Transfer"
|
|
1668
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
|
+
}
|
|
1669
1903
|
function parseBlock(value) {
|
|
1670
1904
|
if (value === void 0 || value === null) return void 0;
|
|
1671
1905
|
return typeof value === "string" && /^\d{1,19}$/.test(value) ? BigInt(value) : null;
|
|
@@ -1690,11 +1924,12 @@ async function scanWithdrawalPayouts(client, input) {
|
|
|
1690
1924
|
const candidates = [];
|
|
1691
1925
|
if (start > end) {
|
|
1692
1926
|
const kept = input.cursor ?? (start > 0n ? start - 1n : 0n);
|
|
1693
|
-
return { candidates, scannedTo: kept.toString() };
|
|
1927
|
+
return { candidates, scannedTo: kept.toString(), caughtUp: true };
|
|
1694
1928
|
}
|
|
1695
1929
|
const tokenAddress = input.token.toLowerCase();
|
|
1696
1930
|
const owner = input.owner.toLowerCase();
|
|
1697
|
-
|
|
1931
|
+
const key2 = providerKey(client);
|
|
1932
|
+
let size = (key2 !== void 0 ? acceptedChunk.get(key2) : acceptedChunkByClient.get(client)) ?? PAYOUT_SCAN_CHUNK_BLOCKS;
|
|
1698
1933
|
let from = start;
|
|
1699
1934
|
let scannedTo = start - 1n;
|
|
1700
1935
|
for (let requests = 0; from <= end && requests < MAX_CHUNKS_PER_SCAN; ) {
|
|
@@ -1709,12 +1944,21 @@ async function scanWithdrawalPayouts(client, input) {
|
|
|
1709
1944
|
toBlock: to
|
|
1710
1945
|
});
|
|
1711
1946
|
} catch (error) {
|
|
1712
|
-
if (
|
|
1713
|
-
|
|
1714
|
-
|
|
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
|
+
}
|
|
1715
1955
|
}
|
|
1716
1956
|
throw error;
|
|
1717
1957
|
}
|
|
1958
|
+
if (size < PAYOUT_SCAN_CHUNK_BLOCKS) {
|
|
1959
|
+
if (key2 !== void 0) acceptedChunk.set(key2, size);
|
|
1960
|
+
else acceptedChunkByClient.set(client, size);
|
|
1961
|
+
}
|
|
1718
1962
|
check();
|
|
1719
1963
|
requests++;
|
|
1720
1964
|
for (const log of logs) {
|
|
@@ -1732,7 +1976,8 @@ async function scanWithdrawalPayouts(client, input) {
|
|
|
1732
1976
|
scannedTo = to;
|
|
1733
1977
|
from = to + 1n;
|
|
1734
1978
|
}
|
|
1735
|
-
|
|
1979
|
+
const stalled = input.cursor !== void 0 && scannedTo <= input.cursor;
|
|
1980
|
+
return { candidates, scannedTo: (scannedTo < 0n ? 0n : scannedTo).toString(), caughtUp: from > end || stalled };
|
|
1736
1981
|
}
|
|
1737
1982
|
async function estimateBlockAt(client, latest, targetMs) {
|
|
1738
1983
|
const head = await client.getBlock({ blockNumber: latest });
|
|
@@ -1757,6 +2002,7 @@ function causeChain(error) {
|
|
|
1757
2002
|
chain2.push({
|
|
1758
2003
|
code: value.code,
|
|
1759
2004
|
status: value.status,
|
|
2005
|
+
data: value.data,
|
|
1760
2006
|
text: [value.message, value.details, value.shortMessage].filter((part) => typeof part === "string").join(" ")
|
|
1761
2007
|
});
|
|
1762
2008
|
current = value.cause;
|
|
@@ -1766,6 +2012,14 @@ function causeChain(error) {
|
|
|
1766
2012
|
function isRateLimited(error) {
|
|
1767
2013
|
return causeChain(error).some((entry) => entry.status === 429 || entry.code === 429 || /rate|too many requests/i.test(entry.text));
|
|
1768
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
|
+
}
|
|
1769
2023
|
function isRangeError(error) {
|
|
1770
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));
|
|
1771
2025
|
}
|
|
@@ -1871,6 +2125,7 @@ async function getSponsorRelayerAddress(input) {
|
|
|
1871
2125
|
return parsed.data.relayer;
|
|
1872
2126
|
}
|
|
1873
2127
|
async function postSponsorBatchTransfer(input) {
|
|
2128
|
+
const flaggedPrepare = input.body.serverBroadcast === true && !input.body.serializedTransaction;
|
|
1874
2129
|
let res;
|
|
1875
2130
|
try {
|
|
1876
2131
|
res = await fetch(
|
|
@@ -1889,7 +2144,7 @@ async function postSponsorBatchTransfer(input) {
|
|
|
1889
2144
|
throw new OwneyError(
|
|
1890
2145
|
"SPONSOR_REQUEST_FAILED",
|
|
1891
2146
|
"Deposit status is unknown. Retry the same amount to check it.",
|
|
1892
|
-
{ safeToFallback: false }
|
|
2147
|
+
{ safeToFallback: false, ...flaggedPrepare ? { outcomeUnknown: true } : {} }
|
|
1893
2148
|
);
|
|
1894
2149
|
}
|
|
1895
2150
|
const parsed = await res.json().catch(() => null);
|
|
@@ -1900,7 +2155,13 @@ async function postSponsorBatchTransfer(input) {
|
|
|
1900
2155
|
{
|
|
1901
2156
|
statusCode: res.status,
|
|
1902
2157
|
safeToFallback: false,
|
|
1903
|
-
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 } : {}
|
|
1904
2165
|
}
|
|
1905
2166
|
);
|
|
1906
2167
|
}
|
|
@@ -1917,19 +2178,19 @@ async function postSponsorBatchRecovery(input) {
|
|
|
1917
2178
|
input.signal?.throwIfAborted();
|
|
1918
2179
|
if (!response.ok || payload?.success !== true) throw failure();
|
|
1919
2180
|
const value = payload.data;
|
|
1920
|
-
const { isAddress:
|
|
1921
|
-
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");
|
|
1922
2183
|
const phase = input.body.phase;
|
|
1923
2184
|
const original = phase === "approval" ? input.body.serializedPermitTransaction : input.body.serializedTransaction;
|
|
1924
2185
|
if (!original) throw failure();
|
|
1925
2186
|
const hash2 = keccak2564(original), relayer = await recoverTransactionAddress({ serializedTransaction: original });
|
|
1926
|
-
const
|
|
1927
|
-
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();
|
|
1928
2189
|
if (value.status !== "prepared" && value.txHash.toLowerCase() !== hash2.toLowerCase()) throw failure();
|
|
1929
2190
|
if (value.status === "unresolved") {
|
|
1930
2191
|
if (!["nonce-not-consumed", "authorization-used", "authorization-expired", "state-unavailable", "preparation-rejected", "relayer-changed"].includes(value.reason)) throw failure();
|
|
1931
2192
|
} else {
|
|
1932
|
-
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();
|
|
1933
2194
|
if (value.status === "prepared") {
|
|
1934
2195
|
if (value.predecessorHash?.toLowerCase() !== hash2.toLowerCase()) throw failure();
|
|
1935
2196
|
const tx = parseTransaction3(value.serializedTransaction), old = parseTransaction3(original);
|
|
@@ -2227,11 +2488,19 @@ var AgentReads = class {
|
|
|
2227
2488
|
}
|
|
2228
2489
|
};
|
|
2229
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
|
+
|
|
2230
2497
|
// src/agents/zyfai/zyfai.withdraw-submission.ts
|
|
2231
2498
|
var BELOW_MINIMUM = /^Idle balance (\d+(?:\.\d+)?) (USDC|WETH|ETH|EURC) is below the minimum withdrawal of (\d+(?:\.\d+)?) \2\.?$/;
|
|
2232
2499
|
var listenersByClient = /* @__PURE__ */ new WeakMap();
|
|
2500
|
+
var ZYFAI_WITHDRAW_DISPATCH_CHECKPOINT = 1;
|
|
2233
2501
|
var MAX_WITHDRAWAL_ID_LENGTH = 128;
|
|
2234
|
-
|
|
2502
|
+
var WITHDRAWAL_PATHS = { get: "/users/withdraw/v2", post: "/users/partial-withdraw/v2" };
|
|
2503
|
+
function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent, beforeDispatch) {
|
|
2235
2504
|
const client = sdk.httpClient;
|
|
2236
2505
|
if (!client || typeof client.get !== "function" || typeof client.post !== "function")
|
|
2237
2506
|
return {
|
|
@@ -2251,7 +2520,7 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
2251
2520
|
const original = client[method];
|
|
2252
2521
|
client[method] = async function(...args) {
|
|
2253
2522
|
const [url, options] = args;
|
|
2254
|
-
const withdrawal =
|
|
2523
|
+
const withdrawal = url === WITHDRAWAL_PATHS[method];
|
|
2255
2524
|
const payload = method === "get" ? options?.params : options;
|
|
2256
2525
|
const matching = withdrawal ? [...observers].filter((observer) => observer.chainId === payload?.chainId && observer.token === payload?.tokenSymbol) : [];
|
|
2257
2526
|
for (const observer of matching) observer.assertCurrent?.();
|
|
@@ -2259,9 +2528,12 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
2259
2528
|
const request3 = {
|
|
2260
2529
|
attempted: true,
|
|
2261
2530
|
method: method === "get" ? "GET" : "POST",
|
|
2262
|
-
path: method
|
|
2531
|
+
path: WITHDRAWAL_PATHS[method]
|
|
2263
2532
|
};
|
|
2264
|
-
if (withdrawal)
|
|
2533
|
+
if (withdrawal) {
|
|
2534
|
+
for (const observer of matching) await observer.beforeDispatch?.({ ...request3 });
|
|
2535
|
+
for (const observer of matching) observer.assertCurrent?.();
|
|
2536
|
+
}
|
|
2265
2537
|
let belowMinimum2;
|
|
2266
2538
|
const notify2 = (outcome) => {
|
|
2267
2539
|
if (outcome === "rejected" && matching.length !== 1) return;
|
|
@@ -2294,23 +2566,11 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
2294
2566
|
captureStatus
|
|
2295
2567
|
] };
|
|
2296
2568
|
}
|
|
2297
|
-
const send = (route) => original.apply(this, [route, ...callArgs.slice(1)]);
|
|
2298
2569
|
try {
|
|
2299
|
-
|
|
2300
|
-
if (
|
|
2301
|
-
else {
|
|
2302
|
-
try {
|
|
2303
|
-
result = await send(`${url}/v2`);
|
|
2304
|
-
} catch (error) {
|
|
2305
|
-
const status = request3.httpStatus ?? error?.response?.status;
|
|
2306
|
-
if (status !== 404) throw error;
|
|
2307
|
-
for (const observer of matching) observer.assertCurrent?.();
|
|
2308
|
-
request3.path = url;
|
|
2309
|
-
delete request3.httpStatus;
|
|
2310
|
-
result = await send(url);
|
|
2311
|
-
}
|
|
2570
|
+
const result = await original.apply(this, callArgs);
|
|
2571
|
+
if (withdrawal) {
|
|
2312
2572
|
const id = result?.id;
|
|
2313
|
-
if (
|
|
2573
|
+
if (typeof id === "string" && id.length >= 1 && id.length <= MAX_WITHDRAWAL_ID_LENGTH)
|
|
2314
2574
|
request3.providerWithdrawalId = id;
|
|
2315
2575
|
}
|
|
2316
2576
|
return result;
|
|
@@ -2321,7 +2581,7 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
2321
2581
|
const status = error?.response?.status;
|
|
2322
2582
|
if (request3.httpStatus === void 0 && typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599)
|
|
2323
2583
|
request3.httpStatus = status;
|
|
2324
|
-
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";
|
|
2325
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");
|
|
2326
2586
|
throw error;
|
|
2327
2587
|
} finally {
|
|
@@ -2335,7 +2595,7 @@ function observeWithdrawalSubmission(sdk, chainId, token, assertCurrent) {
|
|
|
2335
2595
|
let rejected = false;
|
|
2336
2596
|
let request2 = { attempted: false };
|
|
2337
2597
|
let belowMinimum;
|
|
2338
|
-
const listener = { chainId, token, assertCurrent, notify: (outcome, evidence, detail) => {
|
|
2598
|
+
const listener = { chainId, token, assertCurrent, beforeDispatch, notify: (outcome, evidence, detail) => {
|
|
2339
2599
|
request2 = evidence;
|
|
2340
2600
|
if (outcome === "attempted") submitted = true;
|
|
2341
2601
|
else if (outcome === "rejected") {
|
|
@@ -2476,7 +2736,7 @@ async function submitZyfaiDeposit({ sdk, state, chain: chain2, amount, asset, sm
|
|
|
2476
2736
|
if (!token || !isAddress(owner) || !isAddress(smartWallet) || !/^\d+$/.test(amount) || BigInt(amount) <= 0n)
|
|
2477
2737
|
throw new OwneyError("AGENT_INVALID_RESPONSE", "Invalid Zyfai deposit transfer.", { chainId: chain2.id, asset }, "zyfai");
|
|
2478
2738
|
assertCurrent();
|
|
2479
|
-
const prepared = await sdk.
|
|
2739
|
+
const prepared = await sdk.prepareDeposit({ userAddress: owner, chainId: chain2.id, amount, asset });
|
|
2480
2740
|
assertCurrent();
|
|
2481
2741
|
const expectedData = encodeFunctionData({ abi: erc20Abi2, functionName: "transfer", args: [smartWallet, BigInt(amount)] });
|
|
2482
2742
|
const transfer = prepared?.transfer;
|
|
@@ -2558,6 +2818,13 @@ function tokenDecimals(symbol2, explicit) {
|
|
|
2558
2818
|
return explicit;
|
|
2559
2819
|
return DECIMALS_BY_SYMBOL[(symbol2 ?? "").toLowerCase()] ?? 6;
|
|
2560
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
|
+
}
|
|
2561
2828
|
function mapWithdraw(raw2) {
|
|
2562
2829
|
return {
|
|
2563
2830
|
txHash: raw2.txHash,
|
|
@@ -2607,6 +2874,40 @@ function mapPendingAllocations(raw2) {
|
|
|
2607
2874
|
}
|
|
2608
2875
|
return pending.length > 0 ? pending : void 0;
|
|
2609
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
|
+
}
|
|
2610
2911
|
function mapBalances(raw2, _chainId, smartWallet) {
|
|
2611
2912
|
const portfolio = raw2.portfolio;
|
|
2612
2913
|
if (!portfolio?.portfolioByChain || typeof portfolio.portfolioByChain !== "object")
|
|
@@ -2665,6 +2966,9 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
2665
2966
|
const pendingAllocations = mapPendingAllocations(
|
|
2666
2967
|
portfolio.staleBalances
|
|
2667
2968
|
);
|
|
2969
|
+
const asyncWithdrawals = portfolio.pendingAsyncWithdrawals;
|
|
2970
|
+
const pendingWithdrawals = mapPendingWithdrawals(asyncWithdrawals);
|
|
2971
|
+
const recentAsyncWithdrawals = mapRecentAsyncWithdrawals(asyncWithdrawals);
|
|
2668
2972
|
return {
|
|
2669
2973
|
smartWallet,
|
|
2670
2974
|
totalBalance: String(totalBalance),
|
|
@@ -2680,7 +2984,49 @@ function mapBalances(raw2, _chainId, smartWallet) {
|
|
|
2680
2984
|
blockNumber: provenance.blockNumber === void 0 ? void 0 : String(provenance.blockNumber),
|
|
2681
2985
|
positionsStatus: Array.isArray(portfolio.positions) ? "available" : "unavailable"
|
|
2682
2986
|
})),
|
|
2683
|
-
...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 } : {}
|
|
2684
3030
|
};
|
|
2685
3031
|
}
|
|
2686
3032
|
function sumTokenValues(tokens) {
|
|
@@ -2844,12 +3190,24 @@ function mapEntries(rawEntries, chainId) {
|
|
|
2844
3190
|
const mappedAction = (entry.action ? actionMap[entry.action] : void 0) ?? entry.action;
|
|
2845
3191
|
const rawAmount2 = entry?.positions?.[0]?.amount;
|
|
2846
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;
|
|
2847
3197
|
const mappedPositions = positions.filter((p) => p.protocol_name).map((p) => ({
|
|
2848
3198
|
protocol: p.protocol_name,
|
|
2849
3199
|
pool: p.pool ?? void 0,
|
|
2850
3200
|
tokenSymbol: p.token_symbol ?? void 0,
|
|
2851
|
-
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
|
|
2852
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;
|
|
2853
3211
|
return {
|
|
2854
3212
|
agent: "zyfai",
|
|
2855
3213
|
action: mappedAction,
|
|
@@ -2862,12 +3220,7 @@ function mapEntries(rawEntries, chainId) {
|
|
|
2862
3220
|
txHashes: entry.transactionHash ? [entry.transactionHash] : [],
|
|
2863
3221
|
chainId: entry.chainId,
|
|
2864
3222
|
tokenSymbol: entry?.positions?.[0]?.token_symbol,
|
|
2865
|
-
amount:
|
|
2866
|
-
hexToDecimal(
|
|
2867
|
-
rawAmount2,
|
|
2868
|
-
tokenDecimals(entry?.positions?.[0]?.token_symbol)
|
|
2869
|
-
)
|
|
2870
|
-
) : void 0
|
|
3223
|
+
amount: transactionAmount
|
|
2871
3224
|
}
|
|
2872
3225
|
],
|
|
2873
3226
|
rebalanceLog
|
|
@@ -3066,9 +3419,9 @@ var isJwtExpired = (token) => {
|
|
|
3066
3419
|
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
3067
3420
|
const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
|
|
3068
3421
|
const decoded = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8");
|
|
3069
|
-
const
|
|
3070
|
-
if (typeof
|
|
3071
|
-
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;
|
|
3072
3425
|
} catch {
|
|
3073
3426
|
return false;
|
|
3074
3427
|
}
|
|
@@ -3309,7 +3662,7 @@ function protocolsPolicyNeedsUpdate(current, desiredProtocols, desiredAutoSelect
|
|
|
3309
3662
|
}
|
|
3310
3663
|
|
|
3311
3664
|
// src/lib/agent-accounts.ts
|
|
3312
|
-
var
|
|
3665
|
+
var SCOPE5 = "agent-accounts";
|
|
3313
3666
|
var REQUEST_TIMEOUT_MS3 = 5e3;
|
|
3314
3667
|
function compact3(obj) {
|
|
3315
3668
|
const out = {};
|
|
@@ -3379,7 +3732,7 @@ async function fetchAgentAccounts(input) {
|
|
|
3379
3732
|
signal
|
|
3380
3733
|
});
|
|
3381
3734
|
if (!res.ok) {
|
|
3382
|
-
debugLog(
|
|
3735
|
+
debugLog(SCOPE5, `fetch rejected with ${res.status}`);
|
|
3383
3736
|
return null;
|
|
3384
3737
|
}
|
|
3385
3738
|
const data = unwrap3(await res.json());
|
|
@@ -3394,7 +3747,7 @@ async function fetchAgentAccounts(input) {
|
|
|
3394
3747
|
cancel();
|
|
3395
3748
|
}
|
|
3396
3749
|
} catch (err) {
|
|
3397
|
-
debugLog(
|
|
3750
|
+
debugLog(SCOPE5, `fetch failed: ${describeError4(err)}`);
|
|
3398
3751
|
return null;
|
|
3399
3752
|
}
|
|
3400
3753
|
}
|
|
@@ -3425,7 +3778,7 @@ async function reportAgentAccount(input) {
|
|
|
3425
3778
|
signal
|
|
3426
3779
|
});
|
|
3427
3780
|
if (!res.ok) {
|
|
3428
|
-
debugLog(
|
|
3781
|
+
debugLog(SCOPE5, `report rejected with ${res.status}`);
|
|
3429
3782
|
return null;
|
|
3430
3783
|
}
|
|
3431
3784
|
const data = unwrap3(await res.json());
|
|
@@ -3437,7 +3790,7 @@ async function reportAgentAccount(input) {
|
|
|
3437
3790
|
cancel();
|
|
3438
3791
|
}
|
|
3439
3792
|
} catch (err) {
|
|
3440
|
-
debugLog(
|
|
3793
|
+
debugLog(SCOPE5, `report failed: ${describeError4(err)}`);
|
|
3441
3794
|
return null;
|
|
3442
3795
|
}
|
|
3443
3796
|
}
|
|
@@ -3598,6 +3951,17 @@ var ZYFAI_LEDGER_IDLE_CACHE_MS = 6e4;
|
|
|
3598
3951
|
var ZYFAI_LEDGER_REUSE_MS = 6e4;
|
|
3599
3952
|
var WITHDRAW_BALANCE_TIMEOUT_MS = 2e3;
|
|
3600
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}$/;
|
|
3601
3965
|
function settleWithin(value, ms) {
|
|
3602
3966
|
let timer;
|
|
3603
3967
|
const limit = new Promise((resolve) => {
|
|
@@ -3607,6 +3971,28 @@ function settleWithin(value, ms) {
|
|
|
3607
3971
|
if (timer) clearTimeout(timer);
|
|
3608
3972
|
});
|
|
3609
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
|
+
}
|
|
3610
3996
|
function belowMinimumCeiling(rejection, token, decimals) {
|
|
3611
3997
|
if ((rejection.symbol === "ETH" ? "WETH" : rejection.symbol) !== token) return void 0;
|
|
3612
3998
|
const ceiling = (display) => {
|
|
@@ -3630,6 +4016,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3630
4016
|
referralSource;
|
|
3631
4017
|
accountReporting;
|
|
3632
4018
|
id = "zyfai";
|
|
4019
|
+
/** This adapter can hand its withdrawals to our server (part 4). */
|
|
4020
|
+
supportsServerTasks = true;
|
|
3633
4021
|
supportedChainIds = [8453, 42161, 1];
|
|
3634
4022
|
supportedAssets = [
|
|
3635
4023
|
{
|
|
@@ -3706,6 +4094,18 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3706
4094
|
/** A ledger read slower than this counts as "no ledger information" for that read. */
|
|
3707
4095
|
ledgerBudgetMs = LEDGER_READ_BUDGET_MS;
|
|
3708
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();
|
|
3709
4109
|
/** A replaced wallet session gets its own vendor client. In-flight calls
|
|
3710
4110
|
* keep their original client; authentication must never change owners under
|
|
3711
4111
|
* an operation that is waiting for provider reads or an HTTP response. */
|
|
@@ -3916,10 +4316,11 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3916
4316
|
* Wraps `sdk.connectAccount` with a one-shot retry on auth errors. If the
|
|
3917
4317
|
* cached JWT is rejected by the backend (revoked, signing-key rotated, etc.)
|
|
3918
4318
|
* we drop the cache and try again — the second attempt has no cache entry,
|
|
3919
|
-
* so it falls through to a real SIWE prompt.
|
|
3920
|
-
*
|
|
3921
|
-
* JWT from a misconfigured API key; we
|
|
3922
|
-
* 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.
|
|
3923
4324
|
*/
|
|
3924
4325
|
async connectAccountWithRetry(provider, chainId, executionSdk = this.sdk) {
|
|
3925
4326
|
try {
|
|
@@ -3956,9 +4357,14 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
3956
4357
|
}
|
|
3957
4358
|
}
|
|
3958
4359
|
/** A rejected execution session must not be reused by the next foreground attempt. */
|
|
3959
|
-
invalidateRejectedWithdrawalSession(error, state, chainId, authToken) {
|
|
4360
|
+
invalidateRejectedWithdrawalSession(error, state, chainId, authToken, httpStatus) {
|
|
3960
4361
|
const message = error instanceof Error ? error.message : "";
|
|
3961
|
-
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) {
|
|
3962
4368
|
const internals = this.sdk;
|
|
3963
4369
|
if (typeof authToken !== "string" || internals.httpClient.authToken !== authToken || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase() || this.connectedChainId !== chainId) return;
|
|
3964
4370
|
clearSession(state.walletAddress, chainId);
|
|
@@ -4566,7 +4972,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4566
4972
|
await this.ensureSessionKey(ownerAddress, chainId, redeployed.address);
|
|
4567
4973
|
return redeployed.address;
|
|
4568
4974
|
}
|
|
4569
|
-
async deposit(state, chainId, amount, asset, depositCallback, onProgress, onBeforeSubmission) {
|
|
4975
|
+
async deposit(state, chainId, amount, asset, depositCallback, onProgress, onBeforeSubmission, context) {
|
|
4570
4976
|
const validChainId = isValidChainId(chainId);
|
|
4571
4977
|
const owner = state.walletAddress;
|
|
4572
4978
|
const provider = state.provider;
|
|
@@ -4581,6 +4987,12 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4581
4987
|
assertCurrent();
|
|
4582
4988
|
const smartWallet = await this.ensureWalletDeployed(owner, validChainId);
|
|
4583
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 ?? {};
|
|
4584
4996
|
notifyFunding(onProgress, {
|
|
4585
4997
|
agentId: this.id,
|
|
4586
4998
|
chainId,
|
|
@@ -4590,7 +5002,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4590
5002
|
intendedAmount: amount,
|
|
4591
5003
|
transfer: "pending",
|
|
4592
5004
|
registration: "pending",
|
|
4593
|
-
delivery: "not-applicable"
|
|
5005
|
+
delivery: "not-applicable",
|
|
5006
|
+
...mark
|
|
4594
5007
|
});
|
|
4595
5008
|
const txHash = depositCallback ? await depositCallback(smartWallet, validChainId, amount) : await submitZyfaiDeposit({
|
|
4596
5009
|
sdk: executionSdk,
|
|
@@ -4609,7 +5022,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4609
5022
|
intendedAmount: amount,
|
|
4610
5023
|
transfer: "pending",
|
|
4611
5024
|
registration: "pending",
|
|
4612
|
-
delivery: "not-applicable"
|
|
5025
|
+
delivery: "not-applicable",
|
|
5026
|
+
...mark
|
|
4613
5027
|
}) : void 0
|
|
4614
5028
|
});
|
|
4615
5029
|
let evidence = {
|
|
@@ -4622,7 +5036,8 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4622
5036
|
txHash,
|
|
4623
5037
|
transfer: "pending",
|
|
4624
5038
|
registration: "pending",
|
|
4625
|
-
delivery: "not-applicable"
|
|
5039
|
+
delivery: "not-applicable",
|
|
5040
|
+
...mark
|
|
4626
5041
|
};
|
|
4627
5042
|
notifyFunding(onProgress, evidence);
|
|
4628
5043
|
try {
|
|
@@ -4642,11 +5057,32 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4642
5057
|
throw new OwneyError("AGENT_TRANSACTION_REVERTED", "Zyfai deposit transaction reverted.", { txHash }, this.id);
|
|
4643
5058
|
if (evidence.transfer !== "confirmed") return { txHash, smartWallet, amount, evidence };
|
|
4644
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
|
+
}
|
|
4645
5080
|
try {
|
|
4646
5081
|
assertCurrent();
|
|
4647
5082
|
const tokenAddress = asset === "WETH" ? WETH_ADDRESS_BY_CHAIN[validChainId] : void 0;
|
|
4648
|
-
await (tokenAddress ? executionSdk.logDeposit(validChainId, txHash, amount, tokenAddress) : executionSdk.logDeposit(validChainId, txHash, amount));
|
|
4649
|
-
|
|
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 } : {} };
|
|
4650
5086
|
} catch {
|
|
4651
5087
|
evidence = { ...evidence, registration: "unknown" };
|
|
4652
5088
|
this.notePendingRegistration(owner, validChainId, txHash, Date.now(), asset);
|
|
@@ -4766,10 +5202,69 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4766
5202
|
throw unavailable();
|
|
4767
5203
|
}
|
|
4768
5204
|
}
|
|
4769
|
-
|
|
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) {
|
|
4770
5240
|
if (amount !== void 0 && (typeof amount !== "string" || !/^[0-9]+$/.test(amount) || BigInt(amount) <= 0n))
|
|
4771
5241
|
throw new OwneyError("WITHDRAW_INVALID_AMOUNT", "Withdrawal amount must be a positive integer in token base units.", void 0, this.id);
|
|
4772
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
|
+
});
|
|
4773
5268
|
try {
|
|
4774
5269
|
await this.ensureConnected(state, validChainId);
|
|
4775
5270
|
} catch (error) {
|
|
@@ -4806,15 +5301,35 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4806
5301
|
assertCurrent();
|
|
4807
5302
|
assertOperation?.();
|
|
4808
5303
|
};
|
|
4809
|
-
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);
|
|
4810
5325
|
const authToken = internals.httpClient?.authToken;
|
|
4811
5326
|
let raw2;
|
|
4812
|
-
let headRead = Promise.resolve(void 0);
|
|
4813
5327
|
try {
|
|
4814
5328
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
4815
5329
|
try {
|
|
4816
5330
|
assertCurrent();
|
|
4817
5331
|
headRead = this.readPayoutSearchHead(validChainId);
|
|
5332
|
+
settledHead = void 0;
|
|
4818
5333
|
raw2 = await executionSdk.withdrawFunds(owner, validChainId, amount, token);
|
|
4819
5334
|
break;
|
|
4820
5335
|
} catch (error) {
|
|
@@ -4843,7 +5358,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4843
5358
|
assertSubmission();
|
|
4844
5359
|
await this.recoverWithdrawalSession(state, validChainId, token, owner);
|
|
4845
5360
|
observation.stop();
|
|
4846
|
-
observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertSubmission);
|
|
5361
|
+
observation = observeWithdrawalSubmission(executionSdk, validChainId, token, assertSubmission, checkpoint2);
|
|
4847
5362
|
}
|
|
4848
5363
|
}
|
|
4849
5364
|
if (!raw2) throw new OwneyError(
|
|
@@ -4854,7 +5369,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4854
5369
|
);
|
|
4855
5370
|
} catch (error) {
|
|
4856
5371
|
if (staleSessionError && observation.attempted() === false) error = staleSessionError;
|
|
4857
|
-
this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken);
|
|
5372
|
+
this.invalidateRejectedWithdrawalSession(error, state, validChainId, authToken, observation.request()?.httpStatus);
|
|
4858
5373
|
if (observation.attempted() !== void 0) {
|
|
4859
5374
|
const failed = observation.attempted() === false || observation.rejected();
|
|
4860
5375
|
notifyFunding(onProgress, {
|
|
@@ -4908,7 +5423,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4908
5423
|
if (!raw2.txHash) {
|
|
4909
5424
|
const acceptedAt = Date.now();
|
|
4910
5425
|
const agentWallet = this.smartWalletCache.get(`${owner.toLowerCase()}_${validChainId}`);
|
|
4911
|
-
const head = await
|
|
5426
|
+
const head = await searchHead();
|
|
4912
5427
|
evidence = {
|
|
4913
5428
|
...evidence,
|
|
4914
5429
|
...agentWallet ? { agentWallet } : {},
|
|
@@ -4920,7 +5435,456 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4920
5435
|
this.ledgerReads.clear();
|
|
4921
5436
|
return { ...mapWithdraw(raw2), evidence };
|
|
4922
5437
|
}
|
|
4923
|
-
/**
|
|
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). */
|
|
4924
5888
|
async retryDepositRegistration(state, evidence) {
|
|
4925
5889
|
const executionSdk = this.sdk;
|
|
4926
5890
|
const internals = executionSdk;
|
|
@@ -4931,11 +5895,37 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4931
5895
|
throw new OwneyError("AGENT_API_ERROR", "Deposit transfer must be verified before registration recovery.");
|
|
4932
5896
|
}
|
|
4933
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
|
+
}
|
|
4934
5911
|
const tokenAddress = evidence.token === "WETH" ? WETH_ADDRESS_BY_CHAIN[cid] : void 0;
|
|
4935
|
-
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);
|
|
4936
5914
|
this.pendingRegistrations.get(`${evidence.walletAddress.toLowerCase()}:${cid}`)?.delete(evidence.txHash.toLowerCase());
|
|
4937
5915
|
this.ledgerReads.clear();
|
|
4938
|
-
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
|
+
}
|
|
4939
5929
|
}
|
|
4940
5930
|
/** Reads a withdrawal's lifecycle by its id. Never signs, prompts or authenticates:
|
|
4941
5931
|
* it uses only a session the vendor client already holds, and any failure is `null`. */
|
|
@@ -4944,11 +5934,7 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
4944
5934
|
const client = internals.httpClient;
|
|
4945
5935
|
if (!client?.authToken || typeof client.get !== "function" || this.connectedAddress?.toLowerCase() !== state.walletAddress.toLowerCase()) return null;
|
|
4946
5936
|
try {
|
|
4947
|
-
|
|
4948
|
-
if (!body || typeof body !== "object" || typeof body.status !== "string" || typeof body.syncSettled !== "boolean")
|
|
4949
|
-
return null;
|
|
4950
|
-
const hash2 = typeof body.syncTxHash === "string" && /^0x[0-9a-fA-F]{64}$/.test(body.syncTxHash) ? body.syncTxHash : void 0;
|
|
4951
|
-
return { status: body.status, syncSettled: body.syncSettled, ...hash2 ? { syncTxHash: hash2 } : {} };
|
|
5937
|
+
return mapWithdrawalLifecycle(await client.get(`/users/withdrawals/${encodeURIComponent(withdrawalId)}`));
|
|
4952
5938
|
} catch {
|
|
4953
5939
|
return null;
|
|
4954
5940
|
}
|
|
@@ -5181,16 +6167,16 @@ var ZyfaiAgent = class _ZyfaiAgent {
|
|
|
5181
6167
|
}
|
|
5182
6168
|
const now = Date.now();
|
|
5183
6169
|
const pending = [...this.pendingRegistrations.get(scope)?.values() ?? []].map((entry) => entry.at);
|
|
5184
|
-
const
|
|
6170
|
+
const active = zyfaiHoldActive({
|
|
5185
6171
|
movements,
|
|
5186
6172
|
pendingRegistrations: pending,
|
|
5187
6173
|
lastCheckTimestamp: raw2?.data?.lastCheckTimestamp,
|
|
5188
6174
|
lastLogDate: raw2?.data?.lastLogDate?.[String(chainId)],
|
|
5189
6175
|
now
|
|
5190
6176
|
});
|
|
5191
|
-
if (
|
|
6177
|
+
if (active) this.holdActiveScopes.add(scope);
|
|
5192
6178
|
else this.holdActiveScopes.delete(scope);
|
|
5193
|
-
if (!
|
|
6179
|
+
if (!active) {
|
|
5194
6180
|
this.lastGoodEarnings.set(key2, raw2);
|
|
5195
6181
|
return { value: raw2, active: false };
|
|
5196
6182
|
}
|
|
@@ -6173,7 +7159,7 @@ function actionEntry(action) {
|
|
|
6173
7159
|
rebalanceLog: []
|
|
6174
7160
|
};
|
|
6175
7161
|
}
|
|
6176
|
-
var
|
|
7162
|
+
var TX_HASH3 = /^0x[a-fA-F0-9]{64}$/;
|
|
6177
7163
|
function allocationLegs(value, context, requireAmount) {
|
|
6178
7164
|
if (!Array.isArray(value)) return void 0;
|
|
6179
7165
|
const legs = [];
|
|
@@ -6202,7 +7188,7 @@ function allocationEntry(action, context) {
|
|
|
6202
7188
|
if (action.actionType.toLowerCase() !== "autoseek") return void 0;
|
|
6203
7189
|
const hash2 = action.details?.depositTransactionHash;
|
|
6204
7190
|
const withdrawHash = action.details?.withdrawTransactionHash;
|
|
6205
|
-
if (typeof hash2 !== "string" || !
|
|
7191
|
+
if (typeof hash2 !== "string" || !TX_HASH3.test(hash2)) return null;
|
|
6206
7192
|
const deposits = allocationLegs(action.details?.deposits, context, true);
|
|
6207
7193
|
const withdrawals = action.details?.withdrawals == null ? [] : allocationLegs(action.details.withdrawals, context, false);
|
|
6208
7194
|
if (!deposits?.length || !withdrawals) return null;
|
|
@@ -6215,7 +7201,7 @@ function allocationEntry(action, context) {
|
|
|
6215
7201
|
}));
|
|
6216
7202
|
const amountRaw = deposits.reduce((total, leg) => total + leg.rawAmount, 0n);
|
|
6217
7203
|
const rebalance = withdrawals.length > 0;
|
|
6218
|
-
const hashes = rebalance && typeof withdrawHash === "string" &&
|
|
7204
|
+
const hashes = rebalance && typeof withdrawHash === "string" && TX_HASH3.test(withdrawHash) && withdrawHash.toLowerCase() !== hash2.toLowerCase() ? [hash2, withdrawHash] : [hash2];
|
|
6219
7205
|
const source = withdrawals.length === 1 ? withdrawals[0] : {
|
|
6220
7206
|
protocol: [...new Set(withdrawals.map((leg) => leg.protocol))].join(" + "),
|
|
6221
7207
|
pool: withdrawals.map((leg) => leg.pool).filter(Boolean).join(" + ") || void 0
|
|
@@ -6607,6 +7593,7 @@ function operationHash(operation) {
|
|
|
6607
7593
|
}
|
|
6608
7594
|
});
|
|
6609
7595
|
}
|
|
7596
|
+
var YIELDSEEKER_WITHDRAW_SERVER_SUBMIT = 1;
|
|
6610
7597
|
function sponsorError(message, safeToFallback, details = {}) {
|
|
6611
7598
|
return new OwneyError(
|
|
6612
7599
|
"SPONSOR_REQUEST_FAILED",
|
|
@@ -6629,6 +7616,22 @@ function assertIntentFresh(expiresAt) {
|
|
|
6629
7616
|
{ failureCode: "SPONSOR_INTENT_EXPIRED" }
|
|
6630
7617
|
);
|
|
6631
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
|
+
}
|
|
6632
7635
|
function providerReason(value) {
|
|
6633
7636
|
if (typeof value !== "string") return void 0;
|
|
6634
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);
|
|
@@ -6710,6 +7713,12 @@ async function checkpoint(callback, pending) {
|
|
|
6710
7713
|
);
|
|
6711
7714
|
}
|
|
6712
7715
|
}
|
|
7716
|
+
function reportAccepted(onAccepted, accepted) {
|
|
7717
|
+
try {
|
|
7718
|
+
onAccepted?.(accepted);
|
|
7719
|
+
} catch {
|
|
7720
|
+
}
|
|
7721
|
+
}
|
|
6713
7722
|
function finalPaymasterFields(value) {
|
|
6714
7723
|
const result = {};
|
|
6715
7724
|
for (const key2 of [
|
|
@@ -6788,15 +7797,57 @@ async function requestIntent(input) {
|
|
|
6788
7797
|
url.searchParams.set("intent", intent.intent);
|
|
6789
7798
|
return { intent, rpcUrl: url.toString() };
|
|
6790
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
|
+
}
|
|
6791
7847
|
async function rpc(url, method, params, phase) {
|
|
6792
7848
|
let response;
|
|
6793
7849
|
try {
|
|
6794
|
-
response = await
|
|
6795
|
-
method: "POST",
|
|
6796
|
-
headers: { "content-type": "application/json" },
|
|
6797
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
6798
|
-
signal: AbortSignal.timeout(15e3)
|
|
6799
|
-
});
|
|
7850
|
+
response = await postRpc(url, method, params, RPC_TIMEOUT_MS);
|
|
6800
7851
|
} catch {
|
|
6801
7852
|
throw sponsorError(
|
|
6802
7853
|
"Withdrawal sponsorship request status is unknown.",
|
|
@@ -6810,43 +7861,37 @@ async function rpc(url, method, params, phase) {
|
|
|
6810
7861
|
);
|
|
6811
7862
|
}
|
|
6812
7863
|
const payload = await response.json().catch(() => null);
|
|
6813
|
-
if (!response.ok || payload?.error || payload?.result === void 0)
|
|
6814
|
-
|
|
6815
|
-
const diagnostics = rpcDiagnostics(rejection?.data);
|
|
6816
|
-
const executionReverted = rejection?.code === -32521 || diagnostics.failureCode === "SPONSOR_EXECUTION_REVERTED";
|
|
6817
|
-
const details = {
|
|
6818
|
-
statusCode: response.status,
|
|
6819
|
-
rpcCode: rejection?.code,
|
|
6820
|
-
rpcMethod: method,
|
|
6821
|
-
phase,
|
|
6822
|
-
providerReason: providerReason(rejection?.data?.providerReason) ?? providerReason(rejection?.message),
|
|
6823
|
-
failureCode: "RPC_REJECTED",
|
|
6824
|
-
...diagnostics
|
|
6825
|
-
};
|
|
6826
|
-
if (phase === "prepare" && executionReverted)
|
|
6827
|
-
throw new OwneyError(
|
|
6828
|
-
"WITHDRAW_FAILED",
|
|
6829
|
-
"Yieldseeker withdrawal simulation reverted before submission.",
|
|
6830
|
-
{
|
|
6831
|
-
...details,
|
|
6832
|
-
failureCode: "SPONSOR_EXECUTION_REVERTED",
|
|
6833
|
-
safeToFallback: false,
|
|
6834
|
-
rejectedBeforeSubmission: true
|
|
6835
|
-
},
|
|
6836
|
-
"yieldseeker"
|
|
6837
|
-
);
|
|
6838
|
-
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 ?? ""));
|
|
6839
|
-
throw sponsorError(
|
|
6840
|
-
phase !== "prepare" && !definitiveRejection ? "Sponsored withdrawal submission status is unknown." : "Withdrawal sponsorship was declined before submission.",
|
|
6841
|
-
phase === "prepare" || definitiveRejection,
|
|
6842
|
-
{
|
|
6843
|
-
...details,
|
|
6844
|
-
...phase === "prepare" || definitiveRejection ? { rejectedBeforeSubmission: true } : {}
|
|
6845
|
-
}
|
|
6846
|
-
);
|
|
6847
|
-
}
|
|
7864
|
+
if (!response.ok || payload?.error || payload?.result === void 0)
|
|
7865
|
+
throw rpcRejection(response.status, payload, method, phase);
|
|
6848
7866
|
return payload.result;
|
|
6849
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
|
+
}
|
|
6850
7895
|
async function waitForTransaction(client, txHash) {
|
|
6851
7896
|
const receipt = await client.waitForTransactionReceipt({ hash: txHash, confirmations: 1 });
|
|
6852
7897
|
if (receipt.status !== "success")
|
|
@@ -6928,6 +7973,7 @@ async function submitEoa(input) {
|
|
|
6928
7973
|
operation = { ...operation, signature };
|
|
6929
7974
|
await assertOwnerSession(input.state, input.owner);
|
|
6930
7975
|
assertIntentFresh(input.expiresAt);
|
|
7976
|
+
assertPaymasterFresh(operation.paymasterAndData);
|
|
6931
7977
|
const userOperation = {
|
|
6932
7978
|
sender: operation.sender,
|
|
6933
7979
|
nonce: operation.nonce,
|
|
@@ -6944,18 +7990,19 @@ async function submitEoa(input) {
|
|
|
6944
7990
|
input.assertCurrent?.();
|
|
6945
7991
|
await checkpoint(input.onBeforeSubmission, { userOpHash, userOperation });
|
|
6946
7992
|
input.assertCurrent?.();
|
|
6947
|
-
const
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
);
|
|
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
|
+
}
|
|
6959
8006
|
const deadline = Date.now() + OBSERVATION_WINDOW_MS;
|
|
6960
8007
|
let lastReadError;
|
|
6961
8008
|
for (let poll = 0; poll < 40 && Date.now() < deadline; poll++) {
|
|
@@ -6988,6 +8035,7 @@ async function submitEoa(input) {
|
|
|
6988
8035
|
userOpHash,
|
|
6989
8036
|
phase: "observe",
|
|
6990
8037
|
failureCode: "RECEIPT_UNRESOLVED",
|
|
8038
|
+
...serverSubmit ? { serverSubmit } : {},
|
|
6991
8039
|
...lastReadError instanceof OwneyError ? { lastObservationError: lastReadError.details } : {}
|
|
6992
8040
|
}
|
|
6993
8041
|
);
|
|
@@ -7048,6 +8096,7 @@ async function submitSmart(input) {
|
|
|
7048
8096
|
{ callsId: requestedId, phase: "send", failureCode: "WALLET_SEND_UNRESOLVED" }
|
|
7049
8097
|
);
|
|
7050
8098
|
savePending(input.onPending, { callsId });
|
|
8099
|
+
reportAccepted(input.onAccepted, { callsId });
|
|
7051
8100
|
const deadline = Date.now() + OBSERVATION_WINDOW_MS;
|
|
7052
8101
|
for (let poll = 0; poll < 40 && Date.now() < deadline; poll++) {
|
|
7053
8102
|
let status;
|
|
@@ -7341,6 +8390,9 @@ var YieldseekerAgent = class {
|
|
|
7341
8390
|
endpointReadFailures = /* @__PURE__ */ new Map();
|
|
7342
8391
|
reconcileUntil = /* @__PURE__ */ new Map();
|
|
7343
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();
|
|
7344
8396
|
activityRefreshUntil = /* @__PURE__ */ new Map();
|
|
7345
8397
|
// null means a confirmed movement has no reliable baseline. Do not guess
|
|
7346
8398
|
// settlement; keep slow reconciliation after the fast window expires.
|
|
@@ -7747,6 +8799,7 @@ var YieldseekerAgent = class {
|
|
|
7747
8799
|
this.endpointReadFailures.clear();
|
|
7748
8800
|
this.reconcileUntil.clear();
|
|
7749
8801
|
this.allocationRefresh.clear();
|
|
8802
|
+
this.forceNextSnapshot.clear();
|
|
7750
8803
|
this.activityRefreshUntil.clear();
|
|
7751
8804
|
this.snapshotMovements.clear();
|
|
7752
8805
|
this.movementEpochs.clear();
|
|
@@ -7860,7 +8913,7 @@ var YieldseekerAgent = class {
|
|
|
7860
8913
|
notifyFunding(onProgress, evidence);
|
|
7861
8914
|
return { txHash, smartWallet: context.wallet.walletAddress, amount, evidence };
|
|
7862
8915
|
}
|
|
7863
|
-
async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto", onBeforeSubmission, assertOperation) {
|
|
8916
|
+
async withdraw(state, chainId, asset, amount, onProgress, gasMode = "auto", onBeforeSubmission, assertOperation, submission) {
|
|
7864
8917
|
let ownerTransferAttempted = false;
|
|
7865
8918
|
let evidence = {
|
|
7866
8919
|
agentId: this.id,
|
|
@@ -7873,12 +8926,23 @@ var YieldseekerAgent = class {
|
|
|
7873
8926
|
delivery: "pending"
|
|
7874
8927
|
};
|
|
7875
8928
|
try {
|
|
7876
|
-
return await this.withdrawInternal(
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
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
|
+
);
|
|
7882
8946
|
} catch (error) {
|
|
7883
8947
|
if (error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED") {
|
|
7884
8948
|
this.withdrawalVersionCache.clear();
|
|
@@ -7894,7 +8958,7 @@ var YieldseekerAgent = class {
|
|
|
7894
8958
|
throw error;
|
|
7895
8959
|
}
|
|
7896
8960
|
}
|
|
7897
|
-
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) {
|
|
7898
8962
|
this.assertChain(chainId);
|
|
7899
8963
|
this.assertAsset(asset);
|
|
7900
8964
|
if (amount !== void 0 && BigInt(amount) <= 0n) {
|
|
@@ -8026,13 +9090,13 @@ var YieldseekerAgent = class {
|
|
|
8026
9090
|
const verifyKnownPositions = async (blockNumber, values = [...knownPositions.values()], timeoutMs = YIELDSEEKER_MAX_POSITION_VERIFICATION_MS) => {
|
|
8027
9091
|
const verifiedPositions = [];
|
|
8028
9092
|
if (timeoutMs <= 0) return { verified: false, positions: verifiedPositions };
|
|
8029
|
-
let
|
|
9093
|
+
let active = true;
|
|
8030
9094
|
const checks = this.readVaultShares(values.map((position2) => position2.vaultOption), agentWallet, blockNumber).catch(() => []);
|
|
8031
9095
|
const reads = values.map(async (position2, index) => {
|
|
8032
9096
|
const address = getAddress6(position2.vaultOption.address);
|
|
8033
9097
|
try {
|
|
8034
9098
|
const group = (await checks).slice(index * 2, index * 2 + 2);
|
|
8035
|
-
if (!
|
|
9099
|
+
if (!active || group.length !== 2 || group.some((value) => value.status !== "success")) return null;
|
|
8036
9100
|
this.assertSession(generation, chainId, asset);
|
|
8037
9101
|
const [underlying, shares] = group.map((value) => value.result);
|
|
8038
9102
|
if (typeof underlying !== "string" || !isAddress4(underlying) || underlying.toLowerCase() !== metadata.address.toLowerCase() || typeof shares !== "bigint" || shares < 0n) return null;
|
|
@@ -8045,7 +9109,7 @@ var YieldseekerAgent = class {
|
|
|
8045
9109
|
shares,
|
|
8046
9110
|
blockNumber
|
|
8047
9111
|
);
|
|
8048
|
-
if (!
|
|
9112
|
+
if (!active || value === void 0) return null;
|
|
8049
9113
|
this.assertSession(generation, chainId, asset);
|
|
8050
9114
|
verified.withdrawableAssetsRaw = value.toString();
|
|
8051
9115
|
verifiedPositions.push(verified);
|
|
@@ -8062,7 +9126,7 @@ var YieldseekerAgent = class {
|
|
|
8062
9126
|
args: [shares],
|
|
8063
9127
|
blockNumber
|
|
8064
9128
|
});
|
|
8065
|
-
if (!
|
|
9129
|
+
if (!active || typeof value !== "bigint" || value < 0n) return null;
|
|
8066
9130
|
this.assertSession(generation, chainId, asset);
|
|
8067
9131
|
if (value > 0n && !positiveValue) {
|
|
8068
9132
|
positiveValue = true;
|
|
@@ -8090,19 +9154,20 @@ var YieldseekerAgent = class {
|
|
|
8090
9154
|
}
|
|
8091
9155
|
});
|
|
8092
9156
|
const result = await boundedVerification(Promise.all(reads), timeoutMs);
|
|
8093
|
-
|
|
9157
|
+
active = false;
|
|
8094
9158
|
this.assertSession(generation, chainId, asset);
|
|
8095
9159
|
return {
|
|
8096
9160
|
verified: result !== void 0 && result.every((position2) => position2 !== null),
|
|
8097
9161
|
positions: [...verifiedPositions]
|
|
8098
9162
|
};
|
|
8099
9163
|
};
|
|
8100
|
-
const refreshPositions = async () => {
|
|
9164
|
+
const refreshPositions = async (promptless = false) => {
|
|
8101
9165
|
this.assertSession(generation, chainId, asset);
|
|
8102
9166
|
const response = await this.walletRequest(
|
|
8103
9167
|
state,
|
|
8104
9168
|
chainId,
|
|
8105
|
-
this.agentPath(context, "vault-positions")
|
|
9169
|
+
this.agentPath(context, "vault-positions"),
|
|
9170
|
+
{ promptless }
|
|
8106
9171
|
);
|
|
8107
9172
|
this.assertSession(generation, chainId, asset);
|
|
8108
9173
|
if (!Array.isArray(response?.vaultPositions)) throw this.invalidResponse("vault positions");
|
|
@@ -8333,6 +9398,8 @@ var YieldseekerAgent = class {
|
|
|
8333
9398
|
agentWallet: getAddress6(context.wallet.walletAddress),
|
|
8334
9399
|
asset: metadata.address,
|
|
8335
9400
|
callData: data,
|
|
9401
|
+
// The EOA send opts in to the server-owned send with it.
|
|
9402
|
+
operationId,
|
|
8336
9403
|
onPending: (pending) => {
|
|
8337
9404
|
submissionEvidence = { ...submissionEvidence, ...pending };
|
|
8338
9405
|
notifyFunding(onProgress, submissionEvidence);
|
|
@@ -8341,6 +9408,11 @@ var YieldseekerAgent = class {
|
|
|
8341
9408
|
assertOperation?.();
|
|
8342
9409
|
await onBeforeSubmission?.({ ...base7, ...pending });
|
|
8343
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);
|
|
8344
9416
|
}
|
|
8345
9417
|
});
|
|
8346
9418
|
txHash = submitted.txHash;
|
|
@@ -8357,7 +9429,10 @@ var YieldseekerAgent = class {
|
|
|
8357
9429
|
value: "0",
|
|
8358
9430
|
chainId
|
|
8359
9431
|
},
|
|
8360
|
-
(hash2) =>
|
|
9432
|
+
(hash2) => {
|
|
9433
|
+
submissionEvidence = { ...submissionEvidence, txHash: hash2, ownerAuthorization: "accepted" };
|
|
9434
|
+
notifyFunding(onProgress, submissionEvidence);
|
|
9435
|
+
},
|
|
8361
9436
|
(value) => {
|
|
8362
9437
|
receipt = value;
|
|
8363
9438
|
},
|
|
@@ -8388,7 +9463,9 @@ var YieldseekerAgent = class {
|
|
|
8388
9463
|
const verificationDeadline = Date.now() + YIELDSEEKER_MAX_POSITION_VERIFICATION_MS;
|
|
8389
9464
|
const knownAtPayout = new Set(knownPositions.keys());
|
|
8390
9465
|
const [catalogVerified, knownProof] = await Promise.all([
|
|
8391
|
-
|
|
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) => {
|
|
8392
9469
|
rememberPositions(values);
|
|
8393
9470
|
return true;
|
|
8394
9471
|
}).catch(() => false)),
|
|
@@ -8496,14 +9573,14 @@ var YieldseekerAgent = class {
|
|
|
8496
9573
|
}
|
|
8497
9574
|
/** Recheck MAX without re-unwinding, paying out, or requesting a signature. */
|
|
8498
9575
|
async verifyWithdrawalCompletion(state, evidence) {
|
|
8499
|
-
const
|
|
9576
|
+
const unknown2 = (reason2) => ({ status: "unavailable", reason: reason2 });
|
|
8500
9577
|
const proof = evidence.maxCompletion;
|
|
8501
|
-
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");
|
|
8502
9579
|
this.assertAsset(evidence.token);
|
|
8503
9580
|
const asset = evidence.token;
|
|
8504
9581
|
const metadata = YIELDSEEKER_ASSET_METADATA[asset];
|
|
8505
9582
|
const signature = this.auth.peekToken(state, evidence.chainId);
|
|
8506
|
-
if (!signature) return
|
|
9583
|
+
if (!signature) return unknown2("AUTHENTICATION_REQUIRED");
|
|
8507
9584
|
let reason = "CATALOG_UNAVAILABLE";
|
|
8508
9585
|
try {
|
|
8509
9586
|
return await bounded(async () => {
|
|
@@ -8511,7 +9588,7 @@ var YieldseekerAgent = class {
|
|
|
8511
9588
|
`/users/${encodeURIComponent(proof.userId)}/agents/${encodeURIComponent(proof.agentId)}/vault-positions`,
|
|
8512
9589
|
{ signature }
|
|
8513
9590
|
);
|
|
8514
|
-
if (!Array.isArray(response?.vaultPositions)) return
|
|
9591
|
+
if (!Array.isArray(response?.vaultPositions)) return unknown2("CATALOG_INVALID");
|
|
8515
9592
|
const positions = this.validateWithdrawalPositions(response.vaultPositions, evidence.chainId, asset);
|
|
8516
9593
|
const vaults = new Map(proof.vaults.map((address) => [address.toLowerCase(), { address }]));
|
|
8517
9594
|
positions.forEach((p) => vaults.set(p.vaultOption.address.toLowerCase(), p.vaultOption));
|
|
@@ -8519,14 +9596,14 @@ var YieldseekerAgent = class {
|
|
|
8519
9596
|
if (needsInventory || [...vaults.values()].some((option) => !option.provider)) {
|
|
8520
9597
|
const catalog = await this.loadVaultOptions(asset).catch(() => []);
|
|
8521
9598
|
const eligible = catalog.filter((o) => o.chainId === evidence.chainId && o.assetAddress?.toLowerCase() === metadata.address.toLowerCase() && isAddress4(o.address));
|
|
8522
|
-
if (needsInventory && !eligible.length) return
|
|
9599
|
+
if (needsInventory && !eligible.length) return unknown2("INVENTORY_INCOMPLETE");
|
|
8523
9600
|
for (const option of eligible) {
|
|
8524
9601
|
const key2 = option.address.toLowerCase();
|
|
8525
9602
|
if (needsInventory || vaults.has(key2)) vaults.set(key2, option);
|
|
8526
9603
|
}
|
|
8527
9604
|
}
|
|
8528
9605
|
if (vaults.size > YIELDSEEKER_WITHDRAWAL_CANDIDATE_LIMIT || [...vaults.keys()].some((a) => !isAddress4(a)))
|
|
8529
|
-
return
|
|
9606
|
+
return unknown2("INVENTORY_INVALID");
|
|
8530
9607
|
reason = "VAULT_PROOF_UNAVAILABLE";
|
|
8531
9608
|
const blockNumber = BigInt(evidence.blockNumber);
|
|
8532
9609
|
const options = [...vaults.values()];
|
|
@@ -8557,7 +9634,7 @@ var YieldseekerAgent = class {
|
|
|
8557
9634
|
};
|
|
8558
9635
|
}, 8e3, () => new Error("MAX proof read timed out"));
|
|
8559
9636
|
} catch {
|
|
8560
|
-
return
|
|
9637
|
+
return unknown2(reason);
|
|
8561
9638
|
}
|
|
8562
9639
|
}
|
|
8563
9640
|
async readVaultShares(options, wallet, blockNumber) {
|
|
@@ -8638,12 +9715,12 @@ var YieldseekerAgent = class {
|
|
|
8638
9715
|
{ asset: context.asset, reason: "YIELDSEEKER_LIQUIDITY_UNVERIFIED" },
|
|
8639
9716
|
this.id
|
|
8640
9717
|
);
|
|
8641
|
-
let
|
|
9718
|
+
let active = true;
|
|
8642
9719
|
let knownProof;
|
|
8643
9720
|
const hasUnverifiedIndexedFunds = (proof) => proof.indexedPositions.some((position2) => !proof.resolvedVaults.includes(position2.vaultOption.address.toLowerCase()) && BigInt(position2.withdrawableAssetsRaw) > 0n);
|
|
8644
9721
|
const assertReadCurrent = () => {
|
|
8645
9722
|
this.assertSession(generation, chainId, context.asset);
|
|
8646
|
-
if (!
|
|
9723
|
+
if (!active || (this.liquidityVersions.get(contextKey) ?? 0) !== version) throw unavailable();
|
|
8647
9724
|
};
|
|
8648
9725
|
const read2 = async () => {
|
|
8649
9726
|
const client = this.getReceiptClient();
|
|
@@ -8832,7 +9909,7 @@ var YieldseekerAgent = class {
|
|
|
8832
9909
|
if (error instanceof OwneyError) throw error;
|
|
8833
9910
|
throw unavailable();
|
|
8834
9911
|
} finally {
|
|
8835
|
-
|
|
9912
|
+
active = false;
|
|
8836
9913
|
if (timer) clearTimeout(timer);
|
|
8837
9914
|
}
|
|
8838
9915
|
}
|
|
@@ -8917,6 +9994,7 @@ var YieldseekerAgent = class {
|
|
|
8917
9994
|
this.assertAsset(asset);
|
|
8918
9995
|
const until = Math.min(refreshUntil, Date.now() + YIELDSEEKER_SETTLEMENT_SNAPSHOT_WINDOW_MS);
|
|
8919
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);
|
|
8920
9998
|
this.invalidateSlot(state, chainId, asset, (key2) => {
|
|
8921
9999
|
const failure = this.snapshotFailures.get(key2);
|
|
8922
10000
|
this.advancePortfolioVersion(key2);
|
|
@@ -8928,6 +10006,7 @@ var YieldseekerAgent = class {
|
|
|
8928
10006
|
}
|
|
8929
10007
|
this.reconcileUntil.set(key2, Math.max(this.reconcileUntil.get(key2) ?? 0, until));
|
|
8930
10008
|
this.activityRefreshUntil.set(key2, Math.max(this.activityRefreshUntil.get(key2) ?? 0, activityUntil));
|
|
10009
|
+
if (hash2) this.forceNextSnapshot.add(key2);
|
|
8931
10010
|
});
|
|
8932
10011
|
}
|
|
8933
10012
|
endFundingBalanceRefresh(state, chainId, asset, read2) {
|
|
@@ -9763,7 +10842,7 @@ var YieldseekerAgent = class {
|
|
|
9763
10842
|
return this.walletRequest(
|
|
9764
10843
|
state,
|
|
9765
10844
|
chainId,
|
|
9766
|
-
|
|
10845
|
+
this.snapshotPath(context, forceRefresh)
|
|
9767
10846
|
).then((response) => {
|
|
9768
10847
|
if (!response?.agentSnapshot) {
|
|
9769
10848
|
throw this.invalidResponse("agent snapshot");
|
|
@@ -9793,6 +10872,16 @@ var YieldseekerAgent = class {
|
|
|
9793
10872
|
throw error;
|
|
9794
10873
|
});
|
|
9795
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
|
+
}
|
|
9796
10885
|
portfolioSnapshot(state, chainId, context, contextKey) {
|
|
9797
10886
|
const key2 = `snapshot:${contextKey}`;
|
|
9798
10887
|
const cached = this.readCache.get(key2);
|
|
@@ -9811,6 +10900,7 @@ var YieldseekerAgent = class {
|
|
|
9811
10900
|
const settling = inWindow && !this.deferredToLedger(contextKey);
|
|
9812
10901
|
if (inWindow) this.settlementChecks.set(contextKey, (this.settlementChecks.get(contextKey) ?? 0) + 1);
|
|
9813
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);
|
|
9814
10904
|
return this.requestPortfolioSnapshot(
|
|
9815
10905
|
state,
|
|
9816
10906
|
chainId,
|
|
@@ -9818,7 +10908,7 @@ var YieldseekerAgent = class {
|
|
|
9818
10908
|
contextKey,
|
|
9819
10909
|
version,
|
|
9820
10910
|
generation,
|
|
9821
|
-
allocationChanged || settling || ready
|
|
10911
|
+
allocationChanged || settling || ready || movementForce
|
|
9822
10912
|
);
|
|
9823
10913
|
})
|
|
9824
10914
|
};
|
|
@@ -10268,7 +11358,7 @@ var YieldseekerAgent = class {
|
|
|
10268
11358
|
}
|
|
10269
11359
|
const ttl = this.portfolioCacheMs(contextKey);
|
|
10270
11360
|
this.settlementChecks.set(contextKey, 1);
|
|
10271
|
-
const force =
|
|
11361
|
+
const force = true;
|
|
10272
11362
|
await this.cachedRead(
|
|
10273
11363
|
`snapshot:${contextKey}`,
|
|
10274
11364
|
ttl,
|
|
@@ -10323,13 +11413,22 @@ var YieldseekerAgent = class {
|
|
|
10323
11413
|
options.assertCurrent?.();
|
|
10324
11414
|
return this.api.request(path, { method: options.method, body: options.body, signature: signature2 });
|
|
10325
11415
|
};
|
|
10326
|
-
|
|
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);
|
|
10327
11425
|
try {
|
|
10328
11426
|
return await request2(signature);
|
|
10329
11427
|
} catch (error) {
|
|
10330
11428
|
if (!(error instanceof YieldseekerApiError)) throw error;
|
|
10331
11429
|
if (error.providerCode === "NO_USER") throw error;
|
|
10332
11430
|
if (!error.isAuthenticationError) throw error;
|
|
11431
|
+
if (options.promptless) throw error;
|
|
10333
11432
|
signature = await this.auth.refreshToken(state, chainId, signature);
|
|
10334
11433
|
try {
|
|
10335
11434
|
return await request2(signature);
|
|
@@ -10557,6 +11656,16 @@ var YieldseekerAgent = class {
|
|
|
10557
11656
|
};
|
|
10558
11657
|
|
|
10559
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
|
+
}
|
|
10560
11669
|
async function request(baseUrl, apiKey, path, init) {
|
|
10561
11670
|
const url = `${baseUrl}/api/v1/swap${path}`;
|
|
10562
11671
|
const controller = new AbortController();
|
|
@@ -10569,7 +11678,8 @@ async function request(baseUrl, apiKey, path, init) {
|
|
|
10569
11678
|
"Content-Type": "application/json",
|
|
10570
11679
|
"x-owney-api-key": apiKey
|
|
10571
11680
|
},
|
|
10572
|
-
...init ? { body: JSON.stringify(init.body) } : {}
|
|
11681
|
+
...init ? { body: JSON.stringify(init.body) } : {},
|
|
11682
|
+
...init?.redirect ? { redirect: init.redirect } : {}
|
|
10573
11683
|
});
|
|
10574
11684
|
if (!res.ok) {
|
|
10575
11685
|
const text = await res.text().catch(() => "");
|
|
@@ -10602,7 +11712,7 @@ async function request(baseUrl, apiKey, path, init) {
|
|
|
10602
11712
|
);
|
|
10603
11713
|
}
|
|
10604
11714
|
return json.data;
|
|
10605
|
-
}, 2e4, () => new OwneyError(
|
|
11715
|
+
}, init?.timeoutMs ?? 2e4, () => new OwneyError(
|
|
10606
11716
|
"SWAP_REQUEST_FAILED",
|
|
10607
11717
|
"Swap request timed out. Its outcome may still need confirmation.",
|
|
10608
11718
|
{ requestTimedOut: true, phase: path === "/order" ? "submit" : "read" }
|
|
@@ -10686,8 +11796,9 @@ function createSwapApi(baseUrl, apiKey) {
|
|
|
10686
11796
|
/**
|
|
10687
11797
|
* Builds a Fusion+ order server-side and returns EIP-712 typed data.
|
|
10688
11798
|
*
|
|
10689
|
-
* Only HASHES go over the wire. The preimages
|
|
10690
|
-
* 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.
|
|
10691
11802
|
*/
|
|
10692
11803
|
buildOrder: (params) => request(baseUrl, apiKey, "/order/build", {
|
|
10693
11804
|
method: "POST",
|
|
@@ -10714,6 +11825,23 @@ function createSwapApi(baseUrl, apiKey) {
|
|
|
10714
11825
|
method: "POST",
|
|
10715
11826
|
body: { orderHash, secret }
|
|
10716
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
|
+
}),
|
|
10717
11845
|
/**
|
|
10718
11846
|
* @param chainId present only for a SAME-CHAIN order. That product keys its
|
|
10719
11847
|
* orders per chain and the Fusion+ endpoint does not know them, so asking
|
|
@@ -10897,14 +12025,14 @@ function swapChain(chainId) {
|
|
|
10897
12025
|
}
|
|
10898
12026
|
|
|
10899
12027
|
// src/lib/swap/swap.arrival.ts
|
|
10900
|
-
var
|
|
12028
|
+
var DEFAULT_TIMEOUT_MS2 = 18e4;
|
|
10901
12029
|
var DEFAULT_POLL_MS = 4e3;
|
|
10902
12030
|
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
10903
12031
|
async function awaitBalanceIncrease(options) {
|
|
10904
12032
|
const {
|
|
10905
12033
|
readBalance,
|
|
10906
12034
|
baseline,
|
|
10907
|
-
timeoutMs =
|
|
12035
|
+
timeoutMs = DEFAULT_TIMEOUT_MS2,
|
|
10908
12036
|
pollMs = DEFAULT_POLL_MS
|
|
10909
12037
|
} = options;
|
|
10910
12038
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -10998,14 +12126,14 @@ var isSwapTerminal = (status) => SWAP_TERMINAL_STATUSES.includes(status);
|
|
|
10998
12126
|
var DEFAULT_POLL_MS2 = 5e3;
|
|
10999
12127
|
var MAX_BACKOFF_MS = 3e4;
|
|
11000
12128
|
var backoffFor = (failures, base7) => Math.min(base7 * 2 ** Math.min(failures, 5), MAX_BACKOFF_MS);
|
|
11001
|
-
var
|
|
12129
|
+
var DEFAULT_TIMEOUT_MS3 = 15 * 60 * 1e3;
|
|
11002
12130
|
async function runFusionOrderInternal(deps, options) {
|
|
11003
12131
|
const {
|
|
11004
12132
|
orderHash,
|
|
11005
12133
|
secrets,
|
|
11006
12134
|
onStage,
|
|
11007
12135
|
pollIntervalMs = DEFAULT_POLL_MS2,
|
|
11008
|
-
timeoutMs =
|
|
12136
|
+
timeoutMs = DEFAULT_TIMEOUT_MS3
|
|
11009
12137
|
} = options;
|
|
11010
12138
|
const deadline = deps.now() + timeoutMs;
|
|
11011
12139
|
let partial = options.hasPartialFill === true;
|
|
@@ -11086,14 +12214,14 @@ async function runFusionOrderInternal(deps, options) {
|
|
|
11086
12214
|
}
|
|
11087
12215
|
}
|
|
11088
12216
|
function runFusionOrder(deps, options) {
|
|
11089
|
-
return observeOrder(options.orderHash, () => runFusionOrderInternal(deps, options));
|
|
12217
|
+
return observeOrder(options.orderHash, () => runFusionOrderInternal(deps, options), { wait: options.waitForWatcher === true });
|
|
11090
12218
|
}
|
|
11091
12219
|
|
|
11092
12220
|
// src/lib/swap/swap.same-chain-runner.ts
|
|
11093
12221
|
var DEFAULT_POLL_MS3 = 5e3;
|
|
11094
12222
|
var MAX_BACKOFF_MS2 = 3e4;
|
|
11095
12223
|
var backoffFor2 = (failures, base7) => Math.min(base7 * 2 ** Math.min(failures, 5), MAX_BACKOFF_MS2);
|
|
11096
|
-
var
|
|
12224
|
+
var DEFAULT_TIMEOUT_MS4 = 3 * 60 * 1e3;
|
|
11097
12225
|
function explain(status) {
|
|
11098
12226
|
switch (status) {
|
|
11099
12227
|
case "expired":
|
|
@@ -11117,7 +12245,7 @@ async function runSameChainOrderInternal(deps, options) {
|
|
|
11117
12245
|
orderHash,
|
|
11118
12246
|
onStage,
|
|
11119
12247
|
pollIntervalMs = DEFAULT_POLL_MS3,
|
|
11120
|
-
timeoutMs =
|
|
12248
|
+
timeoutMs = DEFAULT_TIMEOUT_MS4
|
|
11121
12249
|
} = options;
|
|
11122
12250
|
const deadline = deps.now() + timeoutMs;
|
|
11123
12251
|
let partial = options.hasPartialFill === true;
|
|
@@ -11170,7 +12298,7 @@ async function runSameChainOrderInternal(deps, options) {
|
|
|
11170
12298
|
}
|
|
11171
12299
|
}
|
|
11172
12300
|
function runSameChainOrder(deps, options) {
|
|
11173
|
-
return observeOrder(options.orderHash, () => runSameChainOrderInternal(deps, options));
|
|
12301
|
+
return observeOrder(options.orderHash, () => runSameChainOrderInternal(deps, options), { wait: options.waitForWatcher === true });
|
|
11174
12302
|
}
|
|
11175
12303
|
|
|
11176
12304
|
// src/lib/swap/swap.secret-store.ts
|
|
@@ -11195,6 +12323,21 @@ function saveOrder(order) {
|
|
|
11195
12323
|
return false;
|
|
11196
12324
|
}
|
|
11197
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
|
+
}
|
|
11198
12341
|
function clearOrder(orderHash) {
|
|
11199
12342
|
const store = storage5();
|
|
11200
12343
|
if (!store) return;
|
|
@@ -11273,10 +12416,10 @@ async function affordableAmount(deps, quoted, reserve = 0n) {
|
|
|
11273
12416
|
}
|
|
11274
12417
|
async function executeSwap(deps, options) {
|
|
11275
12418
|
const { quote, walletAddress, onStage } = options;
|
|
11276
|
-
let
|
|
12419
|
+
let active = true;
|
|
11277
12420
|
const original = deps;
|
|
11278
12421
|
const assertActive = () => {
|
|
11279
|
-
if (!
|
|
12422
|
+
if (!active) throw new OwneyError("SWAP_REQUEST_FAILED", "This swap attempt is no longer active.");
|
|
11280
12423
|
};
|
|
11281
12424
|
const call = async (work, write = false) => {
|
|
11282
12425
|
assertActive();
|
|
@@ -11337,7 +12480,7 @@ async function executeSwap(deps, options) {
|
|
|
11337
12480
|
}
|
|
11338
12481
|
return { received: arrived.toString(), ...result };
|
|
11339
12482
|
} finally {
|
|
11340
|
-
|
|
12483
|
+
active = false;
|
|
11341
12484
|
}
|
|
11342
12485
|
}
|
|
11343
12486
|
async function runClassic(deps, options) {
|
|
@@ -11456,6 +12599,7 @@ async function runSameChainFusion(deps, options, walletAddress) {
|
|
|
11456
12599
|
}
|
|
11457
12600
|
await runSameChainOrder(deps.sameChainRunner ?? deps.runner, {
|
|
11458
12601
|
orderHash: built.orderHash,
|
|
12602
|
+
waitForWatcher: true,
|
|
11459
12603
|
...onStage ? { onStage } : {}
|
|
11460
12604
|
});
|
|
11461
12605
|
return { orderHash: built.orderHash, spent: amount.toString() };
|
|
@@ -11524,67 +12668,118 @@ async function runFusion(deps, options, walletAddress) {
|
|
|
11524
12668
|
...direction ? { direction } : {},
|
|
11525
12669
|
...permit ? { permit } : {}
|
|
11526
12670
|
});
|
|
11527
|
-
const
|
|
11528
|
-
orderHash: built.orderHash,
|
|
11529
|
-
walletAddress,
|
|
11530
|
-
secrets,
|
|
11531
|
-
srcChainId: quote.src.chainId,
|
|
11532
|
-
srcSymbol: quote.src.symbol,
|
|
11533
|
-
dstChainId: quote.dst.chainId,
|
|
11534
|
-
dstSymbol: quote.dst.symbol,
|
|
11535
|
-
amount: amount.toString(),
|
|
11536
|
-
createdAt: Date.now()
|
|
11537
|
-
});
|
|
11538
|
-
debugLog("owney-sdk", "swap: order built", { orderHash: built.orderHash });
|
|
11539
|
-
let signature;
|
|
12671
|
+
const release = claimOrder(built.orderHash);
|
|
11540
12672
|
try {
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
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
|
+
}
|
|
11547
12740
|
clearOrder(built.orderHash);
|
|
11548
|
-
|
|
11549
|
-
}
|
|
11550
|
-
|
|
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) {
|
|
11551
12759
|
try {
|
|
11552
|
-
await deps.api.
|
|
12760
|
+
const answer = await bounded(() => deps.api.handoffSecrets({
|
|
12761
|
+
operation: handoff.operation,
|
|
12762
|
+
orderHash: built.orderHash,
|
|
11553
12763
|
srcChainId: quote.src.chainId,
|
|
11554
|
-
|
|
11555
|
-
// the envelope here gets a bare 500 from the relayer.
|
|
12764
|
+
dstChainId: quote.dst.chainId,
|
|
11556
12765
|
order: built.order,
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
...built.extension ? { extension: built.extension } : {}
|
|
11565
|
-
});
|
|
11566
|
-
} catch (error) {
|
|
11567
|
-
throw submissionUnknown(error, built.orderHash);
|
|
11568
|
-
}
|
|
11569
|
-
debugLog("owney-sdk", "swap: order submitted, polling escrows");
|
|
11570
|
-
try {
|
|
11571
|
-
options.onOrderSubmitted?.(built.orderHash, { spent: amount.toString(), expected: built.dstAmount, durable });
|
|
11572
|
-
} catch {
|
|
11573
|
-
}
|
|
11574
|
-
try {
|
|
11575
|
-
await runFusionOrder(deps.runner, {
|
|
11576
|
-
orderHash: built.orderHash,
|
|
11577
|
-
secrets,
|
|
11578
|
-
...onStage ? { onStage } : {}
|
|
11579
|
-
});
|
|
11580
|
-
} catch (error) {
|
|
11581
|
-
if (error instanceof OwneyError && error.code === "SWAP_ORDER_REFUNDED" && error.details?.outcome === "refunded") {
|
|
11582
|
-
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;
|
|
11583
12773
|
}
|
|
11584
|
-
|
|
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");
|
|
11585
12778
|
}
|
|
11586
|
-
|
|
11587
|
-
|
|
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;
|
|
11588
12783
|
}
|
|
11589
12784
|
function submissionUnknown(error, orderHash) {
|
|
11590
12785
|
return new OwneyError(
|
|
@@ -11607,7 +12802,7 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
11607
12802
|
const payouts = new Map(Object.entries(options.results).map(([id, result]) => [id, { result, evidence: result.evidence }]));
|
|
11608
12803
|
const timeoutMs = Math.max(0, options.timeoutMs ?? 18e4);
|
|
11609
12804
|
const deadline = options.deadline ?? Date.now() + timeoutMs;
|
|
11610
|
-
let
|
|
12805
|
+
let active = true;
|
|
11611
12806
|
let timer;
|
|
11612
12807
|
const incomplete = (reason, agentId) => new OwneyError(
|
|
11613
12808
|
"WITHDRAW_SWAP_INCOMPLETE",
|
|
@@ -11625,7 +12820,7 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
11625
12820
|
);
|
|
11626
12821
|
const assertActive = () => {
|
|
11627
12822
|
options.assertCurrent();
|
|
11628
|
-
if (!
|
|
12823
|
+
if (!active || Date.now() > deadline) throw incomplete("PAYOUT_CONFIRMATION_TIMEOUT");
|
|
11629
12824
|
};
|
|
11630
12825
|
const pause = async () => {
|
|
11631
12826
|
assertActive();
|
|
@@ -11802,7 +12997,7 @@ async function awaitWithdrawalPayouts(options) {
|
|
|
11802
12997
|
timer = setTimeout(() => reject(incomplete("PAYOUT_CONFIRMATION_TIMEOUT")), Math.max(0, deadline - Date.now()));
|
|
11803
12998
|
})]);
|
|
11804
12999
|
} finally {
|
|
11805
|
-
|
|
13000
|
+
active = false;
|
|
11806
13001
|
if (timer) clearTimeout(timer);
|
|
11807
13002
|
}
|
|
11808
13003
|
}
|
|
@@ -12370,7 +13565,8 @@ import {
|
|
|
12370
13565
|
createPublicClient as createPublicClient4,
|
|
12371
13566
|
createWalletClient as createWalletClient5,
|
|
12372
13567
|
custom as custom5,
|
|
12373
|
-
erc20Abi as erc20Abi7
|
|
13568
|
+
erc20Abi as erc20Abi7,
|
|
13569
|
+
isAddress as isAddress5
|
|
12374
13570
|
} from "viem";
|
|
12375
13571
|
import { base as base6, arbitrum as arbitrum3, mainnet as mainnet3 } from "viem/chains";
|
|
12376
13572
|
|
|
@@ -12379,21 +13575,42 @@ import {
|
|
|
12379
13575
|
keccak256 as keccak2562,
|
|
12380
13576
|
parseTransaction
|
|
12381
13577
|
} from "viem";
|
|
13578
|
+
var SPONSORED_DEPOSIT_SERVER_BROADCAST = 1;
|
|
12382
13579
|
async function executeTokenBatch(input, initial) {
|
|
12383
13580
|
let body = initial, family, ended = false;
|
|
12384
13581
|
const deadlineAt = Date.now() + 9e4, controller = new AbortController();
|
|
12385
13582
|
let timer;
|
|
12386
|
-
const
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
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
|
+
};
|
|
12397
13614
|
const assertCurrent = () => {
|
|
12398
13615
|
if (ended || Date.now() >= deadlineAt) throw pending();
|
|
12399
13616
|
input.assertCurrent?.();
|
|
@@ -12418,20 +13635,52 @@ async function executeTokenBatch(input, initial) {
|
|
|
12418
13635
|
}
|
|
12419
13636
|
return operationId ? { ...value, operationId } : value;
|
|
12420
13637
|
};
|
|
12421
|
-
const
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
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
|
+
};
|
|
12429
13678
|
const replacements = { approval: 0, deposit: 0 };
|
|
12430
13679
|
const unavailable = /* @__PURE__ */ new Set();
|
|
12431
13680
|
const observedApprovals = /* @__PURE__ */ new Set();
|
|
12432
13681
|
const checkpointed = /* @__PURE__ */ new Set();
|
|
12433
13682
|
const accounted = /* @__PURE__ */ new Set();
|
|
12434
|
-
const remember = async (phase, bytes, hash2, predecessorHash) => {
|
|
13683
|
+
const remember = async (phase, bytes, hash2, predecessorHash, submissionAttempted = false) => {
|
|
12435
13684
|
assertCurrent();
|
|
12436
13685
|
if (keccak2562(bytes) !== hash2) throw pending();
|
|
12437
13686
|
const tx = parseTransaction(bytes);
|
|
@@ -12450,7 +13699,7 @@ async function executeTokenBatch(input, initial) {
|
|
|
12450
13699
|
nonce: String(tx.nonce),
|
|
12451
13700
|
predecessorHash,
|
|
12452
13701
|
preparedAt: Date.now(),
|
|
12453
|
-
submissionAttempted
|
|
13702
|
+
submissionAttempted
|
|
12454
13703
|
},
|
|
12455
13704
|
assertCurrent
|
|
12456
13705
|
)
|
|
@@ -12464,13 +13713,18 @@ async function executeTokenBatch(input, initial) {
|
|
|
12464
13713
|
const markSubmission = async (phase) => {
|
|
12465
13714
|
const hash2 = phase === "deposit" ? family?.current.deposit : family?.current.approval;
|
|
12466
13715
|
if (!family || !hash2) throw pending();
|
|
13716
|
+
const flag = serverSent.has(hash2) ? [true] : [];
|
|
12467
13717
|
if (phase === "deposit" && !checkpointed.has(hash2)) {
|
|
12468
13718
|
try {
|
|
12469
13719
|
await bounded2(
|
|
12470
|
-
Promise.resolve(
|
|
13720
|
+
Promise.resolve(
|
|
13721
|
+
input.onBeforeSubmission?.(hash2, reference(hash2), ...flag)
|
|
13722
|
+
)
|
|
12471
13723
|
);
|
|
13724
|
+
checkpointed.add(hash2);
|
|
12472
13725
|
} catch (error) {
|
|
12473
|
-
if (
|
|
13726
|
+
if (serverSent.has(hash2)) {
|
|
13727
|
+
} else if (error instanceof OwneyError && error.code === "DEPOSIT_CHECKPOINT_FAILED")
|
|
12474
13728
|
throw new OwneyError(error.code, error.message, {
|
|
12475
13729
|
...error.details,
|
|
12476
13730
|
notSubmitted: !family.attempts.some(
|
|
@@ -12478,9 +13732,8 @@ async function executeTokenBatch(input, initial) {
|
|
|
12478
13732
|
),
|
|
12479
13733
|
safeToFallback: false
|
|
12480
13734
|
});
|
|
12481
|
-
throw error;
|
|
13735
|
+
else throw error;
|
|
12482
13736
|
}
|
|
12483
|
-
checkpointed.add(hash2);
|
|
12484
13737
|
}
|
|
12485
13738
|
assertCurrent();
|
|
12486
13739
|
const attempt = family.attempts.find((a) => a.hash === hash2);
|
|
@@ -12495,7 +13748,7 @@ async function executeTokenBatch(input, initial) {
|
|
|
12495
13748
|
assertCurrent();
|
|
12496
13749
|
if (phase === "deposit")
|
|
12497
13750
|
try {
|
|
12498
|
-
input.onSubmitted?.(hash2, reference(hash2));
|
|
13751
|
+
input.onSubmitted?.(hash2, reference(hash2), ...flag);
|
|
12499
13752
|
} catch {
|
|
12500
13753
|
}
|
|
12501
13754
|
assertCurrent();
|
|
@@ -12531,12 +13784,15 @@ async function executeTokenBatch(input, initial) {
|
|
|
12531
13784
|
}
|
|
12532
13785
|
return observation.txHash;
|
|
12533
13786
|
}
|
|
12534
|
-
if (observation.status === "failed" || observation.status === "expired")
|
|
12535
|
-
|
|
13787
|
+
if (observation.status === "failed" || observation.status === "expired") {
|
|
13788
|
+
const failure = new OwneyError(
|
|
12536
13789
|
"SPONSOR_REQUEST_FAILED",
|
|
12537
13790
|
observation.status === "expired" ? "The deposit authorization expired without transferring funds. Start a new deposit." : "Sponsored deposit transactions reverted. Please retry.",
|
|
12538
13791
|
{ notSubmitted: true, safeToFallback: false }
|
|
12539
13792
|
);
|
|
13793
|
+
verifiedFailures.add(failure);
|
|
13794
|
+
throw failure;
|
|
13795
|
+
}
|
|
12540
13796
|
};
|
|
12541
13797
|
try {
|
|
12542
13798
|
assertCurrent();
|
|
@@ -12552,6 +13808,14 @@ async function executeTokenBatch(input, initial) {
|
|
|
12552
13808
|
tokenBatchKey(input),
|
|
12553
13809
|
JSON.stringify(tokenBatchStoredBody(body))
|
|
12554
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
|
+
}
|
|
12555
13819
|
if (family) {
|
|
12556
13820
|
const winner = await observe();
|
|
12557
13821
|
if (winner) return winner;
|
|
@@ -12575,6 +13839,20 @@ async function executeTokenBatch(input, initial) {
|
|
|
12575
13839
|
result = await post2();
|
|
12576
13840
|
} catch (error) {
|
|
12577
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();
|
|
12578
13856
|
if (!family) {
|
|
12579
13857
|
tokenBatchClear(
|
|
12580
13858
|
tokenBatchKey(input),
|
|
@@ -12597,7 +13875,9 @@ async function executeTokenBatch(input, initial) {
|
|
|
12597
13875
|
await remember(
|
|
12598
13876
|
"deposit",
|
|
12599
13877
|
result.serializedTransaction,
|
|
12600
|
-
result.txHash
|
|
13878
|
+
result.txHash,
|
|
13879
|
+
void 0,
|
|
13880
|
+
serverSent.has(result.txHash)
|
|
12601
13881
|
);
|
|
12602
13882
|
needsPost = true;
|
|
12603
13883
|
continue;
|
|
@@ -12673,6 +13953,10 @@ async function executeTokenBatch(input, initial) {
|
|
|
12673
13953
|
)
|
|
12674
13954
|
);
|
|
12675
13955
|
}
|
|
13956
|
+
} catch (error) {
|
|
13957
|
+
if ((serverSent.size || maybeAccepted) && !(error && typeof error === "object" && verifiedFailures.has(error)))
|
|
13958
|
+
throw pending();
|
|
13959
|
+
throw error;
|
|
12676
13960
|
} finally {
|
|
12677
13961
|
ended = true;
|
|
12678
13962
|
if (timer) clearTimeout(timer);
|
|
@@ -12702,14 +13986,14 @@ async function releaseExpiredTokenBatch(i) {
|
|
|
12702
13986
|
let timer;
|
|
12703
13987
|
let expired = false;
|
|
12704
13988
|
const check = async () => {
|
|
12705
|
-
let
|
|
12706
|
-
if (
|
|
12707
|
-
const current = JSON.parse(
|
|
13989
|
+
let active = read(key2);
|
|
13990
|
+
if (active && i.expectedTxHash) {
|
|
13991
|
+
const current = JSON.parse(active);
|
|
12708
13992
|
if (!current.serializedTransaction || keccak2563(current.serializedTransaction).toLowerCase() !== i.expectedTxHash.toLowerCase())
|
|
12709
|
-
|
|
13993
|
+
active = void 0;
|
|
12710
13994
|
}
|
|
12711
13995
|
const archiveKey = i.expectedTxHash ? `${key2}:expired:${i.expectedTxHash.toLowerCase()}` : void 0;
|
|
12712
|
-
const saved =
|
|
13996
|
+
const saved = active ?? (archiveKey && (expiredRecords.get(archiveKey) ?? (typeof window === "undefined" ? void 0 : window.localStorage.getItem(archiveKey))));
|
|
12713
13997
|
if (!saved) return;
|
|
12714
13998
|
const body = JSON.parse(saved);
|
|
12715
13999
|
if (!body.serializedTransaction || body.chainId !== i.chainId || !isAddressEqual2(body.from, i.owner) || !isAddressEqual2(body.token, i.token) || !body.transfers?.length) return;
|
|
@@ -12733,8 +14017,8 @@ async function releaseExpiredTokenBatch(i) {
|
|
|
12733
14017
|
args: [i.owner, nonce >> 8n],
|
|
12734
14018
|
blockNumber: block.number
|
|
12735
14019
|
});
|
|
12736
|
-
if (expired || typeof bitmap !== "bigint" || bitmap < 0n || bitmap > MAX_UINT256 || (bitmap & 1n << (nonce & 255n)) !== 0n ||
|
|
12737
|
-
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) {
|
|
12738
14022
|
const terminalKey = `${key2}:expired:${txHash.toLowerCase()}`;
|
|
12739
14023
|
expiredRecords.set(terminalKey, saved);
|
|
12740
14024
|
let persisted = typeof window === "undefined";
|
|
@@ -12771,15 +14055,15 @@ function clear(key2, body) {
|
|
|
12771
14055
|
function sponsorTokenBatch(i) {
|
|
12772
14056
|
const key2 = pendingKey(i);
|
|
12773
14057
|
const plan = planOf(i.transfers);
|
|
12774
|
-
const
|
|
12775
|
-
if (
|
|
12776
|
-
if (
|
|
14058
|
+
const active = inflight.get(key2);
|
|
14059
|
+
if (active) {
|
|
14060
|
+
if (active.plan !== plan)
|
|
12777
14061
|
return Promise.reject(
|
|
12778
14062
|
new Error(
|
|
12779
14063
|
"A token deposit is already in progress. Wait for its result before depositing again."
|
|
12780
14064
|
)
|
|
12781
14065
|
);
|
|
12782
|
-
return
|
|
14066
|
+
return active.promise;
|
|
12783
14067
|
}
|
|
12784
14068
|
const promise = execute(i, key2, plan).finally(() => inflight.delete(key2));
|
|
12785
14069
|
inflight.set(key2, { plan, promise });
|
|
@@ -12791,8 +14075,15 @@ async function execute(i, key2, plan) {
|
|
|
12791
14075
|
) || new Set(i.transfers.map((t) => t.to.toLowerCase())).size !== i.transfers.length)
|
|
12792
14076
|
throw new Error("Invalid token deposit shares.");
|
|
12793
14077
|
const send = (body) => executeTokenBatch(i, body);
|
|
14078
|
+
if (!i.resumeTxHash) await dropExpiredHashlessTokenBatch(i, i.pub);
|
|
12794
14079
|
const family = await loadTokenBatchFamily(i, i.resumeTxHash);
|
|
12795
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
|
+
);
|
|
12796
14087
|
if (saved) {
|
|
12797
14088
|
const previous = JSON.parse(saved);
|
|
12798
14089
|
if (previous.chainId !== i.chainId || !isAddressEqual2(previous.from, i.owner) || !isAddressEqual2(previous.token, i.token) || planOf(previous.transfers) !== plan)
|
|
@@ -12902,10 +14193,13 @@ function makeSponsoredTokenCallback(deps) {
|
|
|
12902
14193
|
pub,
|
|
12903
14194
|
wallet,
|
|
12904
14195
|
onApproved: deps.onApproved,
|
|
12905
|
-
|
|
12906
|
-
|
|
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),
|
|
12907
14199
|
assertCurrent: deps.assertCurrent,
|
|
12908
14200
|
resumeTxHash: deps.resumeTxHash,
|
|
14201
|
+
...deps.resumeSaved ? { resumeSaved: true } : {},
|
|
14202
|
+
...deps.onAuthorization ? { onAuthorization: deps.onAuthorization } : {},
|
|
12909
14203
|
operationId: deps.operationId
|
|
12910
14204
|
});
|
|
12911
14205
|
};
|
|
@@ -13212,6 +14506,33 @@ function sponsoredTokensFor(asset) {
|
|
|
13212
14506
|
function shouldFallbackToUserPaid(error, asset, appCallback) {
|
|
13213
14507
|
return (asset === "WETH" || asset === "USDC") && appCallback === void 0 && error instanceof OwneyError && error.code === "SPONSOR_REQUEST_FAILED" && error.details?.safeToFallback === true;
|
|
13214
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;
|
|
13215
14536
|
var OwneySDK = class {
|
|
13216
14537
|
agents = /* @__PURE__ */ new Map();
|
|
13217
14538
|
activeAgents = /* @__PURE__ */ new Set();
|
|
@@ -13240,13 +14561,21 @@ var OwneySDK = class {
|
|
|
13240
14561
|
ledger;
|
|
13241
14562
|
reservationMode;
|
|
13242
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;
|
|
13243
14569
|
cachedSponsoredCallback = null;
|
|
13244
14570
|
cachedWethSponsoredCallback = null;
|
|
13245
14571
|
paymasterServiceUrl;
|
|
13246
14572
|
cachedSponsoredCallsCallbacks = /* @__PURE__ */ new Map();
|
|
13247
14573
|
agentReads = new AgentReads();
|
|
13248
14574
|
depositFamilyReads = /* @__PURE__ */ new Map();
|
|
13249
|
-
|
|
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();
|
|
13250
14579
|
fundingBalanceChecks = /* @__PURE__ */ new WeakMap();
|
|
13251
14580
|
activationChecks = /* @__PURE__ */ new Map();
|
|
13252
14581
|
// Protect the SDK entry point as well as the mini-app hook. This owns only
|
|
@@ -13284,6 +14613,8 @@ var OwneySDK = class {
|
|
|
13284
14613
|
});
|
|
13285
14614
|
this.reservationMode = config.withdrawalReservation === "fail-closed" || config.withdrawalReservation === "off" ? config.withdrawalReservation : "fail-open";
|
|
13286
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;
|
|
13287
14618
|
}
|
|
13288
14619
|
/** Finishes a funding-ledger operation on success and adds its reference to
|
|
13289
14620
|
* the result. The result object is copied, never mutated or replaced.
|
|
@@ -13349,16 +14680,34 @@ var OwneySDK = class {
|
|
|
13349
14680
|
return void 0;
|
|
13350
14681
|
}
|
|
13351
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
|
+
}
|
|
13352
14700
|
closeSwapLedger(op) {
|
|
13353
14701
|
if (op && this.openSwapLedgers.get(op.operationId) === op) this.openSwapLedgers.delete(op.operationId);
|
|
13354
14702
|
}
|
|
13355
|
-
/** Reports one agent's withdrawal evidence as its leg
|
|
13356
|
-
|
|
14703
|
+
/** Reports one agent's withdrawal evidence as its leg (with `errorCode`, when
|
|
14704
|
+
* given). Never throws. */
|
|
14705
|
+
ledgerWithdrawalLeg(op, evidence, errorCode) {
|
|
13357
14706
|
if (!op) return;
|
|
13358
14707
|
try {
|
|
13359
14708
|
const leg = ledgerLegFromEvidence(evidence, "withdraw");
|
|
13360
14709
|
const rail = withdrawalLedgerRail(evidence);
|
|
13361
|
-
if (leg) op.leg(rail ? { ...leg, rail } : leg);
|
|
14710
|
+
if (leg) op.leg({ ...rail ? { ...leg, rail } : leg, ...errorCode ? { errorCode } : {} });
|
|
13362
14711
|
if (leg?.status === "confirmed") this.ledgerProviderSamples(op, evidence, "withdraw");
|
|
13363
14712
|
} catch {
|
|
13364
14713
|
}
|
|
@@ -13593,8 +14942,8 @@ var OwneySDK = class {
|
|
|
13593
14942
|
return createPaidRpcClient(VIEM_CHAIN2[chainId], this.rpcUrls);
|
|
13594
14943
|
}
|
|
13595
14944
|
/** Builds the default USDC batch callback for the connected wallet. */
|
|
13596
|
-
getDefaultSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission, assertSubmissionCurrent, operationId) {
|
|
13597
|
-
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)
|
|
13598
14947
|
return this.cachedSponsoredCallback;
|
|
13599
14948
|
const provider = this.requireConnectedProvider();
|
|
13600
14949
|
const connection = this.requireState();
|
|
@@ -13608,6 +14957,8 @@ var OwneySDK = class {
|
|
|
13608
14957
|
onSubmitted,
|
|
13609
14958
|
onBeforeSubmission,
|
|
13610
14959
|
resumeTxHash,
|
|
14960
|
+
...resumeSaved ? { resumeSaved: true } : {},
|
|
14961
|
+
...onAuthorization ? { onAuthorization } : {},
|
|
13611
14962
|
operationId,
|
|
13612
14963
|
assertCurrent: () => {
|
|
13613
14964
|
assertSubmissionCurrent?.();
|
|
@@ -13628,7 +14979,7 @@ var OwneySDK = class {
|
|
|
13628
14979
|
transport: custom5(provider)
|
|
13629
14980
|
})
|
|
13630
14981
|
});
|
|
13631
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId) this.cachedSponsoredCallback = callback;
|
|
14982
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId && !resumeSaved && !onAuthorization) this.cachedSponsoredCallback = callback;
|
|
13632
14983
|
return callback;
|
|
13633
14984
|
}
|
|
13634
14985
|
/** Builds the wallet-native sponsored calls callback for compatible paymasters. */
|
|
@@ -13657,8 +15008,8 @@ var OwneySDK = class {
|
|
|
13657
15008
|
* Mirrors `getDefaultSponsoredCallback()` but signs a Permit2
|
|
13658
15009
|
* single-use batch authorization instead of an EIP-3009 authorization.
|
|
13659
15010
|
*/
|
|
13660
|
-
getDefaultWethSponsoredCallback(onApproved, onSubmitted, resumeTxHash, onBeforeSubmission, assertSubmissionCurrent, operationId) {
|
|
13661
|
-
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)
|
|
13662
15013
|
return this.cachedWethSponsoredCallback;
|
|
13663
15014
|
const provider = this.requireConnectedProvider();
|
|
13664
15015
|
const connection = this.requireState();
|
|
@@ -13672,6 +15023,8 @@ var OwneySDK = class {
|
|
|
13672
15023
|
onSubmitted,
|
|
13673
15024
|
onBeforeSubmission,
|
|
13674
15025
|
resumeTxHash,
|
|
15026
|
+
...resumeSaved ? { resumeSaved: true } : {},
|
|
15027
|
+
...onAuthorization ? { onAuthorization } : {},
|
|
13675
15028
|
operationId,
|
|
13676
15029
|
assertCurrent: () => {
|
|
13677
15030
|
assertSubmissionCurrent?.();
|
|
@@ -13692,7 +15045,7 @@ var OwneySDK = class {
|
|
|
13692
15045
|
transport: custom5(provider)
|
|
13693
15046
|
})
|
|
13694
15047
|
});
|
|
13695
|
-
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId) this.cachedWethSponsoredCallback = callback;
|
|
15048
|
+
if (!onApproved && !onSubmitted && !resumeTxHash && !onBeforeSubmission && !assertSubmissionCurrent && !operationId && !resumeSaved && !onAuthorization) this.cachedWethSponsoredCallback = callback;
|
|
13696
15049
|
return callback;
|
|
13697
15050
|
}
|
|
13698
15051
|
getAgent(agentId) {
|
|
@@ -13706,12 +15059,12 @@ var OwneySDK = class {
|
|
|
13706
15059
|
return [...this.getActiveAgents().keys()].sort();
|
|
13707
15060
|
}
|
|
13708
15061
|
getActiveAgents() {
|
|
13709
|
-
const
|
|
15062
|
+
const active = /* @__PURE__ */ new Map();
|
|
13710
15063
|
for (const id of this.activeAgents) {
|
|
13711
15064
|
const agent = this.agents.get(id);
|
|
13712
|
-
if (agent)
|
|
15065
|
+
if (agent) active.set(id, agent);
|
|
13713
15066
|
}
|
|
13714
|
-
return
|
|
15067
|
+
return active;
|
|
13715
15068
|
}
|
|
13716
15069
|
/**
|
|
13717
15070
|
* Read the org's agent policy once and reuse it. fetchOrgAgentConfig never
|
|
@@ -13810,7 +15163,13 @@ var OwneySDK = class {
|
|
|
13810
15163
|
this.routingApiBaseUrl
|
|
13811
15164
|
) : this.rpcUrls,
|
|
13812
15165
|
this.referralSource,
|
|
13813
|
-
{
|
|
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
|
+
}
|
|
13814
15173
|
);
|
|
13815
15174
|
}
|
|
13816
15175
|
if (agentId === "yieldseeker") {
|
|
@@ -14032,10 +15391,21 @@ var OwneySDK = class {
|
|
|
14032
15391
|
} catch {
|
|
14033
15392
|
}
|
|
14034
15393
|
};
|
|
14035
|
-
|
|
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) => {
|
|
14036
15400
|
for (const destination of destinations) {
|
|
14037
15401
|
const evidence = prepared.get(destination.to.toLowerCase());
|
|
14038
|
-
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
|
+
});
|
|
14039
15409
|
}
|
|
14040
15410
|
};
|
|
14041
15411
|
const onCallsProgress = (callsId, destinations, transfer) => {
|
|
@@ -14044,6 +15414,7 @@ var OwneySDK = class {
|
|
|
14044
15414
|
if (evidence) notifyFunding(onProgress, { ...evidence, callsId, transfer });
|
|
14045
15415
|
}
|
|
14046
15416
|
};
|
|
15417
|
+
const serverTaskContext = (agent) => this.zyfaiServerTasks !== void 0 && agent.supportsServerTasks === true && ledgerOp ? { operationId: ledgerOp.operationId } : void 0;
|
|
14047
15418
|
const ledgerDone = (result) => this.ledgerSucceeded(ledgerOp, result);
|
|
14048
15419
|
const ledgerFailed = (error) => this.ledgerFailed(ledgerOp, error);
|
|
14049
15420
|
if (BigInt(amount) === 0n) {
|
|
@@ -14079,7 +15450,7 @@ var OwneySDK = class {
|
|
|
14079
15450
|
});
|
|
14080
15451
|
await checkpoint2(evidence);
|
|
14081
15452
|
} : void 0;
|
|
14082
|
-
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;
|
|
14083
15454
|
const checkpointCalls = checkpointDestinations ? (id, destinations) => checkpointDestinations(destinations, { callsId: id }) : void 0;
|
|
14084
15455
|
const checkpointPaid = checkpoint2 ? (evidence) => checkpoint2([evidence]) : void 0;
|
|
14085
15456
|
const customCallback = depositCallback && checkpointDestinations ? makeVerificationAwareDepositCallback(async (to, cid, value, verification) => {
|
|
@@ -14087,9 +15458,10 @@ var OwneySDK = class {
|
|
|
14087
15458
|
await checkpointDestinations([{ to, amount: value }], {});
|
|
14088
15459
|
return depositCallback(to, cid, value);
|
|
14089
15460
|
}) : depositCallback;
|
|
14090
|
-
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))
|
|
14091
15462
|
throw new OwneyError("DEPOSIT_IN_PROGRESS", "This deposit cannot resume the saved relayer transaction.");
|
|
14092
|
-
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;
|
|
14093
15465
|
try {
|
|
14094
15466
|
const ledgerToken = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
14095
15467
|
ledgerOp = this.startLedger({
|
|
@@ -14104,16 +15476,19 @@ var OwneySDK = class {
|
|
|
14104
15476
|
} catch {
|
|
14105
15477
|
}
|
|
14106
15478
|
try {
|
|
14107
|
-
if (options.resumeTxHash) {
|
|
15479
|
+
if (options.resumeTxHash || options.resumeSavedBatch) {
|
|
14108
15480
|
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === asset);
|
|
14109
|
-
const
|
|
15481
|
+
const scope = token && {
|
|
14110
15482
|
apiKey: this.apiKey,
|
|
14111
15483
|
baseUrl: this.routingApiBaseUrl,
|
|
14112
15484
|
owner: state.walletAddress,
|
|
14113
15485
|
chainId,
|
|
14114
15486
|
token: token.address
|
|
14115
|
-
}
|
|
14116
|
-
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 };
|
|
14117
15492
|
const invalid2 = () => new OwneyError("DEPOSIT_IN_PROGRESS", "The saved deposit does not match this request. Check its status before depositing again.");
|
|
14118
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();
|
|
14119
15494
|
const candidates = agentId ? [this.getAgent(agentId)] : this.getEligibleAgents(chainId, asset);
|
|
@@ -14133,12 +15508,10 @@ var OwneySDK = class {
|
|
|
14133
15508
|
return {
|
|
14134
15509
|
id: agent.id,
|
|
14135
15510
|
amount: transfer.amount,
|
|
14136
|
-
run: (callback) => withFailureReporting(
|
|
14137
|
-
|
|
14138
|
-
agent.
|
|
14139
|
-
|
|
14140
|
-
this.routingApiBaseUrl
|
|
14141
|
-
)
|
|
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)
|
|
14142
15515
|
};
|
|
14143
15516
|
});
|
|
14144
15517
|
const batchTransfer2 = getDepositBatchTransfer(effectiveCallback);
|
|
@@ -14177,7 +15550,8 @@ var OwneySDK = class {
|
|
|
14177
15550
|
onProgress,
|
|
14178
15551
|
() => paidFallbackAgents.add(agent.id),
|
|
14179
15552
|
!!options.resumeTxHash,
|
|
14180
|
-
checkpointPaid
|
|
15553
|
+
checkpointPaid,
|
|
15554
|
+
serverTaskContext(agent)
|
|
14181
15555
|
));
|
|
14182
15556
|
}
|
|
14183
15557
|
const totalAmount = BigInt(amount);
|
|
@@ -14239,7 +15613,10 @@ var OwneySDK = class {
|
|
|
14239
15613
|
run: (callback) => withFailureReporting(
|
|
14240
15614
|
this.apiKey,
|
|
14241
15615
|
agent.id,
|
|
14242
|
-
() =>
|
|
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
|
+
},
|
|
14243
15620
|
this.routingApiBaseUrl
|
|
14244
15621
|
)
|
|
14245
15622
|
})),
|
|
@@ -14280,7 +15657,8 @@ var OwneySDK = class {
|
|
|
14280
15657
|
onProgress,
|
|
14281
15658
|
() => paidFallbackAgents.add(agent.id),
|
|
14282
15659
|
!!options.resumeTxHash,
|
|
14283
|
-
checkpointPaid
|
|
15660
|
+
checkpointPaid,
|
|
15661
|
+
serverTaskContext(agent)
|
|
14284
15662
|
);
|
|
14285
15663
|
} catch (error) {
|
|
14286
15664
|
if (Object.keys(agentResults).length === 0) throw error;
|
|
@@ -14300,7 +15678,22 @@ var OwneySDK = class {
|
|
|
14300
15678
|
}
|
|
14301
15679
|
return ledgerDone({ agentResults });
|
|
14302
15680
|
} catch (error) {
|
|
14303
|
-
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)) {
|
|
14304
15697
|
error = error instanceof OwneyError && error.code === "DEPOSIT_PARTIAL_FAILURE" ? new OwneyError(
|
|
14305
15698
|
error.code,
|
|
14306
15699
|
"Some deposits completed before another agent could be submitted.",
|
|
@@ -14318,6 +15711,19 @@ var OwneySDK = class {
|
|
|
14318
15711
|
const rejected = error instanceof OwneyError && error.details?.notSubmitted === true;
|
|
14319
15712
|
if (!rejected && paidFallbackAgents.has(id) || evidence?.transfer === "confirmed" || evidence?.transfer === "failed") continue;
|
|
14320
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
|
+
}
|
|
14321
15727
|
notifyFunding(onProgress, { ...evidence ?? {
|
|
14322
15728
|
agentId: id,
|
|
14323
15729
|
chainId,
|
|
@@ -14364,11 +15770,11 @@ var OwneySDK = class {
|
|
|
14364
15770
|
* propagates unchanged. Scoped per agent invocation so one agent's failure
|
|
14365
15771
|
* doesn't force a retry of sibling agents in a multi-agent split.
|
|
14366
15772
|
*/
|
|
14367
|
-
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) {
|
|
14368
15774
|
const attempt = (callback) => withFailureReporting(
|
|
14369
15775
|
this.apiKey,
|
|
14370
15776
|
agent.id,
|
|
14371
|
-
() => 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),
|
|
14372
15778
|
this.routingApiBaseUrl
|
|
14373
15779
|
);
|
|
14374
15780
|
let approvalAttempted = false;
|
|
@@ -14665,11 +16071,36 @@ var OwneySDK = class {
|
|
|
14665
16071
|
const key2 = `${state.walletAddress.toLowerCase()}:${chainId}:${evidence.agentId}:${evidence.token}:${evidence.txHash}`;
|
|
14666
16072
|
if (options.refreshBalance !== false && (verified.transfer === "confirmed" || verified.transfer === "failed" && verified.confirmedAmount === "0") && !this.recoveredFundingReads.has(key2)) {
|
|
14667
16073
|
agent.invalidateFundingRead?.(state, chainId, evidence.token, options.balanceRefreshUntil, evidence.txHash);
|
|
14668
|
-
|
|
14669
|
-
|
|
16074
|
+
const until = options.balanceRefreshUntil;
|
|
16075
|
+
this.noteFundingRefresh(key2, until === void 0 || until > Date.now());
|
|
14670
16076
|
}
|
|
14671
16077
|
return verified;
|
|
14672
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
|
+
}
|
|
14673
16104
|
/**
|
|
14674
16105
|
* Lists on-chain payout candidates for a withdrawal the provider accepted
|
|
14675
16106
|
* without a hash: ERC-20 `Transfer` logs on the evidence's token that reach
|
|
@@ -14791,22 +16222,25 @@ var OwneySDK = class {
|
|
|
14791
16222
|
try {
|
|
14792
16223
|
if (this.paymasterServiceUrl || !evidences.length || !/^\d+$/.test(amount)) return false;
|
|
14793
16224
|
const first = evidences[0];
|
|
14794
|
-
|
|
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;
|
|
14795
16227
|
const { state, chainId, assertCurrent } = this.captureReadScope(first.chainId);
|
|
14796
16228
|
const token = SupportedAssets.find((value) => value.chainId === chainId && value.symbol === first.token);
|
|
14797
16229
|
if (!token) return false;
|
|
14798
|
-
const
|
|
16230
|
+
const scope = {
|
|
14799
16231
|
apiKey: this.apiKey,
|
|
14800
16232
|
baseUrl: this.routingApiBaseUrl,
|
|
14801
16233
|
owner: state.walletAddress,
|
|
14802
16234
|
chainId,
|
|
14803
16235
|
token: token.address
|
|
14804
|
-
}
|
|
14805
|
-
const
|
|
14806
|
-
|
|
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;
|
|
14807
16241
|
const destinations = /* @__PURE__ */ new Set();
|
|
14808
16242
|
for (const e of evidences) {
|
|
14809
|
-
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;
|
|
14810
16244
|
const agent = this.getAgent(e.agentId);
|
|
14811
16245
|
const wallet = agent.getFundingWallet ? await agent.getFundingWallet(state, chainId, e.token) : (await agent.getUserProfile(state, chainId)).smartWallet;
|
|
14812
16246
|
assertCurrent();
|
|
@@ -14819,6 +16253,62 @@ var OwneySDK = class {
|
|
|
14819
16253
|
return false;
|
|
14820
16254
|
}
|
|
14821
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
|
+
}
|
|
14822
16312
|
/** Validate signed journal membership, never just a caller-supplied family ID. */
|
|
14823
16313
|
async validateDepositReplacement(previous, next) {
|
|
14824
16314
|
try {
|
|
@@ -14919,6 +16409,14 @@ var OwneySDK = class {
|
|
|
14919
16409
|
if (evidence.agentId !== "zyfai" || !id || !state || state.walletAddress.toLowerCase() !== evidence.walletAddress.toLowerCase()) return null;
|
|
14920
16410
|
return this.getAgent("zyfai").getWithdrawalLifecycle(state, id);
|
|
14921
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
|
+
}
|
|
14922
16420
|
// --- Fund operations ---
|
|
14923
16421
|
/** On-chain version and ownership status for this owner's Yieldseeker agent. */
|
|
14924
16422
|
async getYieldseekerWalletStatus(asset) {
|
|
@@ -15501,6 +16999,17 @@ var OwneySDK = class {
|
|
|
15501
16999
|
onStage: (stage) => options.onStage(order.orderHash, stage)
|
|
15502
17000
|
} : {}
|
|
15503
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
|
+
});
|
|
15504
17013
|
clearOrder(order.orderHash);
|
|
15505
17014
|
updateWithdrawSwap(order.orderHash, { status: "landed" });
|
|
15506
17015
|
return { orderHash: order.orderHash, status: "completed" };
|
|
@@ -15671,14 +17180,16 @@ var OwneySDK = class {
|
|
|
15671
17180
|
options.to.symbol
|
|
15672
17181
|
);
|
|
15673
17182
|
}
|
|
15674
|
-
|
|
17183
|
+
let ledgerInput;
|
|
17184
|
+
const ledgerOp = this.openSwapLedger(() => ledgerInput = {
|
|
15675
17185
|
operationId: options.operationId,
|
|
15676
17186
|
kind: "swap_deposit",
|
|
15677
17187
|
owner: state.walletAddress,
|
|
15678
17188
|
chainId: options.to.chainId,
|
|
15679
17189
|
token: SupportedAssets.find((value) => value.chainId === options.to.chainId && value.symbol === options.to.symbol)?.address ?? "",
|
|
15680
17190
|
asset: options.to.symbol.toLowerCase()
|
|
15681
|
-
})
|
|
17191
|
+
});
|
|
17192
|
+
const secretHandoff = this.secretHandoffFor(ledgerOp, ledgerInput);
|
|
15682
17193
|
try {
|
|
15683
17194
|
const api = this.swapApi();
|
|
15684
17195
|
const quoted = await api.quote({
|
|
@@ -15706,6 +17217,17 @@ var OwneySDK = class {
|
|
|
15706
17217
|
...options.assertCurrent ? { assertCurrent: options.assertCurrent } : {},
|
|
15707
17218
|
...options.slippage === void 0 ? {} : { slippage: options.slippage },
|
|
15708
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 } : {},
|
|
15709
17231
|
...ledgerOp ? { onOrderSubmitted: (orderHash) => {
|
|
15710
17232
|
swapIds.orderHash = orderHash;
|
|
15711
17233
|
this.ledgerSwap(ledgerOp, quote, { status: "submitted", orderHash });
|
|
@@ -15725,6 +17247,7 @@ var OwneySDK = class {
|
|
|
15725
17247
|
receivedDstAmountRaw: swap.received
|
|
15726
17248
|
});
|
|
15727
17249
|
options.onSwapProgress?.("depositing");
|
|
17250
|
+
const backgroundDepositId = swap.orderHash ? `${quote.dst.chainId}-${options.to.symbol}-${swap.orderHash}` : void 0;
|
|
15728
17251
|
try {
|
|
15729
17252
|
options.assertCurrent?.();
|
|
15730
17253
|
await this.ensureSwapChain(quote.dst.chainId);
|
|
@@ -15754,10 +17277,13 @@ var OwneySDK = class {
|
|
|
15754
17277
|
...options.assertCurrent ? { assertSubmissionCurrent: options.assertCurrent } : {},
|
|
15755
17278
|
...ledgerOp ? { operationId: ledgerOp.operationId } : {}
|
|
15756
17279
|
});
|
|
17280
|
+
if (backgroundDepositId) clearPendingDeposit(backgroundDepositId);
|
|
15757
17281
|
return this.ledgerSucceeded(ledgerOp, { swap, deposit, deposited: amount });
|
|
15758
17282
|
} catch (error) {
|
|
15759
17283
|
const pending = {
|
|
15760
|
-
|
|
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()}`,
|
|
15761
17287
|
received: swap.received,
|
|
15762
17288
|
symbol: options.to.symbol,
|
|
15763
17289
|
chainId: quote.dst.chainId,
|
|
@@ -15913,7 +17439,7 @@ var OwneySDK = class {
|
|
|
15913
17439
|
const state = this.requireState();
|
|
15914
17440
|
const activeChainId = this.requireChainId();
|
|
15915
17441
|
const revision = this.connectionRevision;
|
|
15916
|
-
let
|
|
17442
|
+
let active = true;
|
|
15917
17443
|
let record2;
|
|
15918
17444
|
let ledgerOp;
|
|
15919
17445
|
let hadDurableRecord = false;
|
|
@@ -15921,7 +17447,7 @@ var OwneySDK = class {
|
|
|
15921
17447
|
let confirmationDeadline = Infinity;
|
|
15922
17448
|
const assertCurrent = () => {
|
|
15923
17449
|
options.assertCurrent?.();
|
|
15924
|
-
if (!
|
|
17450
|
+
if (!active) throw new OwneyError("WITHDRAW_SWAP_INCOMPLETE", "This withdrawal attempt is no longer active.", { swapSubmitted: false });
|
|
15925
17451
|
if (this.state !== state || this.connectionRevision !== revision || state.chainId !== activeChainId)
|
|
15926
17452
|
throw new OwneyError("NOT_CONNECTED", "Wallet changed during withdrawal and swap.");
|
|
15927
17453
|
};
|
|
@@ -15966,9 +17492,10 @@ var OwneySDK = class {
|
|
|
15966
17492
|
const saved = options.recoveryId ? listWithdrawSwaps(Date.now(), true).find((row) => row.orderHash === options.recoveryId) : void 0;
|
|
15967
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))
|
|
15968
17494
|
throw new OwneyError("WITHDRAW_SWAP_INCOMPLETE", "This withdrawal is not ready to continue.");
|
|
17495
|
+
let ledgerInput;
|
|
15969
17496
|
ledgerOp = this.openSwapLedger(() => {
|
|
15970
17497
|
const requestedRaw = saved ? saved.recovery.requested : options.amount === void 0 ? void 0 : parseUnits4(options.amount, asset.decimals).toString();
|
|
15971
|
-
return {
|
|
17498
|
+
return ledgerInput = {
|
|
15972
17499
|
operationId: saved?.operationId ?? options.operationId,
|
|
15973
17500
|
kind: "withdraw_swap",
|
|
15974
17501
|
owner: state.walletAddress,
|
|
@@ -15979,6 +17506,7 @@ var OwneySDK = class {
|
|
|
15979
17506
|
...requestedRaw === void 0 ? {} : { requestedAmountRaw: requestedRaw }
|
|
15980
17507
|
};
|
|
15981
17508
|
});
|
|
17509
|
+
const secretHandoff = this.secretHandoffFor(ledgerOp, ledgerInput);
|
|
15982
17510
|
const prepareTimeout = () => new OwneyError(
|
|
15983
17511
|
"WITHDRAW_SWAP_INCOMPLETE",
|
|
15984
17512
|
"Could not read the source wallet. No new withdrawal was submitted.",
|
|
@@ -16038,7 +17566,7 @@ var OwneySDK = class {
|
|
|
16038
17566
|
evidence.set(next.agentId, next);
|
|
16039
17567
|
if (record2?.recovery) {
|
|
16040
17568
|
record2.recovery.evidence[next.agentId] = next;
|
|
16041
|
-
if (
|
|
17569
|
+
if (active) saveWithdrawSwap(record2);
|
|
16042
17570
|
else {
|
|
16043
17571
|
const latest = listWithdrawSwaps(Date.now(), true).find((row) => row.recovery?.attemptId === record2.recovery.attemptId);
|
|
16044
17572
|
if (latest?.recovery) updateWithdrawSwap(latest.orderHash, { recovery: {
|
|
@@ -16154,6 +17682,7 @@ var OwneySDK = class {
|
|
|
16154
17682
|
quote,
|
|
16155
17683
|
walletAddress: state.walletAddress,
|
|
16156
17684
|
direction: "withdraw",
|
|
17685
|
+
...secretHandoff ? { secretHandoff } : {},
|
|
16157
17686
|
...options.slippage === void 0 ? {} : { slippage: options.slippage },
|
|
16158
17687
|
...options.onSwapProgress ? { onStage: options.onSwapProgress } : {},
|
|
16159
17688
|
onOrderPrepared: (orderHash, details) => {
|
|
@@ -16242,7 +17771,7 @@ var OwneySDK = class {
|
|
|
16242
17771
|
this.ledgerFailed(ledgerOp, error);
|
|
16243
17772
|
throw error;
|
|
16244
17773
|
} finally {
|
|
16245
|
-
|
|
17774
|
+
active = false;
|
|
16246
17775
|
releaseSubmission();
|
|
16247
17776
|
this.closeSwapLedger(ledgerOp);
|
|
16248
17777
|
}
|
|
@@ -16290,11 +17819,13 @@ var OwneySDK = class {
|
|
|
16290
17819
|
});
|
|
16291
17820
|
} catch {
|
|
16292
17821
|
}
|
|
16293
|
-
const ledgerLeg = (evidence) => this.ledgerWithdrawalLeg(ledgerOp, evidence);
|
|
17822
|
+
const ledgerLeg = (evidence, errorCode) => this.ledgerWithdrawalLeg(ledgerOp, evidence, errorCode);
|
|
16294
17823
|
let hold;
|
|
16295
17824
|
let reservationRejected = false;
|
|
16296
17825
|
let innerStarted = false;
|
|
16297
17826
|
const reservationLegs = /* @__PURE__ */ new Map();
|
|
17827
|
+
const plannedLegs = /* @__PURE__ */ new Map();
|
|
17828
|
+
const serverLegs = /* @__PURE__ */ new Map();
|
|
16298
17829
|
const reservation = {
|
|
16299
17830
|
dispatching: (id) => hold?.dispatch(id),
|
|
16300
17831
|
settled: (id, leg) => {
|
|
@@ -16304,11 +17835,22 @@ var OwneySDK = class {
|
|
|
16304
17835
|
const settleHold = () => {
|
|
16305
17836
|
if (!hold) return;
|
|
16306
17837
|
hold.stopHeartbeat();
|
|
16307
|
-
|
|
17838
|
+
const serverOwned = new Set(ids.filter((id) => !attempted.has(id) && serverMayOwn(serverLegs.get(id))));
|
|
17839
|
+
this.settleWithdrawalReservation(hold, ids, attempted, reservationLegs, serverOwned);
|
|
16308
17840
|
};
|
|
16309
17841
|
const runAgents = () => {
|
|
16310
17842
|
innerStarted = true;
|
|
16311
|
-
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
|
+
);
|
|
16312
17854
|
void inner.then(settleHold, settleHold);
|
|
16313
17855
|
return inner;
|
|
16314
17856
|
};
|
|
@@ -16332,7 +17874,9 @@ var OwneySDK = class {
|
|
|
16332
17874
|
} catch (error) {
|
|
16333
17875
|
if (!reservationRejected) {
|
|
16334
17876
|
for (const id of ids) if (!attempted.has(id)) {
|
|
16335
|
-
|
|
17877
|
+
if (serverMayOwn(serverLegs.get(id))) continue;
|
|
17878
|
+
const planned = plannedLegs.get(id);
|
|
17879
|
+
notifyFunding(options.onProgress, planned ? { ...planned, transfer: "failed", delivery: "failed" } : {
|
|
16336
17880
|
agentId: id,
|
|
16337
17881
|
chainId,
|
|
16338
17882
|
token: options.asset,
|
|
@@ -16454,12 +17998,13 @@ var OwneySDK = class {
|
|
|
16454
17998
|
* - a throw without submission evidence: `not_submitted`;
|
|
16455
17999
|
* - Zyfai "withdrawal already in progress": kept, dispatch `provider_in_progress`.
|
|
16456
18000
|
*/
|
|
16457
|
-
settleWithdrawalReservation(hold, ids, attempted, legs) {
|
|
18001
|
+
settleWithdrawalReservation(hold, ids, attempted, legs, keep) {
|
|
16458
18002
|
try {
|
|
16459
18003
|
if ([...attempted].some((id) => !legs.has(id))) return;
|
|
16460
18004
|
const releases = /* @__PURE__ */ new Map();
|
|
16461
18005
|
for (const id of new Set(ids)) {
|
|
16462
18006
|
if (!RESERVATION_AGENTS.includes(id)) continue;
|
|
18007
|
+
if (keep?.has(id)) continue;
|
|
16463
18008
|
const leg = legs.get(id);
|
|
16464
18009
|
const outcome = attempted.has(id) && leg ? reservationLegOutcome(leg) : "not_submitted";
|
|
16465
18010
|
if (outcome === "keep") continue;
|
|
@@ -16490,6 +18035,31 @@ var OwneySDK = class {
|
|
|
16490
18035
|
} catch {
|
|
16491
18036
|
}
|
|
16492
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
|
+
}
|
|
16493
18063
|
/**
|
|
16494
18064
|
* Reports a withdrawal leg that was proven after its call ended (a hashless
|
|
16495
18065
|
* Zyfai payout found on chain or in history) to the funding ledger, then
|
|
@@ -16539,7 +18109,36 @@ var OwneySDK = class {
|
|
|
16539
18109
|
} catch {
|
|
16540
18110
|
}
|
|
16541
18111
|
}
|
|
16542
|
-
|
|
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()) {
|
|
16543
18142
|
const { asset, amount, agentId, onAgentResult } = options;
|
|
16544
18143
|
const notifyAgentResult = (id, result) => {
|
|
16545
18144
|
try {
|
|
@@ -16585,7 +18184,22 @@ var OwneySDK = class {
|
|
|
16585
18184
|
{ asset, amount, agentErrors: agentErrors2, agentFailures, pendingSubmissions: pending }
|
|
16586
18185
|
);
|
|
16587
18186
|
};
|
|
16588
|
-
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);
|
|
16589
18203
|
if (this.state !== state || this.state.chainId !== chainId)
|
|
16590
18204
|
throw new OwneyError("NOT_CONNECTED", "Wallet connection changed before withdrawal submission.");
|
|
16591
18205
|
attempted.add(agent.id);
|
|
@@ -16607,6 +18221,7 @@ var OwneySDK = class {
|
|
|
16607
18221
|
submissionEvidence.set(agent.id, evidence);
|
|
16608
18222
|
notifyFunding(options.onProgress, evidence);
|
|
16609
18223
|
ledgerLeg?.(evidence);
|
|
18224
|
+
if (onAccepted && evidence.ownerAuthorization === "accepted" && evidence.transfer !== "failed" && (evidence.userOpHash || evidence.callsId || evidence.txHash)) onAccepted();
|
|
16610
18225
|
};
|
|
16611
18226
|
progress(evidence);
|
|
16612
18227
|
adapterReported = false;
|
|
@@ -16621,14 +18236,26 @@ var OwneySDK = class {
|
|
|
16621
18236
|
reservation?.dispatching(agent.id);
|
|
16622
18237
|
} catch {
|
|
16623
18238
|
}
|
|
16624
|
-
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(
|
|
16625
18252
|
state,
|
|
16626
18253
|
chainId,
|
|
16627
18254
|
token,
|
|
16628
18255
|
requested2,
|
|
16629
18256
|
progress,
|
|
16630
18257
|
options.gasMode ?? "auto",
|
|
16631
|
-
|
|
18258
|
+
onBeforeSubmission,
|
|
16632
18259
|
options.assertCurrent
|
|
16633
18260
|
) : await (options.gasMode === void 0 ? agent.withdraw(state, chainId, token, requested2, progress) : agent.withdraw(state, chainId, token, requested2, progress, options.gasMode));
|
|
16634
18261
|
settled({
|
|
@@ -16639,6 +18266,10 @@ var OwneySDK = class {
|
|
|
16639
18266
|
} catch (error) {
|
|
16640
18267
|
if (!adapterReported && !evidence.txHash && !evidence.userOpHash && !evidence.callsId && (this.isUserRejectedWithdrawal(error) || /insufficient (balance|funds)/i.test(error instanceof Error ? error.message : "")))
|
|
16641
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");
|
|
16642
18273
|
settled({ settled: "threw", evidence });
|
|
16643
18274
|
throw error;
|
|
16644
18275
|
}
|
|
@@ -16664,40 +18295,151 @@ var OwneySDK = class {
|
|
|
16664
18295
|
notifyAgentResult(agent.id, result);
|
|
16665
18296
|
return result;
|
|
16666
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
|
+
};
|
|
16667
18379
|
const eligibleAgents = this.getEligibleAgents(chainId, asset).filter((agent) => !options.excludeAgentIds?.includes(agent.id));
|
|
16668
18380
|
const withdrawalAgents = this.orderAgentsForWithdrawal(eligibleAgents);
|
|
16669
18381
|
if (!amount) {
|
|
16670
18382
|
const results2 = {};
|
|
16671
18383
|
const agentErrors2 = {};
|
|
16672
18384
|
const incompleteWithdrawals = {};
|
|
16673
|
-
|
|
16674
|
-
|
|
16675
|
-
results2[agent.id] = await submit(agent);
|
|
16676
|
-
notifyAgentResult(agent.id, results2[agent.id]);
|
|
16677
|
-
} catch (err) {
|
|
18385
|
+
const failed2 = (agent, err, handedOver) => {
|
|
18386
|
+
if (!handedOver) {
|
|
16678
18387
|
if (err instanceof OwneyError && err.code === "SPONSOR_REQUEST_FAILED" && err.details?.safeToFallback === true) throw err;
|
|
16679
18388
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
16680
|
-
|
|
16681
|
-
|
|
16682
|
-
|
|
16683
|
-
|
|
16684
|
-
|
|
16685
|
-
|
|
16686
|
-
|
|
16687
|
-
|
|
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);
|
|
16688
18435
|
}
|
|
16689
|
-
console.error(`withdraw failed for agent "${agent.id}":`, err);
|
|
16690
|
-
agentErrors2[agent.id] = err instanceof Error ? err.message : String(err);
|
|
16691
|
-
recordAgentFailure(agent.id, err);
|
|
16692
|
-
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
16693
|
-
void reportAgentFailure(
|
|
16694
|
-
this.apiKey,
|
|
16695
|
-
agent.id,
|
|
16696
|
-
code,
|
|
16697
|
-
this.routingApiBaseUrl
|
|
16698
|
-
);
|
|
16699
18436
|
}
|
|
18437
|
+
} catch (error) {
|
|
18438
|
+
if (serverLegs.size) await awaitServerHandoffs();
|
|
18439
|
+
await settleHandedOver2();
|
|
18440
|
+
throw error;
|
|
16700
18441
|
}
|
|
18442
|
+
await settleHandedOver2();
|
|
16701
18443
|
if (Object.keys(results2).length === 0) {
|
|
16702
18444
|
throwUnconfirmed(agentErrors2);
|
|
16703
18445
|
if (Object.keys(agentErrors2).length === 0)
|
|
@@ -16842,32 +18584,65 @@ var OwneySDK = class {
|
|
|
16842
18584
|
const agentErrors = {
|
|
16843
18585
|
...aggregated.agentErrors ?? {}
|
|
16844
18586
|
};
|
|
16845
|
-
|
|
16846
|
-
|
|
16847
|
-
assertPlan();
|
|
16848
|
-
if (p.planned <= 0n) continue;
|
|
16849
|
-
try {
|
|
16850
|
-
results[p.agent.id] = await submit(p.agent, p.planned.toString());
|
|
16851
|
-
notifyAgentResult(p.agent.id, results[p.agent.id]);
|
|
16852
|
-
} catch (err) {
|
|
18587
|
+
const failed = (p, index, err, handedOver) => {
|
|
18588
|
+
if (!handedOver) {
|
|
16853
18589
|
if (err instanceof OwneyError && err.code === "SPONSOR_REQUEST_FAILED" && err.details?.safeToFallback === true) throw err;
|
|
16854
18590
|
if (this.isUserRejectedWithdrawal(err)) throw err;
|
|
16855
|
-
console.error(`withdraw failed for agent "${p.agent.id}":`, err);
|
|
16856
|
-
agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
|
|
16857
|
-
recordAgentFailure(p.agent.id, err);
|
|
16858
|
-
const code = err instanceof OwneyError ? err.code : "SDK_AGENT_FAILURE";
|
|
16859
|
-
void reportAgentFailure(
|
|
16860
|
-
this.apiKey,
|
|
16861
|
-
p.agent.id,
|
|
16862
|
-
code,
|
|
16863
|
-
this.routingApiBaseUrl
|
|
16864
|
-
);
|
|
16865
|
-
const failedAmount = p.planned;
|
|
16866
|
-
p.planned = 0n;
|
|
16867
|
-
if (submissionEvidence.get(p.agent.id)?.transfer === "failed")
|
|
16868
|
-
redistributeShare(orderedPlans, i, failedAmount);
|
|
16869
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;
|
|
16870
18644
|
}
|
|
18645
|
+
await settleHandedOver();
|
|
16871
18646
|
if (Object.keys(results).length === 0) {
|
|
16872
18647
|
throwUnconfirmed(agentErrors);
|
|
16873
18648
|
throw new OwneyError(
|
|
@@ -17803,12 +19578,20 @@ function createOwneySIWX(config) {
|
|
|
17803
19578
|
export {
|
|
17804
19579
|
AgentChainIncompatibleError,
|
|
17805
19580
|
AgentNotFoundError,
|
|
19581
|
+
FUNDING_DEPOSIT_RECOVERY_REPORT,
|
|
19582
|
+
FUSION_SECRET_SERVER_PUBLISH,
|
|
17806
19583
|
InvalidHistoryCursorError,
|
|
17807
19584
|
NotConnectedError,
|
|
17808
19585
|
OwneyError,
|
|
17809
19586
|
OwneySDK,
|
|
17810
19587
|
SDK_VERSION,
|
|
19588
|
+
SPONSORED_DEPOSIT_SERVER_BROADCAST,
|
|
19589
|
+
YIELDSEEKER_WITHDRAW_SERVER_SUBMIT,
|
|
17811
19590
|
YieldseekerAgent,
|
|
19591
|
+
ZYFAI_REGISTRATION_NOT_SENT_CODES,
|
|
19592
|
+
ZYFAI_SERVER_TASKS,
|
|
19593
|
+
ZYFAI_WITHDRAWAL_NOT_SENT_CODES,
|
|
19594
|
+
ZYFAI_WITHDRAW_DISPATCH_CHECKPOINT,
|
|
17812
19595
|
clearWithdrawSwap,
|
|
17813
19596
|
createOwneySIWX,
|
|
17814
19597
|
listPendingDeposits,
|