@bananapus/nana-sdk-core 2.38.2 → 2.40.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.
Files changed (89) hide show
  1. package/dist/cjs/review/heldWrite.d.ts +113 -0
  2. package/dist/cjs/review/heldWrite.d.ts.map +1 -0
  3. package/dist/cjs/review/heldWrite.js +569 -0
  4. package/dist/cjs/review/heldWrite.js.map +1 -0
  5. package/dist/cjs/review/index.d.ts +2 -0
  6. package/dist/cjs/review/index.d.ts.map +1 -1
  7. package/dist/cjs/review/index.js +2 -0
  8. package/dist/cjs/review/index.js.map +1 -1
  9. package/dist/cjs/review/messages.d.ts +30 -0
  10. package/dist/cjs/review/messages.d.ts.map +1 -1
  11. package/dist/cjs/review/messages.js +34 -1
  12. package/dist/cjs/review/messages.js.map +1 -1
  13. package/dist/cjs/review/react/HeldWritesNotice.d.ts +11 -0
  14. package/dist/cjs/review/react/HeldWritesNotice.d.ts.map +1 -0
  15. package/dist/cjs/review/react/HeldWritesNotice.js +22 -0
  16. package/dist/cjs/review/react/HeldWritesNotice.js.map +1 -0
  17. package/dist/cjs/review/react/index.d.ts +5 -0
  18. package/dist/cjs/review/react/index.d.ts.map +1 -0
  19. package/dist/cjs/review/react/index.js +21 -0
  20. package/dist/cjs/review/react/index.js.map +1 -0
  21. package/dist/cjs/review/react/useHeldWrite.d.ts +63 -0
  22. package/dist/cjs/review/react/useHeldWrite.d.ts.map +1 -0
  23. package/dist/cjs/review/react/useHeldWrite.js +107 -0
  24. package/dist/cjs/review/react/useHeldWrite.js.map +1 -0
  25. package/dist/cjs/review/react/useHeldWrites.d.ts +40 -0
  26. package/dist/cjs/review/react/useHeldWrites.d.ts.map +1 -0
  27. package/dist/cjs/review/react/useHeldWrites.js +95 -0
  28. package/dist/cjs/review/react/useHeldWrites.js.map +1 -0
  29. package/dist/cjs/review/react/useSavedAttempt.d.ts +54 -0
  30. package/dist/cjs/review/react/useSavedAttempt.d.ts.map +1 -0
  31. package/dist/cjs/review/react/useSavedAttempt.js +105 -0
  32. package/dist/cjs/review/react/useSavedAttempt.js.map +1 -0
  33. package/dist/cjs/review/savedAttempt.d.ts +180 -0
  34. package/dist/cjs/review/savedAttempt.d.ts.map +1 -0
  35. package/dist/cjs/review/savedAttempt.js +527 -0
  36. package/dist/cjs/review/savedAttempt.js.map +1 -0
  37. package/dist/cjs/review/writeHold.d.ts +94 -2
  38. package/dist/cjs/review/writeHold.d.ts.map +1 -1
  39. package/dist/cjs/review/writeHold.js +284 -43
  40. package/dist/cjs/review/writeHold.js.map +1 -1
  41. package/dist/cjs/review/writeRecovery.d.ts +58 -0
  42. package/dist/cjs/review/writeRecovery.d.ts.map +1 -1
  43. package/dist/cjs/review/writeRecovery.js +161 -2
  44. package/dist/cjs/review/writeRecovery.js.map +1 -1
  45. package/dist/esm/review/heldWrite.d.ts +113 -0
  46. package/dist/esm/review/heldWrite.d.ts.map +1 -0
  47. package/dist/esm/review/heldWrite.js +562 -0
  48. package/dist/esm/review/heldWrite.js.map +1 -0
  49. package/dist/esm/review/index.d.ts +2 -0
  50. package/dist/esm/review/index.d.ts.map +1 -1
  51. package/dist/esm/review/index.js +2 -0
  52. package/dist/esm/review/index.js.map +1 -1
  53. package/dist/esm/review/messages.d.ts +30 -0
  54. package/dist/esm/review/messages.d.ts.map +1 -1
  55. package/dist/esm/review/messages.js +32 -0
  56. package/dist/esm/review/messages.js.map +1 -1
  57. package/dist/esm/review/react/HeldWritesNotice.d.ts +11 -0
  58. package/dist/esm/review/react/HeldWritesNotice.d.ts.map +1 -0
  59. package/dist/esm/review/react/HeldWritesNotice.js +19 -0
  60. package/dist/esm/review/react/HeldWritesNotice.js.map +1 -0
  61. package/dist/esm/review/react/index.d.ts +5 -0
  62. package/dist/esm/review/react/index.d.ts.map +1 -0
  63. package/dist/esm/review/react/index.js +5 -0
  64. package/dist/esm/review/react/index.js.map +1 -0
  65. package/dist/esm/review/react/useHeldWrite.d.ts +63 -0
  66. package/dist/esm/review/react/useHeldWrite.d.ts.map +1 -0
  67. package/dist/esm/review/react/useHeldWrite.js +104 -0
  68. package/dist/esm/review/react/useHeldWrite.js.map +1 -0
  69. package/dist/esm/review/react/useHeldWrites.d.ts +40 -0
  70. package/dist/esm/review/react/useHeldWrites.d.ts.map +1 -0
  71. package/dist/esm/review/react/useHeldWrites.js +91 -0
  72. package/dist/esm/review/react/useHeldWrites.js.map +1 -0
  73. package/dist/esm/review/react/useSavedAttempt.d.ts +54 -0
  74. package/dist/esm/review/react/useSavedAttempt.d.ts.map +1 -0
  75. package/dist/esm/review/react/useSavedAttempt.js +102 -0
  76. package/dist/esm/review/react/useSavedAttempt.js.map +1 -0
  77. package/dist/esm/review/savedAttempt.d.ts +180 -0
  78. package/dist/esm/review/savedAttempt.d.ts.map +1 -0
  79. package/dist/esm/review/savedAttempt.js +524 -0
  80. package/dist/esm/review/savedAttempt.js.map +1 -0
  81. package/dist/esm/review/writeHold.d.ts +94 -2
  82. package/dist/esm/review/writeHold.d.ts.map +1 -1
  83. package/dist/esm/review/writeHold.js +285 -44
  84. package/dist/esm/review/writeHold.js.map +1 -1
  85. package/dist/esm/review/writeRecovery.d.ts +58 -0
  86. package/dist/esm/review/writeRecovery.d.ts.map +1 -1
  87. package/dist/esm/review/writeRecovery.js +160 -2
  88. package/dist/esm/review/writeRecovery.js.map +1 -1
  89. package/package.json +16 -2
@@ -1,14 +1,22 @@
1
1
  import { type Address, type Hex, type PublicClient, type TransactionReceipt } from "viem";
2
2
  import { type SafeQueuedTransaction, type SafeServiceOptions } from "../safeService.js";
3
3
  import { type HeldWriteStorage, type ReviewedWriteRecoveryInput, type ReviewedWriteRecoveryRecord } from "./writeRecovery.js";
4
+ /** A saved success the user hasn't acknowledged: it blocks sending the action again. */
5
+ export type UnacknowledgedSuccess = {
6
+ result: "success";
7
+ hash: Hex;
8
+ /** The reservation it settled. */
9
+ reservation?: string;
10
+ };
4
11
  /** The reads a write hold makes on the call's chain; a viem PublicClient satisfies it. */
5
12
  export type WriteHoldClient = Pick<PublicClient, "getBlock" | "getBlockNumber" | "getChainId" | "getTransaction" | "getTransactionCount" | "getTransactionReceipt" | "request">;
6
13
  /** How a held write ended, when the hold saw it end. */
7
14
  export type WriteHoldSettled =
8
- /** Its verified receipt (a revert only once final). */
15
+ /** Its verified receipt (a revert only once final); `acknowledged` once the user saw it. */
9
16
  {
10
17
  result: "success" | "failed";
11
18
  hash: Hex;
19
+ acknowledged?: true;
12
20
  }
13
21
  /** A Safe proposal's expiry before it ran is proven. */
14
22
  | {
@@ -230,13 +238,97 @@ export declare function createWriteHold(options?: {
230
238
  safe?: {
231
239
  safeTxHash: Hex;
232
240
  };
233
- } | undefined, client?: WriteHoldClient | undefined) => Promise<WriteHoldState>;
241
+ } | undefined, client?: WriteHoldClient | undefined) => Promise<WriteHoldState | (WriteHoldState & {
242
+ declined?: "refused" | "unreadable";
243
+ })>;
234
244
  /**
235
245
  * Every state this controller reads or makes for the call, and other
236
246
  * tabs' changes. The latest one, if any, is replayed at once.
237
247
  */
238
248
  subscribe(listener: (state: WriteHoldState) => void): () => void;
249
+ /**
250
+ * The send gate's evidence for this key: its saved success, while no
251
+ * record is held and the user hasn't acknowledged it. Pure, from storage.
252
+ */
253
+ unacknowledgedSuccess(skip?: (hash: Hex) => boolean): UnacknowledgedSuccess | undefined;
254
+ /** How `reservation` settled, if it is this key's latest and settled. */
255
+ settlementOf(reservation: string): WriteHoldSettled | undefined;
256
+ /**
257
+ * The user acknowledged a success the hold settled while they were
258
+ * away (or a view saw it settle): its saved settlement is marked
259
+ * acknowledged, keeping its result for every reader, so it blocks no
260
+ * send. Under the call's lock; nothing is sent.
261
+ */
262
+ acknowledge(settled: {
263
+ result: "success" | "failed";
264
+ hash: Hex;
265
+ }): Promise<WriteHoldState>;
239
266
  };
267
+ list: ({ account, chainId }: {
268
+ account: Address;
269
+ chainId: number;
270
+ }) => {
271
+ key: string;
272
+ record: ReviewedWriteRecoveryRecord;
273
+ handle: {
274
+ peek: () => WriteHoldState;
275
+ state(client: WriteHoldClient, checks?: {
276
+ effect?: (chainId: number) => Promise<boolean>;
277
+ }): Promise<WriteHoldState>;
278
+ send<T>(client: WriteHoldClient, run: (gate: WriteHoldGate) => Promise<T>): Promise<T>;
279
+ exit(snapshot: WriteHoldState, acknowledgement: {
280
+ checkedWallet: true;
281
+ }, client: WriteHoldClient): Promise<{
282
+ released: boolean;
283
+ state: WriteHoldState;
284
+ }>;
285
+ onReceipt: (receipt: TransactionReceipt, client: WriteHoldClient, proof?: {
286
+ calls: readonly ReviewedWriteRecoveryInput["call"][];
287
+ batch?: boolean;
288
+ } | undefined) => Promise<WriteHoldState>;
289
+ onUnproven: (client: WriteHoldClient, proposalHash?: `0x${string}` | undefined) => Promise<WriteHoldState>;
290
+ onExecuted: (client: WriteHoldClient, executionHash?: `0x${string}` | undefined) => Promise<WriteHoldState>;
291
+ attachHash: (client: WriteHoldClient, hash: `0x${string}`) => Promise<WriteHoldState>;
292
+ attachSafeNonce: (client: WriteHoldClient, args_1: {
293
+ safeTxHash: Hex;
294
+ nonce: number;
295
+ }) => Promise<WriteHoldState>;
296
+ adopt: (snapshot: WriteHoldState, evidence?: {
297
+ nonce?: number | bigint;
298
+ sentNonce?: number | bigint;
299
+ fromBlock?: bigint;
300
+ submittedAt?: number;
301
+ hash?: Hex;
302
+ safe?: {
303
+ safeTxHash: Hex;
304
+ };
305
+ } | undefined, client?: WriteHoldClient | undefined) => Promise<WriteHoldState | (WriteHoldState & {
306
+ declined?: "refused" | "unreadable";
307
+ })>;
308
+ /**
309
+ * Every state this controller reads or makes for the call, and other
310
+ * tabs' changes. The latest one, if any, is replayed at once.
311
+ */
312
+ subscribe(listener: (state: WriteHoldState) => void): () => void;
313
+ /**
314
+ * The send gate's evidence for this key: its saved success, while no
315
+ * record is held and the user hasn't acknowledged it. Pure, from storage.
316
+ */
317
+ unacknowledgedSuccess(skip?: (hash: Hex) => boolean): UnacknowledgedSuccess | undefined;
318
+ /** How `reservation` settled, if it is this key's latest and settled. */
319
+ settlementOf(reservation: string): WriteHoldSettled | undefined;
320
+ /**
321
+ * The user acknowledged a success the hold settled while they were
322
+ * away (or a view saw it settle): its saved settlement is marked
323
+ * acknowledged, keeping its result for every reader, so it blocks no
324
+ * send. Under the call's lock; nothing is sent.
325
+ */
326
+ acknowledge(settled: {
327
+ result: "success" | "failed";
328
+ hash: Hex;
329
+ }): Promise<WriteHoldState>;
330
+ };
331
+ }[];
240
332
  /** Whether holds outlive this tab right now. */
241
333
  durable: () => boolean;
242
334
  /** The review's line while holds last only in this tab, or null. */
@@ -1 +1 @@
1
- {"version":3,"file":"writeHold.d.ts","sourceRoot":"","sources":["../../../src/review/writeHold.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"writeHold.d.ts","sourceRoot":"","sources":["../../../src/review/writeHold.ts"],"names":[],"mappings":"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"}
@@ -82,8 +82,10 @@ const UNSEEN_HASH_MS = 30 * 60_000;
82
82
  /** The longest one request to Safe's service may take inside the hold. */
83
83
  const SAFE_SERVICE_READ_MS = 8_000;
84
84
  /** The 30 minutes have passed since the wallet returned the hash, or since it was first read. A clock that couldn't be saved (NaN) never runs out. */
85
- function unseenTooLong(record) {
86
- return (Date.now() - Number(record.submittedAt ?? record.firstSeenAt) >=
85
+ function unseenTooLong(record,
86
+ /** A later authenticated sighting, when there is one: the clock runs from it. */
87
+ seen) {
88
+ return (Date.now() - Number(seen ?? record.submittedAt ?? record.firstSeenAt) >=
87
89
  UNSEEN_HASH_MS);
88
90
  }
89
91
  /** Whether a canonical finalized block shows the account's nonce still at or below the sent nonce; false when it can't say. */
@@ -388,10 +390,14 @@ function createWriteHold(options = {}) {
388
390
  * exactly this call. One that can't be read is unknown, not refused.
389
391
  */
390
392
  async function adoptable(hash, client) {
391
- if (!(0, untrusted_js_1.isBytes32)(hash) ||
392
- !client ||
393
- (await client.getChainId().catch(() => null)) !== call.chainId)
394
- return false;
393
+ if (!(0, untrusted_js_1.isBytes32)(hash))
394
+ return "refused";
395
+ if (!client)
396
+ return "unreadable";
397
+ const chainId = await client.getChainId().catch(() => null);
398
+ // A client on another chain can't check this chain's transaction.
399
+ if (chainId !== call.chainId)
400
+ return "unreadable";
395
401
  const reviewed = {
396
402
  account: call.account.toLowerCase(),
397
403
  chainId: call.chainId,
@@ -401,22 +407,35 @@ function createWriteHold(options = {}) {
401
407
  value: String(call.call.value ?? 0),
402
408
  },
403
409
  };
410
+ // One the chain or the service can't show is this call's, held.
404
411
  if (call.safe) {
405
412
  const proposal = await (0, safeService_js_1.readSafeTransaction)(call.chainId, call.account, hash, service).catch(() => null);
406
- return !proposal || (0, writeRecovery_js_1.safeProposalIsHeldCall)(proposal, call);
413
+ return !proposal || (0, writeRecovery_js_1.safeProposalIsHeldCall)(proposal, call)
414
+ ? "yes"
415
+ : "refused";
407
416
  }
408
417
  const sent = await client.getTransaction({ hash }).catch(() => null);
409
- return !sent || sentIsCall(sent, hash, reviewed);
418
+ return !sent || sentIsCall(sent, hash, reviewed) ? "yes" : "refused";
410
419
  }
411
- /** The authenticated nonce of a Safe proposal, from Safe's service. */
412
- async function proposalNonceOf(record) {
413
- try {
414
- const proposal = await (0, safeService_js_1.readSafeTransaction)(record.chainId, record.account, record.hash, service);
415
- return (0, safeService_js_1.safeTransactionMessage)(proposal).nonce;
416
- }
417
- catch {
418
- return null;
419
- }
420
+ /**
421
+ * Safe's authenticated record of the held proposal, or null while the
422
+ * service can't return it (down, or it has none).
423
+ */
424
+ async function proposalOf(record) {
425
+ return (0, safeService_js_1.readSafeTransaction)(record.chainId, record.account, record.hash, service).catch(() => null);
426
+ }
427
+ /**
428
+ * A Safe write unseen too long: not proven run, not ended unproven (that
429
+ * has its own exit), and not listed unexecuted by the service for 30
430
+ * minutes since it was sent, adopted, or last listed.
431
+ */
432
+ function unboundedSafe(record) {
433
+ return (record.safe &&
434
+ !!record.hash &&
435
+ !record.executed &&
436
+ !record.unproven &&
437
+ !record.replaced &&
438
+ unseenTooLong(record, record.pendingSeenAt));
420
439
  }
421
440
  /** Follow a saved execution's receipt; null while the node has none. */
422
441
  async function execution(record, client) {
@@ -615,13 +634,18 @@ function createWriteHold(options = {}) {
615
634
  return settled ?? ranExecuted(record);
616
635
  }
617
636
  }
618
- // The chain's proof, from the proposal's nonce: Safe's service is asked
619
- // only for the nonce, once, and for an expiry.
620
- if (record.safeNonce === undefined) {
621
- const nonce = await proposalNonceOf(record);
622
- if (nonce !== null)
623
- record = recovery.proposalNonce(record, nonce);
624
- }
637
+ // The chain's proof, from the proposal's nonce, saved once. Only the
638
+ // service returning this proposal unexecuted shows it truly pending:
639
+ // that read restarts its unseen clock. A Safe nonce still waiting shows
640
+ // only that nothing ran there.
641
+ const proposal = await proposalOf(record);
642
+ const listed = !!proposal && proposal.isExecuted !== true;
643
+ if (proposal && record.safeNonce === undefined)
644
+ record = recovery.proposalNonce(record, (0, safeService_js_1.safeTransactionMessage)(proposal).nonce);
645
+ // Listed: truly pending, an unproven end included. The fresh record
646
+ // carries the new stamp, so the checked answer below removes any exit.
647
+ if (listed)
648
+ record = recovery.listedAgain(record);
625
649
  const end = (record.safeNonce !== undefined &&
626
650
  (await (0, safeService_js_1.provenSafeProposalEnd)(client, record.account, hash, BigInt(record.safeNonce)))) ||
627
651
  (await (0, safeService_js_1.lookAtSafeProposalEnd)(client, record.chainId, record.account, hash, service));
@@ -657,10 +681,9 @@ function createWriteHold(options = {}) {
657
681
  return clear(settled, record.id);
658
682
  }
659
683
  }
660
- if (!record.executed &&
661
- !record.unproven &&
662
- record.safeNonce === undefined &&
663
- unseenTooLong(record))
684
+ // Its wait is bounded like any unchecked write, the clock restarted by
685
+ // each listing (an unproven end has its own exit).
686
+ if (unboundedSafe(record))
664
687
  return unchecked(record);
665
688
  if (record.executed)
666
689
  follow(client, record.id);
@@ -698,9 +721,13 @@ function createWriteHold(options = {}) {
698
721
  // A mirror carries no evidence of its own: its source attempt does.
699
722
  if (record.mirrorOf)
700
723
  return classify(record);
701
- // No evidence from another chain.
702
- if ((await client.getChainId().catch(() => null)) !== record.chainId)
703
- return classify(record);
724
+ // No evidence from another chain. Unreadable chain identity still
725
+ // bounds a Safe write's unchecked wait.
726
+ const chainId = await client.getChainId().catch(() => null);
727
+ if (chainId !== record.chainId)
728
+ return chainId === null && unboundedSafe(record)
729
+ ? unchecked(record)
730
+ : classify(record);
704
731
  if (record.safe)
705
732
  return safeState(record, client);
706
733
  const check = await recovery
@@ -1045,10 +1072,13 @@ function createWriteHold(options = {}) {
1045
1072
  if (hooks.across) {
1046
1073
  // Under an intent: the same atomic reserve as a send, under every
1047
1074
  // legacy key's lock, mirrored at each so older versions see it.
1048
- if (!free() ||
1049
- (hash !== undefined && !(await adoptable(hash, client))))
1075
+ if (!free())
1050
1076
  return shown(peek());
1077
+ const verdict = hash === undefined ? "yes" : await adoptable(hash, client);
1078
+ if (verdict !== "yes")
1079
+ return { ...shown(peek()), declined: verdict };
1051
1080
  let adopted = false;
1081
+ let busy = false;
1052
1082
  try {
1053
1083
  const { released } = await hooks.across((work) => recovery.withLock(work), () => {
1054
1084
  if (!free())
@@ -1058,21 +1088,34 @@ function createWriteHold(options = {}) {
1058
1088
  }, recovery.rejected, Promise.resolve(), hooks.generations());
1059
1089
  await released;
1060
1090
  }
1061
- catch {
1062
- /* Held elsewhere, busy, or no longer free: nothing adopted. */
1091
+ catch (error) {
1092
+ // Held elsewhere or no longer free: what the hold shows. Busy: unread.
1093
+ busy = error instanceof WriteHoldBusyError;
1063
1094
  }
1064
1095
  // A mined hash settles on its verified receipt, through the gate.
1065
- return adopted && hash && client ? state(client) : shown(peek());
1096
+ if (adopted && hash && client)
1097
+ return state(client);
1098
+ const now = shown(peek());
1099
+ return busy && now.status === "clear"
1100
+ ? { ...now, declined: "unreadable" }
1101
+ : now;
1066
1102
  }
1067
1103
  const result = await locked(async () => {
1068
1104
  if (!free())
1069
1105
  return peek();
1070
- if (hash !== undefined && !(await adoptable(hash, client)))
1071
- return peek();
1106
+ const verdict = hash === undefined ? "yes" : await adoptable(hash, client);
1107
+ if (verdict !== "yes")
1108
+ return { ...peek(), declined: verdict };
1072
1109
  reserve();
1073
1110
  return hash && client ? inspect(client) : peek();
1074
1111
  }, true);
1075
- return shown(result?.value ?? peek());
1112
+ if (!result)
1113
+ return { ...shown(peek()), declined: "unreadable" };
1114
+ const { declined, ...answer } = result.value;
1115
+ return {
1116
+ ...shown(answer),
1117
+ ...(declined ? { declined } : {}),
1118
+ };
1076
1119
  },
1077
1120
  /**
1078
1121
  * Save the Safe nonce of a held Safe proposal adopted before it was
@@ -1099,6 +1142,46 @@ function createWriteHold(options = {}) {
1099
1142
  }, true);
1100
1143
  return shown(result?.value ?? peek());
1101
1144
  },
1145
+ /**
1146
+ * The send gate's evidence for this key: its saved success, while no
1147
+ * record is held and the user hasn't acknowledged it. Pure, from storage.
1148
+ */
1149
+ unacknowledgedSuccess(
1150
+ /** A success to look past (one a view already told, its acknowledgement under way). */
1151
+ skip) {
1152
+ if (recovery.read())
1153
+ return undefined;
1154
+ const settled = recovery.settled();
1155
+ return settled?.result === "success" &&
1156
+ !settled.acknowledged &&
1157
+ !skip?.(settled.hash)
1158
+ ? {
1159
+ result: "success",
1160
+ hash: settled.hash,
1161
+ reservation: recovery.latestReservation(),
1162
+ }
1163
+ : undefined;
1164
+ },
1165
+ /** How `reservation` settled, if it is this key's latest and settled. */
1166
+ settlementOf(reservation) {
1167
+ const now = peek();
1168
+ return now.status === "clear" && now.reservation === reservation
1169
+ ? now.settled
1170
+ : undefined;
1171
+ },
1172
+ /**
1173
+ * The user acknowledged a success the hold settled while they were
1174
+ * away (or a view saw it settle): its saved settlement is marked
1175
+ * acknowledged, keeping its result for every reader, so it blocks no
1176
+ * send. Under the call's lock; nothing is sent.
1177
+ */
1178
+ async acknowledge(settled) {
1179
+ const result = await locked(async () => {
1180
+ recovery.acknowledgeSettled(settled);
1181
+ return peek();
1182
+ }, true);
1183
+ return shown(result?.value ?? peek());
1184
+ },
1102
1185
  async attachHash(client, hash) {
1103
1186
  const result = await locked(async () => {
1104
1187
  const record = recovery.read();
@@ -1261,10 +1344,20 @@ function createWriteHold(options = {}) {
1261
1344
  ? [{ input: { ...exact, call: source.call } }]
1262
1345
  : []));
1263
1346
  rescan();
1347
+ /** The legacy hold this intent last stood for, with its reservation. */
1348
+ let routed;
1264
1349
  /** A legacy key holding anything but this intent's own mirror. */
1265
1350
  const foreign = (index) => holds(journals[index]) && delegates[index].peek().status !== "clear";
1266
1351
  /** The legacy hold the intent stands for right now, from storage alone. */
1267
- const active = () => delegates.find((_, index) => foreign(index));
1352
+ const active = () => {
1353
+ const index = delegates.findIndex((_, at) => foreign(at));
1354
+ if (index < 0)
1355
+ return undefined;
1356
+ const now = delegates[index].peek();
1357
+ if (now.status !== "clear")
1358
+ routed = { handle: delegates[index], reservation: now.record.id };
1359
+ return delegates[index];
1360
+ };
1268
1361
  /** The first legacy key holding an unresolved write, through its own gate. */
1269
1362
  async function delegated(client) {
1270
1363
  rescan();
@@ -1272,11 +1365,83 @@ function createWriteHold(options = {}) {
1272
1365
  if (!foreign(index))
1273
1366
  continue;
1274
1367
  const state = await handle.state(client);
1275
- if (state.status !== "clear")
1368
+ if (state.status !== "clear") {
1369
+ routed = { handle, reservation: state.record.id };
1276
1370
  return state;
1371
+ }
1277
1372
  }
1278
1373
  return null;
1279
1374
  }
1375
+ /**
1376
+ * How a legacy hold this intent stood for ended, while the intent itself
1377
+ * never reserved: its settlement (acknowledged or not) is the intent's,
1378
+ * so a view that saw it held shows its result, and an unacknowledged
1379
+ * success still blocks a fresh one.
1380
+ */
1381
+ /**
1382
+ * Every legacy settlement that may be the intent's: the reservation it
1383
+ * was seen standing for (first), and any reserved after (or not provably
1384
+ * before) its own latest reservation. Times that can't be compared count
1385
+ * as newer: a false block is the safe side.
1386
+ */
1387
+ function eligibleLegacy() {
1388
+ const eligible = [];
1389
+ if (routed) {
1390
+ const now = routed.handle.peek();
1391
+ if (now.status === "clear" &&
1392
+ now.settled &&
1393
+ now.reservation === routed.reservation)
1394
+ eligible.push({ handle: routed.handle, state: now });
1395
+ }
1396
+ const ownReserved = ownJournal.latestReservation() !== undefined;
1397
+ const ownAt = ownJournal.latestReservedAt();
1398
+ for (const [index, handle] of delegates.entries()) {
1399
+ if (eligible.some((entry) => entry.handle === handle))
1400
+ continue;
1401
+ const now = handle.peek();
1402
+ if (now.status !== "clear" || !now.settled)
1403
+ continue;
1404
+ const legacyAt = journals[index].latestReservedAt() ?? journals[index].settledAt();
1405
+ if (!ownReserved ||
1406
+ ownAt === undefined ||
1407
+ legacyAt === undefined ||
1408
+ // The same moment is no proof of order: counted as newer.
1409
+ legacyAt >= ownAt)
1410
+ eligible.push({ handle, state: now });
1411
+ }
1412
+ return eligible;
1413
+ }
1414
+ /**
1415
+ * What the intent shows of its legacy holds: the settlement of the one
1416
+ * it was seen standing for (so it is delivered), else an unacknowledged
1417
+ * success, else any eligible one. Blocking is the send gate's
1418
+ * (unacknowledgedSuccess), never this choice.
1419
+ */
1420
+ function legacySettled() {
1421
+ const eligible = eligibleLegacy();
1422
+ return (eligible.find(({ handle, state }) => handle === routed?.handle &&
1423
+ state.reservation === routed.reservation) ??
1424
+ eligible.find(({ state }) => state.settled.result === "success" &&
1425
+ !("acknowledged" in state.settled && state.settled.acknowledged)) ??
1426
+ eligible[0]);
1427
+ }
1428
+ /**
1429
+ * `state`, or, when it is a clear, the legacy settlement that owns the
1430
+ * intent's result instead: the one it was seen standing for, or one
1431
+ * reserved after its own latest reservation, whatever the intent's own
1432
+ * (older) settlement says. One choice for reads, events and the guard.
1433
+ */
1434
+ function withLegacy(state) {
1435
+ if (state.status !== "clear")
1436
+ return state;
1437
+ const legacy = legacySettled();
1438
+ return legacy
1439
+ ? {
1440
+ ...legacy.state,
1441
+ ...(state.checked === undefined ? {} : { checked: state.checked }),
1442
+ }
1443
+ : state;
1444
+ }
1280
1445
  /**
1281
1446
  * The final check and reservation, under every legacy key's lock taken
1282
1447
  * in one sorted order (the intent's own is held by its send), each
@@ -1403,9 +1568,64 @@ function createWriteHold(options = {}) {
1403
1568
  };
1404
1569
  return {
1405
1570
  ...own,
1406
- peek: () => active()?.peek() ?? own.peek(),
1571
+ peek: () => active()?.peek() ?? withLegacy(own.peek()),
1572
+ /**
1573
+ * The intent's events, through the same owner as its state(): while a
1574
+ * legacy hold stands for it, its own empty clear is that hold's state
1575
+ * (or its settlement), never a release.
1576
+ */
1577
+ subscribe: (listener) => own.subscribe((state) => listener(state.status === "clear"
1578
+ ? (active()?.peek() ?? withLegacy(state))
1579
+ : state)),
1407
1580
  async state(client, checks) {
1408
- return ((await delegated(client)) ?? after(await own.state(client, checks)));
1581
+ return ((await delegated(client)) ??
1582
+ withLegacy(await after(await own.state(client, checks))));
1583
+ },
1584
+ /**
1585
+ * The send gate: any unacknowledged success, the intent's own or an
1586
+ * eligible legacy hold's. Pure, from storage, whatever a view shows.
1587
+ */
1588
+ unacknowledgedSuccess(skip) {
1589
+ // Every eligible success is considered: one looked past never hides
1590
+ // another.
1591
+ const mine = own.unacknowledgedSuccess(skip);
1592
+ if (mine)
1593
+ return mine;
1594
+ for (const { state } of eligibleLegacy()) {
1595
+ const settled = state.settled;
1596
+ if (settled.result === "success" &&
1597
+ "hash" in settled &&
1598
+ !settled.acknowledged &&
1599
+ !skip?.(settled.hash))
1600
+ return {
1601
+ result: "success",
1602
+ hash: settled.hash,
1603
+ reservation: state.reservation,
1604
+ };
1605
+ }
1606
+ return undefined;
1607
+ },
1608
+ /**
1609
+ * How a reservation this intent stood for settled, on its own key or a
1610
+ * legacy one, whichever hold is shown now.
1611
+ */
1612
+ settlementOf(reservation) {
1613
+ for (const handle of [own, ...delegates]) {
1614
+ const settled = handle.settlementOf(reservation);
1615
+ if (settled)
1616
+ return settled;
1617
+ }
1618
+ return undefined;
1619
+ },
1620
+ /** A settlement is acknowledged where it was saved, found by its hash. */
1621
+ async acknowledge(settled) {
1622
+ const hash = settled.hash.toLowerCase();
1623
+ const legacy = eligibleLegacy().find(({ state }) => state.settled !== undefined &&
1624
+ "hash" in state.settled &&
1625
+ state.settled.hash === hash);
1626
+ return legacy
1627
+ ? withLegacy(await legacy.handle.acknowledge(settled))
1628
+ : own.acknowledge(settled);
1409
1629
  },
1410
1630
  async send(client, run) {
1411
1631
  const held = await delegated(client);
@@ -1521,8 +1741,29 @@ function createWriteHold(options = {}) {
1521
1741
  adopt: async (...args) => route()?.peek() ?? own.adopt(...args),
1522
1742
  };
1523
1743
  }
1744
+ /**
1745
+ * Every write held for `account` on `chainId`, with a handle to each at
1746
+ * the key it was saved under: for an app-wide notice of everything held,
1747
+ * whatever action made it. Read from storage alone; each handle's state()
1748
+ * is the gated answer.
1749
+ */
1750
+ function list({ account, chainId }) {
1751
+ return (0, writeRecovery_js_1.listReviewedWrites)({ account, chainId }, { storage }).map(({ key, record }) => {
1752
+ const input = {
1753
+ chainId: record.chainId,
1754
+ account: record.account,
1755
+ safe: record.safe,
1756
+ call: record.call,
1757
+ };
1758
+ const handle = record.namespace === undefined
1759
+ ? openExact(input, key)
1760
+ : open({ ...input, namespace: record.namespace });
1761
+ return { key, record, handle };
1762
+ });
1763
+ }
1524
1764
  return {
1525
1765
  open,
1766
+ list,
1526
1767
  /** Whether holds outlive this tab right now. */
1527
1768
  durable: () => storage.durable(),
1528
1769
  /** The review's line while holds last only in this tab, or null. */