@unconfirmed/sui-effect 0.1.0 → 0.1.2

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 (82) hide show
  1. package/AGENTS.md +47 -14
  2. package/CHANGELOG.md +86 -0
  3. package/LLMS.md +2889 -1726
  4. package/README.md +517 -11
  5. package/dist/domain/bcs.d.ts +44 -0
  6. package/dist/domain/bcs.d.ts.map +1 -1
  7. package/dist/domain/bcs.js +64 -0
  8. package/dist/domain/bcs.js.map +1 -1
  9. package/dist/domain/errors.d.ts +164 -26
  10. package/dist/domain/errors.d.ts.map +1 -1
  11. package/dist/domain/errors.js +193 -15
  12. package/dist/domain/errors.js.map +1 -1
  13. package/dist/domain/executed.d.ts +123 -18
  14. package/dist/domain/executed.d.ts.map +1 -1
  15. package/dist/domain/executed.js +196 -5
  16. package/dist/domain/executed.js.map +1 -1
  17. package/dist/domain/journal-entry.d.ts +6 -2
  18. package/dist/domain/journal-entry.d.ts.map +1 -1
  19. package/dist/domain/schemas.d.ts +234 -67
  20. package/dist/domain/schemas.d.ts.map +1 -1
  21. package/dist/domain/schemas.js +82 -4
  22. package/dist/domain/schemas.js.map +1 -1
  23. package/dist/domain/sui-schema.d.ts +3 -2
  24. package/dist/domain/sui-schema.d.ts.map +1 -1
  25. package/dist/domain/sui-schema.js +3 -2
  26. package/dist/domain/sui-schema.js.map +1 -1
  27. package/dist/extension.d.ts +1 -1
  28. package/dist/extension.d.ts.map +1 -1
  29. package/dist/extension.js +1 -1
  30. package/dist/extension.js.map +1 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +1 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/script.d.ts +1 -1
  36. package/dist/script.d.ts.map +1 -1
  37. package/dist/script.js +1 -1
  38. package/dist/script.js.map +1 -1
  39. package/dist/services/Script.d.ts +42 -0
  40. package/dist/services/Script.d.ts.map +1 -1
  41. package/dist/services/Script.js +69 -2
  42. package/dist/services/Script.js.map +1 -1
  43. package/dist/services/Signer.d.ts +32 -7
  44. package/dist/services/Signer.d.ts.map +1 -1
  45. package/dist/services/Signer.js +69 -10
  46. package/dist/services/Signer.js.map +1 -1
  47. package/dist/services/Sui.d.ts +42 -1
  48. package/dist/services/Sui.d.ts.map +1 -1
  49. package/dist/services/Sui.js +18 -4
  50. package/dist/services/Sui.js.map +1 -1
  51. package/dist/services/SuiCore.d.ts +12 -0
  52. package/dist/services/SuiCore.d.ts.map +1 -1
  53. package/dist/services/SuiCore.js +124 -0
  54. package/dist/services/SuiCore.js.map +1 -1
  55. package/dist/services/SuiCoreFake.d.ts +59 -4
  56. package/dist/services/SuiCoreFake.d.ts.map +1 -1
  57. package/dist/services/SuiCoreFake.js +199 -23
  58. package/dist/services/SuiCoreFake.js.map +1 -1
  59. package/dist/services/SuiExtension.d.ts +127 -14
  60. package/dist/services/SuiExtension.d.ts.map +1 -1
  61. package/dist/services/SuiExtension.js +125 -28
  62. package/dist/services/SuiExtension.js.map +1 -1
  63. package/dist/services/SuiGraphQL.d.ts +13 -0
  64. package/dist/services/SuiGraphQL.d.ts.map +1 -1
  65. package/dist/services/SuiGraphQL.js +13 -0
  66. package/dist/services/SuiGraphQL.js.map +1 -1
  67. package/dist/services/Tx.d.ts +630 -12
  68. package/dist/services/Tx.d.ts.map +1 -1
  69. package/dist/services/Tx.js +187 -8
  70. package/dist/services/Tx.js.map +1 -1
  71. package/dist/testing.d.ts +22 -3
  72. package/dist/testing.d.ts.map +1 -1
  73. package/dist/testing.js +39 -3
  74. package/dist/testing.js.map +1 -1
  75. package/dist/tx.d.ts +1 -1
  76. package/dist/tx.d.ts.map +1 -1
  77. package/dist/tx.js +1 -1
  78. package/dist/tx.js.map +1 -1
  79. package/docs/extensions.md +871 -33
  80. package/examples/extension-template/src/Escrow.ts +1 -1
  81. package/examples/extension-template/test/escrow.test.ts +91 -2
  82. package/package.json +3 -2
@@ -1,8 +1,8 @@
1
1
  import { Transaction } from "@mysten/sui/transactions";
2
- import { Effect, Schema } from "effect";
2
+ import { DateTime, Effect, type Option, Schema } from "effect";
3
3
  import { BuildError, ExecutionFailed, JournalError, NotApplied, PolicyDenied, SigningError, SimulationFailed, SubmissionUnknown, TransportError } from "../domain/errors.ts";
4
4
  import { Executed } from "../domain/executed.ts";
5
- import { Digest, SignedTransaction, SuiAddress } from "../domain/schemas.ts";
5
+ import { Digest, type Signature, SignedTransaction, SuiAddress } from "../domain/schemas.ts";
6
6
  import type { Signer } from "./Signer.ts";
7
7
  import type { Recipe } from "./Sui.ts";
8
8
  import { Sui } from "./Sui.ts";
@@ -15,7 +15,9 @@ export declare const Signed: Schema.Struct<{
15
15
  readonly digest: Schema.brand<Schema.String, "Digest">;
16
16
  readonly bytes: Schema.Uint8ArrayFromBase64;
17
17
  readonly signatures: Schema.$Array<Schema.brand<Schema.String, "Signature">>;
18
- readonly sender: Schema.brand<Schema.decodeTo<Schema.toType<Schema.String>, Schema.String, never, never>, "SuiAddress">;
18
+ readonly sender: Schema.brand<Schema.decodeTo<Schema.toType<Schema.String>, Schema.String, never, never>, "SuiAddress"> & {
19
+ normalize: (input: string) => string & import("effect/Brand").Brand<"SuiAddress">;
20
+ };
19
21
  readonly expiration: Schema.optional<Schema.toTaggedUnion<"$kind", readonly [Schema.Struct<{
20
22
  readonly $kind: Schema.Literal<"None">;
21
23
  readonly None: Schema.Literal<true>;
@@ -57,14 +59,49 @@ export type ReconcileInput = Digest | Signed | SubmissionUnknown;
57
59
  * An extension that wraps a submission spells its own errors plus this, rather
58
60
  * than repeating four tags that will grow with the taxonomy.
59
61
  */
60
- export type SubmitError = ExecutionFailed | NotApplied | SubmissionUnknown | JournalError;
62
+ export type SubmitError = ExecutionFailed | NotApplied | SubmissionUnknown | JournalError | TransportError;
61
63
  /**
62
64
  * Everything {@link run} can fail with, as one name: {@link SubmitError} plus
63
65
  * what building, preflighting and signing can produce.
64
66
  */
65
67
  export type RunError = BuildError | SimulationFailed | PolicyDenied | SigningError | SubmitError | TransportError;
66
- /** What one entry of `Tx.reconcileAll` settled to. */
67
- export type Reconciled = Executed | ExecutionFailed | NotApplied | SubmissionUnknown;
68
+ /**
69
+ * What one entry of `Tx.reconcileAll` settled to, as a tagged union.
70
+ *
71
+ * Every case carries **one** discriminator in the same place: `_tag` is
72
+ * `"Executed"`, `"ExecutionFailed"`, `"NotApplied"` or `"SubmissionUnknown"`,
73
+ * and the payload is `executed` for the first and `error` for the other three.
74
+ * Before 0.1.2 this was a bare union of an `Executed` (which has no `_tag`) and
75
+ * three errors (which do), so the only way to tell a success from a failure was
76
+ * `"_tag" in entry` — a shape nothing could `Schema.match` or serialize.
77
+ *
78
+ * @since 0.1.2
79
+ */
80
+ export declare const Reconciled: Schema.TaggedUnion<{
81
+ readonly ExecutionFailed: Schema.TaggedStruct<"ExecutionFailed", {
82
+ readonly error: typeof ExecutionFailed;
83
+ }>;
84
+ readonly SubmissionUnknown: Schema.TaggedStruct<"SubmissionUnknown", {
85
+ readonly error: typeof SubmissionUnknown;
86
+ }>;
87
+ readonly NotApplied: Schema.TaggedStruct<"NotApplied", {
88
+ readonly error: typeof NotApplied;
89
+ }>;
90
+ readonly Executed: Schema.TaggedStruct<"Executed", {
91
+ readonly executed: typeof Executed;
92
+ }>;
93
+ }>;
94
+ /** One settled entry of `Tx.reconcileAll`. @since 0.1.2 */
95
+ export type Reconciled = typeof Reconciled.Type;
96
+ /**
97
+ * The shape `Tx.reconcileAll` returned in 0.1.0 and 0.1.1: the four outcomes as
98
+ * a bare union, with the successful one carrying no discriminator at all.
99
+ *
100
+ * @deprecated Use {@link Reconciled}, whose cases all carry `_tag`. This name
101
+ * exists so a 0.1.1 consumer that spelled the old union in its own types still
102
+ * compiles.
103
+ */
104
+ export type ReconciledOutcome = Executed | ExecutionFailed | NotApplied | SubmissionUnknown;
68
105
  /**
69
106
  * Builds a transaction into signable bytes.
70
107
  *
@@ -335,9 +372,16 @@ export declare const sponsored: (opts: {
335
372
  * `Tx.reconcile`, which either finds the transaction, proves it never applied,
336
373
  * or says it does not know.
337
374
  *
338
- * `TransportError` never escapes: once bytes may have been sent, "the network
339
- * was unreachable" is not an answer a caller can act on, so it becomes
340
- * `SubmissionUnknown` carrying the signed bytes.
375
+ * `TransportError` almost never escapes: once bytes may have been sent, "the
376
+ * network was unreachable" is not an answer a caller can act on, so it becomes
377
+ * `SubmissionUnknown` carrying the signed bytes. **The one exception is a node
378
+ * that refused the request outright** — gRPC `INVALID_ARGUMENT`, which is what
379
+ * a validator answers for malformed bytes or for a sponsored transaction
380
+ * carrying one signature. That answer came from the node, it is final, and
381
+ * nothing was executed, so it is reported as the `TransportError` it is rather
382
+ * than reconciled: a reconcile would go on to ask "is this digest on chain?",
383
+ * a question about a transaction that was never sent, and a lagging or scripted
384
+ * node can answer it yes.
341
385
  *
342
386
  * `JournalError` can only come from the `Signed` write, before anything has
343
387
  * been sent. Once the network has answered, a journal write that fails is
@@ -348,9 +392,97 @@ export declare const sponsored: (opts: {
348
392
  * Fails with: `ExecutionFailed` (applied on chain and failed; gas was charged),
349
393
  * `NotApplied` (provably never applied), `SubmissionUnknown` (the outcome is
350
394
  * not known and the bytes are in the error), `JournalError` (only before the
351
- * first send).
395
+ * first send), `TransportError` (only `INVALID_ARGUMENT`: the node refused the
396
+ * submission and nothing was executed).
352
397
  */
353
398
  export declare const submit: (signed: Signed) => Effect.Effect<Executed, SubmitError, Sui>;
399
+ /**
400
+ * What a third party answers when it has submitted your bytes.
401
+ *
402
+ * Three shapes are understood, in this order: an SDK `TransactionResult` (the
403
+ * service ran `executeTransaction` and passed the whole thing on), a reduced
404
+ * execute envelope (anything with a `digest` or an `effects`, decoded through
405
+ * {@link Executed.fromPartial}), and a bare digest string. Anything else — a
406
+ * `void`, an acknowledgement with no digest — means "ask the chain", and
407
+ * `submitVia` reconciles by the digest it already has, which is the digest of
408
+ * the bytes it handed over.
409
+ *
410
+ * @since 0.1.2
411
+ */
412
+ export type SubmitViaReply = unknown;
413
+ /** Everything {@link submitVia} can fail with, before the sender's own errors. */
414
+ export type SubmitViaError = ExecutionFailed | NotApplied | SubmissionUnknown | JournalError;
415
+ /**
416
+ * Submits through **someone else** — a relay, a sponsorship service, a backend
417
+ * that holds the only key allowed to talk to the node — and keeps the journal
418
+ * and the evidence rules that `Tx.submit` would have kept.
419
+ *
420
+ * The bytes never reach `executeTransaction` here; `send` does whatever the
421
+ * service needs (an HTTP POST, a queue, another process) and answers with
422
+ * whatever the service returns. What this owns is everything around it:
423
+ *
424
+ * - a `Signed` journal entry is written **before** `send` is called, so a crash
425
+ * between here and the service leaves the same record a direct submit leaves;
426
+ * - `send` is called **once**. A third party's submit is not known to be
427
+ * idempotent and re-sending is not this function's decision;
428
+ * - the reply is turned into an `Executed` when it carries one (see
429
+ * {@link SubmitViaReply}), and otherwise the chain is asked — by the digest
430
+ * of the bytes that were handed over, which a co-signature does not change;
431
+ * - a failure from `send` is **ambiguous** unless it says otherwise, so it ends
432
+ * in `Tx.reconcile` with the full evidence rules: the ordered expiry check,
433
+ * the chain-identity guard, the versioned consumer check. A sender error that
434
+ * **declares** `outcome: "not_applied"` on the instance — a 400 from the
435
+ * service, a refusal before anything went out — is taken at its word and
436
+ * fails straight through without spending a reconcile. A `TransportError` is
437
+ * not that: the taxonomy calls it `not_applied`, but a transport failure
438
+ * *talking to the relay* is exactly the ambiguous case, so it reconciles;
439
+ * - every terminal answer is journalled, exactly as `Tx.submit` journals it.
440
+ *
441
+ * Fails with: `ExecutionFailed`, `NotApplied`, `SubmissionUnknown` (carrying
442
+ * the signed bytes, with the sender's failure as its `cause`), `JournalError`
443
+ * (only from the write before `send`), and `E` — whatever `send` fails with —
444
+ * when that error declares `outcome: "not_applied"`.
445
+ *
446
+ * @since 0.1.2
447
+ */
448
+ export declare const submitVia: <E, R>(signed: {
449
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
450
+ readonly sender: string & import("effect/Brand").Brand<"SuiAddress">;
451
+ readonly signatures: readonly (string & import("effect/Brand").Brand<"Signature">)[];
452
+ readonly bytes: Uint8Array<ArrayBufferLike>;
453
+ readonly chain?: string | undefined;
454
+ readonly expiration?: {
455
+ readonly $kind: "None";
456
+ readonly None: true;
457
+ } | {
458
+ readonly $kind: "Epoch";
459
+ readonly Epoch: bigint;
460
+ } | {
461
+ readonly $kind: "ValidDuring";
462
+ readonly ValidDuring: {
463
+ readonly minEpoch: bigint | null;
464
+ readonly maxEpoch: bigint | null;
465
+ readonly minTimestamp: bigint | null;
466
+ readonly maxTimestamp: bigint | null;
467
+ readonly chain: string;
468
+ readonly nonce: number;
469
+ };
470
+ } | {
471
+ readonly $kind: "Validity";
472
+ readonly Validity: {
473
+ readonly allowedProposers: {
474
+ readonly epoch: bigint;
475
+ readonly proposers: readonly number[];
476
+ } | null;
477
+ readonly minEpoch: bigint | null;
478
+ readonly maxEpoch: bigint | null;
479
+ readonly minTimestamp: bigint | null;
480
+ readonly maxTimestamp: bigint | null;
481
+ readonly chain: string;
482
+ readonly nonce: number;
483
+ };
484
+ } | undefined;
485
+ }, send: (bytes: Uint8Array, signatures: ReadonlyArray<Signature>) => Effect.Effect<SubmitViaReply, E, R>) => Effect.Effect<Executed, SubmitViaError | E, Sui | R>;
354
486
  /**
355
487
  * Finds out what happened to a transaction that was sent but never answered
356
488
  * for.
@@ -436,6 +568,14 @@ export declare const reconcile: (input: ReconcileInput) => Effect.Effect<Execute
436
568
  * explicit lifecycle (`Tx.build`, `Tx.sign`, `Tx.cosign`, `Tx.submit`) when the
437
569
  * two parties cannot both sign in one process.
438
570
  *
571
+ * **`onSigned` is the hook between signing and sending.** A program with its
572
+ * own record to keep — a batch row, an outbox, an idempotency key — has to
573
+ * write the digest before the first send, and that used to mean giving up
574
+ * `Tx.run` and reassembling `withSenderLock(build → sign → record → submit)` by
575
+ * hand. Pass `onSigned` instead: it runs inside the sender lock, after the last
576
+ * signature and before `Tx.submit`'s first `executeTransaction`, and failing it
577
+ * fails the run with nothing sent.
578
+ *
439
579
  * Fails with: `BuildError`, `SimulationFailed`, `PolicyDenied`, `SigningError`,
440
580
  * `ExecutionFailed`, `NotApplied`, `SubmissionUnknown`, `JournalError`,
441
581
  * `TransportError` (from the build reads; once bytes are sent, transport
@@ -449,11 +589,231 @@ export declare const run: (recipe: Transaction | Recipe, opts: {
449
589
  * the bytes name a gas owner that is not the sender.
450
590
  */
451
591
  readonly sponsor?: Signer;
592
+ /**
593
+ * Called with the signed bytes **after every signature is on them and
594
+ * before the first `executeTransaction`**, which is the one moment a
595
+ * consumer's own record has to be written: the digest is final from here
596
+ * on, and anything that happens next may have reached the network.
597
+ *
598
+ * `Tx.submit` already writes its `Signed` journal entry at this point; this
599
+ * is for the record the journal does not hold — a domain row joining the
600
+ * digest to a batch, an outbox, a log line an operator greps. It runs
601
+ * inside the sender lock, so it is ordered with the submission it belongs
602
+ * to.
603
+ *
604
+ * Failing it fails the run **before anything is sent**, which is why its
605
+ * error is a `JournalError`: that is the taxonomy's "the record could not
606
+ * be written and nothing has gone out yet", it is already in `Tx.run`'s
607
+ * union, and it is `not_applied`, so the documented retry idiom is correct.
608
+ * Map your own persistence failure into it
609
+ * (`Effect.mapError((cause) => new JournalError({ cause }))`).
610
+ *
611
+ * @since 0.1.2
612
+ */
613
+ readonly onSigned?: (signed: Signed) => Effect.Effect<void, JournalError>;
452
614
  }) => Effect.Effect<Executed, TransportError | SimulationFailed | ExecutionFailed | SubmissionUnknown | NotApplied | SigningError | BuildError | PolicyDenied | JournalError, Sui>;
615
+ /**
616
+ * What the journal recorded for one digest, if anything.
617
+ *
618
+ * `Tx.reconcileAll` returns **only the entries that were still unresolved**,
619
+ * because those are the ones it had work to do about; a transaction that had
620
+ * already settled — executed, failed, or proven never applied — is not in its
621
+ * answer and never will be. This is how to ask about one of those: the entry is
622
+ * `Executed`, `Failed` or `NotApplied` for a settled digest, `Signed` or
623
+ * `Unknown` for one still in flight, and `None` for a digest this journal has
624
+ * never seen (including every digest at all, when the journal is the in-memory
625
+ * default and the process restarted).
626
+ *
627
+ * It is exactly `(yield* Journal).get(digest)`, named so that the recovery path
628
+ * does not have to reach for the reference.
629
+ *
630
+ * Fails with: `JournalError`.
631
+ *
632
+ * @since 0.1.2
633
+ */
634
+ export declare const recorded: (digest: string & import("effect/Brand").Brand<"Digest">) => Effect.Effect<Option.Option<{
635
+ readonly _tag: "Unknown";
636
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
637
+ readonly signed: {
638
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
639
+ readonly sender: string & import("effect/Brand").Brand<"SuiAddress">;
640
+ readonly signatures: readonly (string & import("effect/Brand").Brand<"Signature">)[];
641
+ readonly bytes: Uint8Array<ArrayBufferLike>;
642
+ readonly chain?: string | undefined;
643
+ readonly expiration?: {
644
+ readonly $kind: "None";
645
+ readonly None: true;
646
+ } | {
647
+ readonly $kind: "Epoch";
648
+ readonly Epoch: bigint;
649
+ } | {
650
+ readonly $kind: "ValidDuring";
651
+ readonly ValidDuring: {
652
+ readonly minEpoch: bigint | null;
653
+ readonly maxEpoch: bigint | null;
654
+ readonly minTimestamp: bigint | null;
655
+ readonly maxTimestamp: bigint | null;
656
+ readonly chain: string;
657
+ readonly nonce: number;
658
+ };
659
+ } | {
660
+ readonly $kind: "Validity";
661
+ readonly Validity: {
662
+ readonly allowedProposers: {
663
+ readonly epoch: bigint;
664
+ readonly proposers: readonly number[];
665
+ } | null;
666
+ readonly minEpoch: bigint | null;
667
+ readonly maxEpoch: bigint | null;
668
+ readonly minTimestamp: bigint | null;
669
+ readonly maxTimestamp: bigint | null;
670
+ readonly chain: string;
671
+ readonly nonce: number;
672
+ };
673
+ } | undefined;
674
+ };
675
+ readonly lastError: string;
676
+ readonly attempts: number;
677
+ readonly at: DateTime.Utc;
678
+ } | {
679
+ readonly _tag: "NotApplied";
680
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
681
+ readonly evidence: "expired" | "inputConsumed";
682
+ readonly at: DateTime.Utc;
683
+ } | {
684
+ readonly _tag: "Signed";
685
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
686
+ readonly signed: {
687
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
688
+ readonly sender: string & import("effect/Brand").Brand<"SuiAddress">;
689
+ readonly signatures: readonly (string & import("effect/Brand").Brand<"Signature">)[];
690
+ readonly bytes: Uint8Array<ArrayBufferLike>;
691
+ readonly chain?: string | undefined;
692
+ readonly expiration?: {
693
+ readonly $kind: "None";
694
+ readonly None: true;
695
+ } | {
696
+ readonly $kind: "Epoch";
697
+ readonly Epoch: bigint;
698
+ } | {
699
+ readonly $kind: "ValidDuring";
700
+ readonly ValidDuring: {
701
+ readonly minEpoch: bigint | null;
702
+ readonly maxEpoch: bigint | null;
703
+ readonly minTimestamp: bigint | null;
704
+ readonly maxTimestamp: bigint | null;
705
+ readonly chain: string;
706
+ readonly nonce: number;
707
+ };
708
+ } | {
709
+ readonly $kind: "Validity";
710
+ readonly Validity: {
711
+ readonly allowedProposers: {
712
+ readonly epoch: bigint;
713
+ readonly proposers: readonly number[];
714
+ } | null;
715
+ readonly minEpoch: bigint | null;
716
+ readonly maxEpoch: bigint | null;
717
+ readonly minTimestamp: bigint | null;
718
+ readonly maxTimestamp: bigint | null;
719
+ readonly chain: string;
720
+ readonly nonce: number;
721
+ };
722
+ } | undefined;
723
+ };
724
+ readonly signedAt: DateTime.Utc;
725
+ } | {
726
+ readonly at: DateTime.Utc;
727
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
728
+ readonly _tag: "Executed";
729
+ readonly checkpoint?: bigint | undefined;
730
+ } | {
731
+ readonly _tag: "Failed";
732
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
733
+ readonly reason: {
734
+ readonly $kind: "MoveAbort";
735
+ readonly MoveAbort: {
736
+ readonly abortCode: bigint;
737
+ readonly location?: {
738
+ readonly function?: number | undefined;
739
+ readonly package?: string | undefined;
740
+ readonly module?: string | undefined;
741
+ readonly functionName?: string | undefined;
742
+ readonly instruction?: number | undefined;
743
+ } | undefined;
744
+ readonly cleverError?: {
745
+ readonly value?: string | undefined;
746
+ readonly errorCode?: number | undefined;
747
+ readonly lineNumber?: number | undefined;
748
+ readonly constantName?: string | undefined;
749
+ readonly constantType?: string | undefined;
750
+ } | undefined;
751
+ };
752
+ } | {
753
+ readonly $kind: "SizeError";
754
+ readonly SizeError: {
755
+ readonly name: string;
756
+ readonly size: number;
757
+ readonly maxSize: number;
758
+ };
759
+ } | {
760
+ readonly $kind: "CommandArgumentError";
761
+ readonly CommandArgumentError: {
762
+ readonly argument: number;
763
+ readonly name: string;
764
+ };
765
+ } | {
766
+ readonly $kind: "TypeArgumentError";
767
+ readonly TypeArgumentError: {
768
+ readonly typeArgument: number;
769
+ readonly name: string;
770
+ };
771
+ } | {
772
+ readonly $kind: "PackageUpgradeError";
773
+ readonly PackageUpgradeError: {
774
+ readonly name: string;
775
+ readonly digest?: string | undefined;
776
+ readonly packageId?: string | undefined;
777
+ };
778
+ } | {
779
+ readonly $kind: "IndexError";
780
+ readonly IndexError: {
781
+ readonly index?: number | undefined;
782
+ readonly subresult?: number | undefined;
783
+ };
784
+ } | {
785
+ readonly $kind: "CoinDenyListError";
786
+ readonly CoinDenyListError: {
787
+ readonly coinType: string;
788
+ readonly name: string;
789
+ readonly address?: string | undefined;
790
+ };
791
+ } | {
792
+ readonly $kind: "CongestedObjects";
793
+ readonly CongestedObjects: {
794
+ readonly name: string;
795
+ readonly objects: readonly string[];
796
+ };
797
+ } | {
798
+ readonly $kind: "ObjectIdError";
799
+ readonly ObjectIdError: {
800
+ readonly objectId: string;
801
+ readonly name?: string | undefined;
802
+ };
803
+ } | {
804
+ readonly $kind: "Unknown";
805
+ };
806
+ readonly at: DateTime.Utc;
807
+ }>, JournalError, never>;
453
808
  /**
454
809
  * Settles every unresolved entry in the journal: the explicit startup call a
455
810
  * long-lived application makes after building a durable `Journal`.
456
811
  *
812
+ * **Only unresolved entries come back.** `Signed` and `Unknown` are the tags
813
+ * that still need an answer; a digest that already settled is not in the
814
+ * journal's unresolved index and is not in this array. Ask about one of those
815
+ * with {@link recorded}.
816
+ *
457
817
  * Nothing here fails per entry: each one settles to an `Executed`, an
458
818
  * `ExecutionFailed`, a `NotApplied` or a `SubmissionUnknown`, in the order the
459
819
  * journal listed them, and the journal is updated to match. Every settled entry
@@ -469,7 +829,19 @@ export declare const run: (recipe: Transaction | Recipe, opts: {
469
829
  *
470
830
  * Fails with: `JournalError`, `TransportError`.
471
831
  */
472
- export declare const reconcileAll: () => Effect.Effect<readonly Reconciled[], TransportError | JournalError, Sui>;
832
+ export declare const reconcileAll: () => Effect.Effect<readonly ({
833
+ readonly _tag: "ExecutionFailed";
834
+ readonly error: ExecutionFailed;
835
+ } | {
836
+ readonly _tag: "SubmissionUnknown";
837
+ readonly error: SubmissionUnknown;
838
+ } | {
839
+ readonly _tag: "NotApplied";
840
+ readonly error: NotApplied;
841
+ } | {
842
+ readonly _tag: "Executed";
843
+ readonly executed: Executed;
844
+ })[], TransportError | JournalError, Sui>;
473
845
  /**
474
846
  * The lifecycle, namespaced the way the spec spells it: `Tx.build`, `Tx.sign`,
475
847
  * `Tx.cosign`, `Tx.sponsored`, `Tx.submit`, `Tx.reconcile`, `Tx.run`,
@@ -672,7 +1044,219 @@ export declare const Tx: {
672
1044
  readonly gasOwner: SuiAddress;
673
1045
  }) => (recipe: Recipe) => Recipe;
674
1046
  readonly submit: (signed: Signed) => Effect.Effect<Executed, SubmitError, Sui>;
1047
+ readonly submitVia: <E, R>(signed: {
1048
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
1049
+ readonly sender: string & import("effect/Brand").Brand<"SuiAddress">;
1050
+ readonly signatures: readonly (string & import("effect/Brand").Brand<"Signature">)[];
1051
+ readonly bytes: Uint8Array<ArrayBufferLike>;
1052
+ readonly chain?: string | undefined;
1053
+ readonly expiration?: {
1054
+ readonly $kind: "None";
1055
+ readonly None: true;
1056
+ } | {
1057
+ readonly $kind: "Epoch";
1058
+ readonly Epoch: bigint;
1059
+ } | {
1060
+ readonly $kind: "ValidDuring";
1061
+ readonly ValidDuring: {
1062
+ readonly minEpoch: bigint | null;
1063
+ readonly maxEpoch: bigint | null;
1064
+ readonly minTimestamp: bigint | null;
1065
+ readonly maxTimestamp: bigint | null;
1066
+ readonly chain: string;
1067
+ readonly nonce: number;
1068
+ };
1069
+ } | {
1070
+ readonly $kind: "Validity";
1071
+ readonly Validity: {
1072
+ readonly allowedProposers: {
1073
+ readonly epoch: bigint;
1074
+ readonly proposers: readonly number[];
1075
+ } | null;
1076
+ readonly minEpoch: bigint | null;
1077
+ readonly maxEpoch: bigint | null;
1078
+ readonly minTimestamp: bigint | null;
1079
+ readonly maxTimestamp: bigint | null;
1080
+ readonly chain: string;
1081
+ readonly nonce: number;
1082
+ };
1083
+ } | undefined;
1084
+ }, send: (bytes: Uint8Array, signatures: ReadonlyArray<Signature>) => Effect.Effect<SubmitViaReply, E, R>) => Effect.Effect<Executed, SubmitViaError | E, Sui | R>;
675
1085
  readonly reconcile: (input: ReconcileInput) => Effect.Effect<Executed, TransportError | ExecutionFailed | SubmissionUnknown | NotApplied, Sui>;
1086
+ readonly recorded: (digest: string & import("effect/Brand").Brand<"Digest">) => Effect.Effect<Option.Option<{
1087
+ readonly _tag: "Unknown";
1088
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
1089
+ readonly signed: {
1090
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
1091
+ readonly sender: string & import("effect/Brand").Brand<"SuiAddress">;
1092
+ readonly signatures: readonly (string & import("effect/Brand").Brand<"Signature">)[];
1093
+ readonly bytes: Uint8Array<ArrayBufferLike>;
1094
+ readonly chain?: string | undefined;
1095
+ readonly expiration?: {
1096
+ readonly $kind: "None";
1097
+ readonly None: true;
1098
+ } | {
1099
+ readonly $kind: "Epoch";
1100
+ readonly Epoch: bigint;
1101
+ } | {
1102
+ readonly $kind: "ValidDuring";
1103
+ readonly ValidDuring: {
1104
+ readonly minEpoch: bigint | null;
1105
+ readonly maxEpoch: bigint | null;
1106
+ readonly minTimestamp: bigint | null;
1107
+ readonly maxTimestamp: bigint | null;
1108
+ readonly chain: string;
1109
+ readonly nonce: number;
1110
+ };
1111
+ } | {
1112
+ readonly $kind: "Validity";
1113
+ readonly Validity: {
1114
+ readonly allowedProposers: {
1115
+ readonly epoch: bigint;
1116
+ readonly proposers: readonly number[];
1117
+ } | null;
1118
+ readonly minEpoch: bigint | null;
1119
+ readonly maxEpoch: bigint | null;
1120
+ readonly minTimestamp: bigint | null;
1121
+ readonly maxTimestamp: bigint | null;
1122
+ readonly chain: string;
1123
+ readonly nonce: number;
1124
+ };
1125
+ } | undefined;
1126
+ };
1127
+ readonly lastError: string;
1128
+ readonly attempts: number;
1129
+ readonly at: DateTime.Utc;
1130
+ } | {
1131
+ readonly _tag: "NotApplied";
1132
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
1133
+ readonly evidence: "expired" | "inputConsumed";
1134
+ readonly at: DateTime.Utc;
1135
+ } | {
1136
+ readonly _tag: "Signed";
1137
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
1138
+ readonly signed: {
1139
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
1140
+ readonly sender: string & import("effect/Brand").Brand<"SuiAddress">;
1141
+ readonly signatures: readonly (string & import("effect/Brand").Brand<"Signature">)[];
1142
+ readonly bytes: Uint8Array<ArrayBufferLike>;
1143
+ readonly chain?: string | undefined;
1144
+ readonly expiration?: {
1145
+ readonly $kind: "None";
1146
+ readonly None: true;
1147
+ } | {
1148
+ readonly $kind: "Epoch";
1149
+ readonly Epoch: bigint;
1150
+ } | {
1151
+ readonly $kind: "ValidDuring";
1152
+ readonly ValidDuring: {
1153
+ readonly minEpoch: bigint | null;
1154
+ readonly maxEpoch: bigint | null;
1155
+ readonly minTimestamp: bigint | null;
1156
+ readonly maxTimestamp: bigint | null;
1157
+ readonly chain: string;
1158
+ readonly nonce: number;
1159
+ };
1160
+ } | {
1161
+ readonly $kind: "Validity";
1162
+ readonly Validity: {
1163
+ readonly allowedProposers: {
1164
+ readonly epoch: bigint;
1165
+ readonly proposers: readonly number[];
1166
+ } | null;
1167
+ readonly minEpoch: bigint | null;
1168
+ readonly maxEpoch: bigint | null;
1169
+ readonly minTimestamp: bigint | null;
1170
+ readonly maxTimestamp: bigint | null;
1171
+ readonly chain: string;
1172
+ readonly nonce: number;
1173
+ };
1174
+ } | undefined;
1175
+ };
1176
+ readonly signedAt: DateTime.Utc;
1177
+ } | {
1178
+ readonly at: DateTime.Utc;
1179
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
1180
+ readonly _tag: "Executed";
1181
+ readonly checkpoint?: bigint | undefined;
1182
+ } | {
1183
+ readonly _tag: "Failed";
1184
+ readonly digest: string & import("effect/Brand").Brand<"Digest">;
1185
+ readonly reason: {
1186
+ readonly $kind: "MoveAbort";
1187
+ readonly MoveAbort: {
1188
+ readonly abortCode: bigint;
1189
+ readonly location?: {
1190
+ readonly function?: number | undefined;
1191
+ readonly package?: string | undefined;
1192
+ readonly module?: string | undefined;
1193
+ readonly functionName?: string | undefined;
1194
+ readonly instruction?: number | undefined;
1195
+ } | undefined;
1196
+ readonly cleverError?: {
1197
+ readonly value?: string | undefined;
1198
+ readonly errorCode?: number | undefined;
1199
+ readonly lineNumber?: number | undefined;
1200
+ readonly constantName?: string | undefined;
1201
+ readonly constantType?: string | undefined;
1202
+ } | undefined;
1203
+ };
1204
+ } | {
1205
+ readonly $kind: "SizeError";
1206
+ readonly SizeError: {
1207
+ readonly name: string;
1208
+ readonly size: number;
1209
+ readonly maxSize: number;
1210
+ };
1211
+ } | {
1212
+ readonly $kind: "CommandArgumentError";
1213
+ readonly CommandArgumentError: {
1214
+ readonly argument: number;
1215
+ readonly name: string;
1216
+ };
1217
+ } | {
1218
+ readonly $kind: "TypeArgumentError";
1219
+ readonly TypeArgumentError: {
1220
+ readonly typeArgument: number;
1221
+ readonly name: string;
1222
+ };
1223
+ } | {
1224
+ readonly $kind: "PackageUpgradeError";
1225
+ readonly PackageUpgradeError: {
1226
+ readonly name: string;
1227
+ readonly digest?: string | undefined;
1228
+ readonly packageId?: string | undefined;
1229
+ };
1230
+ } | {
1231
+ readonly $kind: "IndexError";
1232
+ readonly IndexError: {
1233
+ readonly index?: number | undefined;
1234
+ readonly subresult?: number | undefined;
1235
+ };
1236
+ } | {
1237
+ readonly $kind: "CoinDenyListError";
1238
+ readonly CoinDenyListError: {
1239
+ readonly coinType: string;
1240
+ readonly name: string;
1241
+ readonly address?: string | undefined;
1242
+ };
1243
+ } | {
1244
+ readonly $kind: "CongestedObjects";
1245
+ readonly CongestedObjects: {
1246
+ readonly name: string;
1247
+ readonly objects: readonly string[];
1248
+ };
1249
+ } | {
1250
+ readonly $kind: "ObjectIdError";
1251
+ readonly ObjectIdError: {
1252
+ readonly objectId: string;
1253
+ readonly name?: string | undefined;
1254
+ };
1255
+ } | {
1256
+ readonly $kind: "Unknown";
1257
+ };
1258
+ readonly at: DateTime.Utc;
1259
+ }>, JournalError, never>;
676
1260
  readonly run: (recipe: Transaction | Recipe, opts: {
677
1261
  readonly signer: Signer;
678
1262
  readonly gasOwner?: SuiAddress;
@@ -681,7 +1265,41 @@ export declare const Tx: {
681
1265
  * the bytes name a gas owner that is not the sender.
682
1266
  */
683
1267
  readonly sponsor?: Signer;
1268
+ /**
1269
+ * Called with the signed bytes **after every signature is on them and
1270
+ * before the first `executeTransaction`**, which is the one moment a
1271
+ * consumer's own record has to be written: the digest is final from here
1272
+ * on, and anything that happens next may have reached the network.
1273
+ *
1274
+ * `Tx.submit` already writes its `Signed` journal entry at this point; this
1275
+ * is for the record the journal does not hold — a domain row joining the
1276
+ * digest to a batch, an outbox, a log line an operator greps. It runs
1277
+ * inside the sender lock, so it is ordered with the submission it belongs
1278
+ * to.
1279
+ *
1280
+ * Failing it fails the run **before anything is sent**, which is why its
1281
+ * error is a `JournalError`: that is the taxonomy's "the record could not
1282
+ * be written and nothing has gone out yet", it is already in `Tx.run`'s
1283
+ * union, and it is `not_applied`, so the documented retry idiom is correct.
1284
+ * Map your own persistence failure into it
1285
+ * (`Effect.mapError((cause) => new JournalError({ cause }))`).
1286
+ *
1287
+ * @since 0.1.2
1288
+ */
1289
+ readonly onSigned?: (signed: Signed) => Effect.Effect<void, JournalError>;
684
1290
  }) => Effect.Effect<Executed, TransportError | SimulationFailed | ExecutionFailed | SubmissionUnknown | NotApplied | SigningError | BuildError | PolicyDenied | JournalError, Sui>;
685
- readonly reconcileAll: () => Effect.Effect<readonly Reconciled[], TransportError | JournalError, Sui>;
1291
+ readonly reconcileAll: () => Effect.Effect<readonly ({
1292
+ readonly _tag: "ExecutionFailed";
1293
+ readonly error: ExecutionFailed;
1294
+ } | {
1295
+ readonly _tag: "SubmissionUnknown";
1296
+ readonly error: SubmissionUnknown;
1297
+ } | {
1298
+ readonly _tag: "NotApplied";
1299
+ readonly error: NotApplied;
1300
+ } | {
1301
+ readonly _tag: "Executed";
1302
+ readonly executed: Executed;
1303
+ })[], TransportError | JournalError, Sui>;
686
1304
  };
687
1305
  //# sourceMappingURL=Tx.d.ts.map