@unconfirmed/sui-effect 0.1.1 → 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 (58) hide show
  1. package/AGENTS.md +34 -11
  2. package/CHANGELOG.md +46 -0
  3. package/LLMS.md +953 -301
  4. package/README.md +171 -9
  5. package/dist/domain/bcs.d.ts.map +1 -1
  6. package/dist/domain/bcs.js +7 -0
  7. package/dist/domain/bcs.js.map +1 -1
  8. package/dist/domain/errors.d.ts +101 -5
  9. package/dist/domain/errors.d.ts.map +1 -1
  10. package/dist/domain/errors.js +158 -8
  11. package/dist/domain/errors.js.map +1 -1
  12. package/dist/domain/executed.d.ts +69 -0
  13. package/dist/domain/executed.d.ts.map +1 -1
  14. package/dist/domain/executed.js +196 -5
  15. package/dist/domain/executed.js.map +1 -1
  16. package/dist/domain/schemas.d.ts +27 -1
  17. package/dist/domain/schemas.d.ts.map +1 -1
  18. package/dist/domain/schemas.js +20 -2
  19. package/dist/domain/schemas.js.map +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +1 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/script.d.ts +1 -1
  25. package/dist/script.d.ts.map +1 -1
  26. package/dist/script.js +1 -1
  27. package/dist/script.js.map +1 -1
  28. package/dist/services/Script.d.ts +42 -0
  29. package/dist/services/Script.d.ts.map +1 -1
  30. package/dist/services/Script.js +69 -2
  31. package/dist/services/Script.js.map +1 -1
  32. package/dist/services/Signer.d.ts +32 -7
  33. package/dist/services/Signer.d.ts.map +1 -1
  34. package/dist/services/Signer.js +69 -10
  35. package/dist/services/Signer.js.map +1 -1
  36. package/dist/services/Sui.d.ts +42 -1
  37. package/dist/services/Sui.d.ts.map +1 -1
  38. package/dist/services/Sui.js +18 -4
  39. package/dist/services/Sui.js.map +1 -1
  40. package/dist/services/SuiCoreFake.d.ts +47 -4
  41. package/dist/services/SuiCoreFake.d.ts.map +1 -1
  42. package/dist/services/SuiCoreFake.js +193 -22
  43. package/dist/services/SuiCoreFake.js.map +1 -1
  44. package/dist/services/Tx.d.ts +627 -11
  45. package/dist/services/Tx.d.ts.map +1 -1
  46. package/dist/services/Tx.js +187 -8
  47. package/dist/services/Tx.js.map +1 -1
  48. package/dist/testing.d.ts +1 -0
  49. package/dist/testing.d.ts.map +1 -1
  50. package/dist/testing.js +9 -0
  51. package/dist/testing.js.map +1 -1
  52. package/dist/tx.d.ts +1 -1
  53. package/dist/tx.d.ts.map +1 -1
  54. package/dist/tx.js +1 -1
  55. package/dist/tx.js.map +1 -1
  56. package/docs/extensions.md +533 -8
  57. package/examples/extension-template/src/Escrow.ts +1 -1
  58. package/package.json +1 -1
package/LLMS.md CHANGED
@@ -17,7 +17,7 @@ contract for building an extension package on top of it.
17
17
  ## `@unconfirmed/sui-effect`
18
18
 
19
19
 
20
- 82 exported symbols.
20
+ 84 exported symbols.
21
21
 
22
22
  ### `Balance` (const)
23
23
 
@@ -229,14 +229,43 @@ The return values and mutated references of one command. Mirrors `SuiClientTypes
229
229
 
230
230
  ```ts
231
231
  export declare class DecodeError extends DecodeError_base {
232
- readonly issue: string
233
- readonly objectId: ObjectId | undefined
234
- readonly expectedType: string | undefined
232
+ /** The one actionable line `SuiError.describe` produces for this error. */
233
+ get message(): string;
235
234
  }
236
235
  ```
237
236
 
238
237
  BCS content or a schema boundary did not decode.
239
238
 
239
+ `kind` says which of the three (`"type"`, `"bytes"`, `"shape"`) and is what a
240
+ consumer branches on; `issue` is the sentence for a human and is not stable.
241
+ It defaults to `"shape"` when neither a constructor nor an encoded value
242
+ carries one, so an extension that builds a `DecodeError` with no `kind` still
243
+ compiles and still answers the question conservatively.
244
+
245
+ ### `DecodeKind` (const)
246
+
247
+ ```ts
248
+ declare const DecodeKind: Schema
249
+ // decodes to: "bytes" | "type" | "shape"
250
+ ```
251
+
252
+ Which of the three things that can go wrong at a decode boundary went wrong.
253
+
254
+ - `"type"`: the object, the field or the event **is not of the expected Move
255
+ type**. The bytes were never parsed. This is the one a caller answers with
256
+ "that is not one of mine" — a 404 for a foreign object, a `filter` over a
257
+ heterogeneous list — and the one it is safe to swallow.
258
+ - `"bytes"`: the type matched and the **BCS parse failed**, or left trailing
259
+ bytes. Either the layout this package was built with is not the layout the
260
+ package on chain writes, or the object is corrupt. Never safe to swallow.
261
+ - `"shape"`: a **domain schema** refused a value that was already parsed or
262
+ that came from the node as JSON — a missing field in a node response, a
263
+ number that is not a timestamp, a simulation with no such command. A bug
264
+ here is in this library, the node, or the caller's expectations.
265
+
266
+ Branch on this, never on `DecodeError`'s `issue`: `issue` is a human
267
+ sentence and its wording changes between releases.
268
+
240
269
  ### `Digest` (const)
241
270
 
242
271
  ```ts
@@ -258,8 +287,8 @@ declare const DynamicField: Schema
258
287
  // readonly type: string;
259
288
  // readonly bcs: Uint8Array<ArrayBufferLike>;
260
289
  // };
261
- // readonly fieldId: ObjectId;
262
290
  // readonly type: string;
291
+ // readonly fieldId: ObjectId;
263
292
  // readonly name: {
264
293
  // readonly type: string;
265
294
  // readonly bcs: Uint8Array<ArrayBufferLike>;
@@ -279,8 +308,8 @@ declare const DynamicFieldEntry: Schema
279
308
  // decodes to:
280
309
  // {
281
310
  // readonly $kind: "DynamicField" | "DynamicObject";
282
- // readonly fieldId: ObjectId;
283
311
  // readonly type: string;
312
+ // readonly fieldId: ObjectId;
284
313
  // readonly name: {
285
314
  // readonly type: string;
286
315
  // readonly bcs: Uint8Array<ArrayBufferLike>;
@@ -305,10 +334,18 @@ The BCS-encoded name of a dynamic field. Mirrors `SuiClientTypes.DynamicFieldNam
305
334
 
306
335
  ```ts
307
336
  declare const Event: Schema
308
- // decodes to: { readonly packageId: ObjectId; readonly module: string; readonly sender: SuiAddress; readonly eventType: string; readonly bcs: Uint8Array<ArrayBufferLike>; }
337
+ // decodes to: { readonly sender: SuiAddress; readonly bcs: Uint8Array<ArrayBufferLike>; readonly packageId: ObjectId; readonly module: string; readonly eventType: string; readonly json?: unknown; }
309
338
  ```
310
339
 
311
- An emitted Move event. Mirrors `SuiClientTypes.Event`; `json` is dropped on purpose.
340
+ An emitted Move event. Mirrors `SuiClientTypes.Event` with the branded ids
341
+ this package uses.
342
+
343
+ The decode a caller wants is `SuiSchema.decode(codec, event.bcs)`, which
344
+ gives a typed value; `json` is the node's own rendering and is **not**
345
+ something to build on — it is absent on most transports and its shape follows
346
+ whatever the node feels like. It is kept only when the source carried it,
347
+ which in practice means a relay or sponsor envelope decoded through
348
+ `Executed.fromPartial`, where it may be the only form of the event there is.
312
349
 
313
350
  ### `Executed` (class)
314
351
 
@@ -364,6 +401,16 @@ export declare class Executed extends Executed_base {
364
401
  * Never fails.
365
402
  */
366
403
  wrapped(): ReadonlyArray<ChangedRef>;
404
+ /**
405
+ * Whether this change wrote an object, treating `Unknown` as "the envelope
406
+ * did not say".
407
+ *
408
+ * A node always reports the output state; a reduced envelope from a relay or
409
+ * a sponsor often reports nothing but the id and the id operation, and
410
+ * {@link Executed.fromPartial} leaves what it was not told as `Unknown`
411
+ * rather than inventing `ObjectWrite`. Reading `Unknown` as "not an object
412
+ * write" would make `created()` silently empty for exactly those envelopes.
413
+ */
367
414
  /**
368
415
  * Packages this transaction published (`PackageWrite` plus `Created`), as
369
416
  * full refs like every other accessor. A package's `type` is the literal
@@ -376,6 +423,53 @@ export declare class Executed extends Executed_base {
376
423
  balanceChange(address: SuiAddress, coinType: CoinType): bigint;
377
424
  /** Computation plus storage less the storage rebate, in MIST. Can be negative. Never fails. */
378
425
  get gasUsedTotal(): bigint;
426
+ /**
427
+ * An `Executed` from the SDK's own `TransactionResult`, read with
428
+ * {@link EXECUTE_INCLUDE}.
429
+ *
430
+ * This is what `Tx.submit` uses, exported so a caller holding a result from
431
+ * somewhere else — `client.core.executeTransaction`, a sponsor's SDK call —
432
+ * can get the accessors without re-implementing the decode.
433
+ *
434
+ * Fails with: `ExecutionFailed` (the transaction applied and failed),
435
+ * `DecodeError` (the response does not carry the include set).
436
+ *
437
+ * @since 0.1.2
438
+ */
439
+ static readonly fromTransactionResult: (result: SuiClientTypes.TransactionResult<typeof EXECUTE_INCLUDE>) => Effect.Effect<Executed, ExecutionFailed | DecodeError>;
440
+ /**
441
+ * An `Executed` from a **reduced** execute envelope: what a relay, a sponsor
442
+ * or another service hands back, over JSON, after submitting on your behalf.
443
+ *
444
+ * Such an envelope is rarely the SDK's full include set. Everything optional
445
+ * is filled in with "the node did not say" rather than refused:
446
+ *
447
+ * - `changedObjects` entries need only `objectId` and `idOperation`; the
448
+ * version, digest and owner on either side default to `null`, and
449
+ * `outputState` defaults to `ObjectWrite` (`DoesNotExist` for a
450
+ * `Deleted`), so {@link created} and {@link deleted} classify correctly
451
+ * from the id operation alone.
452
+ * - `objectTypes` defaults to `{}`. **The type filters need it**:
453
+ * `created(type)`, `mutated(type)` and `expectCreated(type)` can only
454
+ * match a change whose type the envelope carried, so without
455
+ * `objectTypes` they return nothing. `created()` with no argument, and
456
+ * {@link createdWhere}, still list every created id.
457
+ * - `balanceChanges` and `events` default to `[]`, `checkpoint` and
458
+ * `timestampMs` to `null`, `gasUsed` to zeros, and `effects.status` to
459
+ * success.
460
+ * - **JSON spellings are accepted** where the SDK's types are not JSON:
461
+ * `bcs` as base64 or as an array of byte values as well as a
462
+ * `Uint8Array`, and every `u64` (versions, balances, gas, `checkpoint`)
463
+ * as a number or a `bigint` as well as the decimal string the wire uses.
464
+ *
465
+ * An envelope with no usable digest, or whose values are the wrong shape
466
+ * rather than merely absent, fails: absence is filled in, nonsense is not.
467
+ *
468
+ * Fails with: `DecodeError`.
469
+ *
470
+ * @since 0.1.2
471
+ */
472
+ static readonly fromPartial: (envelope: unknown) => Effect.Effect<Executed, DecodeError>;
379
473
  /**
380
474
  * The single object of this type the transaction created.
381
475
  *
@@ -389,220 +483,22 @@ export declare class Executed extends Executed_base {
389
483
  A transaction the network executed, built from the fixed execute include set:
390
484
  effects, events, balance changes and object types.
391
485
 
486
+ **`events` is `ReadonlyArray<Event>`, not `SuiClientTypes.Event[]`.** It is
487
+ that type minus `json`: `packageId`, `module`, `sender`, `eventType` and
488
+ `bcs`, with the branded ids this package uses. The SDK's `json` is dropped on
489
+ purpose — it is the node's own rendering, it is absent on most transports,
490
+ and the decode a caller wants is `SuiSchema.decode(codec, event.bcs)`, which
491
+ gives a typed value rather than a shape that changes with the node. Code
492
+ typed against the SDK's `Event[]` therefore does not accept these; take
493
+ `ReadonlyArray<Event>` from `@unconfirmed/sui-effect`, or map the fields you
494
+ need.
495
+
392
496
  ### `ExecutionFailed` (class)
393
497
 
394
498
  ```ts
395
499
  export declare class ExecutionFailed extends ExecutionFailed_base {
396
- readonly digest: Digest
397
- readonly reason: {
398
- readonly $kind: "MoveAbort";
399
- readonly MoveAbort: {
400
- readonly abortCode: bigint;
401
- readonly location?: {
402
- readonly function?: number | undefined;
403
- readonly module?: string | undefined;
404
- readonly package?: string | undefined;
405
- readonly functionName?: string | undefined;
406
- readonly instruction?: number | undefined;
407
- } | undefined;
408
- readonly cleverError?: {
409
- readonly errorCode?: number | undefined;
410
- readonly lineNumber?: number | undefined;
411
- readonly constantName?: string | undefined;
412
- readonly constantType?: string | undefined;
413
- readonly value?: string | undefined;
414
- } | undefined;
415
- };
416
- } | {
417
- readonly $kind: "SizeError";
418
- readonly SizeError: {
419
- readonly name: string;
420
- readonly size: number;
421
- readonly maxSize: number;
422
- };
423
- } | {
424
- readonly $kind: "CommandArgumentError";
425
- readonly CommandArgumentError: {
426
- readonly argument: number;
427
- readonly name: string;
428
- };
429
- } | {
430
- readonly $kind: "TypeArgumentError";
431
- readonly TypeArgumentError: {
432
- readonly typeArgument: number;
433
- readonly name: string;
434
- };
435
- } | {
436
- readonly $kind: "PackageUpgradeError";
437
- readonly PackageUpgradeError: {
438
- readonly name: string;
439
- readonly digest?: string | undefined;
440
- readonly packageId?: string | undefined;
441
- };
442
- } | {
443
- readonly $kind: "IndexError";
444
- readonly IndexError: {
445
- readonly index?: number | undefined;
446
- readonly subresult?: number | undefined;
447
- };
448
- } | {
449
- readonly $kind: "CoinDenyListError";
450
- readonly CoinDenyListError: {
451
- readonly coinType: string;
452
- readonly name: string;
453
- readonly address?: string | undefined;
454
- };
455
- } | {
456
- readonly $kind: "CongestedObjects";
457
- readonly CongestedObjects: {
458
- readonly name: string;
459
- readonly objects: readonly string[];
460
- };
461
- } | {
462
- readonly $kind: "ObjectIdError";
463
- readonly ObjectIdError: {
464
- readonly objectId: string;
465
- readonly name?: string | undefined;
466
- };
467
- } | {
468
- readonly $kind: "Unknown";
469
- }
470
- readonly effects: {
471
- readonly version: number;
472
- readonly status: {
473
- readonly success: boolean;
474
- };
475
- readonly gasUsed: {
476
- readonly computationCost: Mist;
477
- readonly storageCost: Mist;
478
- readonly storageRebate: Mist;
479
- readonly nonRefundableStorageFee: Mist;
480
- };
481
- readonly transactionDigest: Digest;
482
- readonly gasObject: {
483
- readonly objectId: ObjectId;
484
- readonly inputState: "Unknown" | "DoesNotExist" | "Exists";
485
- readonly inputVersion: Version | null;
486
- readonly inputDigest: string | null;
487
- readonly inputOwner: {
488
- readonly $kind: "AddressOwner";
489
- readonly AddressOwner: SuiAddress;
490
- } | {
491
- readonly $kind: "ObjectOwner";
492
- readonly ObjectOwner: ObjectId;
493
- } | {
494
- readonly $kind: "Shared";
495
- readonly Shared: {
496
- readonly initialSharedVersion: Version;
497
- };
498
- } | {
499
- readonly $kind: "Immutable";
500
- readonly Immutable: true;
501
- } | {
502
- readonly $kind: "ConsensusAddressOwner";
503
- readonly ConsensusAddressOwner: {
504
- readonly startVersion: Version;
505
- readonly owner: SuiAddress;
506
- };
507
- } | {
508
- readonly $kind: "Unknown";
509
- } | null;
510
- readonly outputState: "Unknown" | "DoesNotExist" | "ObjectWrite" | "PackageWrite" | "AccumulatorWriteV1";
511
- readonly outputVersion: Version | null;
512
- readonly outputDigest: string | null;
513
- readonly outputOwner: {
514
- readonly $kind: "AddressOwner";
515
- readonly AddressOwner: SuiAddress;
516
- } | {
517
- readonly $kind: "ObjectOwner";
518
- readonly ObjectOwner: ObjectId;
519
- } | {
520
- readonly $kind: "Shared";
521
- readonly Shared: {
522
- readonly initialSharedVersion: Version;
523
- };
524
- } | {
525
- readonly $kind: "Immutable";
526
- readonly Immutable: true;
527
- } | {
528
- readonly $kind: "ConsensusAddressOwner";
529
- readonly ConsensusAddressOwner: {
530
- readonly startVersion: Version;
531
- readonly owner: SuiAddress;
532
- };
533
- } | {
534
- readonly $kind: "Unknown";
535
- } | null;
536
- readonly idOperation: "None" | "Unknown" | "Created" | "Deleted";
537
- } | null;
538
- readonly eventsDigest: string | null;
539
- readonly dependencies: readonly string[];
540
- readonly lamportVersion: Version | null;
541
- readonly changedObjects: readonly {
542
- readonly objectId: ObjectId;
543
- readonly inputState: "Unknown" | "DoesNotExist" | "Exists";
544
- readonly inputVersion: Version | null;
545
- readonly inputDigest: string | null;
546
- readonly inputOwner: {
547
- readonly $kind: "AddressOwner";
548
- readonly AddressOwner: SuiAddress;
549
- } | {
550
- readonly $kind: "ObjectOwner";
551
- readonly ObjectOwner: ObjectId;
552
- } | {
553
- readonly $kind: "Shared";
554
- readonly Shared: {
555
- readonly initialSharedVersion: Version;
556
- };
557
- } | {
558
- readonly $kind: "Immutable";
559
- readonly Immutable: true;
560
- } | {
561
- readonly $kind: "ConsensusAddressOwner";
562
- readonly ConsensusAddressOwner: {
563
- readonly startVersion: Version;
564
- readonly owner: SuiAddress;
565
- };
566
- } | {
567
- readonly $kind: "Unknown";
568
- } | null;
569
- readonly outputState: "Unknown" | "DoesNotExist" | "ObjectWrite" | "PackageWrite" | "AccumulatorWriteV1";
570
- readonly outputVersion: Version | null;
571
- readonly outputDigest: string | null;
572
- readonly outputOwner: {
573
- readonly $kind: "AddressOwner";
574
- readonly AddressOwner: SuiAddress;
575
- } | {
576
- readonly $kind: "ObjectOwner";
577
- readonly ObjectOwner: ObjectId;
578
- } | {
579
- readonly $kind: "Shared";
580
- readonly Shared: {
581
- readonly initialSharedVersion: Version;
582
- };
583
- } | {
584
- readonly $kind: "Immutable";
585
- readonly Immutable: true;
586
- } | {
587
- readonly $kind: "ConsensusAddressOwner";
588
- readonly ConsensusAddressOwner: {
589
- readonly startVersion: Version;
590
- readonly owner: SuiAddress;
591
- };
592
- } | {
593
- readonly $kind: "Unknown";
594
- } | null;
595
- readonly idOperation: "None" | "Unknown" | "Created" | "Deleted";
596
- }[];
597
- readonly unchangedConsensusObjects: readonly {
598
- readonly kind: "Unknown" | "ReadOnlyRoot" | "MutateConsensusStreamEnded" | "ReadConsensusStreamEnded" | "Cancelled" | "PerEpochConfig";
599
- readonly objectId: ObjectId;
600
- readonly version: Version | null;
601
- readonly digest: string | null;
602
- }[];
603
- readonly auxiliaryDataDigest: string | null;
604
- }
605
- readonly command: number | undefined
500
+ /** The one actionable line `SuiError.describe` produces for this error. */
501
+ get message(): string;
606
502
  }
607
503
  ```
608
504
 
@@ -710,8 +606,8 @@ representation of an on-chain failure.
710
606
 
711
607
  ```ts
712
608
  export declare class ExtensionNotReady extends ExtensionNotReady_base {
713
- readonly extension: string
714
- readonly member: string
609
+ /** The one actionable line `SuiError.describe` produces for this error. */
610
+ get message(): string;
715
611
  }
716
612
  ```
717
613
 
@@ -749,8 +645,8 @@ What can go wrong reading one object with a schema.
749
645
 
750
646
  ```ts
751
647
  export declare class GraphQLUnavailable extends GraphQLUnavailable_base {
752
- readonly method: string
753
- readonly reason: string
648
+ /** The one actionable line `SuiError.describe` produces for this error. */
649
+ get message(): string;
754
650
  }
755
651
  ```
756
652
 
@@ -786,7 +682,8 @@ the documented retry idiom to send again on no evidence at all.
786
682
 
787
683
  ```ts
788
684
  export declare class JournalError extends JournalError_base {
789
- readonly cause: unknown
685
+ /** The one actionable line `SuiError.describe` produces for this error. */
686
+ get message(): string;
790
687
  }
791
688
  ```
792
689
 
@@ -854,8 +751,8 @@ The network a client is pointed at. Mirrors `SuiClientTypes.Network`.
854
751
 
855
752
  ```ts
856
753
  export declare class NetworkMismatch extends NetworkMismatch_base {
857
- readonly expected: string
858
- readonly actual: string
754
+ /** The one actionable line `SuiError.describe` produces for this error. */
755
+ get message(): string;
859
756
  }
860
757
  ```
861
758
 
@@ -865,8 +762,8 @@ The chain identifier the node reported is not the one the layer was built for.
865
762
 
866
763
  ```ts
867
764
  export declare class NotApplied extends NotApplied_base {
868
- readonly digest: Digest
869
- readonly evidence: "expired" | "inputConsumed"
765
+ /** The one actionable line `SuiError.describe` produces for this error. */
766
+ get message(): string;
870
767
  }
871
768
  ```
872
769
 
@@ -900,8 +797,8 @@ cannot drift.
900
797
 
901
798
  ```ts
902
799
  export declare class ObjectDeleted extends ObjectDeleted_base {
903
- readonly objectId: ObjectId
904
- readonly version: Version | undefined
800
+ /** The one actionable line `SuiError.describe` produces for this error. */
801
+ get message(): string;
905
802
  }
906
803
  ```
907
804
 
@@ -967,8 +864,17 @@ The failures of an object lookup.
967
864
 
968
865
  ```ts
969
866
  export declare class ObjectNotFound extends ObjectNotFound_base {
970
- readonly objectId: ObjectId
971
- readonly version: Version | undefined
867
+ /**
868
+ * The one actionable line `SuiError.describe` produces for this error.
869
+ *
870
+ * `Schema.TaggedError` gives every class the `Error` constructor and no
871
+ * message of its own, so `error.message` was the empty string — and a
872
+ * consumer that surfaces `.message` (a log line, a UI, another library's
873
+ * error formatter) showed nothing at all. A getter rather than a schema
874
+ * field, so it is always in step with `describe`, costs nothing to
875
+ * construct, and stays out of `SuiError.toJson`'s encoding.
876
+ */
877
+ get message(): string;
972
878
  }
973
879
  ```
974
880
 
@@ -1029,8 +935,8 @@ readable object like any other.
1029
935
 
1030
936
  ```ts
1031
937
  export declare class ObjectUnavailable extends ObjectUnavailable_base {
1032
- readonly objectId: ObjectId
1033
- readonly version: Version | undefined
938
+ /** The one actionable line `SuiError.describe` produces for this error. */
939
+ get message(): string;
1034
940
  }
1035
941
  ```
1036
942
 
@@ -1045,6 +951,22 @@ export type Outcome = "applied" | "not_applied" | "unknown";
1045
951
  What a failure says about the transaction it came from, on the axis a caller
1046
952
  or a wrapper script acts on.
1047
953
 
954
+ ### `OutcomePhase` (type)
955
+
956
+ ```ts
957
+ export type OutcomePhase = "pre-submit" | "post-submit";
958
+ ```
959
+
960
+ Where in the lifecycle a failure was caught, which is the only thing that
961
+ can classify an error the taxonomy does not own.
962
+
963
+ `"post-submit"` (the default, and the 0.1.1 behaviour) is "bytes may have
964
+ gone out": a tag nobody here recognises proves nothing, so the answer is
965
+ `"unknown"`. `"pre-submit"` is a failure caught while **building, simulating
966
+ or signing** — an extension's own `PriceTooLow`, a validation error from the
967
+ caller's code — where nothing has been sent by construction and the honest
968
+ answer is `"not_applied"`.
969
+
1048
970
  ### `Owner` (const)
1049
971
 
1050
972
  ```ts
@@ -1178,7 +1100,8 @@ The expiration the transaction was built with rides along, because it is what
1178
1100
 
1179
1101
  ```ts
1180
1102
  export declare class SigningError extends SigningError_base {
1181
- readonly cause: unknown
1103
+ /** The one actionable line `SuiError.describe` produces for this error. */
1104
+ get message(): string;
1182
1105
  }
1183
1106
  ```
1184
1107
 
@@ -1327,11 +1250,12 @@ declare const Simulation: Schema
1327
1250
  // readonly auxiliaryDataDigest: string | null;
1328
1251
  // };
1329
1252
  // readonly events: readonly {
1253
+ // readonly sender: SuiAddress;
1254
+ // readonly bcs: Uint8Array<ArrayBufferLike>;
1330
1255
  // readonly packageId: ObjectId;
1331
1256
  // readonly module: string;
1332
- // readonly sender: SuiAddress;
1333
1257
  // readonly eventType: string;
1334
- // readonly bcs: Uint8Array<ArrayBufferLike>;
1258
+ // readonly json?: unknown;
1335
1259
  // }[];
1336
1260
  // readonly balanceChanges: readonly {
1337
1261
  // readonly coinType: CoinType;
@@ -1460,46 +1384,8 @@ decode so `0x2::sui::SUI` and its padded form compare equal.
1460
1384
 
1461
1385
  ```ts
1462
1386
  export declare class SubmissionUnknown extends SubmissionUnknown_base {
1463
- readonly cause: unknown
1464
- readonly digest: Digest
1465
- readonly signed: {
1466
- readonly digest: Digest;
1467
- readonly bytes: Uint8Array<ArrayBufferLike>;
1468
- readonly sender: SuiAddress;
1469
- readonly signatures: readonly Signature[];
1470
- readonly expiration?: {
1471
- readonly $kind: "None";
1472
- readonly None: true;
1473
- } | {
1474
- readonly $kind: "Epoch";
1475
- readonly Epoch: bigint;
1476
- } | {
1477
- readonly $kind: "ValidDuring";
1478
- readonly ValidDuring: {
1479
- readonly minEpoch: bigint | null;
1480
- readonly maxEpoch: bigint | null;
1481
- readonly minTimestamp: bigint | null;
1482
- readonly maxTimestamp: bigint | null;
1483
- readonly chain: string;
1484
- readonly nonce: number;
1485
- };
1486
- } | {
1487
- readonly $kind: "Validity";
1488
- readonly Validity: {
1489
- readonly allowedProposers: {
1490
- readonly epoch: bigint;
1491
- readonly proposers: readonly number[];
1492
- } | null;
1493
- readonly minEpoch: bigint | null;
1494
- readonly maxEpoch: bigint | null;
1495
- readonly minTimestamp: bigint | null;
1496
- readonly maxTimestamp: bigint | null;
1497
- readonly chain: string;
1498
- readonly nonce: number;
1499
- };
1500
- } | undefined;
1501
- readonly chain?: string | undefined;
1502
- } | undefined
1387
+ /** The one actionable line `SuiError.describe` produces for this error. */
1388
+ get message(): string;
1503
1389
  }
1504
1390
  ```
1505
1391
 
@@ -1826,6 +1712,26 @@ export interface SuiLayerOptions {
1826
1712
  * network are regenerated, so set this when the program must pin one.
1827
1713
  */
1828
1714
  readonly chainId?: string;
1715
+ /**
1716
+ * How to retry the one `getChainIdentifier` this layer makes, when it fails
1717
+ * with a **retryable** `TransportError`.
1718
+ *
1719
+ * It matters more than one round trip usually would, because of what holds
1720
+ * the result: a `ManagedRuntime` — which is how a browser app, a Worker or a
1721
+ * Durable Object keeps one runtime per isolate — **memoizes the layer build,
1722
+ * failure included, for its whole lifetime**. One unlucky request at boot and
1723
+ * every later use of that runtime fails with the same stale
1724
+ * `TransportError`, until something disposes it. A short schedule here is the
1725
+ * cheap half of the cure; disposing the runtime when its build failed is the
1726
+ * other half (see the guide's "Application consumers" section), and
1727
+ * `layerNoDepsPinned` avoids the read altogether when the chain id is already
1728
+ * known.
1729
+ *
1730
+ * Unset, the read is made once, as it always was.
1731
+ *
1732
+ * @since 0.1.2
1733
+ */
1734
+ readonly retry?: Schedule.Schedule<unknown, TransportError>;
1829
1735
  }
1830
1736
  ```
1831
1737
 
@@ -2063,6 +1969,27 @@ export interface SuiService {
2063
1969
  *
2064
1970
  * Fails with: `ObjectNotFound`, `ObjectDeleted`, `ObjectUnavailable`,
2065
1971
  * `DecodeError`, `TransportError`.
1972
+ *
1973
+ * @since 0.1.2
1974
+ */
1975
+ readonly getObjectsStrict: {
1976
+ <S>(ids: ReadonlyArray<ObjectId>, opts: {
1977
+ readonly schema: Schema.Codec<S, Uint8Array>;
1978
+ readonly expectedType?: string;
1979
+ }): Effect.Effect<ReadonlyArray<SuiObject<S>>, BatchItemError | TransportError>;
1980
+ (ids: ReadonlyArray<ObjectId>, opts?: {
1981
+ readonly schema?: undefined;
1982
+ readonly expectedType?: string;
1983
+ }): Effect.Effect<ReadonlyArray<SuiObject<Uint8Array>>, BatchItemError | TransportError>;
1984
+ };
1985
+ /**
1986
+ * {@link getObjectsStrict} under its 0.1.0 name.
1987
+ *
1988
+ * `getObjectsOrFail` reads, out of context, as though failing were the point
1989
+ * — `getObjects(ids).orFail()` — rather than as the strictness of the read.
1990
+ * The two are the same function.
1991
+ *
1992
+ * @deprecated Use `getObjectsStrict`. This alias stays for 0.1.x.
2066
1993
  */
2067
1994
  readonly getObjectsOrFail: {
2068
1995
  <S>(ids: ReadonlyArray<ObjectId>, opts: {
@@ -2357,7 +2284,8 @@ The failures of a transaction lookup.
2357
2284
 
2358
2285
  ```ts
2359
2286
  export declare class TransactionNotFound extends TransactionNotFound_base {
2360
- readonly digest: Digest
2287
+ /** The one actionable line `SuiError.describe` produces for this error. */
2288
+ get message(): string;
2361
2289
  }
2362
2290
  ```
2363
2291
 
@@ -2398,6 +2326,8 @@ export declare class TransportError extends TransportError_base {
2398
2326
  * ```
2399
2327
  */
2400
2328
  static readonly fromUnknown: (method: string, cause: unknown, retryable?: boolean) => TransportError;
2329
+ /** The one actionable line `SuiError.describe` produces for this error. */
2330
+ get message(): string;
2401
2331
  }
2402
2332
  ```
2403
2333
 
@@ -2428,9 +2358,8 @@ Mirrors `SuiClientTypes.UnchangedConsensusObject`.
2428
2358
 
2429
2359
  ```ts
2430
2360
  export declare class UnexpectedEffects extends UnexpectedEffects_base {
2431
- readonly digest: Digest
2432
- readonly expected: string
2433
- readonly found: readonly ObjectId[]
2361
+ /** The one actionable line `SuiError.describe` produces for this error. */
2362
+ get message(): string;
2434
2363
  }
2435
2364
  ```
2436
2365
 
@@ -2577,7 +2506,7 @@ object has no output version, and nothing can be consumed without both.
2577
2506
  ## `@unconfirmed/sui-effect/tx`
2578
2507
 
2579
2508
 
2580
- 37 exported symbols.
2509
+ 42 exported symbols.
2581
2510
 
2582
2511
  ### `Built` (const)
2583
2512
 
@@ -2873,13 +2802,43 @@ export type ReconcileInput = Digest | Signed | SubmissionUnknown;
2873
2802
 
2874
2803
  What `Tx.reconcile` can be asked about.
2875
2804
 
2876
- ### `Reconciled` (type)
2805
+ ### `Reconciled` (const)
2806
+
2807
+ ```ts
2808
+ declare const Reconciled: Schema
2809
+ // decodes to:
2810
+ // {
2811
+ // readonly _tag: "NotApplied";
2812
+ // readonly error: NotApplied;
2813
+ // } | {
2814
+ // readonly _tag: "Executed";
2815
+ // readonly executed: Executed;
2816
+ // } | {
2817
+ // readonly _tag: "ExecutionFailed";
2818
+ // readonly error: ExecutionFailed;
2819
+ // } | {
2820
+ // readonly _tag: "SubmissionUnknown";
2821
+ // readonly error: SubmissionUnknown;
2822
+ // }
2823
+ ```
2824
+
2825
+ What one entry of `Tx.reconcileAll` settled to, as a tagged union.
2826
+
2827
+ Every case carries **one** discriminator in the same place: `_tag` is
2828
+ `"Executed"`, `"ExecutionFailed"`, `"NotApplied"` or `"SubmissionUnknown"`,
2829
+ and the payload is `executed` for the first and `error` for the other three.
2830
+ Before 0.1.2 this was a bare union of an `Executed` (which has no `_tag`) and
2831
+ three errors (which do), so the only way to tell a success from a failure was
2832
+ `"_tag" in entry` — a shape nothing could `Schema.match` or serialize.
2833
+
2834
+ ### `ReconciledOutcome` (type)
2877
2835
 
2878
2836
  ```ts
2879
- export type Reconciled = Executed | ExecutionFailed | NotApplied | SubmissionUnknown;
2837
+ export type ReconciledOutcome = Executed | ExecutionFailed | NotApplied | SubmissionUnknown;
2880
2838
  ```
2881
2839
 
2882
- What one entry of `Tx.reconcileAll` settled to.
2840
+ The shape `Tx.reconcileAll` returned in 0.1.0 and 0.1.1: the four outcomes as
2841
+ a bare union, with the successful one carrying no discriminator at all.
2883
2842
 
2884
2843
  ### `RemoteSigner` (interface)
2885
2844
 
@@ -3094,7 +3053,7 @@ Every decision `Tx.build`, `Tx.submit` and `Tx.reconcile` read from context.
3094
3053
  ### `SubmitError` (type)
3095
3054
 
3096
3055
  ```ts
3097
- export type SubmitError = ExecutionFailed | NotApplied | SubmissionUnknown | JournalError;
3056
+ export type SubmitError = ExecutionFailed | NotApplied | SubmissionUnknown | JournalError | TransportError;
3098
3057
  ```
3099
3058
 
3100
3059
  Everything `submit` can fail with, as one name.
@@ -3102,6 +3061,30 @@ Everything `submit` can fail with, as one name.
3102
3061
  An extension that wraps a submission spells its own errors plus this, rather
3103
3062
  than repeating four tags that will grow with the taxonomy.
3104
3063
 
3064
+ ### `SubmitViaError` (type)
3065
+
3066
+ ```ts
3067
+ export type SubmitViaError = ExecutionFailed | NotApplied | SubmissionUnknown | JournalError;
3068
+ ```
3069
+
3070
+ Everything `submitVia` can fail with, before the sender's own errors.
3071
+
3072
+ ### `SubmitViaReply` (type)
3073
+
3074
+ ```ts
3075
+ export type SubmitViaReply = unknown;
3076
+ ```
3077
+
3078
+ What a third party answers when it has submitted your bytes.
3079
+
3080
+ Three shapes are understood, in this order: an SDK `TransactionResult` (the
3081
+ service ran `executeTransaction` and passed the whole thing on), a reduced
3082
+ execute envelope (anything with a `digest` or an `effects`, decoded through
3083
+ `Executed.fromPartial`), and a bare digest string. Anything else — a
3084
+ `void`, an acknowledgement with no digest — means "ask the chain", and
3085
+ `submitVia` reconciles by the digest it already has, which is the digest of
3086
+ the bytes it handed over.
3087
+
3105
3088
  ### `TransactionExpiration` (const)
3106
3089
 
3107
3090
  Re-exported from `@unconfirmed/sui-effect`.
@@ -3306,7 +3289,219 @@ declare const Tx: {
3306
3289
  readonly gasOwner: SuiAddress;
3307
3290
  }) => (recipe: Recipe) => Recipe;
3308
3291
  readonly submit: (signed: Signed) => Effect.Effect<Executed, SubmitError, Sui>;
3292
+ readonly submitVia: <E, R>(signed: {
3293
+ readonly digest: Digest;
3294
+ readonly sender: SuiAddress;
3295
+ readonly signatures: readonly Signature[];
3296
+ readonly bytes: Uint8Array<ArrayBufferLike>;
3297
+ readonly chain?: string | undefined;
3298
+ readonly expiration?: {
3299
+ readonly $kind: "None";
3300
+ readonly None: true;
3301
+ } | {
3302
+ readonly $kind: "Epoch";
3303
+ readonly Epoch: bigint;
3304
+ } | {
3305
+ readonly $kind: "ValidDuring";
3306
+ readonly ValidDuring: {
3307
+ readonly minEpoch: bigint | null;
3308
+ readonly maxEpoch: bigint | null;
3309
+ readonly minTimestamp: bigint | null;
3310
+ readonly maxTimestamp: bigint | null;
3311
+ readonly chain: string;
3312
+ readonly nonce: number;
3313
+ };
3314
+ } | {
3315
+ readonly $kind: "Validity";
3316
+ readonly Validity: {
3317
+ readonly allowedProposers: {
3318
+ readonly epoch: bigint;
3319
+ readonly proposers: readonly number[];
3320
+ } | null;
3321
+ readonly minEpoch: bigint | null;
3322
+ readonly maxEpoch: bigint | null;
3323
+ readonly minTimestamp: bigint | null;
3324
+ readonly maxTimestamp: bigint | null;
3325
+ readonly chain: string;
3326
+ readonly nonce: number;
3327
+ };
3328
+ } | undefined;
3329
+ }, send: (bytes: Uint8Array, signatures: ReadonlyArray<Signature>) => Effect.Effect<SubmitViaReply, E, R>) => Effect.Effect<Executed, SubmitViaError | E, Sui | R>;
3309
3330
  readonly reconcile: (input: ReconcileInput) => Effect.Effect<Executed, TransportError | ExecutionFailed | SubmissionUnknown | NotApplied, Sui>;
3331
+ readonly recorded: (digest: Digest => Effect.Effect<Option.Option<{
3332
+ readonly _tag: "Unknown";
3333
+ readonly digest: Digest;
3334
+ readonly signed: {
3335
+ readonly digest: Digest;
3336
+ readonly sender: SuiAddress;
3337
+ readonly signatures: readonly Signature[];
3338
+ readonly bytes: Uint8Array<ArrayBufferLike>;
3339
+ readonly chain?: string | undefined;
3340
+ readonly expiration?: {
3341
+ readonly $kind: "None";
3342
+ readonly None: true;
3343
+ } | {
3344
+ readonly $kind: "Epoch";
3345
+ readonly Epoch: bigint;
3346
+ } | {
3347
+ readonly $kind: "ValidDuring";
3348
+ readonly ValidDuring: {
3349
+ readonly minEpoch: bigint | null;
3350
+ readonly maxEpoch: bigint | null;
3351
+ readonly minTimestamp: bigint | null;
3352
+ readonly maxTimestamp: bigint | null;
3353
+ readonly chain: string;
3354
+ readonly nonce: number;
3355
+ };
3356
+ } | {
3357
+ readonly $kind: "Validity";
3358
+ readonly Validity: {
3359
+ readonly allowedProposers: {
3360
+ readonly epoch: bigint;
3361
+ readonly proposers: readonly number[];
3362
+ } | null;
3363
+ readonly minEpoch: bigint | null;
3364
+ readonly maxEpoch: bigint | null;
3365
+ readonly minTimestamp: bigint | null;
3366
+ readonly maxTimestamp: bigint | null;
3367
+ readonly chain: string;
3368
+ readonly nonce: number;
3369
+ };
3370
+ } | undefined;
3371
+ };
3372
+ readonly lastError: string;
3373
+ readonly attempts: number;
3374
+ readonly at: DateTime.Utc;
3375
+ } | {
3376
+ readonly _tag: "NotApplied";
3377
+ readonly digest: Digest;
3378
+ readonly evidence: "expired" | "inputConsumed";
3379
+ readonly at: DateTime.Utc;
3380
+ } | {
3381
+ readonly _tag: "Signed";
3382
+ readonly digest: Digest;
3383
+ readonly signed: {
3384
+ readonly digest: Digest;
3385
+ readonly sender: SuiAddress;
3386
+ readonly signatures: readonly Signature[];
3387
+ readonly bytes: Uint8Array<ArrayBufferLike>;
3388
+ readonly chain?: string | undefined;
3389
+ readonly expiration?: {
3390
+ readonly $kind: "None";
3391
+ readonly None: true;
3392
+ } | {
3393
+ readonly $kind: "Epoch";
3394
+ readonly Epoch: bigint;
3395
+ } | {
3396
+ readonly $kind: "ValidDuring";
3397
+ readonly ValidDuring: {
3398
+ readonly minEpoch: bigint | null;
3399
+ readonly maxEpoch: bigint | null;
3400
+ readonly minTimestamp: bigint | null;
3401
+ readonly maxTimestamp: bigint | null;
3402
+ readonly chain: string;
3403
+ readonly nonce: number;
3404
+ };
3405
+ } | {
3406
+ readonly $kind: "Validity";
3407
+ readonly Validity: {
3408
+ readonly allowedProposers: {
3409
+ readonly epoch: bigint;
3410
+ readonly proposers: readonly number[];
3411
+ } | null;
3412
+ readonly minEpoch: bigint | null;
3413
+ readonly maxEpoch: bigint | null;
3414
+ readonly minTimestamp: bigint | null;
3415
+ readonly maxTimestamp: bigint | null;
3416
+ readonly chain: string;
3417
+ readonly nonce: number;
3418
+ };
3419
+ } | undefined;
3420
+ };
3421
+ readonly signedAt: DateTime.Utc;
3422
+ } | {
3423
+ readonly at: DateTime.Utc;
3424
+ readonly digest: Digest;
3425
+ readonly _tag: "Executed";
3426
+ readonly checkpoint?: bigint | undefined;
3427
+ } | {
3428
+ readonly _tag: "Failed";
3429
+ readonly digest: Digest;
3430
+ readonly reason: {
3431
+ readonly $kind: "MoveAbort";
3432
+ readonly MoveAbort: {
3433
+ readonly abortCode: bigint;
3434
+ readonly location?: {
3435
+ readonly function?: number | undefined;
3436
+ readonly package?: string | undefined;
3437
+ readonly module?: string | undefined;
3438
+ readonly functionName?: string | undefined;
3439
+ readonly instruction?: number | undefined;
3440
+ } | undefined;
3441
+ readonly cleverError?: {
3442
+ readonly value?: string | undefined;
3443
+ readonly errorCode?: number | undefined;
3444
+ readonly lineNumber?: number | undefined;
3445
+ readonly constantName?: string | undefined;
3446
+ readonly constantType?: string | undefined;
3447
+ } | undefined;
3448
+ };
3449
+ } | {
3450
+ readonly $kind: "SizeError";
3451
+ readonly SizeError: {
3452
+ readonly name: string;
3453
+ readonly size: number;
3454
+ readonly maxSize: number;
3455
+ };
3456
+ } | {
3457
+ readonly $kind: "CommandArgumentError";
3458
+ readonly CommandArgumentError: {
3459
+ readonly argument: number;
3460
+ readonly name: string;
3461
+ };
3462
+ } | {
3463
+ readonly $kind: "TypeArgumentError";
3464
+ readonly TypeArgumentError: {
3465
+ readonly typeArgument: number;
3466
+ readonly name: string;
3467
+ };
3468
+ } | {
3469
+ readonly $kind: "PackageUpgradeError";
3470
+ readonly PackageUpgradeError: {
3471
+ readonly name: string;
3472
+ readonly digest?: string | undefined;
3473
+ readonly packageId?: string | undefined;
3474
+ };
3475
+ } | {
3476
+ readonly $kind: "IndexError";
3477
+ readonly IndexError: {
3478
+ readonly index?: number | undefined;
3479
+ readonly subresult?: number | undefined;
3480
+ };
3481
+ } | {
3482
+ readonly $kind: "CoinDenyListError";
3483
+ readonly CoinDenyListError: {
3484
+ readonly coinType: string;
3485
+ readonly name: string;
3486
+ readonly address?: string | undefined;
3487
+ };
3488
+ } | {
3489
+ readonly $kind: "CongestedObjects";
3490
+ readonly CongestedObjects: {
3491
+ readonly name: string;
3492
+ readonly objects: readonly string[];
3493
+ };
3494
+ } | {
3495
+ readonly $kind: "ObjectIdError";
3496
+ readonly ObjectIdError: {
3497
+ readonly objectId: string;
3498
+ readonly name?: string | undefined;
3499
+ };
3500
+ } | {
3501
+ readonly $kind: "Unknown";
3502
+ };
3503
+ readonly at: DateTime.Utc;
3504
+ }>, JournalError, never>;
3310
3505
  readonly run: (recipe: Transaction | Recipe, opts: {
3311
3506
  readonly signer: Signer;
3312
3507
  readonly gasOwner?: SuiAddress;
@@ -3315,8 +3510,42 @@ declare const Tx: {
3315
3510
  * the bytes name a gas owner that is not the sender.
3316
3511
  */
3317
3512
  readonly sponsor?: Signer;
3513
+ /**
3514
+ * Called with the signed bytes **after every signature is on them and
3515
+ * before the first `executeTransaction`**, which is the one moment a
3516
+ * consumer's own record has to be written: the digest is final from here
3517
+ * on, and anything that happens next may have reached the network.
3518
+ *
3519
+ * `Tx.submit` already writes its `Signed` journal entry at this point; this
3520
+ * is for the record the journal does not hold — a domain row joining the
3521
+ * digest to a batch, an outbox, a log line an operator greps. It runs
3522
+ * inside the sender lock, so it is ordered with the submission it belongs
3523
+ * to.
3524
+ *
3525
+ * Failing it fails the run **before anything is sent**, which is why its
3526
+ * error is a `JournalError`: that is the taxonomy's "the record could not
3527
+ * be written and nothing has gone out yet", it is already in `Tx.run`'s
3528
+ * union, and it is `not_applied`, so the documented retry idiom is correct.
3529
+ * Map your own persistence failure into it
3530
+ * (`Effect.mapError((cause) => new JournalError({ cause }))`).
3531
+ *
3532
+ * @since 0.1.2
3533
+ */
3534
+ readonly onSigned?: (signed: Signed) => Effect.Effect<void, JournalError>;
3318
3535
  }) => Effect.Effect<Executed, TransportError | SimulationFailed | ExecutionFailed | SubmissionUnknown | NotApplied | SigningError | BuildError | PolicyDenied | JournalError, Sui>;
3319
- readonly reconcileAll: () => Effect.Effect<readonly Reconciled[], TransportError | JournalError, Sui>;
3536
+ readonly reconcileAll: () => Effect.Effect<readonly ({
3537
+ readonly _tag: "ExecutionFailed";
3538
+ readonly error: ExecutionFailed;
3539
+ } | {
3540
+ readonly _tag: "SubmissionUnknown";
3541
+ readonly error: SubmissionUnknown;
3542
+ } | {
3543
+ readonly _tag: "NotApplied";
3544
+ readonly error: NotApplied;
3545
+ } | {
3546
+ readonly _tag: "Executed";
3547
+ readonly executed: Executed;
3548
+ })[], TransportError | JournalError, Sui>;
3320
3549
  }
3321
3550
  ```
3322
3551
 
@@ -3526,15 +3755,23 @@ be a security bug, not a convenience.
3526
3755
  declare const fromConfig: (name?: string) => Effect.Effect<Signer, Config.ConfigError>
3527
3756
  ```
3528
3757
 
3529
- Reads a Bech32 `suiprivkey1…` secret key from configuration and builds the
3530
- signer for whichever of the three schemes its flag names.
3758
+ Reads a secret key from configuration and builds its signer.
3759
+
3760
+ Two spellings, told apart by the text itself:
3761
+
3762
+ - a **Bech32 `suiprivkey1…`**, whose flag names one of the three schemes;
3763
+ - a **32-byte hex seed** (64 hex characters, `0x` optional), which carries no
3764
+ scheme and is read as **Ed25519** — the default every Sui tool uses for a
3765
+ raw seed. This is what a secret manager or another language's SDK hands
3766
+ over, and reading it here is what keeps `fromHex` and the decoded bytes out
3767
+ of application code.
3531
3768
 
3532
3769
  The key is read with `Config.redacted`, and the decoded bytes never leave
3533
3770
  this function. Neither does anything derived from them: the failure carries
3534
3771
  one fixed sentence and no `cause`, because the decoder's own message quotes
3535
3772
  the input it rejected.
3536
3773
 
3537
- **Fails with: `ConfigError` when the variable is missing, is not a Bech32 Sui private key, or names a scheme that has no keypair class (`MultiSig`, `ZkLogin`, `Passkey` — use {@link remote} for those).**
3774
+ **Fails with: `ConfigError` when the variable is missing, is neither spelling, or names a scheme that has no keypair class (`MultiSig`, `ZkLogin`, `Passkey` — use {@link remote} for those).**
3538
3775
 
3539
3776
  ### `fromKeypair` (const)
3540
3777
 
@@ -3563,12 +3800,29 @@ and `signPersonalMessage`. Nothing here needs the secret, so nothing here
3563
3800
  needs a keypair, and the `Signer` this returns exposes no secret material
3564
3801
  either.
3565
3802
 
3803
+ **`toSuiAddress()` and `getKeyScheme()` are read here, synchronously**, and
3804
+ the address and scheme of the returned `Signer` are whatever they answered at
3805
+ this moment: a credential that changes accounts later is a different
3806
+ `Signer`, built again. A test double therefore needs both of those methods,
3807
+ not only `signTransaction` — a double without `getKeyScheme` used to produce
3808
+ `scheme: undefined` and nothing complained until a validator did.
3809
+
3810
+ **Clear-signing inputs are the SDK signer's own concern.** The Ledger signer
3811
+ takes `signTransaction(bytes, bcsObjects?, resolution?)` and resolves those
3812
+ extra arguments through the client it was constructed with; this passes only
3813
+ the bytes, which is the whole of the base `Signer` contract. A device that
3814
+ needs more than the bytes gets it from its own client, or from
3815
+ `remote`.
3816
+
3566
3817
  For a credential that is not an SDK `Signer` at all — a remote service, a
3567
3818
  hardware device behind your own protocol — use `remote`, which takes
3568
3819
  Effects and the address to sign as.
3569
3820
 
3570
- Never fails: a bad address or signature surfaces as a `SigningError` from the
3571
- member that produced it, not from construction.
3821
+ **Throws** a `TypeError` naming the missing member when the argument is not
3822
+ an SDK signer — that is a wiring mistake in the caller, not a runtime
3823
+ failure a program recovers from. Otherwise never fails: a bad address or
3824
+ signature surfaces as a `SigningError` from the member that produced it, not
3825
+ from construction.
3572
3826
 
3573
3827
  ### `isUnresolved` (const)
3574
3828
 
@@ -3909,12 +4163,29 @@ always `SubmissionUnknown`. Pass the `Signed` bytes (or the
3909
4163
  ### `reconcileAll` (const)
3910
4164
 
3911
4165
  ```ts
3912
- declare const reconcileAll: () => Effect.Effect<readonly Reconciled[], TransportError | JournalError, Sui>
4166
+ declare const reconcileAll: () => Effect.Effect<readonly ({
4167
+ readonly _tag: "ExecutionFailed";
4168
+ readonly error: ExecutionFailed;
4169
+ } | {
4170
+ readonly _tag: "SubmissionUnknown";
4171
+ readonly error: SubmissionUnknown;
4172
+ } | {
4173
+ readonly _tag: "NotApplied";
4174
+ readonly error: NotApplied;
4175
+ } | {
4176
+ readonly _tag: "Executed";
4177
+ readonly executed: Executed;
4178
+ })[], TransportError | JournalError, Sui>
3913
4179
  ```
3914
4180
 
3915
4181
  Settles every unresolved entry in the journal: the explicit startup call a
3916
4182
  long-lived application makes after building a durable `Journal`.
3917
4183
 
4184
+ **Only unresolved entries come back.** `Signed` and `Unknown` are the tags
4185
+ that still need an answer; a digest that already settled is not in the
4186
+ journal's unresolved index and is not in this array. Ask about one of those
4187
+ with `recorded`.
4188
+
3918
4189
  Nothing here fails per entry: each one settles to an `Executed`, an
3919
4190
  `ExecutionFailed`, a `NotApplied` or a `SubmissionUnknown`, in the order the
3920
4191
  journal listed them, and the journal is updated to match. Every settled entry
@@ -3930,6 +4201,201 @@ the same rule `Tx.submit` follows.
3930
4201
 
3931
4202
  **Fails with: `JournalError`, `TransportError`.**
3932
4203
 
4204
+ ### `recorded` (const)
4205
+
4206
+ ```ts
4207
+ declare const recorded: (digest: Digest => Effect.Effect<Option.Option<{
4208
+ readonly _tag: "Unknown";
4209
+ readonly digest: Digest;
4210
+ readonly signed: {
4211
+ readonly digest: Digest;
4212
+ readonly sender: SuiAddress;
4213
+ readonly signatures: readonly Signature[];
4214
+ readonly bytes: Uint8Array<ArrayBufferLike>;
4215
+ readonly chain?: string | undefined;
4216
+ readonly expiration?: {
4217
+ readonly $kind: "None";
4218
+ readonly None: true;
4219
+ } | {
4220
+ readonly $kind: "Epoch";
4221
+ readonly Epoch: bigint;
4222
+ } | {
4223
+ readonly $kind: "ValidDuring";
4224
+ readonly ValidDuring: {
4225
+ readonly minEpoch: bigint | null;
4226
+ readonly maxEpoch: bigint | null;
4227
+ readonly minTimestamp: bigint | null;
4228
+ readonly maxTimestamp: bigint | null;
4229
+ readonly chain: string;
4230
+ readonly nonce: number;
4231
+ };
4232
+ } | {
4233
+ readonly $kind: "Validity";
4234
+ readonly Validity: {
4235
+ readonly allowedProposers: {
4236
+ readonly epoch: bigint;
4237
+ readonly proposers: readonly number[];
4238
+ } | null;
4239
+ readonly minEpoch: bigint | null;
4240
+ readonly maxEpoch: bigint | null;
4241
+ readonly minTimestamp: bigint | null;
4242
+ readonly maxTimestamp: bigint | null;
4243
+ readonly chain: string;
4244
+ readonly nonce: number;
4245
+ };
4246
+ } | undefined;
4247
+ };
4248
+ readonly lastError: string;
4249
+ readonly attempts: number;
4250
+ readonly at: DateTime.Utc;
4251
+ } | {
4252
+ readonly _tag: "NotApplied";
4253
+ readonly digest: Digest;
4254
+ readonly evidence: "expired" | "inputConsumed";
4255
+ readonly at: DateTime.Utc;
4256
+ } | {
4257
+ readonly _tag: "Signed";
4258
+ readonly digest: Digest;
4259
+ readonly signed: {
4260
+ readonly digest: Digest;
4261
+ readonly sender: SuiAddress;
4262
+ readonly signatures: readonly Signature[];
4263
+ readonly bytes: Uint8Array<ArrayBufferLike>;
4264
+ readonly chain?: string | undefined;
4265
+ readonly expiration?: {
4266
+ readonly $kind: "None";
4267
+ readonly None: true;
4268
+ } | {
4269
+ readonly $kind: "Epoch";
4270
+ readonly Epoch: bigint;
4271
+ } | {
4272
+ readonly $kind: "ValidDuring";
4273
+ readonly ValidDuring: {
4274
+ readonly minEpoch: bigint | null;
4275
+ readonly maxEpoch: bigint | null;
4276
+ readonly minTimestamp: bigint | null;
4277
+ readonly maxTimestamp: bigint | null;
4278
+ readonly chain: string;
4279
+ readonly nonce: number;
4280
+ };
4281
+ } | {
4282
+ readonly $kind: "Validity";
4283
+ readonly Validity: {
4284
+ readonly allowedProposers: {
4285
+ readonly epoch: bigint;
4286
+ readonly proposers: readonly number[];
4287
+ } | null;
4288
+ readonly minEpoch: bigint | null;
4289
+ readonly maxEpoch: bigint | null;
4290
+ readonly minTimestamp: bigint | null;
4291
+ readonly maxTimestamp: bigint | null;
4292
+ readonly chain: string;
4293
+ readonly nonce: number;
4294
+ };
4295
+ } | undefined;
4296
+ };
4297
+ readonly signedAt: DateTime.Utc;
4298
+ } | {
4299
+ readonly at: DateTime.Utc;
4300
+ readonly digest: Digest;
4301
+ readonly _tag: "Executed";
4302
+ readonly checkpoint?: bigint | undefined;
4303
+ } | {
4304
+ readonly _tag: "Failed";
4305
+ readonly digest: Digest;
4306
+ readonly reason: {
4307
+ readonly $kind: "MoveAbort";
4308
+ readonly MoveAbort: {
4309
+ readonly abortCode: bigint;
4310
+ readonly location?: {
4311
+ readonly function?: number | undefined;
4312
+ readonly package?: string | undefined;
4313
+ readonly module?: string | undefined;
4314
+ readonly functionName?: string | undefined;
4315
+ readonly instruction?: number | undefined;
4316
+ } | undefined;
4317
+ readonly cleverError?: {
4318
+ readonly value?: string | undefined;
4319
+ readonly errorCode?: number | undefined;
4320
+ readonly lineNumber?: number | undefined;
4321
+ readonly constantName?: string | undefined;
4322
+ readonly constantType?: string | undefined;
4323
+ } | undefined;
4324
+ };
4325
+ } | {
4326
+ readonly $kind: "SizeError";
4327
+ readonly SizeError: {
4328
+ readonly name: string;
4329
+ readonly size: number;
4330
+ readonly maxSize: number;
4331
+ };
4332
+ } | {
4333
+ readonly $kind: "CommandArgumentError";
4334
+ readonly CommandArgumentError: {
4335
+ readonly argument: number;
4336
+ readonly name: string;
4337
+ };
4338
+ } | {
4339
+ readonly $kind: "TypeArgumentError";
4340
+ readonly TypeArgumentError: {
4341
+ readonly typeArgument: number;
4342
+ readonly name: string;
4343
+ };
4344
+ } | {
4345
+ readonly $kind: "PackageUpgradeError";
4346
+ readonly PackageUpgradeError: {
4347
+ readonly name: string;
4348
+ readonly digest?: string | undefined;
4349
+ readonly packageId?: string | undefined;
4350
+ };
4351
+ } | {
4352
+ readonly $kind: "IndexError";
4353
+ readonly IndexError: {
4354
+ readonly index?: number | undefined;
4355
+ readonly subresult?: number | undefined;
4356
+ };
4357
+ } | {
4358
+ readonly $kind: "CoinDenyListError";
4359
+ readonly CoinDenyListError: {
4360
+ readonly coinType: string;
4361
+ readonly name: string;
4362
+ readonly address?: string | undefined;
4363
+ };
4364
+ } | {
4365
+ readonly $kind: "CongestedObjects";
4366
+ readonly CongestedObjects: {
4367
+ readonly name: string;
4368
+ readonly objects: readonly string[];
4369
+ };
4370
+ } | {
4371
+ readonly $kind: "ObjectIdError";
4372
+ readonly ObjectIdError: {
4373
+ readonly objectId: string;
4374
+ readonly name?: string | undefined;
4375
+ };
4376
+ } | {
4377
+ readonly $kind: "Unknown";
4378
+ };
4379
+ readonly at: DateTime.Utc;
4380
+ }>, JournalError, never>
4381
+ ```
4382
+
4383
+ What the journal recorded for one digest, if anything.
4384
+
4385
+ `Tx.reconcileAll` returns **only the entries that were still unresolved**,
4386
+ because those are the ones it had work to do about; a transaction that had
4387
+ already settled — executed, failed, or proven never applied — is not in its
4388
+ answer and never will be. This is how to ask about one of those: the entry is
4389
+ `Executed`, `Failed` or `NotApplied` for a settled digest, `Signed` or
4390
+ `Unknown` for one still in flight, and `None` for a digest this journal has
4391
+ never seen (including every digest at all, when the journal is the in-memory
4392
+ default and the process restarted).
4393
+
4394
+ It is exactly `(yield* Journal).get(digest)`, named so that the recovery path
4395
+ does not have to reach for the reference.
4396
+
4397
+ **Fails with: `JournalError`.**
4398
+
3933
4399
  ### `remote` (const)
3934
4400
 
3935
4401
  ```ts
@@ -3957,6 +4423,28 @@ declare const run: (recipe: Transaction | Recipe, opts: {
3957
4423
  * the bytes name a gas owner that is not the sender.
3958
4424
  */
3959
4425
  readonly sponsor?: Signer;
4426
+ /**
4427
+ * Called with the signed bytes **after every signature is on them and
4428
+ * before the first `executeTransaction`**, which is the one moment a
4429
+ * consumer's own record has to be written: the digest is final from here
4430
+ * on, and anything that happens next may have reached the network.
4431
+ *
4432
+ * `Tx.submit` already writes its `Signed` journal entry at this point; this
4433
+ * is for the record the journal does not hold — a domain row joining the
4434
+ * digest to a batch, an outbox, a log line an operator greps. It runs
4435
+ * inside the sender lock, so it is ordered with the submission it belongs
4436
+ * to.
4437
+ *
4438
+ * Failing it fails the run **before anything is sent**, which is why its
4439
+ * error is a `JournalError`: that is the taxonomy's "the record could not
4440
+ * be written and nothing has gone out yet", it is already in `Tx.run`'s
4441
+ * union, and it is `not_applied`, so the documented retry idiom is correct.
4442
+ * Map your own persistence failure into it
4443
+ * (`Effect.mapError((cause) => new JournalError({ cause }))`).
4444
+ *
4445
+ * @since 0.1.2
4446
+ */
4447
+ readonly onSigned?: (signed: Signed) => Effect.Effect<void, JournalError>;
3960
4448
  }) => Effect.Effect<Executed, TransportError | SimulationFailed | ExecutionFailed | SubmissionUnknown | NotApplied | SigningError | BuildError | PolicyDenied | JournalError, Sui>
3961
4449
  ```
3962
4450
 
@@ -3989,6 +4477,14 @@ and immediately after the build when it came out of the recipe. Use the
3989
4477
  explicit lifecycle (`Tx.build`, `Tx.sign`, `Tx.cosign`, `Tx.submit`) when the
3990
4478
  two parties cannot both sign in one process.
3991
4479
 
4480
+ **`onSigned` is the hook between signing and sending.** A program with its
4481
+ own record to keep — a batch row, an outbox, an idempotency key — has to
4482
+ write the digest before the first send, and that used to mean giving up
4483
+ `Tx.run` and reassembling `withSenderLock(build → sign → record → submit)` by
4484
+ hand. Pass `onSigned` instead: it runs inside the sender lock, after the last
4485
+ signature and before `Tx.submit`'s first `executeTransaction`, and failing it
4486
+ fails the run with nothing sent.
4487
+
3992
4488
  **Fails with: `BuildError`, `SimulationFailed`, `PolicyDenied`, `SigningError`, `ExecutionFailed`, `NotApplied`, `SubmissionUnknown`, `JournalError`, `TransportError` (from the build reads; once bytes are sent, transport failures become `SubmissionUnknown`).**
3993
4489
 
3994
4490
  ### `sign` (const)
@@ -4113,9 +4609,16 @@ the transaction that was already signed. When the retries run out it runs
4113
4609
  `Tx.reconcile`, which either finds the transaction, proves it never applied,
4114
4610
  or says it does not know.
4115
4611
 
4116
- `TransportError` never escapes: once bytes may have been sent, "the network
4117
- was unreachable" is not an answer a caller can act on, so it becomes
4118
- `SubmissionUnknown` carrying the signed bytes.
4612
+ `TransportError` almost never escapes: once bytes may have been sent, "the
4613
+ network was unreachable" is not an answer a caller can act on, so it becomes
4614
+ `SubmissionUnknown` carrying the signed bytes. **The one exception is a node
4615
+ that refused the request outright** — gRPC `INVALID_ARGUMENT`, which is what
4616
+ a validator answers for malformed bytes or for a sponsored transaction
4617
+ carrying one signature. That answer came from the node, it is final, and
4618
+ nothing was executed, so it is reported as the `TransportError` it is rather
4619
+ than reconciled: a reconcile would go on to ask "is this digest on chain?",
4620
+ a question about a transaction that was never sent, and a lagging or scripted
4621
+ node can answer it yes.
4119
4622
 
4120
4623
  `JournalError` can only come from the `Signed` write, before anything has
4121
4624
  been sent. Once the network has answered, a journal write that fails is
@@ -4123,7 +4626,77 @@ logged with `Effect.logError` and the answer stands, because "the journal is
4123
4626
  broken" is not a thing a caller can act on and reporting it in place of a
4124
4627
  charged `ExecutionFailed` would invite a second submission.
4125
4628
 
4126
- **Fails with: `ExecutionFailed` (applied on chain and failed; gas was charged), `NotApplied` (provably never applied), `SubmissionUnknown` (the outcome is not known and the bytes are in the error), `JournalError` (only before the first send).**
4629
+ **Fails with: `ExecutionFailed` (applied on chain and failed; gas was charged), `NotApplied` (provably never applied), `SubmissionUnknown` (the outcome is not known and the bytes are in the error), `JournalError` (only before the first send), `TransportError` (only `INVALID_ARGUMENT`: the node refused the submission and nothing was executed).**
4630
+
4631
+ ### `submitVia` (const)
4632
+
4633
+ ```ts
4634
+ declare const submitVia: <E, R>(signed: {
4635
+ readonly digest: Digest;
4636
+ readonly sender: SuiAddress;
4637
+ readonly signatures: readonly Signature[];
4638
+ readonly bytes: Uint8Array<ArrayBufferLike>;
4639
+ readonly chain?: string | undefined;
4640
+ readonly expiration?: {
4641
+ readonly $kind: "None";
4642
+ readonly None: true;
4643
+ } | {
4644
+ readonly $kind: "Epoch";
4645
+ readonly Epoch: bigint;
4646
+ } | {
4647
+ readonly $kind: "ValidDuring";
4648
+ readonly ValidDuring: {
4649
+ readonly minEpoch: bigint | null;
4650
+ readonly maxEpoch: bigint | null;
4651
+ readonly minTimestamp: bigint | null;
4652
+ readonly maxTimestamp: bigint | null;
4653
+ readonly chain: string;
4654
+ readonly nonce: number;
4655
+ };
4656
+ } | {
4657
+ readonly $kind: "Validity";
4658
+ readonly Validity: {
4659
+ readonly allowedProposers: {
4660
+ readonly epoch: bigint;
4661
+ readonly proposers: readonly number[];
4662
+ } | null;
4663
+ readonly minEpoch: bigint | null;
4664
+ readonly maxEpoch: bigint | null;
4665
+ readonly minTimestamp: bigint | null;
4666
+ readonly maxTimestamp: bigint | null;
4667
+ readonly chain: string;
4668
+ readonly nonce: number;
4669
+ };
4670
+ } | undefined;
4671
+ }, send: (bytes: Uint8Array, signatures: ReadonlyArray<Signature>) => Effect.Effect<SubmitViaReply, E, R>) => Effect.Effect<Executed, SubmitViaError | E, Sui | R>
4672
+ ```
4673
+
4674
+ Submits through **someone else** — a relay, a sponsorship service, a backend
4675
+ that holds the only key allowed to talk to the node — and keeps the journal
4676
+ and the evidence rules that `Tx.submit` would have kept.
4677
+
4678
+ The bytes never reach `executeTransaction` here; `send` does whatever the
4679
+ service needs (an HTTP POST, a queue, another process) and answers with
4680
+ whatever the service returns. What this owns is everything around it:
4681
+
4682
+ - a `Signed` journal entry is written **before** `send` is called, so a crash
4683
+ between here and the service leaves the same record a direct submit leaves;
4684
+ - `send` is called **once**. A third party's submit is not known to be
4685
+ idempotent and re-sending is not this function's decision;
4686
+ - the reply is turned into an `Executed` when it carries one (see
4687
+ `SubmitViaReply`), and otherwise the chain is asked — by the digest
4688
+ of the bytes that were handed over, which a co-signature does not change;
4689
+ - a failure from `send` is **ambiguous** unless it says otherwise, so it ends
4690
+ in `Tx.reconcile` with the full evidence rules: the ordered expiry check,
4691
+ the chain-identity guard, the versioned consumer check. A sender error that
4692
+ **declares** `outcome: "not_applied"` on the instance — a 400 from the
4693
+ service, a refusal before anything went out — is taken at its word and
4694
+ fails straight through without spending a reconcile. A `TransportError` is
4695
+ not that: the taxonomy calls it `not_applied`, but a transport failure
4696
+ *talking to the relay* is exactly the ambiguous case, so it reconciles;
4697
+ - every terminal answer is journalled, exactly as `Tx.submit` journals it.
4698
+
4699
+ **Fails with: `ExecutionFailed`, `NotApplied`, `SubmissionUnknown` (carrying the signed bytes, with the sender's failure as its `cause`), `JournalError` (only from the write before `send`), and `E` — whatever `send` fails with — when that error declares `outcome: "not_applied"`.**
4127
4700
 
4128
4701
  ## `@unconfirmed/sui-effect/journal`
4129
4702
 
@@ -4488,7 +5061,28 @@ added to the value itself.
4488
5061
  ## `@unconfirmed/sui-effect/script`
4489
5062
 
4490
5063
 
4491
- 9 exported symbols.
5064
+ 11 exported symbols.
5065
+
5066
+ ### `ReportOptions` (interface)
5067
+
5068
+ ```ts
5069
+ export interface ReportOptions {
5070
+ /** Where the lines go, one at a time. Defaults to `process.stderr`. */
5071
+ readonly stderr?: (line: string) => void;
5072
+ /**
5073
+ * The journal the program ran with, for the unresolved entries.
5074
+ *
5075
+ * A program with its own `ManagedRuntime` has to hand it over — reading the
5076
+ * bare `Journal` reference here would look in the process-wide in-memory
5077
+ * default and find nothing, which is precisely wrong for the program that
5078
+ * provided a durable one. Left out, the default reference is read, which is
5079
+ * right for a program that never provided a journal at all.
5080
+ */
5081
+ readonly journal?: JournalService;
5082
+ }
5083
+ ```
5084
+
5085
+ What `report` needs beyond the `Exit`.
4492
5086
 
4493
5087
  ### `Script` (class)
4494
5088
 
@@ -4537,6 +5131,8 @@ export declare class Script extends Script_base {
4537
5131
  static readonly exitCode: <A, E>(exit: Exit.Exit<A, E>, options?: ExitCodeOptions) => number;
4538
5132
  /** See {@link run}. */
4539
5133
  static readonly run: <A, E>(effect: Effect.Effect<A, E, Script | Sui | SuiCore>, options?: ScriptRunOptions) => Promise<number>;
5134
+ /** See {@link report}. */
5135
+ static readonly report: <A, E>(exit: Exit.Exit<A, E>, options?: ReportOptions) => Promise<number>;
4540
5136
  }
4541
5137
  ```
4542
5138
 
@@ -4663,6 +5259,26 @@ script that means mainnet has to say so twice, in `SUI_NETWORK` and in
4663
5259
 
4664
5260
  **Fails with: `ConfigError`.**
4665
5261
 
5262
+ ### `report` (const)
5263
+
5264
+ ```ts
5265
+ declare const report: <A, E>(exit: Exit.Exit<A, E>, options?: ReportOptions) => Promise<number>
5266
+ ```
5267
+
5268
+ Prints what `run` prints, and answers the exit code, for a program that
5269
+ owns its own process.
5270
+
5271
+ `Script.run` is a whole entrypoint: it builds the layer, forks the root
5272
+ fiber, installs signal handlers and exits. A CLI with its own argv parser and
5273
+ twenty subcommands has all of that already and still wants the two things
5274
+ `run` does at the end — one diagnostic line per failure (with the bytes of a
5275
+ `SubmissionUnknown`), and every unresolved journal entry on a non-zero exit —
5276
+ plus the code itself. That is this: hand it the `Exit` of whatever you ran,
5277
+ get the lines on stderr and the number back, and call `process.exitCode = …`
5278
+ yourself rather than `process.exit`, so buffered output still flushes.
5279
+
5280
+ **Never fails.**
5281
+
4666
5282
  ### `run` (const)
4667
5283
 
4668
5284
  ```ts
@@ -4699,7 +5315,7 @@ Returns the exit code as well as passing it to `exit`, so a test can inject
4699
5315
  ## `@unconfirmed/sui-effect/testing`
4700
5316
 
4701
5317
 
4702
- 16 exported symbols.
5318
+ 17 exported symbols.
4703
5319
 
4704
5320
  ### `CLOCK_TYPE` (const)
4705
5321
 
@@ -4728,6 +5344,19 @@ declare const DEFAULT_EPOCH = 100n
4728
5344
 
4729
5345
  The epoch the fake reports when a script does not set one.
4730
5346
 
5347
+ ### `FakeBalance` (interface)
5348
+
5349
+ ```ts
5350
+ export interface FakeBalance extends SuiClientTypes.Balance {
5351
+ readonly owner?: string;
5352
+ }
5353
+ ```
5354
+
5355
+ A balance the fake serves, optionally for one owner.
5356
+
5357
+ `owner` is the address this balance belongs to. Left out, the entry answers
5358
+ for **any** owner, which is what every script written before 0.1.2 assumed.
5359
+
4731
5360
  ### `FakeChange` (interface)
4732
5361
 
4733
5362
  ```ts
@@ -4860,7 +5489,29 @@ export interface FakeScript {
4860
5489
  readonly coins?: ReadonlyArray<SuiClientTypes.Coin>;
4861
5490
  /** The gas budget the resolve plugin sets when a transaction has none. */
4862
5491
  readonly gasBudget?: bigint;
4863
- readonly balances?: ReadonlyArray<SuiClientTypes.Balance>;
5492
+ /**
5493
+ * The balances `getBalance` and `listBalances` serve.
5494
+ *
5495
+ * Keyed by **owner and coin type**: an entry with an `owner` answers only for
5496
+ * that address, and one without answers for any, which is what a script
5497
+ * written before 0.1.2 meant. A `getBalance` for an owner and coin type no
5498
+ * entry names answers zero, the way a node does for an address that holds
5499
+ * none of that coin.
5500
+ */
5501
+ readonly balances?: ReadonlyArray<FakeBalance>;
5502
+ /**
5503
+ * Scripted outcomes for `getObject`, oldest first; the last repeats forever.
5504
+ *
5505
+ * This is transport-error injection on a **read**: `FakeOutcome.transportError`
5506
+ * and `FakeOutcome.timeoutThen` are what it is for, and they are how a test
5507
+ * drives `SuiCore`'s read retry policy or an extension's own fallback.
5508
+ * `FakeOutcome.notFound` answers the way a missing object does
5509
+ * (`ObjectNotFound`). A `succeed` entry — and an empty or exhausted script —
5510
+ * means "serve the object map", which is the normal behaviour.
5511
+ *
5512
+ * @since 0.1.2
5513
+ */
5514
+ readonly getObject?: ReadonlyArray<FakeOutcome>;
4864
5515
  /**
4865
5516
  * What `getCoinMetadata` answers, keyed by coin type.
4866
5517
  *
@@ -5021,7 +5672,7 @@ export interface SuiCoreFakeState {
5021
5672
  */
5022
5673
  readonly recordTransaction: (digest: string, outcome: FakeOutcome) => Effect.Effect<void>;
5023
5674
  /** Replaces the remaining scripted outcomes of a method. */
5024
- readonly setOutcomes: (method: "simulate" | "execute" | "getTransaction" | "buildSimulate", outcomes: ReadonlyArray<FakeOutcome>) => Effect.Effect<void>;
5675
+ readonly setOutcomes: (method: "simulate" | "execute" | "getTransaction" | "buildSimulate" | "getObject", outcomes: ReadonlyArray<FakeOutcome>) => Effect.Effect<void>;
5025
5676
  }
5026
5677
  ```
5027
5678
 
@@ -5043,6 +5694,7 @@ declare const SuiTest: {
5043
5694
  readonly scriptExecute: (outcomes: ReadonlyArray<FakeOutcome>) => Effect.Effect<void, never, SuiCoreFake>;
5044
5695
  readonly scriptSimulate: (outcomes: ReadonlyArray<FakeOutcome>) => Effect.Effect<void, never, SuiCoreFake>;
5045
5696
  readonly scriptGetTransaction: (outcomes: ReadonlyArray<FakeOutcome>) => Effect.Effect<void, never, SuiCoreFake>;
5697
+ readonly scriptGetObject: (outcomes: ReadonlyArray<FakeOutcome>) => Effect.Effect<void, never, SuiCoreFake>;
5046
5698
  readonly calls: (method?: string) => Effect.Effect<ReadonlyArray<RecordedCall>, never, SuiCoreFake>;
5047
5699
  }
5048
5700
  ```
@@ -6144,7 +6796,7 @@ const make = (
6144
6796
  Effect.flatMap((raw) =>
6145
6797
  decodeAddress(raw).pipe(
6146
6798
  Effect.mapError((issue) =>
6147
- new DecodeError({ expectedType: "SuiAddress", issue: issue.message })
6799
+ new DecodeError({ expectedType: "SuiAddress", kind: "shape", issue: issue.message })
6148
6800
  )
6149
6801
  )
6150
6802
  ),