@bananapus/nana-sdk-core 2.36.0 → 2.37.0
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 +22 -5
- package/dist/cjs/review/relayr.d.ts.map +1 -1
- package/dist/cjs/review/relayr.js +56 -29
- package/dist/cjs/review/relayr.js.map +1 -1
- package/dist/cjs/review/safeRelayr.d.ts +31 -2
- package/dist/cjs/review/safeRelayr.d.ts.map +1 -1
- package/dist/cjs/review/safeRelayr.js +346 -75
- package/dist/cjs/review/safeRelayr.js.map +1 -1
- package/dist/esm/review/relayr.d.ts +22 -5
- package/dist/esm/review/relayr.d.ts.map +1 -1
- package/dist/esm/review/relayr.js +55 -29
- package/dist/esm/review/relayr.js.map +1 -1
- package/dist/esm/review/safeRelayr.d.ts +31 -2
- package/dist/esm/review/safeRelayr.d.ts.map +1 -1
- package/dist/esm/review/safeRelayr.js +346 -75
- 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;AAC3B,OAAO,
|
|
1
|
+
{"version":3,"file":"safeRelayr.d.ts","sourceRoot":"","sources":["../../../src/review/safeRelayr.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SafeRelayrRecoveryError = void 0;
|
|
3
|
+
exports.SafeRelayrRecoveryError = exports.SAFE_RELAYR_UNCONFIRMED_LINE = exports.SAFE_RELAYR_FUNDING_EXIT_LINE = exports.SAFE_RELAYR_QUOTE_CLOSING_LINE = exports.SAFE_RELAYR_STATUS_UNKNOWN_LINE = void 0;
|
|
4
4
|
exports.canReplaceSafeRelayrQuote = canReplaceSafeRelayrQuote;
|
|
5
5
|
exports.safeRelayrReservationKey = safeRelayrReservationKey;
|
|
6
6
|
exports.sameSafeRelayrIntents = sameSafeRelayrIntents;
|
|
@@ -72,6 +72,28 @@ function canReplaceSafeRelayrQuote(session) {
|
|
|
72
72
|
return false;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
+
/** The line a Safe action shows when its payment's status couldn't be read. */
|
|
76
|
+
exports.SAFE_RELAYR_STATUS_UNKNOWN_LINE = "Couldn't check this payment's status. Try again in a moment.";
|
|
77
|
+
/** The line a Safe action shows when its quote can no longer be paid but hasn't expired onchain. */
|
|
78
|
+
exports.SAFE_RELAYR_QUOTE_CLOSING_LINE = "This quote's payment window has closed. Try again in a few minutes for a new quote.";
|
|
79
|
+
/** The line a Safe action shows with its warned exit while Relayr's funding report is unresolved. */
|
|
80
|
+
exports.SAFE_RELAYR_FUNDING_EXIT_LINE = "Relayr reported a payment for this quote. Check your wallet and Relayr before paying again.";
|
|
81
|
+
/** How long after funding was last reported its warned exit waits: an hour. */
|
|
82
|
+
const FUNDING_EXIT_MS = 60 * 60_000;
|
|
83
|
+
/** The line a Safe action shows with its warned exit (see {@link SafeRelayrRecovery.exit}). */
|
|
84
|
+
exports.SAFE_RELAYR_UNCONFIRMED_LINE = "We couldn't confirm this payment's status. If your wallet shows no payment, get a new quote.";
|
|
85
|
+
/** A status read's failure that says nothing: no refusal Relayr gave. */
|
|
86
|
+
function statusUnknown(error) {
|
|
87
|
+
return (!(error instanceof relayr_js_1.RelayrPaymentRetryError) || error.reason === "unknown");
|
|
88
|
+
}
|
|
89
|
+
/** A refusal saying Relayr reports the bundle paid, or a call running or run. */
|
|
90
|
+
function fundingReported(error) {
|
|
91
|
+
return (error instanceof relayr_js_1.RelayrPaymentRetryError &&
|
|
92
|
+
(error.reason === "paid" || error.reason === "running"));
|
|
93
|
+
}
|
|
94
|
+
/** Reads of a bundle's payment status before an unknown answer is reported. */
|
|
95
|
+
const STATUS_READS = 3;
|
|
96
|
+
const STATUS_RETRY_MS = 250;
|
|
75
97
|
class SafeRelayrRecoveryError extends Error {
|
|
76
98
|
constructor(session, message = "This Safe action already has a payment in progress. Check it before paying again.", recovery) {
|
|
77
99
|
super(message);
|
|
@@ -277,7 +299,31 @@ function requireSessionQuote(session) {
|
|
|
277
299
|
}
|
|
278
300
|
/** Shared Safe Relayr state machine. It never signs Safe proposals or submits on prepare/check. */
|
|
279
301
|
function createSafeRelayrController(options) {
|
|
280
|
-
|
|
302
|
+
/**
|
|
303
|
+
* The app's store, with funding evidence sticky in its one save path: a
|
|
304
|
+
* save from an older snapshot never drops `fundingObserved` or an older
|
|
305
|
+
* `fundingObservedAt` another flow saved. Nothing ever clears them; an
|
|
306
|
+
* exit keeps them for reconciliation.
|
|
307
|
+
*/
|
|
308
|
+
const store = {
|
|
309
|
+
get scope() {
|
|
310
|
+
return options.store.scope;
|
|
311
|
+
},
|
|
312
|
+
list: (account) => options.store.list(account),
|
|
313
|
+
withLock: (account, run) => options.store.withLock(account, run),
|
|
314
|
+
async save(next) {
|
|
315
|
+
const saved = (await options.store.list(next.account)).find((session) => session.id === next.id);
|
|
316
|
+
const at = [saved?.fundingObservedAt, next.fundingObservedAt].filter((value) => typeof value === "number");
|
|
317
|
+
// Once seen, funding stays, and its clock only moves forward.
|
|
318
|
+
return options.store.save(saved?.fundingObserved || next.fundingObserved
|
|
319
|
+
? {
|
|
320
|
+
...next,
|
|
321
|
+
fundingObserved: true,
|
|
322
|
+
...(at.length ? { fundingObservedAt: Math.max(...at) } : {}),
|
|
323
|
+
}
|
|
324
|
+
: next);
|
|
325
|
+
},
|
|
326
|
+
};
|
|
281
327
|
const fetchRelayr = options.fetch ?? globalThis.fetch;
|
|
282
328
|
const displayedExecutions = new Map();
|
|
283
329
|
const notify = (progress, rechecking = false) => {
|
|
@@ -465,18 +511,14 @@ function createSafeRelayrController(options) {
|
|
|
465
511
|
checks,
|
|
466
512
|
});
|
|
467
513
|
if (session.bundleUuid) {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
catch {
|
|
514
|
+
const [status, read] = await bundleUnpaid(session);
|
|
515
|
+
session = read;
|
|
516
|
+
if (status !== "unpaid")
|
|
474
517
|
return pending({
|
|
475
518
|
reason: "funding-unresolved",
|
|
476
519
|
message: "Every saved Safe nonce has been used, but the existing Relayr bundle has not been proven unpaid with all calls pending. Keep its payment evidence and inspect the original bundle before replacing this selection.",
|
|
477
520
|
checks,
|
|
478
521
|
});
|
|
479
|
-
}
|
|
480
522
|
}
|
|
481
523
|
session = {
|
|
482
524
|
...session,
|
|
@@ -501,6 +543,192 @@ function createSafeRelayrController(options) {
|
|
|
501
543
|
checks,
|
|
502
544
|
});
|
|
503
545
|
}
|
|
546
|
+
/**
|
|
547
|
+
* `read` of the bundle's status, tried up to {@link STATUS_READS} times
|
|
548
|
+
* while its failure is unknown. Every status read goes through it.
|
|
549
|
+
*/
|
|
550
|
+
async function statusRead(read) {
|
|
551
|
+
for (let attempt = 1;; attempt += 1) {
|
|
552
|
+
try {
|
|
553
|
+
return await read();
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
if (!statusUnknown(error) || attempt >= STATUS_READS)
|
|
557
|
+
throw error;
|
|
558
|
+
await new Promise((resolve) => setTimeout(resolve, STATUS_RETRY_MS));
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
/** `session`, saved with the funding Relayr reported. */
|
|
563
|
+
async function observed(session) {
|
|
564
|
+
session = {
|
|
565
|
+
...session,
|
|
566
|
+
fundingObserved: true,
|
|
567
|
+
fundingObservedAt: Date.now(),
|
|
568
|
+
};
|
|
569
|
+
await store.save(session);
|
|
570
|
+
return session;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* The bundle's payment status, three-valued: proven unpaid, funded (Relayr
|
|
574
|
+
* reports it paid or running, saved as observed funding), or unknown when
|
|
575
|
+
* it couldn't be read. Unknown is never paid, and never free to pay again.
|
|
576
|
+
*/
|
|
577
|
+
async function bundleUnpaid(session) {
|
|
578
|
+
try {
|
|
579
|
+
await statusRead(() => (0, relayr_js_1.requireRelayrBundleUnpaid)(session.bundleUuid, { fetch: fetchRelayr }));
|
|
580
|
+
return ["unpaid", session];
|
|
581
|
+
}
|
|
582
|
+
catch (error) {
|
|
583
|
+
if (!fundingReported(error))
|
|
584
|
+
return ["unknown", session];
|
|
585
|
+
return ["funded", await observed(session)];
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* What a quote whose payments didn't fund it allows
|
|
590
|
+
* ({@link revertedRelayrQuote}), read after {@link bundleUnpaid} proved it
|
|
591
|
+
* unpaid; its own reads are confirmations. Funding they report is saved.
|
|
592
|
+
* Null while unproven, or with `rethrow`, its reason thrown.
|
|
593
|
+
*/
|
|
594
|
+
async function disposed(session, quote, chains, rethrow = false) {
|
|
595
|
+
let state;
|
|
596
|
+
/** The last read before release failed after its retries: unknown. */
|
|
597
|
+
let unconfirmed = false;
|
|
598
|
+
try {
|
|
599
|
+
({ state } = await (0, relayr_js_1.revertedRelayrQuote)(options.clientFor, {
|
|
600
|
+
bundleUuid: quote.bundle_uuid,
|
|
601
|
+
payments: session.payments,
|
|
602
|
+
options: quote.payment_info,
|
|
603
|
+
destinationChainIds: chains,
|
|
604
|
+
account: session.account,
|
|
605
|
+
}, {
|
|
606
|
+
fetch: fetchRelayr,
|
|
607
|
+
// Every read of its bundle, through the same retries.
|
|
608
|
+
statusRead: (read) => statusRead(read).catch((error) => {
|
|
609
|
+
unconfirmed = statusUnknown(error);
|
|
610
|
+
throw error;
|
|
611
|
+
}),
|
|
612
|
+
}));
|
|
613
|
+
}
|
|
614
|
+
catch (error) {
|
|
615
|
+
if (unconfirmed)
|
|
616
|
+
return { state: "unknown", session };
|
|
617
|
+
if (rethrow)
|
|
618
|
+
throw error;
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
return {
|
|
622
|
+
state,
|
|
623
|
+
session: state === "funded" ? await observed(session) : session,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Held while reported funding is unresolved: never a release or a retry.
|
|
628
|
+
* Once every deadline is final and an hour has passed since funding was
|
|
629
|
+
* last reported, the user may leave it, strongly warned: a spurious
|
|
630
|
+
* report is no dead end.
|
|
631
|
+
*/
|
|
632
|
+
async function fundingUnresolved(session) {
|
|
633
|
+
const exit = await fundingExitDue(session, session.quote);
|
|
634
|
+
return {
|
|
635
|
+
...result("pending", session),
|
|
636
|
+
recovery: exit
|
|
637
|
+
? {
|
|
638
|
+
reason: "funding-unresolved",
|
|
639
|
+
message: exports.SAFE_RELAYR_FUNDING_EXIT_LINE,
|
|
640
|
+
exit: true,
|
|
641
|
+
}
|
|
642
|
+
: {
|
|
643
|
+
reason: "funding-unresolved",
|
|
644
|
+
message: "This saved selection has reported funding or execution activity. Check the existing bundle and its funding transactions before submitting another payment.",
|
|
645
|
+
},
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
/** Reported funding's warned exit is due: an hour since it was last reported, and every deadline final. */
|
|
649
|
+
async function fundingExitDue(session, quote) {
|
|
650
|
+
return (Date.now() - Number(session.fundingObservedAt) >= FUNDING_EXIT_MS &&
|
|
651
|
+
(await deadlinesFinal(session, quote)));
|
|
652
|
+
}
|
|
653
|
+
/** Every deadline of the quote's options and payments passed at a canonical finalized block. */
|
|
654
|
+
async function deadlinesFinal(session, quote) {
|
|
655
|
+
const deadlines = [
|
|
656
|
+
...(0, relayr_js_1.relayrQuotedOptions)(quote, session.executions.map(({ entry }) => entry.chain)).map(({ details }) => details),
|
|
657
|
+
...session.payments,
|
|
658
|
+
];
|
|
659
|
+
for (const { chainId, deadline } of deadlines) {
|
|
660
|
+
const client = options.clientFor(chainId);
|
|
661
|
+
if (!client || (await (0, relayr_js_1.relayrDeadlinePassed)(client, deadline)) !== true)
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
return true;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* A held result that says why: in progress, unknown, or closing. Unknown
|
|
668
|
+
* offers the warned exit once nothing shown can fund the quote.
|
|
669
|
+
*/
|
|
670
|
+
async function held(why, session) {
|
|
671
|
+
if (why === "funded")
|
|
672
|
+
return result("pending", session);
|
|
673
|
+
if (why === "closing")
|
|
674
|
+
return {
|
|
675
|
+
...result("pending", session),
|
|
676
|
+
recovery: {
|
|
677
|
+
reason: "quote-closing",
|
|
678
|
+
message: exports.SAFE_RELAYR_QUOTE_CLOSING_LINE,
|
|
679
|
+
retryable: true,
|
|
680
|
+
},
|
|
681
|
+
};
|
|
682
|
+
const quote = session.quote;
|
|
683
|
+
// With funding reported and unresolved, the exit also waits for the
|
|
684
|
+
// funding exit's rule (see fundingUnresolved), with its stronger warning.
|
|
685
|
+
const funded = hasObservedFunding(session);
|
|
686
|
+
const spent = !!quote &&
|
|
687
|
+
(!funded || (await fundingExitDue(session, quote))) &&
|
|
688
|
+
(await (0, relayr_js_1.relayrQuoteUnfundable)(options.clientFor, {
|
|
689
|
+
bundleUuid: quote.bundle_uuid,
|
|
690
|
+
payments: session.payments,
|
|
691
|
+
options: quote.payment_info,
|
|
692
|
+
destinationChainIds: session.executions.map(({ entry }) => entry.chain),
|
|
693
|
+
account: session.account,
|
|
694
|
+
}, { unseen: true }).catch(() => null)) === true;
|
|
695
|
+
return {
|
|
696
|
+
...result("pending", session),
|
|
697
|
+
recovery: spent
|
|
698
|
+
? {
|
|
699
|
+
reason: "status-unknown",
|
|
700
|
+
message: funded
|
|
701
|
+
? exports.SAFE_RELAYR_FUNDING_EXIT_LINE
|
|
702
|
+
: exports.SAFE_RELAYR_UNCONFIRMED_LINE,
|
|
703
|
+
retryable: true,
|
|
704
|
+
exit: true,
|
|
705
|
+
}
|
|
706
|
+
: {
|
|
707
|
+
reason: "status-unknown",
|
|
708
|
+
message: exports.SAFE_RELAYR_STATUS_UNKNOWN_LINE,
|
|
709
|
+
retryable: true,
|
|
710
|
+
},
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
/** The status-unknown refusal, for a status read outside an inspection. */
|
|
714
|
+
const unknownStatus = (session) => new SafeRelayrRecoveryError(session, exports.SAFE_RELAYR_STATUS_UNKNOWN_LINE, {
|
|
715
|
+
reason: "status-unknown",
|
|
716
|
+
message: exports.SAFE_RELAYR_STATUS_UNKNOWN_LINE,
|
|
717
|
+
retryable: true,
|
|
718
|
+
});
|
|
719
|
+
/**
|
|
720
|
+
* Why an overlapping quote is kept, or null when it may be replaced: it is
|
|
721
|
+
* unused (see {@link canReplaceSafeRelayrQuote}) and, once quoted, a status
|
|
722
|
+
* read now proves it unpaid. Funding it reports is saved; unknown keeps it.
|
|
723
|
+
*/
|
|
724
|
+
async function unreplaceable(saved) {
|
|
725
|
+
if (!canReplaceSafeRelayrQuote(saved))
|
|
726
|
+
return result("pending", saved);
|
|
727
|
+
if (!saved.bundleUuid)
|
|
728
|
+
return null;
|
|
729
|
+
const [status, read] = await bundleUnpaid(saved);
|
|
730
|
+
return status === "unpaid" ? null : held(status, read);
|
|
731
|
+
}
|
|
504
732
|
async function inspect(session, signal) {
|
|
505
733
|
const report = (progress, rechecking = false) => {
|
|
506
734
|
if (!signal?.aborted)
|
|
@@ -512,23 +740,28 @@ function createSafeRelayrController(options) {
|
|
|
512
740
|
return result("released", session);
|
|
513
741
|
if (session.state === "complete")
|
|
514
742
|
return result("complete", session);
|
|
515
|
-
if (hasObservedFunding(session) &&
|
|
516
|
-
session
|
|
743
|
+
if (hasObservedFunding(session) &&
|
|
744
|
+
(session.fundingObserved !== true ||
|
|
745
|
+
session.fundingObservedAt === undefined)) {
|
|
746
|
+
// A record saved before the clock existed starts it now.
|
|
747
|
+
session = {
|
|
748
|
+
...session,
|
|
749
|
+
fundingObserved: true,
|
|
750
|
+
fundingObservedAt: session.fundingObservedAt ?? Date.now(),
|
|
751
|
+
};
|
|
517
752
|
await store.save(session);
|
|
518
753
|
}
|
|
519
754
|
if (!session.bundleUuid || !session.quote)
|
|
520
755
|
return inspectMissingQuote(session);
|
|
521
756
|
const quote = requireSessionQuote(session);
|
|
522
757
|
const chains = session.executions.map(({ entry }) => entry.chain);
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
hasFundingRecords(bundle.transactions))
|
|
529
|
-
session =
|
|
530
|
-
await store.save(session);
|
|
531
|
-
}
|
|
758
|
+
const bundleUuid = session.bundleUuid;
|
|
759
|
+
const bundle = await statusRead(() => (0, relayr_js_1.readRelayrBundle)(bundleUuid, { fetch: fetchRelayr })).catch(() => null);
|
|
760
|
+
if (!bundle)
|
|
761
|
+
return held("unknown", session);
|
|
762
|
+
if (bundle.payment_received === true ||
|
|
763
|
+
hasFundingRecords(bundle.transactions))
|
|
764
|
+
session = await observed(session);
|
|
532
765
|
if (!Array.isArray(bundle.transactions))
|
|
533
766
|
throw new SafeRelayrRecoveryError(session, "Relayr has not returned this bundle's transactions. Check the existing bundle again.");
|
|
534
767
|
const records = bundle.transactions;
|
|
@@ -694,18 +927,28 @@ function createSafeRelayrController(options) {
|
|
|
694
927
|
everyReverted = false;
|
|
695
928
|
}
|
|
696
929
|
}
|
|
930
|
+
// Funding reported once stays unresolved until the chain proves a
|
|
931
|
+
// payment funded it (above) or the calls ran: a later unpaid read,
|
|
932
|
+
// or this session's own payments reverting, can't exclude another
|
|
933
|
+
// payment. No release, no retry.
|
|
934
|
+
if (hasObservedFunding(session))
|
|
935
|
+
return await fundingUnresolved(session);
|
|
697
936
|
if (!everyReverted) {
|
|
698
937
|
// A payment through a wallet contract that reverted is no
|
|
699
938
|
// RelayrPaymentRevertedError. Once nothing can fund the quote, it
|
|
700
|
-
// is released like an expired one.
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
939
|
+
// is released like an expired one. A payment that can't be shown
|
|
940
|
+
// reverted (dropped, or unreadable) is unknown: never released on
|
|
941
|
+
// its own, since no node can prove a transaction doesn't exist;
|
|
942
|
+
// the warned exit is offered once the chain says it has none.
|
|
943
|
+
const [status, read] = await bundleUnpaid(session);
|
|
944
|
+
session = read;
|
|
945
|
+
if (status === "funded")
|
|
946
|
+
return held("funded", session);
|
|
947
|
+
const disposition = status === "unpaid" ? await disposed(session, quote, chains) : null;
|
|
948
|
+
if (!disposition || disposition.state === "unknown")
|
|
949
|
+
return held("unknown", session);
|
|
950
|
+
session = disposition.session;
|
|
951
|
+
if (disposition.state !== "released")
|
|
709
952
|
return result("pending", session);
|
|
710
953
|
session = {
|
|
711
954
|
...session,
|
|
@@ -718,13 +961,15 @@ function createSafeRelayrController(options) {
|
|
|
718
961
|
}
|
|
719
962
|
session = { ...session, paymentStatus: "reverted" };
|
|
720
963
|
await store.save(session);
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
964
|
+
const [status, read] = await bundleUnpaid(session);
|
|
965
|
+
session = read;
|
|
966
|
+
if (status !== "unpaid")
|
|
967
|
+
return held(status, session);
|
|
968
|
+
// Unpaid, its release may still be unproven: it says why.
|
|
969
|
+
const disposition = (await disposed(session, quote, chains, true));
|
|
970
|
+
session = disposition.session;
|
|
971
|
+
if (disposition.state === "unknown")
|
|
972
|
+
return held("unknown", session);
|
|
728
973
|
if (disposition.state === "funded")
|
|
729
974
|
return result("pending", session);
|
|
730
975
|
if (disposition.state === "released") {
|
|
@@ -742,13 +987,7 @@ function createSafeRelayrController(options) {
|
|
|
742
987
|
session.fundingObserved) {
|
|
743
988
|
// A wallet invocation without a known hash may still have broadcast.
|
|
744
989
|
return session.fundingObserved && !known.length
|
|
745
|
-
?
|
|
746
|
-
...result("pending", session),
|
|
747
|
-
recovery: {
|
|
748
|
-
reason: "funding-unresolved",
|
|
749
|
-
message: "This saved selection has reported funding or execution activity. Check the existing bundle and its funding transactions before submitting another payment.",
|
|
750
|
-
},
|
|
751
|
-
}
|
|
990
|
+
? await fundingUnresolved(session)
|
|
752
991
|
: result("pending", session);
|
|
753
992
|
}
|
|
754
993
|
const quoted = (0, relayr_js_1.relayrQuotedOptions)(quote, chains);
|
|
@@ -759,18 +998,15 @@ function createSafeRelayrController(options) {
|
|
|
759
998
|
? [false]
|
|
760
999
|
: await Promise.all(quoted.map(async ({ details }) => {
|
|
761
1000
|
const client = options.clientFor(details.chainId);
|
|
762
|
-
return
|
|
763
|
-
|
|
1001
|
+
return client
|
|
1002
|
+
? (0, relayr_js_1.relayrDeadlinePassed)(client, details.deadline)
|
|
1003
|
+
: false;
|
|
764
1004
|
}));
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
}
|
|
771
|
-
catch {
|
|
772
|
-
return result("pending", session);
|
|
773
|
-
}
|
|
1005
|
+
const [unpaid, read] = await bundleUnpaid(session);
|
|
1006
|
+
session = read;
|
|
1007
|
+
if (unpaid !== "unpaid")
|
|
1008
|
+
return held(unpaid, session);
|
|
1009
|
+
if (expired.every((passed) => passed === true)) {
|
|
774
1010
|
session = {
|
|
775
1011
|
...session,
|
|
776
1012
|
state: "released",
|
|
@@ -780,15 +1016,11 @@ function createSafeRelayrController(options) {
|
|
|
780
1016
|
await store.save(session);
|
|
781
1017
|
return result("released", session);
|
|
782
1018
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
catch {
|
|
789
|
-
return result("pending", session);
|
|
790
|
-
}
|
|
791
|
-
const allowed = session.payments.length && payments.length
|
|
1019
|
+
// No option the browser's clock still allows, though the chain hasn't
|
|
1020
|
+
// shown the quote expired: closing, or unknown if the chain couldn't say.
|
|
1021
|
+
if (!payments.length)
|
|
1022
|
+
return held(expired.some((passed) => passed === null) ? "unknown" : "closing", session);
|
|
1023
|
+
const allowed = session.payments.length
|
|
792
1024
|
? [(0, relayr_js_1.relayrRetryOption)(session.payments, payments)]
|
|
793
1025
|
: payments;
|
|
794
1026
|
return allowed.length
|
|
@@ -807,9 +1039,12 @@ function createSafeRelayrController(options) {
|
|
|
807
1039
|
return store.withLock(account, async () => {
|
|
808
1040
|
checkAccount(account, signal);
|
|
809
1041
|
const reservations = (await store.list(account)).filter((session) => reserves(session, executions));
|
|
810
|
-
for (const
|
|
811
|
-
|
|
1042
|
+
for (const found of reservations) {
|
|
1043
|
+
const kept = await unreplaceable(found);
|
|
1044
|
+
if (!kept)
|
|
812
1045
|
continue;
|
|
1046
|
+
// What its status read saved, a funding report included.
|
|
1047
|
+
const saved = kept.session;
|
|
813
1048
|
let checked;
|
|
814
1049
|
try {
|
|
815
1050
|
checked = await inspect(saved, signal);
|
|
@@ -845,8 +1080,9 @@ function createSafeRelayrController(options) {
|
|
|
845
1080
|
for (const saved of await store.list(account)) {
|
|
846
1081
|
if (!reserves(saved, executions))
|
|
847
1082
|
continue;
|
|
848
|
-
|
|
849
|
-
|
|
1083
|
+
const kept = await unreplaceable(saved);
|
|
1084
|
+
if (kept)
|
|
1085
|
+
throw new SafeRelayrRecoveryError(kept.session, kept.recovery?.message, kept.recovery);
|
|
850
1086
|
await store.save({
|
|
851
1087
|
...saved,
|
|
852
1088
|
state: "released",
|
|
@@ -941,17 +1177,21 @@ function createSafeRelayrController(options) {
|
|
|
941
1177
|
phase("payment-checking", session, signal);
|
|
942
1178
|
await revalidate(session.executions, account, onStatus, true, signal);
|
|
943
1179
|
await assertUnreserved(account, session.executions, session.id);
|
|
944
|
-
|
|
945
|
-
|
|
1180
|
+
await statusRead(() => session.payments.length
|
|
1181
|
+
? (0, relayr_js_1.requireRelayrRetry)(options.clientFor, {
|
|
946
1182
|
payments: session.payments,
|
|
947
1183
|
from: account,
|
|
948
1184
|
bundleUuid: session.bundleUuid,
|
|
949
|
-
}, { fetch: fetchRelayr })
|
|
950
|
-
|
|
951
|
-
else
|
|
952
|
-
await (0, relayr_js_1.requireRelayrBundleUnpaid)(session.bundleUuid, {
|
|
1185
|
+
}, { fetch: fetchRelayr })
|
|
1186
|
+
: (0, relayr_js_1.requireRelayrBundleUnpaid)(session.bundleUuid, {
|
|
953
1187
|
fetch: fetchRelayr,
|
|
954
|
-
})
|
|
1188
|
+
})).catch(async (error) => {
|
|
1189
|
+
if (statusUnknown(error))
|
|
1190
|
+
throw unknownStatus(session);
|
|
1191
|
+
if (fundingReported(error))
|
|
1192
|
+
session = await observed(session);
|
|
1193
|
+
throw error;
|
|
1194
|
+
});
|
|
955
1195
|
checkAccount(account, signal);
|
|
956
1196
|
requireOpenPayment();
|
|
957
1197
|
verified = true;
|
|
@@ -960,6 +1200,17 @@ function createSafeRelayrController(options) {
|
|
|
960
1200
|
checkAccount(account, signal);
|
|
961
1201
|
if (!verified || sending)
|
|
962
1202
|
throw new Error("Recheck the Safe execution before funding it.");
|
|
1203
|
+
// The saved record, read again just before the wallet opens: funding
|
|
1204
|
+
// another flow reported, or any change to its payment, refuses.
|
|
1205
|
+
const stored = await load(account, session.id);
|
|
1206
|
+
if (hasObservedFunding(stored) ||
|
|
1207
|
+
stored.state !== session.state ||
|
|
1208
|
+
stored.paymentStatus !== session.paymentStatus ||
|
|
1209
|
+
stored.bundleUuid !== session.bundleUuid ||
|
|
1210
|
+
JSON.stringify(stored.payments) !== JSON.stringify(session.payments))
|
|
1211
|
+
throw new SafeRelayrRecoveryError(stored, hasObservedFunding(stored)
|
|
1212
|
+
? exports.SAFE_RELAYR_FUNDING_EXIT_LINE
|
|
1213
|
+
: "This saved selection changed while it was being paid. Check it again before paying.");
|
|
963
1214
|
session = { ...session, paymentStatus: "sending" };
|
|
964
1215
|
await store.save(session);
|
|
965
1216
|
try {
|
|
@@ -1117,6 +1368,26 @@ function createSafeRelayrController(options) {
|
|
|
1117
1368
|
signal?.removeEventListener("abort", stop);
|
|
1118
1369
|
}
|
|
1119
1370
|
}
|
|
1120
|
-
|
|
1371
|
+
/**
|
|
1372
|
+
* The user's warned exit from a quote whose status stays unknown (see
|
|
1373
|
+
* {@link SafeRelayrRecovery.exit}): rechecked now, then released, its
|
|
1374
|
+
* record kept for reconciliation, so a new quote can follow.
|
|
1375
|
+
*/
|
|
1376
|
+
async function exit({ account, sessionId, signal, }) {
|
|
1377
|
+
return store.withLock(account, async () => {
|
|
1378
|
+
checkAccount(account, signal);
|
|
1379
|
+
const checked = await inspect(await load(account, sessionId), signal);
|
|
1380
|
+
if (!checked.recovery?.exit)
|
|
1381
|
+
throw new SafeRelayrRecoveryError(checked.session, checked.recovery?.message, checked.recovery);
|
|
1382
|
+
const session = {
|
|
1383
|
+
...checked.session,
|
|
1384
|
+
state: "released",
|
|
1385
|
+
releaseReason: "payment-unconfirmed",
|
|
1386
|
+
};
|
|
1387
|
+
await store.save(session);
|
|
1388
|
+
return result("released", session);
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
return { prepare, check, fund, watch, exit };
|
|
1121
1392
|
}
|
|
1122
1393
|
//# sourceMappingURL=safeRelayr.js.map
|