@bananapus/nana-sdk-core 2.24.2 → 2.24.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/review/relayr.d.ts +19 -7
- package/dist/cjs/review/relayr.d.ts.map +1 -1
- package/dist/cjs/review/relayr.js +119 -17
- package/dist/cjs/review/relayr.js.map +1 -1
- package/dist/cjs/review/safeRelayr.d.ts +20 -2
- package/dist/cjs/review/safeRelayr.d.ts.map +1 -1
- package/dist/cjs/review/safeRelayr.js +325 -132
- package/dist/cjs/review/safeRelayr.js.map +1 -1
- package/dist/esm/review/relayr.d.ts +19 -7
- package/dist/esm/review/relayr.d.ts.map +1 -1
- package/dist/esm/review/relayr.js +116 -16
- package/dist/esm/review/relayr.js.map +1 -1
- package/dist/esm/review/safeRelayr.d.ts +20 -2
- package/dist/esm/review/safeRelayr.d.ts.map +1 -1
- package/dist/esm/review/safeRelayr.js +326 -133
- package/dist/esm/review/safeRelayr.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safeRelayr.d.ts","sourceRoot":"","sources":["../../../src/review/safeRelayr.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,OAAO,EACZ,KAAK,GAAG,EACR,KAAK,YAAY,EAClB,MAAM,MAAM,CAAC;AAEd,OAAO,EAKL,KAAK,sBAAsB,EAC5B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,
|
|
1
|
+
{"version":3,"file":"safeRelayr.d.ts","sourceRoot":"","sources":["../../../src/review/safeRelayr.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,OAAO,EACZ,KAAK,GAAG,EACR,KAAK,YAAY,EAClB,MAAM,MAAM,CAAC;AAEd,OAAO,EAKL,KAAK,sBAAsB,EAC5B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EA0BL,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC/B,MAAM,aAAa,CAAC;AAErB,0FAA0F;AAC1F,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,GAAG,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,4FAA4F;AAC5F,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,EACT,UAAU,GACV,SAAS,GACT,WAAW,GACX,WAAW,GACX,UAAU,GACV,SAAS,CAAC;IACd,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,KAAK,EAAE,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC;IACzD,sFAAsF;IACtF,aAAa,CAAC,EAAE,eAAe,GAAG,sBAAsB,GAAG,gBAAgB,CAAC;IAC5E,yGAAyG;IACzG,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,uBAAuB,EAAE,CAAC;IACpC,kFAAkF;IAClF,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,0GAA0G;IAC1G,KAAK,CAAC,EAAE,aAAa,GAAG,gBAAgB,CAAC;IACzC,sFAAsF;IACtF,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACrD,wFAAwF;IACxF,IAAI,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,iIAAiI;IACjI,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAClE,CAAC;AA6CF;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAsB7E;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;IACrD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B,CAAC;AAEF,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EACF,yBAAyB,GACzB,kBAAkB,GAClB,mBAAmB,GACnB,yBAAyB,GACzB,sBAAsB,GACtB,oBAAoB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,OAAO,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,aAAa,CAAC;KAC5C,EAAE,CAAC;CACL,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG;IAC/C,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAC7B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,GAAG,OAAO,GAAG,YAAY,GAAG,QAAQ,EACrD,SAAS,EAAE,mBAAmB,KAC3B,IAAI,CAAC;AAEV,MAAM,MAAM,eAAe,GACvB,WAAW,GACX,SAAS,GACT,gBAAgB,GAChB,oBAAoB,GACpB,oBAAoB,GACpB,WAAW,GACX,UAAU,CAAC;AAEf,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,OAAO,CAAC,EAAE,iBAAiB,CAAA;CAAE,GACtE;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,mBAAmB,CAAC;IAC/B,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,QAAQ,CAAC;IACzD,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEN,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,eAAe,CAAC;IACvB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACpD,SAAS,EAAE,CACT,OAAO,EAAE,MAAM,KAEb,CAAC,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC,GAC9D,SAAS,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,cAAc,EAAE,MAAM,OAAO,GAAG,SAAS,CAAC;IAC1C,0FAA0F;IAC1F,UAAU,EAAE,CACV,SAAS,EAAE,mBAAmB,EAC9B,OAAO,EAAE,OAAO,KACb,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,kFAAkF;IAClF,MAAM,EAAE,CACN,UAAU,EAAE,SAAS,mBAAmB,EAAE,EAC1C,OAAO,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,KAC1B,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB;;;;;OAKG;IACH,WAAW,EAAE,CAAC,OAAO,EAAE;QACrB,OAAO,EAAE,iBAAiB,CAAC;QAC3B,OAAO,EAAE,aAAa,CAAC;QACvB,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QAChC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,EAAE,CAAC,QAAQ,EAAE,iBAAiB,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAC1D,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,QAAQ,EAAE,iBAAiB,EAAE,CAAA;KAAE,CAAC,CAAC;IAC5D,iGAAiG;IACjG,aAAa,CAAC,EAAE,CACd,SAAS,EAAE,mBAAmB,EAC9B,QAAQ,EAAE,yBAAyB,KAChC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC;CACzB,CAAC;AAEF,qBAAa,uBAAwB,SAAQ,KAAK;IAG9C,QAAQ,CAAC,OAAO,EAAE,iBAAiB;IAEnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB;IAJxC,QAAQ,CAAC,IAAI,6BAA6B;gBAE/B,OAAO,EAAE,iBAAiB,EACnC,OAAO,SAAoH,EAClH,QAAQ,CAAC,EAAE,kBAAkB,YAAA;CAIzC;AAqCD,sGAAsG;AACtG,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,IAAI,CAAC,mBAAmB,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,GAC/D,MAAM,CAER;AAMD,yFAAyF;AACzF,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,SAAS,mBAAmB,EAAE,EACpC,KAAK,EAAE,SAAS,mBAAmB,EAAE,GACpC,OAAO,CAST;AAED,2FAA2F;AAC3F,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,mBAAmB,GAC7B,sBAAsB,CA+DxB;AAED,qFAAqF;AACrF,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,mBAAmB,GAC7B;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,GAAG,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE,EAAE,CA6BlD;AA+DD,2FAA2F;AAC3F,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,iBAAiB,GAAG,SAAS,EAC7D,EACE,UAAU,EACV,QAAQ,EACR,OAAO,GACR,EAAE;IACD,UAAU,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC3C,QAAQ,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC9C,OAAO,EAAE,SAAS,uBAAuB,EAAE,CAAC;CAC7C,GACA,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAuBtC;AAkCD,mGAAmG;AACnG,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iBAAiB;iEA4mBhE;QACD,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,EAAE,SAAS,mBAAmB,EAAE,CAAC;QAC3C,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;KAC7B,KAAG,OAAO,CAAC,eAAe,CAAC;6CA6HzB;QACD,OAAO,EAAE,OAAO,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,KAAG,OAAO,CAAC,gBAAgB,CAAC;sEAc1B;QACD,OAAO,EAAE,OAAO,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;QACvB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;KAC7B,KAAG,OAAO,CAAC,gBAAgB,CAAC;8EAoM1B;QACD,OAAO,EAAE,OAAO,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,KAAG,OAAO,CAAC,gBAAgB,CAAC;EAsC9B"}
|
|
@@ -232,6 +232,13 @@ function requireSafeRelayrRecords(bindings, records) {
|
|
|
232
232
|
throw new Error("Relayr's record does not match the saved Safe transaction binding.");
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
|
+
function requireSafeLanding(execution, { receipt }) {
|
|
236
|
+
const { safe, safeTxHash } = execution;
|
|
237
|
+
(0, safeService_js_1.requireSafeExecutionSuccess)(receipt, safe, safeTxHash);
|
|
238
|
+
const event = (0, safeService_js_1.safeExecutionResult)(receipt, safe, safeTxHash);
|
|
239
|
+
if (event.status !== "success" || event.payment !== 0n)
|
|
240
|
+
throw new Error("The Safe execution reimbursed its executor. Keep the existing bundle for verification.");
|
|
241
|
+
}
|
|
235
242
|
/** Canonical exact destination receipts plus matching, refund-free Safe success events. */
|
|
236
243
|
async function verifySafeRelayrLanding(clientFor, { executions, bindings, records, }) {
|
|
237
244
|
validateExecutions(executions);
|
|
@@ -244,13 +251,7 @@ async function verifySafeRelayrLanding(clientFor, { executions, bindings, record
|
|
|
244
251
|
bindings,
|
|
245
252
|
records,
|
|
246
253
|
});
|
|
247
|
-
verified.forEach((
|
|
248
|
-
const { safe, safeTxHash } = executions[index];
|
|
249
|
-
(0, safeService_js_1.requireSafeExecutionSuccess)(receipt, safe, safeTxHash);
|
|
250
|
-
const event = (0, safeService_js_1.safeExecutionResult)(receipt, safe, safeTxHash);
|
|
251
|
-
if (event.status !== "success" || event.payment !== 0n)
|
|
252
|
-
throw new Error("The Safe execution reimbursed its executor. Keep the existing bundle for verification.");
|
|
253
|
-
});
|
|
254
|
+
verified.forEach((item, index) => requireSafeLanding(executions[index], item));
|
|
254
255
|
return verified;
|
|
255
256
|
}
|
|
256
257
|
/** Validate persisted bindings again; local storage is not authenticated evidence. */
|
|
@@ -276,6 +277,49 @@ function requireSessionQuote(session) {
|
|
|
276
277
|
function createSafeRelayrController(options) {
|
|
277
278
|
const { store } = options;
|
|
278
279
|
const fetchRelayr = options.fetch ?? globalThis.fetch;
|
|
280
|
+
const displayedExecutions = new Map();
|
|
281
|
+
const notify = (progress, rechecking = false) => {
|
|
282
|
+
if (progress.type === "execution") {
|
|
283
|
+
const key = `${progress.session.id}:${progress.index}`;
|
|
284
|
+
const display = JSON.stringify([
|
|
285
|
+
progress.status,
|
|
286
|
+
progress.hash,
|
|
287
|
+
progress.message,
|
|
288
|
+
]);
|
|
289
|
+
const previous = displayedExecutions.get(key);
|
|
290
|
+
// Rechecking does not undo a proven display result. A later unavailable,
|
|
291
|
+
// contradictory, or changed-hash result explicitly updates it below.
|
|
292
|
+
if (previous === display ||
|
|
293
|
+
(rechecking &&
|
|
294
|
+
previous === JSON.stringify(["executed", progress.hash, undefined])))
|
|
295
|
+
return;
|
|
296
|
+
displayedExecutions.set(key, display);
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
// A view must not mutate frozen calls or turn a submitted payment into a
|
|
300
|
+
// rejected operation. Also consume rejection from an async UI callback.
|
|
301
|
+
void Promise.resolve(options.onProgress?.(structuredClone(progress))).catch(() => undefined);
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// Display observers cannot change the durable execution lifecycle.
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
const phase = (phase, session, signal) => {
|
|
308
|
+
if (!signal?.aborted)
|
|
309
|
+
notify({ type: "phase", phase, session });
|
|
310
|
+
};
|
|
311
|
+
const savedPhase = (session, signal) => {
|
|
312
|
+
if (session.state === "released")
|
|
313
|
+
return;
|
|
314
|
+
if (session.state === "complete")
|
|
315
|
+
phase("complete", session, signal);
|
|
316
|
+
else if (session.paymentStatus === "confirmed" ||
|
|
317
|
+
hasObservedFunding(session))
|
|
318
|
+
phase("executing", session, signal);
|
|
319
|
+
else if (session.paymentStatus === "submitted" ||
|
|
320
|
+
session.paymentStatus === "sending")
|
|
321
|
+
phase("payment-confirming", session, signal);
|
|
322
|
+
};
|
|
279
323
|
const checkAccount = (account, signal) => {
|
|
280
324
|
signal?.throwIfAborted();
|
|
281
325
|
if (!(0, viem_1.isAddress)(account) ||
|
|
@@ -455,87 +499,256 @@ function createSafeRelayrController(options) {
|
|
|
455
499
|
checks,
|
|
456
500
|
});
|
|
457
501
|
}
|
|
458
|
-
async function inspect(session) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
session
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
if (!Array.isArray(bundle.transactions))
|
|
481
|
-
throw new SafeRelayrRecoveryError(session, "Relayr has not returned this bundle's transactions. Check the existing bundle again.");
|
|
482
|
-
const records = bundle.transactions;
|
|
483
|
-
requireSafeRelayrRecords(quote.expectedTransactions, records);
|
|
484
|
-
session = { ...session, records };
|
|
485
|
-
await store.save(session);
|
|
486
|
-
// A reported hash is only a pointer. Completion requires canonical exact
|
|
487
|
-
// calldata/value receipts AND the matching Safe ExecutionSuccess event.
|
|
488
|
-
if (records.length &&
|
|
489
|
-
records.every((record) => record.status?.data?.hash || record.status?.data?.transaction?.hash)) {
|
|
490
|
-
const verified = await verifySafeRelayrLanding(options.clientFor, {
|
|
491
|
-
executions: session.executions,
|
|
492
|
-
bindings: quote.expectedTransactions,
|
|
493
|
-
records,
|
|
502
|
+
async function inspect(session, signal) {
|
|
503
|
+
const report = (progress, rechecking = false) => {
|
|
504
|
+
if (!signal?.aborted)
|
|
505
|
+
notify(progress, rechecking);
|
|
506
|
+
};
|
|
507
|
+
try {
|
|
508
|
+
savedPhase(session, signal);
|
|
509
|
+
if (session.state === "released")
|
|
510
|
+
return result("released", session);
|
|
511
|
+
if (session.state === "complete")
|
|
512
|
+
return result("complete", session);
|
|
513
|
+
if (hasObservedFunding(session) && session.fundingObserved !== true) {
|
|
514
|
+
session = { ...session, fundingObserved: true };
|
|
515
|
+
await store.save(session);
|
|
516
|
+
}
|
|
517
|
+
if (!session.bundleUuid || !session.quote)
|
|
518
|
+
return inspectMissingQuote(session);
|
|
519
|
+
const quote = requireSessionQuote(session);
|
|
520
|
+
const chains = session.executions.map(({ entry }) => entry.chain);
|
|
521
|
+
const bundle = await (0, relayr_js_1.readRelayrBundle)(session.bundleUuid, {
|
|
522
|
+
fetch: fetchRelayr,
|
|
494
523
|
});
|
|
495
|
-
|
|
496
|
-
|
|
524
|
+
if (hasObservedFunding(session) ||
|
|
525
|
+
bundle.payment_received === true ||
|
|
526
|
+
hasFundingRecords(bundle.transactions)) {
|
|
527
|
+
session = { ...session, fundingObserved: true };
|
|
528
|
+
await store.save(session);
|
|
529
|
+
}
|
|
530
|
+
if (!Array.isArray(bundle.transactions))
|
|
531
|
+
throw new SafeRelayrRecoveryError(session, "Relayr has not returned this bundle's transactions. Check the existing bundle again.");
|
|
532
|
+
const records = bundle.transactions;
|
|
533
|
+
requireSafeRelayrRecords(quote.expectedTransactions, records);
|
|
534
|
+
session = { ...session, records };
|
|
497
535
|
await store.save(session);
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
return result("pending", session);
|
|
536
|
+
savedPhase(session, signal);
|
|
537
|
+
// Each hash is only a pointer. Check available chains independently, using
|
|
538
|
+
// the same complete binding validator and exact receipt proof as completion.
|
|
539
|
+
const indexed = quote.expectedTransactions.map((binding, index) => ({
|
|
540
|
+
binding,
|
|
541
|
+
index,
|
|
542
|
+
record: records.find((record) => record.tx_uuid?.toLowerCase() === binding.txUuid.toLowerCase()),
|
|
543
|
+
}));
|
|
544
|
+
const known = indexed.filter(({ record }) => record.status?.data?.hash !== undefined ||
|
|
545
|
+
record.status?.data?.transaction?.hash !== undefined);
|
|
546
|
+
if (known.length) {
|
|
510
547
|
try {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
payment,
|
|
548
|
+
(0, relayr_js_1.relayrDestinationHashes)({
|
|
549
|
+
bindings: known.map(({ binding }) => binding),
|
|
550
|
+
records: known.map(({ record }) => record),
|
|
515
551
|
});
|
|
516
|
-
session = { ...session, paymentStatus: "confirmed" };
|
|
517
|
-
await store.save(session);
|
|
518
|
-
return result("pending", session);
|
|
519
552
|
}
|
|
520
553
|
catch (error) {
|
|
521
|
-
|
|
522
|
-
|
|
554
|
+
for (const { index, record } of known)
|
|
555
|
+
report({
|
|
556
|
+
type: "execution",
|
|
557
|
+
session,
|
|
558
|
+
index,
|
|
559
|
+
execution: session.executions[index],
|
|
560
|
+
status: "failed",
|
|
561
|
+
hash: (0, relayr_js_1.relayrDestinationHash)(record) ?? undefined,
|
|
562
|
+
message: error instanceof Error
|
|
563
|
+
? error.message
|
|
564
|
+
: "The destination records do not match this quote.",
|
|
565
|
+
});
|
|
566
|
+
throw new SafeRelayrRecoveryError(session, error instanceof Error ? error.message : undefined);
|
|
523
567
|
}
|
|
568
|
+
phase("executing", session, signal);
|
|
524
569
|
}
|
|
525
|
-
if (
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
570
|
+
if (known.length ||
|
|
571
|
+
session.paymentStatus !== "unfunded" ||
|
|
572
|
+
session.fundingObserved) {
|
|
573
|
+
for (const { index, record } of indexed) {
|
|
574
|
+
const hash = (0, relayr_js_1.relayrDestinationHash)(record) ?? undefined;
|
|
575
|
+
report({
|
|
576
|
+
type: "execution",
|
|
577
|
+
session,
|
|
578
|
+
index,
|
|
579
|
+
execution: session.executions[index],
|
|
580
|
+
status: hash
|
|
581
|
+
? "confirming"
|
|
582
|
+
: (0, relayr_js_1.relayrStateIsFailed)(record.status?.state)
|
|
583
|
+
? "failed"
|
|
584
|
+
: "pending",
|
|
585
|
+
hash,
|
|
586
|
+
...(!hash && (0, relayr_js_1.relayrStateIsFailed)(record.status?.state)
|
|
587
|
+
? {
|
|
588
|
+
message: "Relayr reported this execution failed. Its onchain result has not been verified.",
|
|
589
|
+
}
|
|
590
|
+
: {}),
|
|
591
|
+
}, true);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
const landed = await Promise.allSettled(known.map(async ({ binding, index, record }) => {
|
|
595
|
+
const execution = session.executions[index];
|
|
596
|
+
const hash = (0, relayr_js_1.relayrDestinationHash)(record);
|
|
597
|
+
const client = options.clientFor(binding.chain);
|
|
598
|
+
if (!client) {
|
|
599
|
+
report({
|
|
600
|
+
type: "execution",
|
|
601
|
+
session,
|
|
602
|
+
index,
|
|
603
|
+
execution,
|
|
604
|
+
status: "confirming",
|
|
605
|
+
hash,
|
|
606
|
+
message: "Waiting for a connection to this network.",
|
|
607
|
+
});
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
try {
|
|
611
|
+
const receipt = await (0, relayr_js_1.verifyRelayrDestination)(client, {
|
|
612
|
+
entry: binding.entry,
|
|
613
|
+
hash,
|
|
614
|
+
});
|
|
615
|
+
const verified = {
|
|
616
|
+
txUuid: binding.txUuid,
|
|
617
|
+
chainId: binding.chain,
|
|
618
|
+
receipt,
|
|
619
|
+
};
|
|
620
|
+
requireSafeLanding(execution, verified);
|
|
621
|
+
await options.afterVerified?.(execution, verified);
|
|
622
|
+
report({
|
|
623
|
+
type: "execution",
|
|
624
|
+
session,
|
|
625
|
+
index,
|
|
626
|
+
execution,
|
|
627
|
+
status: "executed",
|
|
628
|
+
hash,
|
|
629
|
+
});
|
|
630
|
+
return true;
|
|
631
|
+
}
|
|
632
|
+
catch (error) {
|
|
633
|
+
if (error instanceof relayr_js_1.RelayrProofUnavailableError) {
|
|
634
|
+
report({
|
|
635
|
+
type: "execution",
|
|
636
|
+
session,
|
|
637
|
+
index,
|
|
638
|
+
execution,
|
|
639
|
+
status: "confirming",
|
|
640
|
+
hash,
|
|
641
|
+
message: "Waiting for the network to confirm this transaction.",
|
|
642
|
+
});
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
report({
|
|
646
|
+
type: "execution",
|
|
647
|
+
session,
|
|
648
|
+
index,
|
|
649
|
+
execution,
|
|
650
|
+
status: "failed",
|
|
651
|
+
hash,
|
|
652
|
+
message: error instanceof Error
|
|
653
|
+
? error.message
|
|
654
|
+
: "The Safe execution could not be verified.",
|
|
655
|
+
});
|
|
656
|
+
throw error;
|
|
657
|
+
}
|
|
658
|
+
}));
|
|
659
|
+
const failure = landed.find((item) => item.status === "rejected");
|
|
660
|
+
if (failure?.status === "rejected")
|
|
661
|
+
throw new SafeRelayrRecoveryError(session, failure.reason instanceof Error ? failure.reason.message : undefined);
|
|
662
|
+
if (known.length === session.executions.length &&
|
|
663
|
+
landed.every((item) => item.status === "fulfilled" && item.value)) {
|
|
664
|
+
session = { ...session, state: "complete" };
|
|
665
|
+
await store.save(session);
|
|
666
|
+
phase("complete", session, signal);
|
|
667
|
+
return result("complete", session);
|
|
668
|
+
}
|
|
669
|
+
if (session.paymentStatus === "sending")
|
|
537
670
|
return result("pending", session);
|
|
538
|
-
if (
|
|
671
|
+
if (session.payments.some((payment) => payment.bundleUuid !== session.bundleUuid))
|
|
672
|
+
throw new SafeRelayrRecoveryError(session, "A saved payment belongs to another bundle. Keep the existing bundle for verification.");
|
|
673
|
+
if (session.payments.length) {
|
|
674
|
+
let everyReverted = true;
|
|
675
|
+
for (const payment of session.payments) {
|
|
676
|
+
const client = options.clientFor(payment.chainId);
|
|
677
|
+
if (!client)
|
|
678
|
+
return result("pending", session);
|
|
679
|
+
try {
|
|
680
|
+
await (0, relayr_js_1.verifyRelayrPayment)(client, {
|
|
681
|
+
hash: payment.hash,
|
|
682
|
+
from: session.account,
|
|
683
|
+
payment,
|
|
684
|
+
});
|
|
685
|
+
session = { ...session, paymentStatus: "confirmed" };
|
|
686
|
+
await store.save(session);
|
|
687
|
+
phase("executing", session, signal);
|
|
688
|
+
return result("pending", session);
|
|
689
|
+
}
|
|
690
|
+
catch (error) {
|
|
691
|
+
if (!(error instanceof relayr_js_1.RelayrPaymentRevertedError))
|
|
692
|
+
everyReverted = false;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
if (!everyReverted)
|
|
696
|
+
return result("pending", session);
|
|
697
|
+
session = { ...session, paymentStatus: "reverted" };
|
|
698
|
+
await store.save(session);
|
|
699
|
+
const disposition = await (0, relayr_js_1.revertedRelayrQuote)(options.clientFor, {
|
|
700
|
+
bundleUuid: quote.bundle_uuid,
|
|
701
|
+
payments: session.payments,
|
|
702
|
+
options: quote.payment_info,
|
|
703
|
+
destinationChainIds: chains,
|
|
704
|
+
account: session.account,
|
|
705
|
+
}, { fetch: fetchRelayr });
|
|
706
|
+
if (disposition.state === "funded")
|
|
707
|
+
return result("pending", session);
|
|
708
|
+
if (disposition.state === "released") {
|
|
709
|
+
session = {
|
|
710
|
+
...session,
|
|
711
|
+
state: "released",
|
|
712
|
+
paymentStatus: "expired",
|
|
713
|
+
releaseReason: "quote-expired",
|
|
714
|
+
};
|
|
715
|
+
await store.save(session);
|
|
716
|
+
return result("released", session);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
else if (session.paymentStatus !== "unfunded" ||
|
|
720
|
+
session.fundingObserved) {
|
|
721
|
+
// A wallet invocation without a known hash may still have broadcast.
|
|
722
|
+
return session.fundingObserved && !known.length
|
|
723
|
+
? {
|
|
724
|
+
...result("pending", session),
|
|
725
|
+
recovery: {
|
|
726
|
+
reason: "funding-unresolved",
|
|
727
|
+
message: "This saved selection has reported funding or execution activity. Check the existing bundle and its funding transactions before submitting another payment.",
|
|
728
|
+
},
|
|
729
|
+
}
|
|
730
|
+
: result("pending", session);
|
|
731
|
+
}
|
|
732
|
+
const quoted = (0, relayr_js_1.relayrQuotedOptions)(quote, chains);
|
|
733
|
+
if (!quoted.length)
|
|
734
|
+
return inspectMissingQuote(session);
|
|
735
|
+
const payments = (0, relayr_js_1.relayrPaymentOptions)(quote, chains);
|
|
736
|
+
const expired = payments.length
|
|
737
|
+
? [false]
|
|
738
|
+
: await Promise.all(quoted.map(async ({ details }) => {
|
|
739
|
+
const client = options.clientFor(details.chainId);
|
|
740
|
+
return (!!client &&
|
|
741
|
+
(await (0, relayr_js_1.relayrDeadlinePassed)(client, details.deadline).catch(() => false)));
|
|
742
|
+
}));
|
|
743
|
+
if (expired.every(Boolean)) {
|
|
744
|
+
try {
|
|
745
|
+
await (0, relayr_js_1.requireRelayrBundleUnpaid)(quote.bundle_uuid, {
|
|
746
|
+
fetch: fetchRelayr,
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
catch {
|
|
750
|
+
return result("pending", session);
|
|
751
|
+
}
|
|
539
752
|
session = {
|
|
540
753
|
...session,
|
|
541
754
|
state: "released",
|
|
@@ -545,32 +758,6 @@ function createSafeRelayrController(options) {
|
|
|
545
758
|
await store.save(session);
|
|
546
759
|
return result("released", session);
|
|
547
760
|
}
|
|
548
|
-
}
|
|
549
|
-
else if (session.paymentStatus !== "unfunded" ||
|
|
550
|
-
session.fundingObserved) {
|
|
551
|
-
// A wallet invocation without a known hash may still have broadcast.
|
|
552
|
-
return session.fundingObserved
|
|
553
|
-
? {
|
|
554
|
-
...result("pending", session),
|
|
555
|
-
recovery: {
|
|
556
|
-
reason: "funding-unresolved",
|
|
557
|
-
message: "This saved selection has reported funding or execution activity. Check the existing bundle and its funding transactions before submitting another payment.",
|
|
558
|
-
},
|
|
559
|
-
}
|
|
560
|
-
: result("pending", session);
|
|
561
|
-
}
|
|
562
|
-
const quoted = (0, relayr_js_1.relayrQuotedOptions)(quote, chains);
|
|
563
|
-
if (!quoted.length)
|
|
564
|
-
return inspectMissingQuote(session);
|
|
565
|
-
const payments = (0, relayr_js_1.relayrPaymentOptions)(quote, chains);
|
|
566
|
-
const expired = payments.length
|
|
567
|
-
? [false]
|
|
568
|
-
: await Promise.all(quoted.map(async ({ details }) => {
|
|
569
|
-
const client = options.clientFor(details.chainId);
|
|
570
|
-
return (!!client &&
|
|
571
|
-
(await (0, relayr_js_1.relayrDeadlinePassed)(client, details.deadline).catch(() => false)));
|
|
572
|
-
}));
|
|
573
|
-
if (expired.every(Boolean)) {
|
|
574
761
|
try {
|
|
575
762
|
await (0, relayr_js_1.requireRelayrBundleUnpaid)(quote.bundle_uuid, {
|
|
576
763
|
fetch: fetchRelayr,
|
|
@@ -579,29 +766,18 @@ function createSafeRelayrController(options) {
|
|
|
579
766
|
catch {
|
|
580
767
|
return result("pending", session);
|
|
581
768
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
await store.save(session);
|
|
589
|
-
return result("released", session);
|
|
590
|
-
}
|
|
591
|
-
try {
|
|
592
|
-
await (0, relayr_js_1.requireRelayrBundleUnpaid)(quote.bundle_uuid, {
|
|
593
|
-
fetch: fetchRelayr,
|
|
594
|
-
});
|
|
769
|
+
const allowed = session.payments.length && payments.length
|
|
770
|
+
? [(0, relayr_js_1.relayrRetryOption)(session.payments, payments)]
|
|
771
|
+
: payments;
|
|
772
|
+
return allowed.length
|
|
773
|
+
? result("ready", session, allowed)
|
|
774
|
+
: result("pending", session);
|
|
595
775
|
}
|
|
596
|
-
catch {
|
|
597
|
-
|
|
776
|
+
catch (error) {
|
|
777
|
+
if (error instanceof SafeRelayrRecoveryError)
|
|
778
|
+
throw error;
|
|
779
|
+
throw new SafeRelayrRecoveryError(session, error instanceof Error ? error.message : undefined);
|
|
598
780
|
}
|
|
599
|
-
const allowed = session.payments.length && payments.length
|
|
600
|
-
? [(0, relayr_js_1.relayrRetryOption)(session.payments, payments)]
|
|
601
|
-
: payments;
|
|
602
|
-
return allowed.length
|
|
603
|
-
? result("ready", session, allowed)
|
|
604
|
-
: result("pending", session);
|
|
605
781
|
}
|
|
606
782
|
async function prepare({ account, executions: input, signal, onStatus, }) {
|
|
607
783
|
const executions = structuredClone([...input]);
|
|
@@ -614,9 +790,11 @@ function createSafeRelayrController(options) {
|
|
|
614
790
|
continue;
|
|
615
791
|
let checked;
|
|
616
792
|
try {
|
|
617
|
-
checked = await inspect(saved);
|
|
793
|
+
checked = await inspect(saved, signal);
|
|
618
794
|
}
|
|
619
795
|
catch (error) {
|
|
796
|
+
if (error instanceof SafeRelayrRecoveryError)
|
|
797
|
+
throw error;
|
|
620
798
|
throw new SafeRelayrRecoveryError(saved, error instanceof Error ? error.message : undefined);
|
|
621
799
|
}
|
|
622
800
|
if (checked.state === "released" &&
|
|
@@ -630,12 +808,14 @@ function createSafeRelayrController(options) {
|
|
|
630
808
|
throw new SafeRelayrRecoveryError(checked.session, checked.recovery?.message, checked.recovery);
|
|
631
809
|
await assertUnreserved(account, executions, saved.id);
|
|
632
810
|
await revalidate(saved.executions, account, onStatus, false, signal);
|
|
811
|
+
phase("reviewing", checked.session, signal);
|
|
633
812
|
await options.review(saved.executions, { resumed: true });
|
|
634
813
|
checkAccount(account, signal);
|
|
635
814
|
rememberReview(checked.session);
|
|
636
815
|
return { ...checked, state: "ready", resumed: true };
|
|
637
816
|
}
|
|
638
817
|
await revalidate(executions, account, onStatus, false, signal);
|
|
818
|
+
phase("reviewing", undefined, signal);
|
|
639
819
|
await options.review(executions, { resumed: false });
|
|
640
820
|
checkAccount(account, signal);
|
|
641
821
|
// Re-read under the shared lock after review. A rejected review leaves the
|
|
@@ -672,6 +852,7 @@ function createSafeRelayrController(options) {
|
|
|
672
852
|
throw error;
|
|
673
853
|
}
|
|
674
854
|
const request = (0, relayr_js_1.relayrBundleRequest)(executions.map(({ entry }) => entry));
|
|
855
|
+
phase("quoting", session, signal);
|
|
675
856
|
const response = await fetchRelayr(`${relayr_js_1.RELAYR_API}/v1/bundle/prepaid`, {
|
|
676
857
|
method: "POST",
|
|
677
858
|
headers: { "content-type": "application/json" },
|
|
@@ -700,14 +881,18 @@ function createSafeRelayrController(options) {
|
|
|
700
881
|
return { state: "ready", session, payments, resumed: false };
|
|
701
882
|
});
|
|
702
883
|
}
|
|
703
|
-
async function check({ account, sessionId, }) {
|
|
704
|
-
return store.withLock(account, async () =>
|
|
884
|
+
async function check({ account, sessionId, signal, }) {
|
|
885
|
+
return store.withLock(account, async () => {
|
|
886
|
+
signal?.throwIfAborted();
|
|
887
|
+
const session = await load(account, sessionId);
|
|
888
|
+
return inspect(session, signal);
|
|
889
|
+
});
|
|
705
890
|
}
|
|
706
891
|
async function fund({ account, sessionId, paymentChainId, signal, onStatus, }) {
|
|
707
892
|
return store.withLock(account, async () => {
|
|
708
893
|
checkAccount(account, signal);
|
|
709
894
|
let session = await load(account, sessionId);
|
|
710
|
-
const checked = await inspect(session);
|
|
895
|
+
const checked = await inspect(session, signal);
|
|
711
896
|
session = checked.session;
|
|
712
897
|
if (checked.state !== "ready")
|
|
713
898
|
throw new SafeRelayrRecoveryError(session, checked.recovery?.message, checked.recovery);
|
|
@@ -718,6 +903,7 @@ function createSafeRelayrController(options) {
|
|
|
718
903
|
throw new Error("Choose a current payment option from this saved quote.");
|
|
719
904
|
await assertUnreserved(account, session.executions, session.id);
|
|
720
905
|
if (!reviewedIntents.has(reviewKey(session))) {
|
|
906
|
+
phase("reviewing", session, signal);
|
|
721
907
|
await options.review(session.executions, { resumed: true });
|
|
722
908
|
checkAccount(account, signal);
|
|
723
909
|
rememberReview(session);
|
|
@@ -765,6 +951,7 @@ function createSafeRelayrController(options) {
|
|
|
765
951
|
throw error;
|
|
766
952
|
}
|
|
767
953
|
sending = true;
|
|
954
|
+
phase("payment-submitting", session, signal);
|
|
768
955
|
};
|
|
769
956
|
const onSent = async (payments) => {
|
|
770
957
|
const snapshot = (0, relayr_js_1.relayrSentPaymentsSnapshot)(payments);
|
|
@@ -802,8 +989,10 @@ function createSafeRelayrController(options) {
|
|
|
802
989
|
paymentStatus: "submitted",
|
|
803
990
|
};
|
|
804
991
|
await store.save(session);
|
|
992
|
+
phase("payment-confirming", session, signal);
|
|
805
993
|
};
|
|
806
994
|
try {
|
|
995
|
+
phase("payment-review", session, signal);
|
|
807
996
|
const sent = await options.sendPayment({
|
|
808
997
|
session: structuredClone(session),
|
|
809
998
|
payment,
|
|
@@ -824,10 +1013,14 @@ function createSafeRelayrController(options) {
|
|
|
824
1013
|
paymentStatus: checked.session.paymentStatus,
|
|
825
1014
|
};
|
|
826
1015
|
await store.save(session);
|
|
1016
|
+
throw error;
|
|
827
1017
|
}
|
|
1018
|
+
if (sending ||
|
|
1019
|
+
session.payments.length > checked.session.payments.length)
|
|
1020
|
+
throw new SafeRelayrRecoveryError(session, error instanceof Error ? error.message : undefined);
|
|
828
1021
|
throw error;
|
|
829
1022
|
}
|
|
830
|
-
return inspect(session);
|
|
1023
|
+
return inspect(session, signal);
|
|
831
1024
|
});
|
|
832
1025
|
}
|
|
833
1026
|
/** Read-only progress polling; each read releases its lock before the wait. */
|
|
@@ -840,7 +1033,7 @@ function createSafeRelayrController(options) {
|
|
|
840
1033
|
const started = Date.now();
|
|
841
1034
|
while (true) {
|
|
842
1035
|
signal?.throwIfAborted();
|
|
843
|
-
const next = await check({ account, sessionId });
|
|
1036
|
+
const next = await check({ account, sessionId, signal });
|
|
844
1037
|
signal?.throwIfAborted();
|
|
845
1038
|
await onUpdate?.(next);
|
|
846
1039
|
const remaining = timeoutMs - (Date.now() - started);
|