@ottochain/sdk 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -741,15 +741,32 @@ export interface MintAsset {
741
741
  * Optional fields carry per-kind directives (recipient for Transfer/Wrap; otherAssetIds + compositeId
742
742
  * for Compose; shardIds for Fractionalize; nonce for a commit-reveal symmetric Compose;
743
743
  * priorComponents is the Decompose reveal witness).
744
+ *
745
+ * C2 — cross-holder Compose/Pool consent is MANDATORY. As of the fiber-engine permissionless-hardening
746
+ * (chain branch `fix/fiber-engine-permissionless-hardening`, audit finding C2) a `Compose`/`Pool` that
747
+ * folds in a counter-party the signer does NOT hold is REJECTED (graceful `CombineRejected`) UNLESS a
748
+ * live {@link AuthorizeCompose} nonce authorizes that counter-party. A SAME-holder compose (every part in
749
+ * `otherAssetIds` is signer-owned) still needs no nonce. `otherAssetIds` may no longer contain duplicates
750
+ * or the `assetId` itself (the old self/duplicate-inflation path is rejected).
744
751
  */
745
752
  export interface ApplyMorphism {
746
753
  assetId: string;
747
754
  kind: MorphismKind;
748
755
  targetSequenceNumber: number;
749
756
  recipient?: AssetHolder;
757
+ /**
758
+ * Compose/Pool counter-party asset ids folded into the composite. Each id the signer does NOT hold is a
759
+ * CROSS-HOLDER part and now REQUIRES a live {@link AuthorizeCompose} nonce (see `nonce`) authorizing it
760
+ * — else the whole morphism is rejected (C2). Must be free of duplicates and must not include `assetId`.
761
+ */
750
762
  otherAssetIds?: string[];
751
763
  compositeId?: string;
752
764
  shardIds?: string[];
765
+ /**
766
+ * The cross-holder Compose consent nonce: the reveal half of the {@link AuthorizeCompose} handshake.
767
+ * REQUIRED whenever any `otherAssetIds` entry is not signer-owned (C2); omit it only for a same-holder
768
+ * compose. A nonce-less cross-holder compose is no longer accepted.
769
+ */
753
770
  nonce?: number;
754
771
  priorComponents?: ComponentWitness[];
755
772
  /** ZkVerify-gated morphism: optional witness a `Governed` morphism's guard reads (see {@link MorphismSpec}). */
@@ -758,6 +775,11 @@ export interface ApplyMorphism {
758
775
  /**
759
776
  * Authorize a counter-party policy to Compose with this asset (the commit half of the commit-reveal
760
777
  * symmetric-compose handshake). `nonce` and `expiresAt` are REQUIRED (no sentinel defaults).
778
+ *
779
+ * C2 — this consent is now MANDATORY, not opt-in. A `Compose`/`Pool` ({@link ApplyMorphism}) that consumes
780
+ * a counter-party the signer does not own is rejected unless a matching, unexpired `AuthorizeCompose` nonce
781
+ * is live for that counter-party (chain branch `fix/fiber-engine-permissionless-hardening`, finding C2).
782
+ * The composing party echoes this `nonce` in `ApplyMorphism.nonce`. A same-holder compose needs none.
761
783
  */
762
784
  export interface AuthorizeCompose {
763
785
  assetId: string;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Webhook notification PAYLOAD types — the server-initiated push the chain POSTs to a subscribed
3
+ * callback, NOT part of the client's request surface.
4
+ *
5
+ * These are hand-authored (not generated from the OpenAPI contract) because the push is an OUTBOUND
6
+ * POST from the metagraph to the subscriber's callback URL — it is not an endpoint the chain's tapir
7
+ * `ApiEndpoints` serves, so it never appears in `openapi/ottochain-openapi-ml0.json`. They mirror the
8
+ * chain's shared network DTOs byte-for-byte:
9
+ *
10
+ * chain: modules/models/src/main/scala/xyz/kd5ujc/schema/api/webhooks/Subscriber.scala
11
+ * (SnapshotNotification / NotificationStats / SnapshotRejection)
12
+ * emit: modules/l0/.../webhooks/WebhookDispatcher.scala — `notification.asJson.noSpaces`
13
+ *
14
+ * Field-shape notes tied to the chain source:
15
+ * - The dispatcher serializes with a plain derived encoder (`.asJson`, NO `dropNullValues`), so an
16
+ * `Option[Long]` that is `None` is emitted as an explicit `null` — the KEY is always present, the
17
+ * VALUE is nullable. Hence `number | null` (a required key), not `number | undefined` (an optional
18
+ * key). Consumers must handle `null`.
19
+ * - `Long` → JSON number; `Instant` → ISO-8601 string; `UUID` → string.
20
+ * - The dispatcher only ever emits `"snapshot.finalized"` today (there is a dead, never-dispatched
21
+ * `RejectionNotification`/`transaction.rejected` type in the chain models — deliberately NOT mirrored
22
+ * here, because the SDK should model only what is actually sent). Rejections now ride the finalized
23
+ * snapshot's `rejections[]`, drained from its committed `RejectionReceipt`s.
24
+ *
25
+ * @see modules/models/.../schema/api/webhooks/Subscriber.scala
26
+ * @see modules/l0/.../webhooks/WebhookDispatcher.scala
27
+ * @packageDocumentation
28
+ */
29
+ /** The only event the chain's `WebhookDispatcher` currently emits. */
30
+ export declare const SNAPSHOT_FINALIZED_EVENT: "snapshot.finalized";
31
+ /**
32
+ * Aggregate counters for the finalized snapshot, mirrors chain `NotificationStats`.
33
+ */
34
+ export interface NotificationStats {
35
+ /** Number of data updates processed into this snapshot. */
36
+ updatesProcessed: number;
37
+ /** Count of active state-machine fibers after this snapshot. */
38
+ stateMachinesActive: number;
39
+ /** Count of active script fibers after this snapshot. */
40
+ scriptsActive: number;
41
+ /** Number of updates rejected while combining this snapshot (== `rejections.length`). */
42
+ rejectedCount: number;
43
+ }
44
+ /**
45
+ * A single post-finalization rejection, batched onto the `snapshot.finalized` notification. Drained
46
+ * from the committed snapshot's `RejectionReceipt`s, so it reflects committed state (not a pre-combine
47
+ * guess). Mirrors chain `SnapshotRejection`.
48
+ */
49
+ export interface SnapshotRejection {
50
+ /** The rejected update's type, e.g. `"TransitionStateMachine"`, `"CreateStateMachine"`. */
51
+ updateType: string;
52
+ /** UUID of the target fiber. */
53
+ fiberId: string;
54
+ /**
55
+ * The transition's target sequence number, or `null` for non-sequenced updates. Chain
56
+ * `Option[Long]`, emitted as an explicit `null` when absent (dispatcher does not drop nulls).
57
+ */
58
+ targetSequenceNumber: number | null;
59
+ /**
60
+ * The fiber's actual sequence number at rejection time (for a sequence mismatch), or `null`. Chain
61
+ * `Option[Long]`, emitted as an explicit `null` when absent.
62
+ */
63
+ actualSequenceNumber: number | null;
64
+ /** Human-readable rejection reason (the combiner's `CombineRejected` code/message). */
65
+ reason: string;
66
+ /** Hash of the signed update, for correlation with a submitted transaction. */
67
+ updateHash: string;
68
+ }
69
+ /**
70
+ * The payload POSTed to a subscribed webhook callback when a snapshot finalizes — the confirm TICK.
71
+ * Mirrors chain `SnapshotNotification`.
72
+ *
73
+ * There is NO per-update "accepted" event: an update is accepted iff its `updateHash` lands in a
74
+ * finalized snapshot AND is absent from every `rejections[]` of the snapshots up to that ordinal.
75
+ */
76
+ export interface SnapshotNotification {
77
+ /** Always `"snapshot.finalized"`. */
78
+ event: typeof SNAPSHOT_FINALIZED_EVENT;
79
+ /** The finalized snapshot ordinal. Chain `Long`. */
80
+ ordinal: number;
81
+ /** The finalized snapshot hash. */
82
+ hash: string;
83
+ /** ISO-8601 dispatch timestamp. Chain `Instant`. */
84
+ timestamp: string;
85
+ /** The metagraph token identifier this notification is for. */
86
+ metagraphId: string;
87
+ /** Aggregate counters for the snapshot. */
88
+ stats: NotificationStats;
89
+ /** Updates rejected while combining this snapshot (empty if none). */
90
+ rejections: SnapshotRejection[];
91
+ }
@@ -108,11 +108,20 @@ export declare const triggers: (ts: {
108
108
  * MUST be a literal {@link ProtoStateMachineDefinition} (e.g. a nested `machine().wireDefinition()` /
109
109
  * `toProtoDefinition(child)` output) — NOT an expression the engine would evaluate at runtime.
110
110
  *
111
- * F8 gotcha — `owners` is load-bearing. A spawned child's transitions are gated by
112
- * `owners authorizedSigners` (`riverdale-economy/README.md`), so EVERY party that will later drive
113
- * the child (e.g. every bidder on a spawned auction) MUST be listed in `owners`, or their events are
114
- * rejected. `owners` may be a literal id array or an expression (e.g. `{ var: "event.auctionOwners" }`);
115
- * `childId` / `initialData` likewise accept literals or expressions.
111
+ * H1 constraintchild `owners` MUST be a SUBSET of the SPAWNING PARENT fiber's `owners`. As of the
112
+ * fiber-engine permissionless-hardening (chain branch `fix/fiber-engine-permissionless-hardening`, audit
113
+ * finding H1) the chain FAILS-CLOSED it aborts the whole transition under EVERY `spawnOwnerPolicy` if
114
+ * a `_spawn`'s resolved child `owners` are not the parent's owners. Assigning arbitrary, unsigned owners
115
+ * (the old "auction owned by all its bidders" pattern, where the bidders are NOT parent owners) is no
116
+ * longer valid: those events are not silently rejected, the spawn itself is rejected.
117
+ *
118
+ * So DO NOT try to enroll later participants by listing them here. A spawned child's transitions are gated
119
+ * by `owners ∪ authorizedSigners` for the DIRECT (wallet-signed) path; admit additional drivers through
120
+ * the child's OWN transitions (`authorizedSigners`), or drive it via the cascade (`_triggers`) subject to
121
+ * its `acceptedCallers` — never by widening `owners` beyond the parent's set. `owners` may be a literal id
122
+ * array or an expression (e.g. `{ var: "state.owners" }` — resolve it WITHIN the parent's owners), but a
123
+ * value the parent does not own (e.g. `{ var: "event.auctionOwners" }` supplied by the caller) will be
124
+ * rejected on-chain. `childId` / `initialData` likewise accept literals or expressions.
116
125
  */
117
126
  export declare const spawn: (ds: {
118
127
  childId: JsonLogicValue;
@@ -32,6 +32,11 @@
32
32
  *
33
33
  * leading-dot — `{"var":".foo"}` resolves to null on chain.
34
34
  *
35
+ * H1 — a `_spawn` whose child `owners` are not provably a SUBSET of the spawning
36
+ * parent's owners. The chain now fails-closed (aborts the transition) under
37
+ * every spawnOwnerPolicy when child owners ⊄ parent owners; this advisory
38
+ * flags the strong not-subset signals (hardcoded / `event.*`-derived owners).
39
+ *
35
40
  * This is a STANDALONE validator. It is deliberately NOT wired into
36
41
  * `defineFiberApp` / `toProtoDefinition`: doing so would break the build until
37
42
  * every app is remediated. Run it via `scripts/lint-apps.mjs`.
@@ -58,6 +63,7 @@ export declare const LINT_CODES: {
58
63
  readonly WITNESS_IN_TRANSITION: "witness-in-transition";
59
64
  readonly DROPPED_DIRECTIVE: "dropped-directive";
60
65
  readonly LEADING_DOT_VAR: "leading-dot-var";
66
+ readonly SPAWN_OWNERS: "spawn-owners";
61
67
  };
62
68
  /**
63
69
  * The ONLY `$`-prefixed keys the engine injects (`ReservedKeys`). Anything else
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ottochain/sdk",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "TypeScript SDK for ottochain metagraph operations - signing, encoding, and network interactions",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -148,12 +148,12 @@
148
148
  },
149
149
  "devDependencies": {
150
150
  "@babel/preset-env": "^7.29.7",
151
- "@commitlint/cli": "^21.1.0",
152
- "@commitlint/config-conventional": "^21.1.0",
151
+ "@commitlint/cli": "^21.2.0",
152
+ "@commitlint/config-conventional": "^21.2.0",
153
153
  "@eslint/js": "^10.0.1",
154
154
  "@jest/globals": "^30.3.0",
155
155
  "@types/jest": "^30.0.0",
156
- "@types/node": "^26.0.1",
156
+ "@types/node": "^26.1.0",
157
157
  "@typescript-eslint/eslint-plugin": "^8.62.0",
158
158
  "@typescript-eslint/parser": "^8.62.0",
159
159
  "eslint": "^10.6.0",
@@ -161,8 +161,8 @@
161
161
  "openapi-typescript": "^7.13.0",
162
162
  "prettier": "^3.9.1",
163
163
  "ts-jest": "29.4.9",
164
- "ts-proto": "^2.11.9",
165
- "typedoc": "^0.28.19",
164
+ "ts-proto": "^2.12.0",
165
+ "typedoc": "^0.28.20",
166
166
  "typescript": "^6.0.3"
167
167
  },
168
168
  "peerDependencies": {