@ottochain/sdk 1.0.3 → 1.1.1

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 (181) hide show
  1. package/dist/cjs/apps/contracts/state-machines/contract.json +21 -63
  2. package/dist/cjs/apps/contracts/state-machines/escrow.json +23 -69
  3. package/dist/cjs/apps/governance/state-machines/dao-multisig.json +22 -66
  4. package/dist/cjs/apps/governance/state-machines/dao-single.json +14 -42
  5. package/dist/cjs/apps/governance/state-machines/dao-threshold.json +18 -54
  6. package/dist/cjs/apps/governance/state-machines/dao-token.json +21 -63
  7. package/dist/cjs/apps/governance/state-machines/governance-constitution.json +22 -66
  8. package/dist/cjs/apps/governance/state-machines/governance-executive.json +27 -81
  9. package/dist/cjs/apps/governance/state-machines/governance-judiciary.json +34 -102
  10. package/dist/cjs/apps/governance/state-machines/governance-legislature.json +32 -96
  11. package/dist/cjs/apps/governance/state-machines/governance-simple.json +27 -81
  12. package/dist/cjs/apps/identity/state-machines/agent-identity.json +25 -75
  13. package/dist/cjs/apps/markets/state-machines/market-universal.json +377 -110
  14. package/dist/cjs/apps/oracles/state-machines/oracle.json +22 -66
  15. package/dist/cjs/generated/index.js +11 -1
  16. package/dist/cjs/metakit/drop-nulls.js +50 -0
  17. package/dist/cjs/metakit/normalize.js +147 -0
  18. package/dist/cjs/ottochain/metagraph-client.js +147 -0
  19. package/dist/cjs/validation.js +1 -1
  20. package/dist/esm/apps/contracts/state-machines/contract.json +21 -63
  21. package/dist/esm/apps/contracts/state-machines/escrow.json +23 -69
  22. package/dist/esm/apps/governance/state-machines/dao-multisig.json +22 -66
  23. package/dist/esm/apps/governance/state-machines/dao-single.json +14 -42
  24. package/dist/esm/apps/governance/state-machines/dao-threshold.json +18 -54
  25. package/dist/esm/apps/governance/state-machines/dao-token.json +21 -63
  26. package/dist/esm/apps/governance/state-machines/governance-constitution.json +22 -66
  27. package/dist/esm/apps/governance/state-machines/governance-executive.json +27 -81
  28. package/dist/esm/apps/governance/state-machines/governance-judiciary.json +34 -102
  29. package/dist/esm/apps/governance/state-machines/governance-legislature.json +32 -96
  30. package/dist/esm/apps/governance/state-machines/governance-simple.json +27 -81
  31. package/dist/esm/apps/identity/state-machines/agent-identity.json +25 -75
  32. package/dist/esm/apps/markets/state-machines/market-universal.json +377 -110
  33. package/dist/esm/apps/oracles/state-machines/oracle.json +22 -66
  34. package/dist/esm/generated/index.js +11 -1
  35. package/dist/esm/metakit/drop-nulls.js +46 -0
  36. package/dist/esm/metakit/normalize.js +140 -0
  37. package/dist/esm/ottochain/metagraph-client.js +147 -0
  38. package/dist/esm/validation.js +1 -1
  39. package/dist/types/generated/index.d.ts +11 -1
  40. package/dist/types/metakit/drop-nulls.d.ts +29 -0
  41. package/dist/types/metakit/normalize.d.ts +67 -0
  42. package/dist/types/ottochain/index.d.ts +1 -1
  43. package/dist/types/ottochain/metagraph-client.d.ts +90 -0
  44. package/package.json +12 -12
  45. package/dist/apps/contracts/index.d.ts +0 -21
  46. package/dist/apps/contracts/index.js +0 -39
  47. package/dist/apps/contracts/types.d.ts +0 -24
  48. package/dist/apps/contracts/types.js +0 -48
  49. package/dist/apps/identity/index.d.ts +0 -22
  50. package/dist/apps/identity/index.js +0 -40
  51. package/dist/apps/identity/types.d.ts +0 -30
  52. package/dist/apps/identity/types.js +0 -53
  53. package/dist/apps/index.d.ts +0 -29
  54. package/dist/apps/index.js +0 -60
  55. package/dist/apps/markets/index.d.ts +0 -26
  56. package/dist/apps/markets/index.js +0 -46
  57. package/dist/apps/markets/types.d.ts +0 -185
  58. package/dist/apps/markets/types.js +0 -252
  59. package/dist/apps/oracles/index.d.ts +0 -26
  60. package/dist/apps/oracles/index.js +0 -46
  61. package/dist/apps/oracles/types.d.ts +0 -211
  62. package/dist/apps/oracles/types.js +0 -306
  63. package/dist/cjs/apps/contracts/types.js +0 -48
  64. package/dist/cjs/apps/corporate/types.js +0 -44
  65. package/dist/cjs/apps/governance/types.js +0 -42
  66. package/dist/cjs/apps/identity/types.js +0 -53
  67. package/dist/cjs/apps/markets/types.js +0 -219
  68. package/dist/cjs/apps/oracles/types.js +0 -282
  69. package/dist/cjs/generated/ottochain/apps/contracts/v1/contract_pb.js +0 -100
  70. package/dist/cjs/generated/ottochain/apps/corporate/v1/corporate_pb.js +0 -392
  71. package/dist/cjs/generated/ottochain/apps/governance/v1/governance_pb.js +0 -235
  72. package/dist/cjs/generated/ottochain/apps/identity/v1/agent_pb.js +0 -116
  73. package/dist/cjs/generated/ottochain/apps/identity/v1/attestation_pb.js +0 -79
  74. package/dist/cjs/generated/ottochain/apps/markets/v1/market_pb.js +0 -151
  75. package/dist/cjs/generated/ottochain/apps/oracles/v1/oracle_pb.js +0 -109
  76. package/dist/cjs/generated/ottochain/v1/common_pb.js +0 -37
  77. package/dist/cjs/generated/ottochain/v1/fiber_pb.js +0 -86
  78. package/dist/cjs/generated/ottochain/v1/messages_pb.js +0 -44
  79. package/dist/cjs/generated/ottochain/v1/records_pb.js +0 -44
  80. package/dist/errors.d.ts +0 -221
  81. package/dist/errors.js +0 -293
  82. package/dist/esm/apps/contracts/types.js +0 -44
  83. package/dist/esm/apps/corporate/types.js +0 -38
  84. package/dist/esm/apps/governance/types.js +0 -35
  85. package/dist/esm/apps/identity/types.js +0 -50
  86. package/dist/esm/apps/markets/types.js +0 -206
  87. package/dist/esm/apps/oracles/types.js +0 -267
  88. package/dist/esm/generated/ottochain/apps/contracts/v1/contract_pb.js +0 -97
  89. package/dist/esm/generated/ottochain/apps/corporate/v1/corporate_pb.js +0 -389
  90. package/dist/esm/generated/ottochain/apps/governance/v1/governance_pb.js +0 -232
  91. package/dist/esm/generated/ottochain/apps/identity/v1/agent_pb.js +0 -113
  92. package/dist/esm/generated/ottochain/apps/identity/v1/attestation_pb.js +0 -76
  93. package/dist/esm/generated/ottochain/apps/markets/v1/market_pb.js +0 -148
  94. package/dist/esm/generated/ottochain/apps/oracles/v1/oracle_pb.js +0 -106
  95. package/dist/esm/generated/ottochain/v1/common_pb.js +0 -34
  96. package/dist/esm/generated/ottochain/v1/fiber_pb.js +0 -83
  97. package/dist/esm/generated/ottochain/v1/messages_pb.js +0 -41
  98. package/dist/esm/generated/ottochain/v1/records_pb.js +0 -41
  99. package/dist/generated/index.d.ts +0 -15
  100. package/dist/generated/index.js +0 -34
  101. package/dist/generated/ottochain/apps/contracts/v1/contract_pb.d.ts +0 -274
  102. package/dist/generated/ottochain/apps/contracts/v1/contract_pb.js +0 -100
  103. package/dist/generated/ottochain/apps/identity/v1/agent_pb.d.ts +0 -211
  104. package/dist/generated/ottochain/apps/identity/v1/agent_pb.js +0 -116
  105. package/dist/generated/ottochain/apps/identity/v1/attestation_pb.d.ts +0 -238
  106. package/dist/generated/ottochain/apps/identity/v1/attestation_pb.js +0 -79
  107. package/dist/generated/ottochain/apps/markets/v1/market_pb.d.ts +0 -436
  108. package/dist/generated/ottochain/apps/markets/v1/market_pb.js +0 -151
  109. package/dist/generated/ottochain/apps/oracles/v1/oracle_pb.d.ts +0 -393
  110. package/dist/generated/ottochain/apps/oracles/v1/oracle_pb.js +0 -109
  111. package/dist/generated/ottochain/v1/common_pb.d.ts +0 -86
  112. package/dist/generated/ottochain/v1/common_pb.js +0 -37
  113. package/dist/generated/ottochain/v1/fiber_pb.d.ts +0 -292
  114. package/dist/generated/ottochain/v1/fiber_pb.js +0 -86
  115. package/dist/generated/ottochain/v1/messages_pb.d.ts +0 -190
  116. package/dist/generated/ottochain/v1/messages_pb.js +0 -44
  117. package/dist/generated/ottochain/v1/records_pb.d.ts +0 -221
  118. package/dist/generated/ottochain/v1/records_pb.js +0 -44
  119. package/dist/index.d.ts +0 -21
  120. package/dist/index.js +0 -77
  121. package/dist/metakit/binary.d.ts +0 -38
  122. package/dist/metakit/binary.js +0 -58
  123. package/dist/metakit/canonicalize.d.ts +0 -26
  124. package/dist/metakit/canonicalize.js +0 -40
  125. package/dist/metakit/codec.d.ts +0 -16
  126. package/dist/metakit/codec.js +0 -45
  127. package/dist/metakit/currency-transaction.d.ts +0 -157
  128. package/dist/metakit/currency-transaction.js +0 -319
  129. package/dist/metakit/currency-types.d.ts +0 -55
  130. package/dist/metakit/currency-types.js +0 -13
  131. package/dist/metakit/hash.d.ts +0 -50
  132. package/dist/metakit/hash.js +0 -84
  133. package/dist/metakit/index.d.ts +0 -23
  134. package/dist/metakit/index.js +0 -74
  135. package/dist/metakit/network/client.d.ts +0 -23
  136. package/dist/metakit/network/client.js +0 -78
  137. package/dist/metakit/network/currency-l1-client.d.ts +0 -71
  138. package/dist/metakit/network/currency-l1-client.js +0 -101
  139. package/dist/metakit/network/data-l1-client.d.ts +0 -57
  140. package/dist/metakit/network/data-l1-client.js +0 -76
  141. package/dist/metakit/network/index.d.ts +0 -10
  142. package/dist/metakit/network/index.js +0 -16
  143. package/dist/metakit/network/types.d.ts +0 -74
  144. package/dist/metakit/network/types.js +0 -20
  145. package/dist/metakit/sign.d.ts +0 -65
  146. package/dist/metakit/sign.js +0 -120
  147. package/dist/metakit/signed-object.d.ts +0 -66
  148. package/dist/metakit/signed-object.js +0 -100
  149. package/dist/metakit/types.d.ts +0 -67
  150. package/dist/metakit/types.js +0 -14
  151. package/dist/metakit/verify.d.ts +0 -55
  152. package/dist/metakit/verify.js +0 -217
  153. package/dist/metakit/wallet.d.ts +0 -70
  154. package/dist/metakit/wallet.js +0 -127
  155. package/dist/ottochain/index.d.ts +0 -13
  156. package/dist/ottochain/index.js +0 -45
  157. package/dist/ottochain/metagraph-client.d.ts +0 -111
  158. package/dist/ottochain/metagraph-client.js +0 -157
  159. package/dist/ottochain/snapshot.d.ts +0 -86
  160. package/dist/ottochain/snapshot.js +0 -110
  161. package/dist/ottochain/types.d.ts +0 -278
  162. package/dist/ottochain/types.js +0 -11
  163. package/dist/types/apps/contracts/types.d.ts +0 -24
  164. package/dist/types/apps/corporate/types.d.ts +0 -9861
  165. package/dist/types/apps/governance/types.d.ts +0 -344
  166. package/dist/types/apps/identity/types.d.ts +0 -30
  167. package/dist/types/apps/markets/types.d.ts +0 -155
  168. package/dist/types/apps/oracles/types.d.ts +0 -193
  169. package/dist/types/generated/ottochain/apps/contracts/v1/contract_pb.d.ts +0 -274
  170. package/dist/types/generated/ottochain/apps/corporate/v1/corporate_pb.d.ts +0 -1172
  171. package/dist/types/generated/ottochain/apps/governance/v1/governance_pb.d.ts +0 -772
  172. package/dist/types/generated/ottochain/apps/identity/v1/agent_pb.d.ts +0 -211
  173. package/dist/types/generated/ottochain/apps/identity/v1/attestation_pb.d.ts +0 -238
  174. package/dist/types/generated/ottochain/apps/markets/v1/market_pb.d.ts +0 -436
  175. package/dist/types/generated/ottochain/apps/oracles/v1/oracle_pb.d.ts +0 -393
  176. package/dist/types/generated/ottochain/v1/common_pb.d.ts +0 -86
  177. package/dist/types/generated/ottochain/v1/fiber_pb.d.ts +0 -292
  178. package/dist/types/generated/ottochain/v1/messages_pb.d.ts +0 -190
  179. package/dist/types/generated/ottochain/v1/records_pb.d.ts +0 -221
  180. package/dist/validation.d.ts +0 -449
  181. package/dist/validation.js +0 -312
@@ -6,52 +6,36 @@
6
6
  },
7
7
  "states": {
8
8
  "UNREGISTERED": {
9
- "id": {
10
- "value": "UNREGISTERED"
11
- },
9
+ "id": "UNREGISTERED",
12
10
  "isFinal": false,
13
11
  "metadata": null
14
12
  },
15
13
  "REGISTERED": {
16
- "id": {
17
- "value": "REGISTERED"
18
- },
14
+ "id": "REGISTERED",
19
15
  "isFinal": false,
20
16
  "metadata": null
21
17
  },
22
18
  "ACTIVE": {
23
- "id": {
24
- "value": "ACTIVE"
25
- },
19
+ "id": "ACTIVE",
26
20
  "isFinal": false,
27
21
  "metadata": null
28
22
  },
29
23
  "SLASHED": {
30
- "id": {
31
- "value": "SLASHED"
32
- },
24
+ "id": "SLASHED",
33
25
  "isFinal": false,
34
26
  "metadata": null
35
27
  },
36
28
  "WITHDRAWN": {
37
- "id": {
38
- "value": "WITHDRAWN"
39
- },
29
+ "id": "WITHDRAWN",
40
30
  "isFinal": true,
41
31
  "metadata": null
42
32
  }
43
33
  },
44
- "initialState": {
45
- "value": "UNREGISTERED"
46
- },
34
+ "initialState": "UNREGISTERED",
47
35
  "transitions": [
48
36
  {
49
- "from": {
50
- "value": "UNREGISTERED"
51
- },
52
- "to": {
53
- "value": "REGISTERED"
54
- },
37
+ "from": "UNREGISTERED",
38
+ "to": "REGISTERED",
55
39
  "eventName": "register",
56
40
  "guard": {
57
41
  ">=": [
@@ -95,12 +79,8 @@
95
79
  "dependencies": []
96
80
  },
97
81
  {
98
- "from": {
99
- "value": "REGISTERED"
100
- },
101
- "to": {
102
- "value": "ACTIVE"
103
- },
82
+ "from": "REGISTERED",
83
+ "to": "ACTIVE",
104
84
  "eventName": "activate",
105
85
  "guard": {
106
86
  "or": [
@@ -135,12 +115,8 @@
135
115
  "dependencies": []
136
116
  },
137
117
  {
138
- "from": {
139
- "value": "ACTIVE"
140
- },
141
- "to": {
142
- "value": "ACTIVE"
143
- },
118
+ "from": "ACTIVE",
119
+ "to": "ACTIVE",
144
120
  "eventName": "add_stake",
145
121
  "guard": {
146
122
  "and": [
@@ -189,12 +165,8 @@
189
165
  "dependencies": []
190
166
  },
191
167
  {
192
- "from": {
193
- "value": "ACTIVE"
194
- },
195
- "to": {
196
- "value": "ACTIVE"
197
- },
168
+ "from": "ACTIVE",
169
+ "to": "ACTIVE",
198
170
  "eventName": "record_resolution",
199
171
  "guard": {
200
172
  "var": "event.marketId"
@@ -249,12 +221,8 @@
249
221
  "dependencies": []
250
222
  },
251
223
  {
252
- "from": {
253
- "value": "ACTIVE"
254
- },
255
- "to": {
256
- "value": "SLASHED"
257
- },
224
+ "from": "ACTIVE",
225
+ "to": "SLASHED",
258
226
  "eventName": "slash",
259
227
  "guard": {
260
228
  "and": [
@@ -330,12 +298,8 @@
330
298
  "dependencies": []
331
299
  },
332
300
  {
333
- "from": {
334
- "value": "SLASHED"
335
- },
336
- "to": {
337
- "value": "ACTIVE"
338
- },
301
+ "from": "SLASHED",
302
+ "to": "ACTIVE",
339
303
  "eventName": "reactivate",
340
304
  "guard": {
341
305
  "and": [
@@ -377,12 +341,8 @@
377
341
  "dependencies": []
378
342
  },
379
343
  {
380
- "from": {
381
- "value": "ACTIVE"
382
- },
383
- "to": {
384
- "value": "WITHDRAWN"
385
- },
344
+ "from": "ACTIVE",
345
+ "to": "WITHDRAWN",
386
346
  "eventName": "withdraw",
387
347
  "guard": {
388
348
  "===": [
@@ -413,12 +373,8 @@
413
373
  "dependencies": []
414
374
  },
415
375
  {
416
- "from": {
417
- "value": "SLASHED"
418
- },
419
- "to": {
420
- "value": "WITHDRAWN"
421
- },
376
+ "from": "SLASHED",
377
+ "to": "WITHDRAWN",
422
378
  "eventName": "withdraw",
423
379
  "guard": {
424
380
  "===": [
@@ -2,9 +2,19 @@
2
2
  * Generated Protobuf Types
3
3
  *
4
4
  * Auto-generated from proto/ definitions using ts-proto.
5
- * DO NOT EDIT - regenerate with `pnpm run generate`
5
+ * DO NOT EDIT - regenerate with `npm run generate`
6
6
  *
7
7
  * @packageDocumentation
8
+ *
9
+ * NOTE — Dual-type architecture:
10
+ * These generated types use proto conventions (FIBER_STATUS_ACTIVE, plain string StateId).
11
+ * The wire-format REST API types in src/ottochain/types.ts use plain strings ('Active').
12
+ * Both coexist intentionally until PR #89 (Migrate fiber-engine to generated Scala types) merges.
13
+ *
14
+ * TODO PR #89 migration: after PR #89 merges and cluster confirms new format,
15
+ * migrate all state-machine JSON files in src/apps/ from Circe-format
16
+ * ({ value: '...' } wrapped initialState) to plain string format.
17
+ * See docs/type-architecture.md for full migration plan.
8
18
  */
9
19
  // Core types (primitives - no wrapper messages)
10
20
  // Type aliases are in src/types.ts
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Drop Null Values from JSON Objects
3
+ *
4
+ * Recursively removes null-valued keys from objects to match
5
+ * the Scala-side `JsonBinaryCodec.dropNulls` behavior in metakit.
6
+ *
7
+ * This ensures that the canonical JSON used for signing on the
8
+ * TypeScript side matches what the Scala metagraph produces when
9
+ * verifying signatures.
10
+ *
11
+ * Note: null values inside arrays are preserved (to maintain index
12
+ * positions). Only object field values that are null are removed.
13
+ */
14
+ /**
15
+ * Recursively remove null values from objects
16
+ *
17
+ * @param value - Any JSON-serializable value
18
+ * @returns A deep copy with null object fields removed
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * dropNulls({ a: 1, b: null, c: { d: null, e: 2 } })
23
+ * // => { a: 1, c: { e: 2 } }
24
+ *
25
+ * dropNulls([1, null, 3])
26
+ * // => [1, null, 3] (array nulls preserved)
27
+ * ```
28
+ */
29
+ export function dropNulls(value) {
30
+ if (value === null || value === undefined) {
31
+ return value;
32
+ }
33
+ if (Array.isArray(value)) {
34
+ return value.map(dropNulls);
35
+ }
36
+ if (typeof value === 'object') {
37
+ const result = {};
38
+ for (const [k, v] of Object.entries(value)) {
39
+ if (v !== null && v !== undefined) {
40
+ result[k] = dropNulls(v);
41
+ }
42
+ }
43
+ return result;
44
+ }
45
+ return value;
46
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Normalize OttoChain Messages for Signing
3
+ *
4
+ * Converts OttoChain message objects to the wire format expected by the
5
+ * Scala metagraph. Specifically, ensures all `Option[A]=None` fields are
6
+ * present as explicit `null` values, matching what circe's magnolia encoder
7
+ * produces on the Scala side.
8
+ *
9
+ * This is necessary because metakit's `JsonBinaryCodec.deriveDataUpdate`
10
+ * (rc.8) canonicalizes the circe-encoded JSON **including null fields**.
11
+ * If the TypeScript client omits optional fields (undefined), the canonical
12
+ * JSON won't match and signature verification will fail.
13
+ *
14
+ * Schema mapping (Scala → TypeScript):
15
+ * - `Option[A] = None` → `null` (must be explicit, not undefined/absent)
16
+ * - `Option[A] = Some(v)` → `v`
17
+ * - `List.empty` → `[]`
18
+ * - `Map.empty` → `{}`
19
+ */
20
+ /**
21
+ * Normalize a State object for wire format
22
+ */
23
+ function normalizeState(state) {
24
+ return {
25
+ id: state.id,
26
+ isFinal: state.isFinal ?? false,
27
+ metadata: state.metadata ?? null,
28
+ };
29
+ }
30
+ /**
31
+ * Normalize a Transition object for wire format
32
+ */
33
+ function normalizeTransition(t) {
34
+ return {
35
+ from: t.from,
36
+ eventName: t.eventName,
37
+ to: t.to,
38
+ guard: t.guard ?? null,
39
+ actions: t.actions ?? null,
40
+ metadata: t.metadata ?? null,
41
+ };
42
+ }
43
+ /**
44
+ * Normalize a StateMachineDefinition for wire format
45
+ */
46
+ function normalizeDefinition(def) {
47
+ const states = def.states;
48
+ const normalizedStates = {};
49
+ if (states) {
50
+ for (const [key, state] of Object.entries(states)) {
51
+ normalizedStates[key] = normalizeState(state);
52
+ }
53
+ }
54
+ const transitions = def.transitions ?? [];
55
+ return {
56
+ states: normalizedStates,
57
+ initialState: def.initialState,
58
+ transitions: transitions.map(normalizeTransition),
59
+ metadata: def.metadata ?? null,
60
+ };
61
+ }
62
+ /**
63
+ * Normalize a CreateStateMachine message for wire format
64
+ *
65
+ * Ensures all Option fields are explicit null:
66
+ * - definition.metadata
67
+ * - definition.states[*].metadata
68
+ * - definition.transitions[*].guard
69
+ * - definition.transitions[*].actions
70
+ * - definition.transitions[*].metadata
71
+ * - parentFiberId
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * const message = normalizeCreateStateMachine({
76
+ * fiberId: '...',
77
+ * definition: { states: { INIT: { id: { value: 'INIT' }, isFinal: false } }, ... },
78
+ * initialData: {}
79
+ * });
80
+ * // message now has parentFiberId: null, definition.metadata: null, etc.
81
+ * ```
82
+ */
83
+ export function normalizeCreateStateMachine(msg) {
84
+ return {
85
+ fiberId: msg.fiberId,
86
+ definition: normalizeDefinition(msg.definition),
87
+ initialData: msg.initialData ?? {},
88
+ parentFiberId: msg.parentFiberId ?? null,
89
+ };
90
+ }
91
+ /**
92
+ * Normalize a TransitionStateMachine message for wire format
93
+ */
94
+ export function normalizeTransitionStateMachine(msg) {
95
+ return {
96
+ fiberId: msg.fiberId,
97
+ eventName: msg.eventName,
98
+ eventData: msg.eventData ?? null,
99
+ fiberOrdinal: msg.fiberOrdinal,
100
+ };
101
+ }
102
+ /**
103
+ * Normalize an ArchiveStateMachine message for wire format
104
+ */
105
+ export function normalizeArchiveStateMachine(msg) {
106
+ return {
107
+ fiberId: msg.fiberId,
108
+ reason: msg.reason ?? null,
109
+ };
110
+ }
111
+ /**
112
+ * Normalize any OttochainMessage wrapper for wire format.
113
+ *
114
+ * Detects the message type from the wrapper key and applies the
115
+ * appropriate normalization.
116
+ *
117
+ * @param message - OttochainMessage in wrapper format: `{ CreateStateMachine: {...} }`
118
+ * @returns Normalized message with all Option fields as explicit null
119
+ *
120
+ * @example
121
+ * ```typescript
122
+ * const normalized = normalizeMessage({
123
+ * CreateStateMachine: { fiberId: '...', definition: {...}, initialData: {} }
124
+ * });
125
+ * const signed = await signDataUpdate(normalized, privateKey);
126
+ * ```
127
+ */
128
+ export function normalizeMessage(message) {
129
+ if ('CreateStateMachine' in message) {
130
+ return { CreateStateMachine: normalizeCreateStateMachine(message.CreateStateMachine) };
131
+ }
132
+ if ('TransitionStateMachine' in message) {
133
+ return { TransitionStateMachine: normalizeTransitionStateMachine(message.TransitionStateMachine) };
134
+ }
135
+ if ('ArchiveStateMachine' in message) {
136
+ return { ArchiveStateMachine: normalizeArchiveStateMachine(message.ArchiveStateMachine) };
137
+ }
138
+ // CreateScript and InvokeScript — pass through (no optional fields)
139
+ return message;
140
+ }
@@ -135,6 +135,153 @@ export class MetagraphClient {
135
135
  return snapshot.value.ordinal;
136
136
  }
137
137
  // -------------------------------------------------------------------------
138
+ // Fiber state subscription
139
+ // -------------------------------------------------------------------------
140
+ /**
141
+ * Subscribe to state changes for a state machine fiber.
142
+ *
143
+ * Polls ML0 at the configured interval and compares `sequenceNumber` to
144
+ * detect changes. Fires the callback with `(current, previous)` on the
145
+ * first poll (if `fireImmediately` is true — the default) and on every
146
+ * subsequent change.
147
+ *
148
+ * ML0 consistency: state is read from snapshot-level `CalculatedState`
149
+ * (via `checkpointService.get`), so a poll never returns a mid-transition
150
+ * value — every observation is a fully committed state.
151
+ *
152
+ * Uses `setTimeout` recursion to prevent overlapping polls when a request
153
+ * takes longer than `pollIntervalMs`.
154
+ *
155
+ * @param fiberId - UUID of the state machine fiber to watch
156
+ * @param callback - Called on first observation and on every state change
157
+ * @param options - Polling interval, error handler, fireImmediately flag
158
+ * @returns Unsubscribe function — call to stop polling immediately
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * const unsub = client.subscribeFiberState(fiberId, (current, prev) => {
163
+ * if (current?.currentState === 'Completed') {
164
+ * unsub();
165
+ * }
166
+ * });
167
+ * // Later:
168
+ * unsub();
169
+ * ```
170
+ */
171
+ subscribeFiberState(fiberId, callback, options) {
172
+ const intervalMs = options?.pollIntervalMs ?? 2000;
173
+ const onError = options?.onError ?? ((e) => console.warn('[subscribeFiberState]', e));
174
+ const fireImmediately = options?.fireImmediately ?? true;
175
+ let previous = null;
176
+ let lastSeqNum = null;
177
+ let firstPoll = true; // Track first poll separately — handles null seqNum on missing fiber
178
+ let active = true;
179
+ const invokeCallback = (current, prev) => {
180
+ try {
181
+ callback(current, prev);
182
+ }
183
+ catch (cbErr) {
184
+ try {
185
+ onError(cbErr instanceof Error ? cbErr : new Error(String(cbErr)));
186
+ }
187
+ catch { /* ignore */ }
188
+ }
189
+ };
190
+ const poll = async () => {
191
+ if (!active)
192
+ return;
193
+ let current;
194
+ try {
195
+ current = await this.getStateMachine(fiberId);
196
+ }
197
+ catch (err) {
198
+ // Network / timeout error — notify and reschedule
199
+ if (!active)
200
+ return;
201
+ try {
202
+ onError(err instanceof Error ? err : new Error(String(err)));
203
+ }
204
+ catch { /* ignore */ }
205
+ if (active)
206
+ setTimeout(poll, intervalMs);
207
+ return;
208
+ }
209
+ // Guard: unsubscribe may have been called while awaiting
210
+ if (!active)
211
+ return;
212
+ const currentSeq = current?.sequenceNumber ?? null;
213
+ if (firstPoll) {
214
+ firstPoll = false;
215
+ lastSeqNum = currentSeq;
216
+ previous = current;
217
+ if (fireImmediately) {
218
+ invokeCallback(current, null);
219
+ }
220
+ }
221
+ else if (currentSeq !== lastSeqNum) {
222
+ // State changed — sequenceNumber differs (handles null → value and value → null)
223
+ const prev = previous;
224
+ previous = current;
225
+ lastSeqNum = currentSeq;
226
+ invokeCallback(current, prev);
227
+ }
228
+ // No change → no callback
229
+ // Schedule next poll (setTimeout recursion prevents overlapping polls)
230
+ if (active)
231
+ setTimeout(poll, intervalMs);
232
+ };
233
+ // Start first poll via setTimeout(0) so Jest fake-timer control works correctly:
234
+ // callers can unsubscribe before the first poll runs, and each
235
+ // jest.runOnlyPendingTimersAsync() advances exactly one poll cycle.
236
+ setTimeout(poll, 0);
237
+ return () => { active = false; };
238
+ }
239
+ /**
240
+ * Wait for a fiber to reach a specific state, with an optional timeout.
241
+ *
242
+ * Builds on `subscribeFiberState` — resolves with the fiber record once
243
+ * `currentState === targetState`, or resolves with `null` after `timeoutMs`.
244
+ * Always calls unsubscribe on resolution or timeout (no memory leaks).
245
+ *
246
+ * @param fiberId - UUID of the state machine fiber
247
+ * @param targetState - State name to wait for (e.g. `'Completed'`)
248
+ * @param timeoutMs - Maximum wait in milliseconds (default: 30 000)
249
+ * @returns Fiber record when target state reached, or `null` on timeout
250
+ *
251
+ * @example
252
+ * ```typescript
253
+ * const result = await client.waitForState(fiberId, 'Completed', 15_000);
254
+ * if (result === null) console.warn('Timed out waiting for Completed');
255
+ * ```
256
+ */
257
+ waitForState(fiberId, targetState, timeoutMs = 30000, options) {
258
+ return new Promise((resolve) => {
259
+ let resolved = false;
260
+ let timer = null;
261
+ // unsub starts as a no-op so it's safe to call even if subscribeFiberState
262
+ // invokes the callback synchronously (before returning the real unsubscribe).
263
+ let unsub = () => { };
264
+ const done = (result) => {
265
+ if (resolved)
266
+ return;
267
+ resolved = true;
268
+ if (timer !== null)
269
+ clearTimeout(timer);
270
+ unsub(); // safe: either the real unsub or the initial no-op
271
+ resolve(result);
272
+ };
273
+ unsub = this.subscribeFiberState(fiberId, (current) => {
274
+ if (current?.currentState === targetState) {
275
+ done(current);
276
+ }
277
+ }, { fireImmediately: true, ...options });
278
+ // Only arm the timeout if the callback hasn't already resolved (synchronous case)
279
+ if (!resolved) {
280
+ timer = setTimeout(() => done(null), timeoutMs);
281
+ }
282
+ });
283
+ }
284
+ // -------------------------------------------------------------------------
138
285
  // DL1 data submission (framework POST /data)
139
286
  // -------------------------------------------------------------------------
140
287
  /**
@@ -131,7 +131,7 @@ export const PlatformLinkSchema = z.object({
131
131
  /**
132
132
  * Schema for contract terms (flexible structure)
133
133
  */
134
- export const ContractTermsSchema = z.record(z.unknown());
134
+ export const ContractTermsSchema = z.record(z.string(), z.unknown());
135
135
  /**
136
136
  * Schema for ProposeContractRequest
137
137
  */
@@ -2,9 +2,19 @@
2
2
  * Generated Protobuf Types
3
3
  *
4
4
  * Auto-generated from proto/ definitions using ts-proto.
5
- * DO NOT EDIT - regenerate with `pnpm run generate`
5
+ * DO NOT EDIT - regenerate with `npm run generate`
6
6
  *
7
7
  * @packageDocumentation
8
+ *
9
+ * NOTE — Dual-type architecture:
10
+ * These generated types use proto conventions (FIBER_STATUS_ACTIVE, plain string StateId).
11
+ * The wire-format REST API types in src/ottochain/types.ts use plain strings ('Active').
12
+ * Both coexist intentionally until PR #89 (Migrate fiber-engine to generated Scala types) merges.
13
+ *
14
+ * TODO PR #89 migration: after PR #89 merges and cluster confirms new format,
15
+ * migrate all state-machine JSON files in src/apps/ from Circe-format
16
+ * ({ value: '...' } wrapped initialState) to plain string format.
17
+ * See docs/type-architecture.md for full migration plan.
8
18
  */
9
19
  export { FiberStatus, AccessControlPolicy, PublicAccess, WhitelistAccess, FiberOwnedAccess, StateMachineDefinition, EmittedEvent, EventReceipt, ScriptInvocation, FiberLogEntry, } from './ottochain/v1/fiber.js';
10
20
  export { CreateStateMachine, TransitionStateMachine, ArchiveStateMachine, CreateScript, InvokeScript, OttochainMessage, } from './ottochain/v1/messages.js';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Drop Null Values from JSON Objects
3
+ *
4
+ * Recursively removes null-valued keys from objects to match
5
+ * the Scala-side `JsonBinaryCodec.dropNulls` behavior in metakit.
6
+ *
7
+ * This ensures that the canonical JSON used for signing on the
8
+ * TypeScript side matches what the Scala metagraph produces when
9
+ * verifying signatures.
10
+ *
11
+ * Note: null values inside arrays are preserved (to maintain index
12
+ * positions). Only object field values that are null are removed.
13
+ */
14
+ /**
15
+ * Recursively remove null values from objects
16
+ *
17
+ * @param value - Any JSON-serializable value
18
+ * @returns A deep copy with null object fields removed
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * dropNulls({ a: 1, b: null, c: { d: null, e: 2 } })
23
+ * // => { a: 1, c: { e: 2 } }
24
+ *
25
+ * dropNulls([1, null, 3])
26
+ * // => [1, null, 3] (array nulls preserved)
27
+ * ```
28
+ */
29
+ export declare function dropNulls<T>(value: T): T;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Normalize OttoChain Messages for Signing
3
+ *
4
+ * Converts OttoChain message objects to the wire format expected by the
5
+ * Scala metagraph. Specifically, ensures all `Option[A]=None` fields are
6
+ * present as explicit `null` values, matching what circe's magnolia encoder
7
+ * produces on the Scala side.
8
+ *
9
+ * This is necessary because metakit's `JsonBinaryCodec.deriveDataUpdate`
10
+ * (rc.8) canonicalizes the circe-encoded JSON **including null fields**.
11
+ * If the TypeScript client omits optional fields (undefined), the canonical
12
+ * JSON won't match and signature verification will fail.
13
+ *
14
+ * Schema mapping (Scala → TypeScript):
15
+ * - `Option[A] = None` → `null` (must be explicit, not undefined/absent)
16
+ * - `Option[A] = Some(v)` → `v`
17
+ * - `List.empty` → `[]`
18
+ * - `Map.empty` → `{}`
19
+ */
20
+ /**
21
+ * Normalize a CreateStateMachine message for wire format
22
+ *
23
+ * Ensures all Option fields are explicit null:
24
+ * - definition.metadata
25
+ * - definition.states[*].metadata
26
+ * - definition.transitions[*].guard
27
+ * - definition.transitions[*].actions
28
+ * - definition.transitions[*].metadata
29
+ * - parentFiberId
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const message = normalizeCreateStateMachine({
34
+ * fiberId: '...',
35
+ * definition: { states: { INIT: { id: { value: 'INIT' }, isFinal: false } }, ... },
36
+ * initialData: {}
37
+ * });
38
+ * // message now has parentFiberId: null, definition.metadata: null, etc.
39
+ * ```
40
+ */
41
+ export declare function normalizeCreateStateMachine(msg: Record<string, unknown>): Record<string, unknown>;
42
+ /**
43
+ * Normalize a TransitionStateMachine message for wire format
44
+ */
45
+ export declare function normalizeTransitionStateMachine(msg: Record<string, unknown>): Record<string, unknown>;
46
+ /**
47
+ * Normalize an ArchiveStateMachine message for wire format
48
+ */
49
+ export declare function normalizeArchiveStateMachine(msg: Record<string, unknown>): Record<string, unknown>;
50
+ /**
51
+ * Normalize any OttochainMessage wrapper for wire format.
52
+ *
53
+ * Detects the message type from the wrapper key and applies the
54
+ * appropriate normalization.
55
+ *
56
+ * @param message - OttochainMessage in wrapper format: `{ CreateStateMachine: {...} }`
57
+ * @returns Normalized message with all Option fields as explicit null
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const normalized = normalizeMessage({
62
+ * CreateStateMachine: { fiberId: '...', definition: {...}, initialData: {} }
63
+ * });
64
+ * const signed = await signDataUpdate(normalized, privateKey);
65
+ * ```
66
+ */
67
+ export declare function normalizeMessage(message: Record<string, unknown>): Record<string, unknown>;
@@ -9,5 +9,5 @@ export * as proto from '../generated/index.js';
9
9
  export type { Address, FiberId, StateId, HashValue, FiberOrdinal, SnapshotOrdinal, JsonLogicValue, JsonLogicExpression, FiberStatus, AccessControlPolicy, StateMachineDefinition, EmittedEvent, EventReceipt, OracleInvocation, FiberLogEntry, StateMachineFiberRecord, ScriptFiberRecord, FiberRecord, FiberCommit, OnChain, CalculatedState, CreateStateMachine, TransitionStateMachine, ArchiveStateMachine, CreateScript, InvokeScript, OttochainMessage, } from './types.js';
10
10
  export type { CurrencySnapshotResponse } from './snapshot.js';
11
11
  export { decodeOnChainState, getSnapshotOnChainState, getLatestOnChainState, getLogsForFiber, getEventReceipts, getScriptInvocations, extractOnChainState, } from './snapshot.js';
12
- export type { Checkpoint, MetagraphClientConfig } from './metagraph-client.js';
12
+ export type { Checkpoint, MetagraphClientConfig, SubscribeOptions, FiberStateCallback, Unsubscribe, } from './metagraph-client.js';
13
13
  export { MetagraphClient } from './metagraph-client.js';