@bananapus/nana-sdk-core 6.1.0 → 6.3.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/confirming.d.ts +8 -0
- package/dist/cjs/review/confirming.d.ts.map +1 -0
- package/dist/cjs/review/confirming.js +11 -0
- package/dist/cjs/review/confirming.js.map +1 -0
- package/dist/cjs/review/heldWrite.d.ts +6 -1
- package/dist/cjs/review/heldWrite.d.ts.map +1 -1
- package/dist/cjs/review/heldWrite.js +13 -6
- package/dist/cjs/review/heldWrite.js.map +1 -1
- package/dist/cjs/review/locks.d.ts +141 -2
- package/dist/cjs/review/locks.d.ts.map +1 -1
- package/dist/cjs/review/locks.js +406 -58
- package/dist/cjs/review/locks.js.map +1 -1
- package/dist/cjs/review/messages.d.ts +16 -0
- package/dist/cjs/review/messages.d.ts.map +1 -1
- package/dist/cjs/review/messages.js +17 -1
- package/dist/cjs/review/messages.js.map +1 -1
- package/dist/cjs/review/react/ownership.d.ts +30 -0
- package/dist/cjs/review/react/ownership.d.ts.map +1 -0
- package/dist/cjs/review/react/ownership.js +27 -0
- package/dist/cjs/review/react/ownership.js.map +1 -0
- package/dist/cjs/review/react/useReviewedRun.d.ts +17 -3
- package/dist/cjs/review/react/useReviewedRun.d.ts.map +1 -1
- package/dist/cjs/review/react/useReviewedRun.js +528 -68
- package/dist/cjs/review/react/useReviewedRun.js.map +1 -1
- package/dist/cjs/review/react/useSavedAttempt.d.ts.map +1 -1
- package/dist/cjs/review/react/useSavedAttempt.js +12 -0
- package/dist/cjs/review/react/useSavedAttempt.js.map +1 -1
- package/dist/cjs/review/relayrSend.d.ts +20 -0
- package/dist/cjs/review/relayrSend.d.ts.map +1 -1
- package/dist/cjs/review/relayrSend.js +150 -105
- package/dist/cjs/review/relayrSend.js.map +1 -1
- package/dist/cjs/review/runStatus.d.ts +6 -2
- package/dist/cjs/review/runStatus.d.ts.map +1 -1
- package/dist/cjs/review/runStatus.js +3 -1
- package/dist/cjs/review/runStatus.js.map +1 -1
- package/dist/cjs/review/safeRelayr.d.ts +12 -2
- package/dist/cjs/review/safeRelayr.d.ts.map +1 -1
- package/dist/cjs/review/safeRelayr.js +226 -156
- package/dist/cjs/review/safeRelayr.js.map +1 -1
- package/dist/cjs/review/savedAttempt.d.ts.map +1 -1
- package/dist/cjs/review/savedAttempt.js +7 -2
- package/dist/cjs/review/savedAttempt.js.map +1 -1
- package/dist/cjs/review/send.d.ts +46 -5
- package/dist/cjs/review/send.d.ts.map +1 -1
- package/dist/cjs/review/send.js +251 -58
- package/dist/cjs/review/send.js.map +1 -1
- package/dist/cjs/review/sequence.d.ts +25 -0
- package/dist/cjs/review/sequence.d.ts.map +1 -1
- package/dist/cjs/review/sequence.js +45 -7
- package/dist/cjs/review/sequence.js.map +1 -1
- package/dist/cjs/review/sign.d.ts +6 -1
- package/dist/cjs/review/sign.d.ts.map +1 -1
- package/dist/cjs/review/sign.js +10 -4
- package/dist/cjs/review/sign.js.map +1 -1
- package/dist/cjs/review/tabState.d.ts +4 -0
- package/dist/cjs/review/tabState.d.ts.map +1 -1
- package/dist/cjs/review/tabState.js +4 -0
- package/dist/cjs/review/tabState.js.map +1 -1
- package/dist/cjs/review/wallet.d.ts +16 -4
- package/dist/cjs/review/wallet.d.ts.map +1 -1
- package/dist/cjs/review/wallet.js +62 -17
- package/dist/cjs/review/wallet.js.map +1 -1
- package/dist/cjs/review/writeHold.d.ts +92 -14
- package/dist/cjs/review/writeHold.d.ts.map +1 -1
- package/dist/cjs/review/writeHold.js +102 -26
- package/dist/cjs/review/writeHold.js.map +1 -1
- package/dist/cjs/review/writeRecovery.d.ts +42 -7
- package/dist/cjs/review/writeRecovery.d.ts.map +1 -1
- package/dist/cjs/review/writeRecovery.js +183 -21
- package/dist/cjs/review/writeRecovery.js.map +1 -1
- package/dist/cjs/testing.d.ts +13 -0
- package/dist/cjs/testing.d.ts.map +1 -1
- package/dist/cjs/testing.js +21 -0
- package/dist/cjs/testing.js.map +1 -1
- package/dist/esm/review/confirming.d.ts +8 -0
- package/dist/esm/review/confirming.d.ts.map +1 -0
- package/dist/esm/review/confirming.js +8 -0
- package/dist/esm/review/confirming.js.map +1 -0
- package/dist/esm/review/heldWrite.d.ts +6 -1
- package/dist/esm/review/heldWrite.d.ts.map +1 -1
- package/dist/esm/review/heldWrite.js +13 -6
- package/dist/esm/review/heldWrite.js.map +1 -1
- package/dist/esm/review/locks.d.ts +141 -2
- package/dist/esm/review/locks.d.ts.map +1 -1
- package/dist/esm/review/locks.js +396 -58
- package/dist/esm/review/locks.js.map +1 -1
- package/dist/esm/review/messages.d.ts +16 -0
- package/dist/esm/review/messages.d.ts.map +1 -1
- package/dist/esm/review/messages.js +16 -0
- package/dist/esm/review/messages.js.map +1 -1
- package/dist/esm/review/react/ownership.d.ts +30 -0
- package/dist/esm/review/react/ownership.d.ts.map +1 -0
- package/dist/esm/review/react/ownership.js +24 -0
- package/dist/esm/review/react/ownership.js.map +1 -0
- package/dist/esm/review/react/useReviewedRun.d.ts +17 -3
- package/dist/esm/review/react/useReviewedRun.d.ts.map +1 -1
- package/dist/esm/review/react/useReviewedRun.js +529 -69
- package/dist/esm/review/react/useReviewedRun.js.map +1 -1
- package/dist/esm/review/react/useSavedAttempt.d.ts.map +1 -1
- package/dist/esm/review/react/useSavedAttempt.js +12 -0
- package/dist/esm/review/react/useSavedAttempt.js.map +1 -1
- package/dist/esm/review/relayrSend.d.ts +20 -0
- package/dist/esm/review/relayrSend.d.ts.map +1 -1
- package/dist/esm/review/relayrSend.js +154 -109
- package/dist/esm/review/relayrSend.js.map +1 -1
- package/dist/esm/review/runStatus.d.ts +6 -2
- package/dist/esm/review/runStatus.d.ts.map +1 -1
- package/dist/esm/review/runStatus.js +3 -1
- package/dist/esm/review/runStatus.js.map +1 -1
- package/dist/esm/review/safeRelayr.d.ts +12 -2
- package/dist/esm/review/safeRelayr.d.ts.map +1 -1
- package/dist/esm/review/safeRelayr.js +226 -156
- package/dist/esm/review/safeRelayr.js.map +1 -1
- package/dist/esm/review/savedAttempt.d.ts.map +1 -1
- package/dist/esm/review/savedAttempt.js +7 -2
- package/dist/esm/review/savedAttempt.js.map +1 -1
- package/dist/esm/review/send.d.ts +46 -5
- package/dist/esm/review/send.d.ts.map +1 -1
- package/dist/esm/review/send.js +252 -59
- package/dist/esm/review/send.js.map +1 -1
- package/dist/esm/review/sequence.d.ts +25 -0
- package/dist/esm/review/sequence.d.ts.map +1 -1
- package/dist/esm/review/sequence.js +43 -7
- package/dist/esm/review/sequence.js.map +1 -1
- package/dist/esm/review/sign.d.ts +6 -1
- package/dist/esm/review/sign.d.ts.map +1 -1
- package/dist/esm/review/sign.js +10 -4
- package/dist/esm/review/sign.js.map +1 -1
- package/dist/esm/review/tabState.d.ts +4 -0
- package/dist/esm/review/tabState.d.ts.map +1 -1
- package/dist/esm/review/tabState.js +4 -0
- package/dist/esm/review/tabState.js.map +1 -1
- package/dist/esm/review/wallet.d.ts +16 -4
- package/dist/esm/review/wallet.d.ts.map +1 -1
- package/dist/esm/review/wallet.js +62 -17
- package/dist/esm/review/wallet.js.map +1 -1
- package/dist/esm/review/writeHold.d.ts +92 -14
- package/dist/esm/review/writeHold.d.ts.map +1 -1
- package/dist/esm/review/writeHold.js +102 -26
- package/dist/esm/review/writeHold.js.map +1 -1
- package/dist/esm/review/writeRecovery.d.ts +42 -7
- package/dist/esm/review/writeRecovery.d.ts.map +1 -1
- package/dist/esm/review/writeRecovery.js +180 -22
- package/dist/esm/review/writeRecovery.js.map +1 -1
- package/dist/esm/testing.d.ts +13 -0
- package/dist/esm/testing.d.ts.map +1 -1
- package/dist/esm/testing.js +20 -0
- package/dist/esm/testing.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { SAFE_EXEC_ABI, canonicalSafeTxHash, requireSafeExecutionSuccess, safeEx
|
|
|
5
5
|
import { RELAYR_API, RELAYR_UUID_RE, MAX_RELAYR_SENT_PAYMENTS, atCanonicalFinalizedBlock, bindRelayrQuote, readRelayrBundle, relayrBundleRequest, relayrPaymentOptions, relayrPaymentDetails, relayrPaymentAttemptOutcome, relayrDestinationHash, relayrDestinationHashes, relayrRetryOption, relayrSentPaymentsSnapshot, relayrStateIsPending, relayrStateIsFailed, relayrDeadlinePassed, relayrQuotedOptions, relayrQuoteUnfundable, RelayrPaymentRetryError, requireRelayrBundleUnpaid, requireRelayrRetry, revertedRelayrQuote, verifyRelayrDestinations, verifyRelayrDestination, verifyRelayrPayment, RelayrPaymentRevertedError, RelayrProofUnavailableError, } from "./relayr.js";
|
|
6
6
|
import { ReviewFixedLineError } from "./lineError.js";
|
|
7
7
|
import { TAB, tabShared } from "./tabState.js";
|
|
8
|
+
import { assertLocksHeld, keepOutcome, LockLostError, } from "./locks.js";
|
|
8
9
|
function hasFundingRecords(records) {
|
|
9
10
|
if (records === undefined)
|
|
10
11
|
return false;
|
|
@@ -331,14 +332,28 @@ export function createSafeRelayrController(options) {
|
|
|
331
332
|
* `fundingObservedAt` another flow saved. Nothing ever clears them; an
|
|
332
333
|
* exit keeps them for reconciliation.
|
|
333
334
|
*/
|
|
335
|
+
/** The locks each account's `withLock` holds, while it runs. */
|
|
336
|
+
const storeLocks = new Map();
|
|
334
337
|
const store = {
|
|
335
338
|
get scope() {
|
|
336
339
|
return options.store.scope;
|
|
337
340
|
},
|
|
338
341
|
list: (account) => options.store.list(account),
|
|
339
|
-
withLock: (account, run) => options.store.withLock(account,
|
|
342
|
+
withLock: (account, run) => options.store.withLock(account, async (locks) => {
|
|
343
|
+
const key = account.toLowerCase();
|
|
344
|
+
const outer = storeLocks.get(key);
|
|
345
|
+
storeLocks.set(key, locks);
|
|
346
|
+
try {
|
|
347
|
+
return await run(locks);
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
storeLocks.set(key, outer);
|
|
351
|
+
}
|
|
352
|
+
}),
|
|
340
353
|
async save(next) {
|
|
341
354
|
const saved = (await options.store.list(next.account)).find((session) => session.id === next.id);
|
|
355
|
+
// Under the store's lock: still this tab's after every read, right before the write.
|
|
356
|
+
await assertLocksHeld(storeLocks.get(next.account.toLowerCase()));
|
|
342
357
|
const at = [saved?.fundingObservedAt, next.fundingObservedAt].filter((value) => typeof value === "number");
|
|
343
358
|
// Once seen, funding stays, and its clock only moves forward.
|
|
344
359
|
return options.store.save(saved?.fundingObserved || next.fundingObserved
|
|
@@ -1204,179 +1219,234 @@ export function createSafeRelayrController(options) {
|
|
|
1204
1219
|
});
|
|
1205
1220
|
}
|
|
1206
1221
|
async function fund({ account, sessionId, paymentChainId, signal, onStatus, }) {
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
let sending = false;
|
|
1228
|
-
let paymentPersisted = false;
|
|
1229
|
-
const requireOpenPayment = () => relayrPaymentDetails(payment, {
|
|
1230
|
-
bundleUuid: session.bundleUuid,
|
|
1231
|
-
destinationChainIds: session.executions.map(({ entry }) => entry.chain),
|
|
1222
|
+
// Once this attempt saved "sending", every later save goes through
|
|
1223
|
+
// `keep` (`keepOutcome`): one refused for a lost lock leaves what it
|
|
1224
|
+
// would have saved (the attempt's outcome) to `settle`, applied once the
|
|
1225
|
+
// store's locks are taken back, to the saved record only while it is
|
|
1226
|
+
// still this attempt's. The caller still gets a `LockLostError`.
|
|
1227
|
+
const key = account.toLowerCase();
|
|
1228
|
+
const keep = async (next, settle) => {
|
|
1229
|
+
const locks = storeLocks.get(key);
|
|
1230
|
+
const saved = await keepOutcome(locks, () => store.save(next), async () => {
|
|
1231
|
+
// The save path checks the same locks, taken back.
|
|
1232
|
+
const before = storeLocks.get(key);
|
|
1233
|
+
storeLocks.set(key, locks);
|
|
1234
|
+
try {
|
|
1235
|
+
const settled = settle(await load(account, sessionId));
|
|
1236
|
+
if (settled)
|
|
1237
|
+
await store.save(settled);
|
|
1238
|
+
}
|
|
1239
|
+
finally {
|
|
1240
|
+
storeLocks.set(key, before);
|
|
1241
|
+
}
|
|
1232
1242
|
});
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1243
|
+
if (!saved)
|
|
1244
|
+
throw new LockLostError(locks[0].name);
|
|
1245
|
+
};
|
|
1246
|
+
return funding();
|
|
1247
|
+
async function funding() {
|
|
1248
|
+
return store.withLock(account, async () => {
|
|
1249
|
+
checkAccount(account, signal);
|
|
1250
|
+
let session = await load(account, sessionId);
|
|
1251
|
+
const checked = await inspect(session, signal);
|
|
1252
|
+
session = checked.session;
|
|
1253
|
+
if (checked.state !== "ready")
|
|
1254
|
+
throw new SafeRelayrRecoveryError(session, checked.recovery?.message, checked.recovery);
|
|
1255
|
+
if (session.payments.length >= MAX_RELAYR_SENT_PAYMENTS)
|
|
1256
|
+
throw new SafeRelayrRecoveryError(session, "This quote has reached its saved payment attempt limit. Check the existing bundle.");
|
|
1257
|
+
const payment = checked.payments.find((option) => option.chain === paymentChainId);
|
|
1258
|
+
if (!payment)
|
|
1259
|
+
throw new Error("Choose a current payment option from this saved quote.");
|
|
1236
1260
|
await assertUnreserved(account, session.executions, session.id);
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
session = await observed(session);
|
|
1250
|
-
throw error;
|
|
1261
|
+
if (!reviewedIntents.has(reviewKey(session))) {
|
|
1262
|
+
phase("reviewing", session, signal);
|
|
1263
|
+
await options.review(session.executions, { resumed: true });
|
|
1264
|
+
checkAccount(account, signal);
|
|
1265
|
+
rememberReview(session);
|
|
1266
|
+
}
|
|
1267
|
+
let verified = false;
|
|
1268
|
+
let sending = false;
|
|
1269
|
+
let paymentPersisted = false;
|
|
1270
|
+
const requireOpenPayment = () => relayrPaymentDetails(payment, {
|
|
1271
|
+
bundleUuid: session.bundleUuid,
|
|
1272
|
+
destinationChainIds: session.executions.map(({ entry }) => entry.chain),
|
|
1251
1273
|
});
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
session = { ...session, paymentStatus: "sending" };
|
|
1272
|
-
await store.save(session);
|
|
1273
|
-
try {
|
|
1274
|
+
const beforeSend = async () => {
|
|
1275
|
+
phase("payment-checking", session, signal);
|
|
1276
|
+
await revalidate(session.executions, account, onStatus, true, signal);
|
|
1277
|
+
await assertUnreserved(account, session.executions, session.id);
|
|
1278
|
+
await statusRead(() => session.payments.length
|
|
1279
|
+
? requireRelayrRetry(options.clientFor, {
|
|
1280
|
+
payments: session.payments,
|
|
1281
|
+
from: account,
|
|
1282
|
+
bundleUuid: session.bundleUuid,
|
|
1283
|
+
}, { fetch: fetchRelayr })
|
|
1284
|
+
: requireRelayrBundleUnpaid(session.bundleUuid, {
|
|
1285
|
+
fetch: fetchRelayr,
|
|
1286
|
+
})).catch(async (error) => {
|
|
1287
|
+
if (statusUnknown(error))
|
|
1288
|
+
throw unknownStatus(session);
|
|
1289
|
+
if (fundingReported(error))
|
|
1290
|
+
session = await observed(session);
|
|
1291
|
+
throw error;
|
|
1292
|
+
});
|
|
1274
1293
|
checkAccount(account, signal);
|
|
1275
1294
|
requireOpenPayment();
|
|
1276
|
-
|
|
1277
|
-
|
|
1295
|
+
verified = true;
|
|
1296
|
+
};
|
|
1297
|
+
/**
|
|
1298
|
+
* Nothing was sent after "sending" was saved: the status before this
|
|
1299
|
+
* attempt again. A lost lock leaves it to the fresh-lock reconcile,
|
|
1300
|
+
* and the caller still gets the reason nothing was sent.
|
|
1301
|
+
*/
|
|
1302
|
+
const restoreUnsent = async () => {
|
|
1278
1303
|
session = {
|
|
1279
1304
|
...session,
|
|
1280
1305
|
paymentStatus: checked.session.paymentStatus,
|
|
1281
1306
|
};
|
|
1307
|
+
const restored = session;
|
|
1308
|
+
await keep(restored, (current) => current.paymentStatus === "sending" &&
|
|
1309
|
+
current.bundleUuid === restored.bundleUuid &&
|
|
1310
|
+
JSON.stringify(current.payments) ===
|
|
1311
|
+
JSON.stringify(restored.payments)
|
|
1312
|
+
? { ...current, paymentStatus: restored.paymentStatus }
|
|
1313
|
+
: undefined).catch((saveError) => {
|
|
1314
|
+
if (!(saveError instanceof LockLostError))
|
|
1315
|
+
throw saveError;
|
|
1316
|
+
});
|
|
1317
|
+
};
|
|
1318
|
+
const onSending = async () => {
|
|
1319
|
+
checkAccount(account, signal);
|
|
1320
|
+
if (!verified || sending)
|
|
1321
|
+
throw new Error("Recheck the Safe execution before funding it.");
|
|
1322
|
+
// The saved record, read again just before the wallet opens: funding
|
|
1323
|
+
// another flow reported, or any change to its payment, refuses.
|
|
1324
|
+
const stored = await load(account, session.id);
|
|
1325
|
+
if (hasObservedFunding(stored) ||
|
|
1326
|
+
stored.state !== session.state ||
|
|
1327
|
+
stored.paymentStatus !== session.paymentStatus ||
|
|
1328
|
+
stored.bundleUuid !== session.bundleUuid ||
|
|
1329
|
+
JSON.stringify(stored.payments) !== JSON.stringify(session.payments))
|
|
1330
|
+
throw new SafeRelayrRecoveryError(stored, hasObservedFunding(stored)
|
|
1331
|
+
? SAFE_RELAYR_FUNDING_EXIT_LINE
|
|
1332
|
+
: "This saved selection changed while it was being paid. Check it again before paying.");
|
|
1333
|
+
session = { ...session, paymentStatus: "sending" };
|
|
1282
1334
|
await store.save(session);
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
throw new Error("The payment does not match the saved Safe bundle.");
|
|
1294
|
-
if (!snapshot.some((item) => !checked.session.payments.some((prior) => prior.hash.toLowerCase() === item.hash.toLowerCase())))
|
|
1295
|
-
throw new Error("The new payment attempt has no new transaction hash.");
|
|
1296
|
-
for (const prior of session.payments) {
|
|
1297
|
-
if (!snapshot.some((item) => item.hash.toLowerCase() === prior.hash.toLowerCase() &&
|
|
1298
|
-
item.chainId === prior.chainId &&
|
|
1299
|
-
item.calldata.toLowerCase() === prior.calldata.toLowerCase() &&
|
|
1300
|
-
item.amount === prior.amount &&
|
|
1301
|
-
item.deadline === prior.deadline &&
|
|
1302
|
-
item.target.toLowerCase() === prior.target.toLowerCase()))
|
|
1303
|
-
throw new Error("The saved payment history must retain every known payment.");
|
|
1304
|
-
}
|
|
1305
|
-
const details = relayrPaymentDetails(payment, {
|
|
1306
|
-
bundleUuid: session.bundleUuid,
|
|
1307
|
-
destinationChainIds: session.executions.map(({ entry }) => entry.chain),
|
|
1308
|
-
nowSeconds: 0,
|
|
1309
|
-
});
|
|
1310
|
-
for (const item of snapshot.filter((item) => !session.payments.some((prior) => prior.hash === item.hash))) {
|
|
1311
|
-
if (item.chainId !== details.chainId ||
|
|
1312
|
-
BigInt(item.amount) !== details.amount ||
|
|
1313
|
-
BigInt(item.deadline) !== details.deadline ||
|
|
1314
|
-
item.calldata.toLowerCase() !== details.calldata.toLowerCase() ||
|
|
1315
|
-
item.target.toLowerCase() !== details.target.toLowerCase())
|
|
1316
|
-
throw new Error("The funding payment differs from the selected Relayr option.");
|
|
1317
|
-
}
|
|
1318
|
-
paymentPersisted = false;
|
|
1319
|
-
session = {
|
|
1320
|
-
...session,
|
|
1321
|
-
payments: structuredClone(payments),
|
|
1322
|
-
paymentStatus: "submitted",
|
|
1335
|
+
try {
|
|
1336
|
+
checkAccount(account, signal);
|
|
1337
|
+
requireOpenPayment();
|
|
1338
|
+
}
|
|
1339
|
+
catch (error) {
|
|
1340
|
+
await restoreUnsent();
|
|
1341
|
+
throw error;
|
|
1342
|
+
}
|
|
1343
|
+
sending = true;
|
|
1344
|
+
phase("payment-submitting", session, signal);
|
|
1323
1345
|
};
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
session.payments.
|
|
1346
|
+
const onSent = async (payments) => {
|
|
1347
|
+
const snapshot = relayrSentPaymentsSnapshot(payments);
|
|
1348
|
+
if (!sending ||
|
|
1349
|
+
!snapshot?.length ||
|
|
1350
|
+
snapshot.some((item) => item.bundleUuid !== session.bundleUuid))
|
|
1351
|
+
throw new Error("The payment does not match the saved Safe bundle.");
|
|
1352
|
+
if (!snapshot.some((item) => !checked.session.payments.some((prior) => prior.hash.toLowerCase() === item.hash.toLowerCase())))
|
|
1353
|
+
throw new Error("The new payment attempt has no new transaction hash.");
|
|
1354
|
+
for (const prior of session.payments) {
|
|
1355
|
+
if (!snapshot.some((item) => item.hash.toLowerCase() === prior.hash.toLowerCase() &&
|
|
1356
|
+
item.chainId === prior.chainId &&
|
|
1357
|
+
item.calldata.toLowerCase() ===
|
|
1358
|
+
prior.calldata.toLowerCase() &&
|
|
1359
|
+
item.amount === prior.amount &&
|
|
1360
|
+
item.deadline === prior.deadline &&
|
|
1361
|
+
item.target.toLowerCase() === prior.target.toLowerCase()))
|
|
1362
|
+
throw new Error("The saved payment history must retain every known payment.");
|
|
1363
|
+
}
|
|
1364
|
+
const details = relayrPaymentDetails(payment, {
|
|
1365
|
+
bundleUuid: session.bundleUuid,
|
|
1366
|
+
destinationChainIds: session.executions.map(({ entry }) => entry.chain),
|
|
1367
|
+
nowSeconds: 0,
|
|
1368
|
+
});
|
|
1369
|
+
for (const item of snapshot.filter((item) => !session.payments.some((prior) => prior.hash === item.hash))) {
|
|
1370
|
+
if (item.chainId !== details.chainId ||
|
|
1371
|
+
BigInt(item.amount) !== details.amount ||
|
|
1372
|
+
BigInt(item.deadline) !== details.deadline ||
|
|
1373
|
+
item.calldata.toLowerCase() !== details.calldata.toLowerCase() ||
|
|
1374
|
+
item.target.toLowerCase() !== details.target.toLowerCase())
|
|
1375
|
+
throw new Error("The funding payment differs from the selected Relayr option.");
|
|
1376
|
+
}
|
|
1377
|
+
paymentPersisted = false;
|
|
1348
1378
|
session = {
|
|
1349
1379
|
...session,
|
|
1350
|
-
|
|
1380
|
+
payments: structuredClone(payments),
|
|
1381
|
+
paymentStatus: "submitted",
|
|
1351
1382
|
};
|
|
1352
|
-
|
|
1353
|
-
|
|
1383
|
+
const sent = session.payments;
|
|
1384
|
+
await keep(session, (current) => {
|
|
1385
|
+
const missing = sent.filter((item) => !current.payments.some((prior) => prior.hash.toLowerCase() === item.hash.toLowerCase()));
|
|
1386
|
+
return current.bundleUuid === session.bundleUuid &&
|
|
1387
|
+
(missing.length || current.paymentStatus === "sending")
|
|
1388
|
+
? {
|
|
1389
|
+
...current,
|
|
1390
|
+
payments: [...current.payments, ...missing],
|
|
1391
|
+
paymentStatus: current.paymentStatus === "sending" ||
|
|
1392
|
+
current.paymentStatus === "unfunded"
|
|
1393
|
+
? "submitted"
|
|
1394
|
+
: current.paymentStatus,
|
|
1395
|
+
}
|
|
1396
|
+
: undefined;
|
|
1397
|
+
});
|
|
1398
|
+
paymentPersisted = true;
|
|
1399
|
+
phase("payment-confirming", session, signal);
|
|
1400
|
+
};
|
|
1401
|
+
try {
|
|
1402
|
+
phase("payment-review", session, signal);
|
|
1403
|
+
const sent = await options.sendPayment({
|
|
1404
|
+
session: structuredClone(session),
|
|
1405
|
+
payment,
|
|
1406
|
+
beforeSend,
|
|
1407
|
+
onSending,
|
|
1408
|
+
onSent,
|
|
1409
|
+
});
|
|
1410
|
+
if (!sent.payments.some((item) => item.hash.toLowerCase() === sent.hash.toLowerCase()))
|
|
1411
|
+
throw new Error("The funding result omitted its transaction hash.");
|
|
1412
|
+
await onSent(sent.payments);
|
|
1354
1413
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1414
|
+
catch (error) {
|
|
1415
|
+
if (sending &&
|
|
1416
|
+
// Classify only this attempt; restoring the previous status below
|
|
1417
|
+
// preserves the quote's earlier payments and their retry policy.
|
|
1418
|
+
relayrPaymentAttemptOutcome(error, { sending, paid: false }) ===
|
|
1419
|
+
"unpaid" &&
|
|
1420
|
+
session.payments.length === checked.session.payments.length) {
|
|
1421
|
+
await restoreUnsent();
|
|
1422
|
+
throw error;
|
|
1423
|
+
}
|
|
1424
|
+
if (sending ||
|
|
1425
|
+
session.payments.length > checked.session.payments.length) {
|
|
1426
|
+
// A saved hash may already have funded or completed the bundle even
|
|
1427
|
+
// when the wallet adapter could not prove it. Inspect once, read-only;
|
|
1428
|
+
// never turn an uncertain or unsaved attempt into a retry invitation.
|
|
1429
|
+
if (paymentPersisted) {
|
|
1430
|
+
try {
|
|
1431
|
+
const inspected = await inspect(session, signal);
|
|
1432
|
+
session = inspected.session;
|
|
1433
|
+
if (inspected.state === "complete" ||
|
|
1434
|
+
(inspected.state === "pending" &&
|
|
1435
|
+
session.paymentStatus === "confirmed"))
|
|
1436
|
+
return inspected;
|
|
1437
|
+
}
|
|
1438
|
+
catch (inspectionError) {
|
|
1439
|
+
if (inspectionError instanceof SafeRelayrRecoveryError)
|
|
1440
|
+
session = inspectionError.session;
|
|
1441
|
+
}
|
|
1372
1442
|
}
|
|
1443
|
+
throw new SafeRelayrRecoveryError(session, error instanceof Error ? error.message : undefined);
|
|
1373
1444
|
}
|
|
1374
|
-
throw
|
|
1445
|
+
throw error;
|
|
1375
1446
|
}
|
|
1376
|
-
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
});
|
|
1447
|
+
return inspect(session, signal);
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1380
1450
|
}
|
|
1381
1451
|
/** Read-only progress polling; each read releases its lock before the wait. */
|
|
1382
1452
|
async function watch({ account, sessionId, signal, onUpdate, intervalMs = 2_500, timeoutMs = 300_000, }) {
|