@ottochain/sdk 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/dist/cjs/apps/compute/index.js +33 -0
  2. package/dist/cjs/apps/compute/state-machines/compute-rule110.js +164 -0
  3. package/dist/cjs/apps/compute/state-machines/index.js +9 -0
  4. package/dist/cjs/apps/contracts/index.js +16 -9
  5. package/dist/cjs/apps/contracts/state-machines/contract-agreement.js +134 -161
  6. package/dist/cjs/apps/contracts/state-machines/contract-escrow.js +192 -170
  7. package/dist/cjs/apps/contracts/state-machines/contract-universal.js +46 -58
  8. package/dist/cjs/apps/corporate/index.js +2 -2
  9. package/dist/cjs/apps/corporate/state-machines/corp-board.js +500 -456
  10. package/dist/cjs/apps/corporate/state-machines/corp-entity.js +487 -361
  11. package/dist/cjs/apps/corporate/state-machines/corp-securities.js +769 -576
  12. package/dist/cjs/apps/corporate/state-machines/corp-shareholders.js +567 -557
  13. package/dist/cjs/apps/governance/index.js +21 -21
  14. package/dist/cjs/apps/governance/state-machines/dao-multisig.js +188 -209
  15. package/dist/cjs/apps/governance/state-machines/dao-reputation.js +181 -228
  16. package/dist/cjs/apps/governance/state-machines/dao-single.js +105 -88
  17. package/dist/cjs/apps/governance/state-machines/dao-token.js +199 -227
  18. package/dist/cjs/apps/governance/state-machines/governance-simple.js +231 -231
  19. package/dist/cjs/apps/governance/state-machines/governance-universal.js +64 -64
  20. package/dist/cjs/apps/identity/constants.js +55 -22
  21. package/dist/cjs/apps/identity/index.js +28 -12
  22. package/dist/cjs/apps/identity/state-machines/identity-agent.js +124 -158
  23. package/dist/cjs/apps/identity/state-machines/identity-oracle.js +158 -173
  24. package/dist/cjs/apps/identity/state-machines/identity-registry.js +262 -0
  25. package/dist/cjs/apps/identity/state-machines/identity-universal.js +51 -57
  26. package/dist/cjs/apps/identity/state-machines/index.js +3 -1
  27. package/dist/cjs/apps/index.js +22 -8
  28. package/dist/cjs/apps/lending/assets.js +247 -0
  29. package/dist/cjs/apps/lending/credit-scoring.js +87 -0
  30. package/dist/cjs/apps/lending/eligibility.js +105 -0
  31. package/dist/cjs/apps/lending/index.js +91 -0
  32. package/dist/cjs/apps/lending/state-machines/index.js +9 -0
  33. package/dist/cjs/apps/lending/state-machines/lending-zk-loan.js +433 -0
  34. package/dist/cjs/apps/markets/index.js +22 -12
  35. package/dist/cjs/apps/markets/state-machines/market-auction.js +116 -134
  36. package/dist/cjs/apps/markets/state-machines/market-crowdfund.js +131 -141
  37. package/dist/cjs/apps/markets/state-machines/market-group-buy.js +163 -184
  38. package/dist/cjs/apps/markets/state-machines/market-prediction.js +226 -233
  39. package/dist/cjs/apps/markets/state-machines/market-universal.js +65 -80
  40. package/dist/cjs/apps/oracles/index.js +3 -3
  41. package/dist/cjs/apps/privacy/index.js +67 -0
  42. package/dist/cjs/apps/privacy/state-machines/index.js +17 -0
  43. package/dist/cjs/apps/privacy/state-machines/mixer-ddhRing.js +286 -0
  44. package/dist/cjs/apps/privacy/state-machines/note-pool.js +476 -0
  45. package/dist/cjs/apps/staked-pool/assets.js +103 -0
  46. package/dist/cjs/apps/staked-pool/base.js +498 -0
  47. package/dist/cjs/apps/staked-pool/consumer.js +55 -0
  48. package/dist/cjs/apps/staked-pool/index.js +83 -0
  49. package/dist/cjs/apps/staked-pool/state-machines/index.js +12 -0
  50. package/dist/cjs/apps/staked-pool/state-machines/staked-pool-base.js +49 -0
  51. package/dist/cjs/apps/staked-pool/state-machines/staked-pool-oracle.js +131 -0
  52. package/dist/cjs/errors.js +3 -3
  53. package/dist/cjs/generated/google/protobuf/struct.js +5 -5
  54. package/dist/cjs/generated/google/protobuf/timestamp.js +1 -1
  55. package/dist/cjs/generated/index.js +11 -7
  56. package/dist/cjs/generated/openapi.js +6 -0
  57. package/dist/cjs/generated/ottochain/apps/contracts/v1/contract.js +51 -51
  58. package/dist/cjs/generated/ottochain/apps/corporate/v1/corporate.js +20 -21
  59. package/dist/cjs/generated/ottochain/apps/governance/v1/governance.js +14 -14
  60. package/dist/cjs/generated/ottochain/apps/identity/v1/attestation.js +5 -5
  61. package/dist/cjs/generated/ottochain/apps/identity/v1/identity.js +125 -131
  62. package/dist/cjs/generated/ottochain/apps/markets/v1/market.js +108 -108
  63. package/dist/cjs/generated/ottochain/v1/common.js +5 -5
  64. package/dist/cjs/generated/ottochain/v1/fiber.js +44 -6
  65. package/dist/cjs/generated/ottochain/v1/messages.js +1 -1
  66. package/dist/cjs/generated/ottochain/v1/records.js +1 -1
  67. package/dist/cjs/index.js +58 -7
  68. package/dist/cjs/openapi.js +2 -0
  69. package/dist/cjs/ottochain/drop-nulls.js +1 -2
  70. package/dist/cjs/ottochain/genesis-manifest.js +17 -11
  71. package/dist/cjs/ottochain/index.js +23 -13
  72. package/dist/cjs/ottochain/metagraph-client.js +81 -16
  73. package/dist/cjs/ottochain/snapshot.js +13 -16
  74. package/dist/cjs/ottochain/transaction.js +67 -14
  75. package/dist/cjs/ottochain/types.js +16 -2
  76. package/dist/cjs/privacy/index.js +14 -0
  77. package/dist/cjs/privacy/sealed-bid.js +213 -0
  78. package/dist/cjs/privacy/shield-app.js +131 -0
  79. package/dist/cjs/schema/effects.js +156 -0
  80. package/dist/cjs/schema/fiber-app.js +150 -15
  81. package/dist/cjs/schema/guard-lint.js +343 -0
  82. package/dist/cjs/schema/guards.js +246 -0
  83. package/dist/cjs/signing.js +80 -0
  84. package/dist/cjs/templates/asset-policy.js +190 -0
  85. package/dist/cjs/templates/index.js +86 -0
  86. package/dist/cjs/templates/machine.js +130 -0
  87. package/dist/cjs/templates/migration.js +39 -0
  88. package/dist/cjs/templates/state-shape.js +38 -0
  89. package/dist/cjs/types.js +2 -3
  90. package/dist/cjs/validation.js +9 -11
  91. package/dist/cjs/verify.js +9 -4
  92. package/dist/cjs/zk/commit.js +53 -0
  93. package/dist/cjs/zk/guard.js +107 -0
  94. package/dist/cjs/zk/index.js +48 -0
  95. package/dist/cjs/zk/preimage.js +37 -0
  96. package/dist/cjs/zk/prover.js +87 -0
  97. package/dist/cjs/zk/registry.js +62 -0
  98. package/dist/cjs/zk/types.js +28 -0
  99. package/dist/cjs/zk/witness.js +19 -0
  100. package/dist/esm/apps/compute/index.js +29 -0
  101. package/dist/esm/apps/compute/state-machines/compute-rule110.js +161 -0
  102. package/dist/esm/apps/compute/state-machines/index.js +5 -0
  103. package/dist/esm/apps/contracts/index.js +9 -5
  104. package/dist/esm/apps/contracts/state-machines/contract-agreement.js +135 -162
  105. package/dist/esm/apps/contracts/state-machines/contract-escrow.js +193 -171
  106. package/dist/esm/apps/contracts/state-machines/contract-universal.js +47 -59
  107. package/dist/esm/apps/corporate/index.js +2 -2
  108. package/dist/esm/apps/corporate/state-machines/corp-board.js +501 -457
  109. package/dist/esm/apps/corporate/state-machines/corp-entity.js +488 -362
  110. package/dist/esm/apps/corporate/state-machines/corp-securities.js +770 -577
  111. package/dist/esm/apps/corporate/state-machines/corp-shareholders.js +568 -558
  112. package/dist/esm/apps/governance/index.js +10 -10
  113. package/dist/esm/apps/governance/state-machines/dao-multisig.js +189 -210
  114. package/dist/esm/apps/governance/state-machines/dao-reputation.js +182 -229
  115. package/dist/esm/apps/governance/state-machines/dao-single.js +106 -89
  116. package/dist/esm/apps/governance/state-machines/dao-token.js +200 -228
  117. package/dist/esm/apps/governance/state-machines/governance-simple.js +232 -232
  118. package/dist/esm/apps/governance/state-machines/governance-universal.js +65 -65
  119. package/dist/esm/apps/identity/constants.js +52 -21
  120. package/dist/esm/apps/identity/index.js +14 -9
  121. package/dist/esm/apps/identity/state-machines/identity-agent.js +125 -159
  122. package/dist/esm/apps/identity/state-machines/identity-oracle.js +159 -174
  123. package/dist/esm/apps/identity/state-machines/identity-registry.js +259 -0
  124. package/dist/esm/apps/identity/state-machines/identity-universal.js +52 -58
  125. package/dist/esm/apps/identity/state-machines/index.js +1 -0
  126. package/dist/esm/apps/index.js +9 -5
  127. package/dist/esm/apps/lending/assets.js +231 -0
  128. package/dist/esm/apps/lending/credit-scoring.js +81 -0
  129. package/dist/esm/apps/lending/eligibility.js +99 -0
  130. package/dist/esm/apps/lending/index.js +69 -0
  131. package/dist/esm/apps/lending/state-machines/index.js +5 -0
  132. package/dist/esm/apps/lending/state-machines/lending-zk-loan.js +430 -0
  133. package/dist/esm/apps/markets/index.js +11 -7
  134. package/dist/esm/apps/markets/state-machines/market-auction.js +117 -135
  135. package/dist/esm/apps/markets/state-machines/market-crowdfund.js +132 -142
  136. package/dist/esm/apps/markets/state-machines/market-group-buy.js +164 -185
  137. package/dist/esm/apps/markets/state-machines/market-prediction.js +227 -234
  138. package/dist/esm/apps/markets/state-machines/market-universal.js +66 -81
  139. package/dist/esm/apps/oracles/index.js +3 -3
  140. package/dist/esm/apps/privacy/index.js +56 -0
  141. package/dist/esm/apps/privacy/state-machines/index.js +6 -0
  142. package/dist/esm/apps/privacy/state-machines/mixer-ddhRing.js +282 -0
  143. package/dist/esm/apps/privacy/state-machines/note-pool.js +470 -0
  144. package/dist/esm/apps/staked-pool/assets.js +91 -0
  145. package/dist/esm/apps/staked-pool/base.js +493 -0
  146. package/dist/esm/apps/staked-pool/consumer.js +49 -0
  147. package/dist/esm/apps/staked-pool/index.js +60 -0
  148. package/dist/esm/apps/staked-pool/state-machines/index.js +6 -0
  149. package/dist/esm/apps/staked-pool/state-machines/staked-pool-base.js +46 -0
  150. package/dist/esm/apps/staked-pool/state-machines/staked-pool-oracle.js +127 -0
  151. package/dist/esm/generated/google/protobuf/struct.js +1 -1
  152. package/dist/esm/generated/google/protobuf/timestamp.js +1 -1
  153. package/dist/esm/generated/index.js +9 -5
  154. package/dist/esm/generated/openapi.js +5 -0
  155. package/dist/esm/generated/ottochain/apps/contracts/v1/contract.js +47 -47
  156. package/dist/esm/generated/ottochain/apps/corporate/v1/corporate.js +1 -1
  157. package/dist/esm/generated/ottochain/apps/governance/v1/governance.js +1 -1
  158. package/dist/esm/generated/ottochain/apps/identity/v1/attestation.js +1 -1
  159. package/dist/esm/generated/ottochain/apps/identity/v1/identity.js +115 -121
  160. package/dist/esm/generated/ottochain/apps/markets/v1/market.js +101 -101
  161. package/dist/esm/generated/ottochain/v1/common.js +1 -1
  162. package/dist/esm/generated/ottochain/v1/fiber.js +40 -2
  163. package/dist/esm/generated/ottochain/v1/messages.js +1 -1
  164. package/dist/esm/generated/ottochain/v1/records.js +1 -1
  165. package/dist/esm/index.js +28 -3
  166. package/dist/esm/openapi.js +1 -0
  167. package/dist/esm/ottochain/genesis-manifest.js +15 -9
  168. package/dist/esm/ottochain/index.js +2 -3
  169. package/dist/esm/ottochain/metagraph-client.js +81 -16
  170. package/dist/esm/ottochain/snapshot.js +6 -8
  171. package/dist/esm/ottochain/transaction.js +51 -3
  172. package/dist/esm/ottochain/types.js +15 -1
  173. package/dist/esm/privacy/index.js +6 -0
  174. package/dist/esm/privacy/sealed-bid.js +209 -0
  175. package/dist/esm/privacy/shield-app.js +127 -0
  176. package/dist/esm/schema/effects.js +145 -0
  177. package/dist/esm/schema/fiber-app.js +139 -8
  178. package/dist/esm/schema/guard-lint.js +337 -0
  179. package/dist/esm/schema/guards.js +228 -0
  180. package/dist/esm/signing.js +74 -0
  181. package/dist/esm/templates/asset-policy.js +182 -0
  182. package/dist/esm/templates/index.js +41 -0
  183. package/dist/esm/templates/machine.js +125 -0
  184. package/dist/esm/templates/migration.js +34 -0
  185. package/dist/esm/templates/state-shape.js +34 -0
  186. package/dist/esm/validation.js +1 -3
  187. package/dist/esm/verify.js +8 -2
  188. package/dist/esm/zk/commit.js +44 -0
  189. package/dist/esm/zk/guard.js +99 -0
  190. package/dist/esm/zk/index.js +19 -0
  191. package/dist/esm/zk/preimage.js +30 -0
  192. package/dist/esm/zk/prover.js +82 -0
  193. package/dist/esm/zk/registry.js +55 -0
  194. package/dist/esm/zk/types.js +25 -0
  195. package/dist/esm/zk/witness.js +15 -0
  196. package/dist/types/apps/compute/index.d.ts +176 -0
  197. package/dist/types/apps/compute/state-machines/compute-rule110.d.ts +157 -0
  198. package/dist/types/apps/compute/state-machines/index.d.ts +5 -0
  199. package/dist/types/apps/contracts/index.d.ts +81 -221
  200. package/dist/types/apps/contracts/state-machines/contract-agreement.d.ts +23 -98
  201. package/dist/types/apps/contracts/state-machines/contract-escrow.d.ts +49 -115
  202. package/dist/types/apps/contracts/state-machines/contract-universal.d.ts +4 -4
  203. package/dist/types/apps/corporate/index.d.ts +1176 -440
  204. package/dist/types/apps/corporate/state-machines/corp-board.d.ts +271 -135
  205. package/dist/types/apps/corporate/state-machines/corp-entity.d.ts +279 -74
  206. package/dist/types/apps/corporate/state-machines/corp-securities.d.ts +373 -149
  207. package/dist/types/apps/corporate/state-machines/corp-shareholders.d.ts +252 -80
  208. package/dist/types/apps/governance/index.d.ts +368 -470
  209. package/dist/types/apps/governance/state-machines/dao-multisig.d.ts +92 -107
  210. package/dist/types/apps/governance/state-machines/dao-reputation.d.ts +61 -85
  211. package/dist/types/apps/governance/state-machines/dao-single.d.ts +25 -47
  212. package/dist/types/apps/governance/state-machines/dao-token.d.ts +79 -68
  213. package/dist/types/apps/governance/state-machines/governance-simple.d.ts +95 -125
  214. package/dist/types/apps/governance/state-machines/governance-universal.d.ts +6 -6
  215. package/dist/types/apps/identity/constants.d.ts +33 -5
  216. package/dist/types/apps/identity/index.d.ts +415 -131
  217. package/dist/types/apps/identity/state-machines/identity-agent.d.ts +23 -23
  218. package/dist/types/apps/identity/state-machines/identity-oracle.d.ts +27 -98
  219. package/dist/types/apps/identity/state-machines/identity-registry.d.ts +379 -0
  220. package/dist/types/apps/identity/state-machines/identity-universal.d.ts +4 -4
  221. package/dist/types/apps/identity/state-machines/index.d.ts +1 -0
  222. package/dist/types/apps/index.d.ts +9 -5
  223. package/dist/types/apps/lending/assets.d.ts +258 -0
  224. package/dist/types/apps/lending/credit-scoring.d.ts +80 -0
  225. package/dist/types/apps/lending/eligibility.d.ts +83 -0
  226. package/dist/types/apps/lending/index.d.ts +556 -0
  227. package/dist/types/apps/lending/state-machines/index.d.ts +5 -0
  228. package/dist/types/apps/lending/state-machines/lending-zk-loan.d.ts +536 -0
  229. package/dist/types/apps/markets/index.d.ts +181 -276
  230. package/dist/types/apps/markets/state-machines/market-auction.d.ts +25 -49
  231. package/dist/types/apps/markets/state-machines/market-crowdfund.d.ts +35 -53
  232. package/dist/types/apps/markets/state-machines/market-group-buy.d.ts +35 -61
  233. package/dist/types/apps/markets/state-machines/market-prediction.d.ts +65 -86
  234. package/dist/types/apps/markets/state-machines/market-universal.d.ts +12 -12
  235. package/dist/types/apps/oracles/index.d.ts +1 -1
  236. package/dist/types/apps/privacy/index.d.ts +328 -0
  237. package/dist/types/apps/privacy/state-machines/index.d.ts +6 -0
  238. package/dist/types/apps/privacy/state-machines/mixer-ddhRing.d.ts +337 -0
  239. package/dist/types/apps/privacy/state-machines/note-pool.d.ts +196 -0
  240. package/dist/types/apps/staked-pool/assets.d.ts +53 -0
  241. package/dist/types/apps/staked-pool/base.d.ts +80 -0
  242. package/dist/types/apps/staked-pool/consumer.d.ts +29 -0
  243. package/dist/types/apps/staked-pool/index.d.ts +51 -0
  244. package/dist/types/apps/staked-pool/state-machines/index.d.ts +6 -0
  245. package/dist/types/apps/staked-pool/state-machines/staked-pool-base.d.ts +8 -0
  246. package/dist/types/apps/staked-pool/state-machines/staked-pool-oracle.d.ts +34 -0
  247. package/dist/types/generated/index.d.ts +5 -5
  248. package/dist/types/generated/openapi.d.ts +1270 -0
  249. package/dist/types/generated/ottochain/apps/contracts/v1/contract.d.ts +5 -5
  250. package/dist/types/generated/ottochain/apps/identity/v1/identity.d.ts +12 -12
  251. package/dist/types/generated/ottochain/apps/markets/v1/market.d.ts +11 -11
  252. package/dist/types/generated/ottochain/v1/fiber.d.ts +7 -0
  253. package/dist/types/index.d.ts +7 -3
  254. package/dist/types/openapi.d.ts +24 -0
  255. package/dist/types/ottochain/genesis-manifest.d.ts +11 -5
  256. package/dist/types/ottochain/index.d.ts +5 -6
  257. package/dist/types/ottochain/metagraph-client.d.ts +49 -7
  258. package/dist/types/ottochain/snapshot.d.ts +6 -6
  259. package/dist/types/ottochain/transaction.d.ts +41 -1
  260. package/dist/types/ottochain/types.d.ts +336 -31
  261. package/dist/types/privacy/index.d.ts +6 -0
  262. package/dist/types/privacy/sealed-bid.d.ts +348 -0
  263. package/dist/types/privacy/shield-app.d.ts +48 -0
  264. package/dist/types/schema/effects.d.ts +141 -0
  265. package/dist/types/schema/fiber-app.d.ts +282 -2
  266. package/dist/types/schema/guard-lint.d.ts +111 -0
  267. package/dist/types/schema/guards.d.ts +146 -0
  268. package/dist/types/signing.d.ts +45 -0
  269. package/dist/types/templates/asset-policy.d.ts +119 -0
  270. package/dist/types/templates/index.d.ts +26 -0
  271. package/dist/types/templates/machine.d.ts +139 -0
  272. package/dist/types/templates/migration.d.ts +34 -0
  273. package/dist/types/templates/state-shape.d.ts +30 -0
  274. package/dist/types/validation.d.ts +22 -217
  275. package/dist/types/verify.d.ts +4 -0
  276. package/dist/types/zk/commit.d.ts +28 -0
  277. package/dist/types/zk/guard.d.ts +46 -0
  278. package/dist/types/zk/index.d.ts +19 -0
  279. package/dist/types/zk/preimage.d.ts +10 -0
  280. package/dist/types/zk/prover.d.ts +34 -0
  281. package/dist/types/zk/registry.d.ts +34 -0
  282. package/dist/types/zk/types.d.ts +33 -0
  283. package/dist/types/zk/witness.d.ts +40 -0
  284. package/package.json +47 -15
  285. package/dist/cjs/ottochain/normalize.js +0 -186
  286. package/dist/esm/ottochain/normalize.js +0 -179
  287. package/dist/types/ottochain/normalize.d.ts +0 -79
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Asset-policy preset builders (§1.1 of the SDK std-lib/templates handoff).
3
+ *
4
+ * Each builder is a PURE function that emits the exact {@link CreateAssetPolicy} wire body the chain
5
+ * re-derives — no `Date.now()`/`Math.random()`/`crypto.randomUUID()`; fixed inputs → fixed output. The
6
+ * on-chain policy `schemaHash` and `logicHash` are both `RegistryShape.AssetPolicy.computeDigest` over
7
+ * `behavior`/`supply`/`morphisms`/`stateShape` (no JSON-Logic body), so these presets are pure data.
8
+ *
9
+ * Hard invariants (CLAUDE.md rule #1 — the signed canonical is frozen):
10
+ * - `behavior` is ALWAYS summed from {@link TOKEN_BEHAVIOR_BITS} (T=16, S=8, C=4, E=2, G=1), never a
11
+ * magic literal int.
12
+ * - `morphisms` is REQUIRED on the wire (presence required, emptiness meaningful) — emit `{}` when empty,
13
+ * NEVER omit it.
14
+ * - `supply` is REQUIRED — emit `{}` when there is no supply authority, never omit it.
15
+ * - Absent optionals are OMITTED, never emitted as `null`/`undefined` (the chain signs over
16
+ * `JCS(dropNulls(payload))`).
17
+ *
18
+ * @see src/ottochain/types.ts — CreateAssetPolicy, SupplyPolicy, MorphismSpec, MorphismKind, TOKEN_BEHAVIOR_BITS
19
+ */
20
+ import { TOKEN_BEHAVIOR_BITS, } from '../ottochain/types.js';
21
+ /** The default mint/burn guard the riverdale presets use: an always-true predicate `{"==":[1,1]}`. */
22
+ const alwaysTrue = () => ({ '==': [1, 1] });
23
+ /** A `PUBLIC`-visibility morphism spec (the riverdale preset visibility). Fresh object per call. */
24
+ const publicMorphism = () => ({ visibility: 'PUBLIC' });
25
+ /** Sum behavior bits from their NAMES — never a magic literal int (CLAUDE.md #1). */
26
+ export function sumBehavior(bits) {
27
+ return bits.reduce((acc, name) => acc + TOKEN_BEHAVIOR_BITS[name], 0);
28
+ }
29
+ /**
30
+ * Derive the default `stateShape.typeName`: strip a trailing `.asset` label, PascalCase the remainder
31
+ * (splitting on `.`/`-`/`_`/space), then append `State`.
32
+ * `rvd.asset` → `RvdState`, `goods.asset` → `GoodsState`, `capped.asset` → `CappedState`.
33
+ */
34
+ export function defaultStateTypeName(name) {
35
+ const ASSET_SUFFIX = '.asset';
36
+ const base = name.endsWith(ASSET_SUFFIX) ? name.slice(0, -ASSET_SUFFIX.length) : name;
37
+ const pascal = base
38
+ .split(/[.\-_\s]+/)
39
+ .filter(Boolean)
40
+ .map((seg) => seg.charAt(0).toUpperCase() + seg.slice(1))
41
+ .join('');
42
+ return `${pascal}State`;
43
+ }
44
+ /** Build a {@link SupplyPolicy}, OMITTING every absent field (no `undefined`/`null` keys). */
45
+ function buildSupply(opts) {
46
+ const supply = {};
47
+ if (opts.maxSupply !== undefined)
48
+ supply.maxSupply = opts.maxSupply;
49
+ if (opts.mintPolicy !== undefined)
50
+ supply.mintPolicy = opts.mintPolicy;
51
+ if (opts.burnPolicy !== undefined)
52
+ supply.burnPolicy = opts.burnPolicy;
53
+ if (opts.decimals !== undefined)
54
+ supply.decimals = opts.decimals;
55
+ return supply;
56
+ }
57
+ /** Assemble the final {@link CreateAssetPolicy}, attaching `metadata` only when present. */
58
+ function assemble(args) {
59
+ const stateShape = {
60
+ typeName: args.stateTypeName ?? defaultStateTypeName(args.name),
61
+ fields: [],
62
+ };
63
+ const policy = {
64
+ name: args.name,
65
+ version: args.version,
66
+ behavior: args.behavior,
67
+ supply: args.supply,
68
+ morphisms: args.morphisms,
69
+ stateShape,
70
+ };
71
+ if (args.metadata !== undefined)
72
+ policy.metadata = args.metadata;
73
+ return policy;
74
+ }
75
+ /**
76
+ * Fungible currency. Behavior `T|S|C = 28` (transferable + splittable + combinable). Standard morphism
77
+ * set TRANSFER + FRACTIONALIZE + STAKE (`stakeable`, default true) + BURN (when `burnable`), all PUBLIC.
78
+ *
79
+ * Reproduces `riverdale-economy/rvd-policy.json` exactly via
80
+ * `fungiblePolicy({ name: 'rvd.asset', version: '1.0.0', mintable: true, burnable: true })`.
81
+ */
82
+ export function fungiblePolicy(p) {
83
+ const behavior = sumBehavior(['transferable', 'splittable', 'combinable']); // 28
84
+ const morphisms = {
85
+ TRANSFER: publicMorphism(),
86
+ FRACTIONALIZE: publicMorphism(),
87
+ };
88
+ if (p.stakeable !== false)
89
+ morphisms.STAKE = publicMorphism();
90
+ if (p.burnable)
91
+ morphisms.BURN = publicMorphism();
92
+ let mintPolicy;
93
+ if (p.mintGuard !== undefined)
94
+ mintPolicy = p.mintGuard;
95
+ else if (p.mintable)
96
+ mintPolicy = alwaysTrue();
97
+ const supply = buildSupply({
98
+ maxSupply: p.maxSupply,
99
+ mintPolicy,
100
+ burnPolicy: p.burnable ? alwaysTrue() : undefined,
101
+ decimals: p.decimals,
102
+ });
103
+ return assemble({
104
+ name: p.name,
105
+ version: p.version,
106
+ behavior,
107
+ supply,
108
+ morphisms,
109
+ stateTypeName: p.stateTypeName,
110
+ metadata: p.metadata,
111
+ });
112
+ }
113
+ /**
114
+ * Non-fungible token. Behavior `T = 16`, or `T|C = 20` with `combinable: true` (the riverdale
115
+ * `goods.asset`); `transferable: false` drops the T bit (a bound collectible). `morphisms` is `{}` by
116
+ * default (emitted explicitly, never omitted). Mintable by default ⇒ `supply.mintPolicy = {"==":[1,1]}`.
117
+ *
118
+ * Reproduces `riverdale-economy/goods-policy.json` exactly via
119
+ * `nftPolicy({ name: 'goods.asset', version: '1.0.0', combinable: true })`.
120
+ */
121
+ export function nftPolicy(p) {
122
+ const bits = [];
123
+ if (p.transferable !== false)
124
+ bits.push('transferable');
125
+ if (p.combinable)
126
+ bits.push('combinable');
127
+ const behavior = sumBehavior(bits);
128
+ let mintPolicy;
129
+ if (p.mintGuard !== undefined)
130
+ mintPolicy = p.mintGuard;
131
+ else if (p.mintable !== false)
132
+ mintPolicy = alwaysTrue();
133
+ const supply = buildSupply({ maxSupply: p.maxSupply, mintPolicy });
134
+ return assemble({
135
+ name: p.name,
136
+ version: p.version,
137
+ behavior,
138
+ supply,
139
+ morphisms: {},
140
+ stateTypeName: p.stateTypeName,
141
+ metadata: p.metadata,
142
+ });
143
+ }
144
+ /**
145
+ * Soulbound token: non-transferable, governable only (`G = 1`; `+E = 3` when `expirable`). No TRANSFER
146
+ * morphism (`morphisms: {}`); minting is closed after issue (empty `supply`).
147
+ */
148
+ export function soulboundPolicy(p) {
149
+ const bits = ['governable'];
150
+ if (p.expirable)
151
+ bits.push('expirable');
152
+ const behavior = sumBehavior(bits);
153
+ return assemble({
154
+ name: p.name,
155
+ version: p.version,
156
+ behavior,
157
+ supply: buildSupply({}), // mint closed after issue
158
+ morphisms: {}, // non-transferable: no TRANSFER morphism
159
+ stateTypeName: p.stateTypeName,
160
+ metadata: p.metadata,
161
+ });
162
+ }
163
+ /**
164
+ * Escape hatch: declare `behavior` by NAME (summed from {@link TOKEN_BEHAVIOR_BITS}, never a magic int)
165
+ * and pass `supply` + `morphisms` through raw. Use when no preset fits.
166
+ *
167
+ * Reproduces `riverdale-economy/capped-policy.json` exactly via
168
+ * `customPolicy({ name: 'capped.asset', version: '1.0.0',
169
+ * behavior: ['transferable', 'splittable', 'combinable'],
170
+ * supply: { maxSupply: 100, mintPolicy: { '==': [1, 1] } }, morphisms: {} })`.
171
+ */
172
+ export function customPolicy(p) {
173
+ return assemble({
174
+ name: p.name,
175
+ version: p.version,
176
+ behavior: sumBehavior(p.behavior),
177
+ supply: p.supply,
178
+ morphisms: p.morphisms,
179
+ stateTypeName: p.stateTypeName,
180
+ metadata: p.metadata,
181
+ });
182
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * `@ottochain/sdk/templates` — the fiber & asset authoring template catalog.
3
+ *
4
+ * Typed builders that emit the EXACT canonical wire shapes app authors otherwise hand-roll as raw
5
+ * JSON-Logic (asset policies, versioned machines, effect directives, migrations, seeded state). Every
6
+ * builder is additive and pure — it returns the same wire shape the chain re-derives, so a template
7
+ * output signs to byte-identical `JCS(dropNulls(payload))` (CLAUDE.md rule #1). Hand-rolled JSON keeps
8
+ * working; these just make the recurring shapes typed, named, and mistake-resistant.
9
+ *
10
+ * Origin: the fiber-ergonomics program (ottochain `docs/proposals/fiber-ergonomics/`), Phase A — the
11
+ * `riverdale-economy` e2e is the migration proof (each preset reproduces its checked-in golden JSON).
12
+ *
13
+ * @example
14
+ * import {
15
+ * fungiblePolicy, machine, transition, effect, guard,
16
+ * transferAsset, triggers, spawn, seedState, seedFields,
17
+ * } from '@ottochain/sdk/templates';
18
+ */
19
+ // ─── 1.1 Asset-policy presets ────────────────────────────────────────────────
20
+ // fungiblePolicy / nftPolicy / soulboundPolicy / customPolicy (+ sumBehavior, defaultStateTypeName,
21
+ // BehaviorBitName) — emit a `CreateAssetPolicy`; behavior is summed from TOKEN_BEHAVIOR_BITS, never a literal.
22
+ export * from './asset-policy.js';
23
+ // ─── 1.2 Versioned-machine skeleton + transition()/effect() ──────────────────
24
+ // machine / transition / effect (+ TransitionSpec, MachineSpec, Machine, …). The skeleton owns ONE
25
+ // canonical wire definition, so publish + create cannot drift (verified binding, F9).
26
+ export * from './machine.js';
27
+ // ─── 1.4 Migration helpers ───────────────────────────────────────────────────
28
+ // seedFields / migration — hides the bare-state-root ({"var":""}) foot-gun.
29
+ export * from './migration.js';
30
+ // ─── 1.5 State-shape-with-defaults ───────────────────────────────────────────
31
+ // seedState / declaredFields / StateShape — auto-seed accumulators so no field reads null (F5).
32
+ export * from './state-shape.js';
33
+ // ─── 1.3 Effect-directive builders ───────────────────────────────────────────
34
+ // transferAsset / triggers / spawn / emit / toFiber / toWallet / addDependency / setDependencyActive /
35
+ // RESERVED_EFFECT_KEYS — a typo'd directive is a TS error, not a silent state field (F4).
36
+ export * from '../schema/effects.js';
37
+ // ─── Authorization-guard builders (namespace) ────────────────────────────────
38
+ // `guard.signerIsParty('state.borrower')` reads as intent at the call site.
39
+ export * as guard from '../schema/guards.js';
40
+ // ─── Existing machine/policy primitives re-surfaced for one-import authoring ──
41
+ export { defineFiberApp, toProtoDefinition, constrained, unconstrained, immutable, projectFiberPolicy, } from '../schema/fiber-app.js';
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Versioned-machine skeleton + `transition()` / `effect()` composition (handoff §1.2).
3
+ *
4
+ * Two moves:
5
+ * 1. `transition()` / `effect()` build the wire `Transition` + its flat effect map from typed
6
+ * fragments instead of a hand-rolled JSON blob.
7
+ * 2. `machine()` is a versioned-package SKELETON that OWNS exactly ONE canonical wire definition
8
+ * (`toProtoDefinition(app)`, computed once) and hands the SAME object to BOTH `publishVersion()`
9
+ * and `create()`. That is the verified-binding invariant (F9): because publish and create cannot
10
+ * drift, the chain's `definition.computeDigest` equals the registered `logicHash` and the bind
11
+ * admits the fiber. The skeleton REUSES `toProtoDefinition` (the one canonicalization path) — it
12
+ * never re-derives the wire shape.
13
+ *
14
+ * Guardrail (CLAUDE.md #1): every builder here is a pure function emitting the exact wire shape the
15
+ * chain re-derives. Absent optionals (`metadata`/`participants`/`parentFiberId`/`migration`) are
16
+ * OMITTED, never `null`; required-no-default fields (`strict` on publish, `dependencies: []` per
17
+ * transition) are ALWAYS present.
18
+ */
19
+ import { toProtoDefinition, } from '../schema/fiber-app.js';
20
+ /**
21
+ * Build a wire {@link Transition} from a typed spec.
22
+ *
23
+ * - `on` → `eventName` (the wire key);
24
+ * - `guard` defaults to `{ "==": [1, 1] }`;
25
+ * - `dependencies` defaults to `[]` and is ALWAYS present;
26
+ * - `effect` is passed through (build it with {@link effect}).
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * transition({
31
+ * from: 'debt_current', to: 'debt_current', on: 'buy',
32
+ * effect: effect(
33
+ * { status: 'debt_current', purchaseCount: { '+': [{ var: 'state.purchaseCount' }, 1] } },
34
+ * triggers([...]), transferAsset([...]),
35
+ * ),
36
+ * });
37
+ * ```
38
+ */
39
+ export function transition(t) {
40
+ return {
41
+ from: t.from,
42
+ to: t.to,
43
+ eventName: t.on,
44
+ guard: t.guard ?? { '==': [1, 1] },
45
+ effect: t.effect,
46
+ dependencies: t.dependencies ?? [],
47
+ };
48
+ }
49
+ // =============================================================================
50
+ // effect()
51
+ // =============================================================================
52
+ /**
53
+ * Compose an effect into ONE FLAT map: the state-update fields PLUS any `_`-directive fragments,
54
+ * all in a single object (`Object.assign({}, stateUpdate, ...directives)`).
55
+ *
56
+ * Riverdale effects are flat maps that mix `_`-reserved directives (`_triggers`, `_transferAsset`,
57
+ * `_spawn`, …) with plain state-update fields — they are NOT `merge`-wrapped. Each directive fragment
58
+ * is a `Record` (e.g. the output of `transferAsset(...)` / `triggers(...)`), spread in here. Later
59
+ * fragments override earlier keys (`Object.assign` semantics).
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * effect(
64
+ * { status: 'debt_current', purchaseCount: { '+': [{ var: 'state.purchaseCount' }, 1] } },
65
+ * { _triggers: [...] },
66
+ * transferAsset([{ assetId: { var: 'event.payAssetId' }, recipient: toFiber({ var: 'event.retailerId' }) }]),
67
+ * );
68
+ * // => { status, purchaseCount, _triggers, _transferAsset }
69
+ * ```
70
+ */
71
+ export function effect(stateUpdate, ...directives) {
72
+ return Object.assign({}, stateUpdate, ...directives);
73
+ }
74
+ /** A pinned-Exact `SchemaRef` for `name@version`. Wire: `{ name, version: { Exact: { version } } }`. */
75
+ function exactRef(name, version) {
76
+ return { name, version: { Exact: { version } } };
77
+ }
78
+ /**
79
+ * Build a versioned-machine skeleton that binds publish + create to ONE canonical definition.
80
+ *
81
+ * The definition is projected ONCE here (`toProtoDefinition(spec.app)`) and the SAME reference is handed
82
+ * to `wireDefinition()`, `publishVersion().definition`, `create().definition`, and
83
+ * `upgradeFrom().newDefinition` — so they cannot drift and the chain's verified binding admits the fiber
84
+ * (F9). REUSES `toProtoDefinition` (the single canonicalization path); never re-derives the wire shape.
85
+ */
86
+ export function machine(spec) {
87
+ // Project the canonical wire definition ONCE; every consumer reuses this exact reference.
88
+ const wireDef = toProtoDefinition(spec.app);
89
+ return {
90
+ wireDefinition() {
91
+ return wireDef;
92
+ },
93
+ publishVersion(o) {
94
+ return {
95
+ name: spec.name,
96
+ version: spec.version,
97
+ // REQUIRED-no-default on the wire; '' is valid empty base64 when no descriptor set is bound.
98
+ schemaB64: o?.schemaB64 ?? '',
99
+ machineShape: spec.schemaShape,
100
+ definition: wireDef, // SAME reference as wireDefinition()/create()
101
+ strict: o?.strict ?? false, // REQUIRED — never omitted
102
+ ...(o?.metadata ? { metadata: o.metadata } : {}), // OMIT when absent
103
+ };
104
+ },
105
+ create(o) {
106
+ return {
107
+ fiberId: o.fiberId,
108
+ definition: wireDef, // SAME reference as wireDefinition()/publishVersion()
109
+ initialData: o.initialData,
110
+ schemaRef: exactRef(spec.name, spec.version), // verified binding: name@version
111
+ ...(o.participants ? { participants: o.participants } : {}), // OMIT when absent
112
+ ...(o.parentFiberId ? { parentFiberId: o.parentFiberId } : {}), // OMIT when absent
113
+ };
114
+ },
115
+ upgradeFrom(o) {
116
+ return {
117
+ fiberId: o.fiberId,
118
+ targetRef: exactRef(spec.name, spec.version),
119
+ newDefinition: wireDef, // SAME reference — re-pin hashes to the same logicHash
120
+ targetSequenceNumber: o.targetSequenceNumber,
121
+ ...(o.migration !== undefined ? { migration: o.migration } : {}), // OMIT (Option) when absent
122
+ };
123
+ },
124
+ };
125
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Migration builders — the `UpgradeFiber.migration` JSON-Logic transform applied on a version upgrade.
3
+ *
4
+ * THE FOOT-GUN (F9): a migration's evaluation root is the BARE prior state, not `state.x`.
5
+ * The chain evaluates the migration expression against `sm.stateData` directly
6
+ * (`MeteredEvaluator.eval(expr, sm.stateData, Migration)`, FiberEngine.scala:300), so:
7
+ * - `{ var: '' }` => the WHOLE prior state object
8
+ * - `{ var: 'loyaltyPoints' }` => the top-level `loyaltyPoints` field
9
+ * This is UNLIKE an effect, where the root is the transition context and you read `{ var: 'state.x' }`.
10
+ * Writing `state.loyaltyPoints` in a migration silently reads `undefined`. These helpers hide that
11
+ * asymmetry by pinning the bare-state root for you.
12
+ *
13
+ * `migration` is `Option` on `UpgradeFiber` — when there is no transform, OMIT the key (never send `null`).
14
+ */
15
+ /**
16
+ * Seed / overwrite top-level state fields on a version upgrade.
17
+ * Emits `{ merge: [{ var: '' }, fields] }` — merges `fields` onto the bare prior state, so existing
18
+ * fields are preserved and the named fields are added or overwritten.
19
+ *
20
+ * The `{ var: '' }` root is the BARE prior state (NOT `state.x` like an effect) — that asymmetry is
21
+ * exactly the F9 foot-gun this helper hides.
22
+ *
23
+ * @example seedFields({ loyaltyPoints: 0 }) // => { merge: [{ var: '' }, { loyaltyPoints: 0 }] }
24
+ */
25
+ export const seedFields = (fields) => ({
26
+ merge: [{ var: '' }, fields],
27
+ });
28
+ /**
29
+ * General migration transform with the bare-state root made explicit. `build` receives `{ var: '' }`
30
+ * (the whole prior state) and returns the migration JSON-Logic.
31
+ *
32
+ * @example migration((s) => ({ merge: [s, { x: 1 }] })) // => { merge: [{ var: '' }, { x: 1 }] }
33
+ */
34
+ export const migration = (build) => build({ var: '' });
@@ -0,0 +1,34 @@
1
+ /**
2
+ * State-shape-with-defaults — the SDK half of Proposal 01's "declared state shape" (removes F5).
3
+ *
4
+ * A declared shape carries a `default` (and optional `type`) per field. It seeds `initialData` so every
5
+ * accumulator starts at a concrete value — a counter read via `{ '+': [{ var: 'state.x' }, ...] }` never
6
+ * resolves `null` because `x` was never written. It also yields the set of declared field names for
7
+ * Proposal 01's var-path checks.
8
+ */
9
+ /**
10
+ * Build `initialData` by overlaying `overrides` onto each field's declared `default`.
11
+ * Every declared field gets a concrete value, so no field reads `null` at runtime. An override for a
12
+ * field replaces that field's default; overrides for undeclared fields are ignored (only declared fields
13
+ * are emitted). Determinism: pure function of its inputs, stable key order (declared order).
14
+ *
15
+ * @example seedState({ fields: { purchaseCount: { default: 0 }, status: { default: 'ACTIVE' } } })
16
+ * // => { purchaseCount: 0, status: 'ACTIVE' }
17
+ */
18
+ export function seedState(shape, overrides) {
19
+ const seeded = {};
20
+ for (const [name, field] of Object.entries(shape.fields)) {
21
+ seeded[name] =
22
+ overrides !== undefined && Object.prototype.hasOwnProperty.call(overrides, name)
23
+ ? overrides[name]
24
+ : field.default;
25
+ }
26
+ return seeded;
27
+ }
28
+ /**
29
+ * The set of declared state-field names — the fields the effects may read.
30
+ * Feeds Proposal 01's var-path checks (a `{ var: 'state.foo' }` whose `foo` is not declared is suspect).
31
+ */
32
+ export function declaredFields(shape) {
33
+ return new Set(Object.keys(shape.fields));
34
+ }
@@ -23,9 +23,7 @@ const hexString = (length) => {
23
23
  /**
24
24
  * Schema for a DAG address
25
25
  */
26
- export const DagAddressSchema = z
27
- .string()
28
- .regex(/^DAG[0-9][a-zA-Z0-9]{36}$/, 'Must be a valid DAG address');
26
+ export const DagAddressSchema = z.string().regex(/^DAG[0-9][a-zA-Z0-9]{36}$/, 'Must be a valid DAG address');
29
27
  // ============================================================================
30
28
  // Core Type Schemas
31
29
  // ============================================================================
@@ -4,10 +4,16 @@
4
4
  * The package embeds `mode: "standard"|"dataUpdate"` in signed objects and
5
5
  * its verify() ignores the `isDataUpdate` parameter when `mode` is present.
6
6
  * This wrapper strips `mode` so callers' explicit `isDataUpdate` always wins.
7
+ *
8
+ * In dataUpdate mode, verification happens over the null-dropped canonical
9
+ * bytes (drop null object fields, preserve array nulls, then RFC 8785) —
10
+ * mirroring the SDK's dataUpdate signers and metakit's content-hash rule.
7
11
  */
8
12
  import { verify as _verify } from '@constellation-network/metagraph-sdk';
13
+ import { dropNulls } from './ottochain/drop-nulls.js';
9
14
  export function verify(signed, isDataUpdate) {
10
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
15
  const { mode, ...rest } = signed;
12
- return _verify(rest, isDataUpdate);
16
+ const isDU = isDataUpdate ?? mode === 'dataUpdate';
17
+ const target = isDU ? { ...rest, value: dropNulls(signed.value) } : rest;
18
+ return _verify(target, isDU);
13
19
  }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Poseidon commitments over BN254 Fr, computed through the JLVM `poseidon` opcode — the SAME
3
+ * byte-for-byte implementation the chain and the Rust/Scala circuits use. We deliberately route
4
+ * through `jsonLogic.apply` rather than reimplementing Poseidon: the opcode locks the field order,
5
+ * the circomlib round constants, and the `0x` 32-byte big-endian Fr encoding every crypto opcode
6
+ * expects (hard acceptance vector: `poseidon([1,2]) == 0x115cc0f5…189a`).
7
+ *
8
+ * (The standalone `poseidonHash`/`encodeFr` functions exist inside `-jlvm` but are NOT part of its
9
+ * public export surface — only the VM opcodes are — so the opcode path is also the only stable one.)
10
+ */
11
+ import { jsonLogic } from '@constellation-network/metagraph-sdk-jlvm';
12
+ import { randomBytes } from 'node:crypto';
13
+ /**
14
+ * BN254 (alt_bn128) scalar field modulus R. A public field constant (not a secret / not Poseidon
15
+ * itself); the `poseidon` opcode re-validates canonicity, so this only drives reduction + sampling.
16
+ */
17
+ export const R = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
18
+ /** Reduce an arbitrary bigint into a canonical BN254 Fr element `[0, R)`. */
19
+ export const toFr = (x) => ((x % R) + R) % R;
20
+ /** Encode a field element as the lowercase `0x` 32-byte big-endian hex the crypto opcodes expect. */
21
+ export const frToHex = (x) => `0x${toFr(x).toString(16).padStart(64, '0')}`;
22
+ const bytesToBig = (b) => b.reduce((a, x) => (a << 8n) | BigInt(x), 0n);
23
+ /** A fresh random salt as a canonical Fr (rejection-sampled to avoid modulo bias near R). */
24
+ export function randomSalt() {
25
+ for (;;) {
26
+ const v = bytesToBig(randomBytes(32));
27
+ if (v < R)
28
+ return v;
29
+ }
30
+ }
31
+ /** `cm = Poseidon([...fields, salt])` via the VM's own opcode. Returns the `0x` 32-byte commitment. */
32
+ export function poseidonCommitN(fields, saltFr) {
33
+ const inputs = [...fields.map(frToHex), frToHex(saltFr)];
34
+ // The bundled poseidon supports width t ≤ 5 (≤ 4 inputs incl. the salt); the opcode throws past it.
35
+ return jsonLogic.apply({ poseidon: inputs }, {});
36
+ }
37
+ /** `cm = Poseidon([fieldFr, saltFr])` — the commitment for one value field (a bid, a score). */
38
+ export function poseidonCommit(fieldFr, saltFr) {
39
+ return poseidonCommitN([fieldFr], saltFr);
40
+ }
41
+ /** Commit `fields` under a fresh (or supplied) salt; returns the commitment + opening. */
42
+ export function openCommitment(fields, salt = randomSalt()) {
43
+ return { cm: poseidonCommitN(fields, salt), fields, salt };
44
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * The semi-private guard: the JLVM predicate a policy embeds, and its client-side mirror.
3
+ *
4
+ * The guard pins THREE facts about the witness the holder carries, using only opcodes metakit
5
+ * already gas-meters — no new `jlvm_pv_decode`, the public-values blob is parsed with native
6
+ * `substr`/`cat`:
7
+ * 1. `groth16_verify[vkey, witness.publicValues, witness.proof]` — the SP1 proof is valid.
8
+ * 2. the `exprHash` word of the public values == the policy's `logicHash` — the proof ran the
9
+ * INTENDED rule (the pinned, legible predicate).
10
+ * 3. the `outputHash` word == `keccak256("true")` — that rule evaluated to TRUE on the hidden data.
11
+ *
12
+ * `publicValues` rides as a `0x` + 256-hex-char string (`0x` | exprHash | dataHash | outputHash | ok),
13
+ * so word w begins at char `2 + 64*w` and is 64 chars wide. `cat("0x", substr(pv, off, 64))` lifts a
14
+ * word back to a `0x`-hex value comparable with a `bytes32` literal. These offsets are the on-chain
15
+ * guard's contract; the Scala policy mirrors this exact shape.
16
+ */
17
+ import { jsonLogic } from '@constellation-network/metagraph-sdk-jlvm';
18
+ import { KECCAK_TRUE } from './preimage';
19
+ import { decodeJlvmPublicValues } from './types';
20
+ const HEX_PREFIX = 2; // "0x"
21
+ const WORD_HEX = 64; // 32-byte ABI word as hex chars
22
+ /** Char offset of public-values word `w` (0=exprHash, 1=dataHash, 2=outputHash, 3=ok) in the `0x` string. */
23
+ export const wordOffset = (w) => HEX_PREFIX + WORD_HEX * w;
24
+ /** Lift public-values word `w` back to a `0x`-hex value (`cat("0x", substr(pv, off, 64))`). */
25
+ const pvWord = (w) => ({
26
+ cat: ['0x', { substr: [{ var: 'witness.publicValues' }, wordOffset(w), WORD_HEX] }],
27
+ });
28
+ /**
29
+ * Build the JLVM guard a `Governed` morphism / mint policy embeds. `vkey` is the zk-jlvm program
30
+ * verification key (a `bytes32` `0x`-hex literal); `logicHash` is the pinned rule's hash (from
31
+ * {@link ExprRegistry.logicHashOf}). Returns plain JSON — feed it into an ASSET policy's guard/mintPolicy
32
+ * (the asset combiner injects the reserved `witness`). In a fiber TRANSITION there is no `witness` key —
33
+ * the proof rides in the event payload, so read `event.witness.*` instead.
34
+ *
35
+ * REPLAY (audit zk-guards): the proven public values commit only {exprHash, dataHash, outputHash, ok} —
36
+ * nothing situational (no fiber / asset / `$ordinal` / nonce). A valid `{publicValues, proof}` is therefore
37
+ * a reusable bearer token, replayable across any action pinning the same vkey + logicHash. Bind the action
38
+ * INSIDE the proven rule (pin a spender/asset/subject, as `reputationCreditRule` pins `subject`) and/or gate
39
+ * with a one-time nonce ledger until the public values carry action context.
40
+ */
41
+ export function semiPrivateGuard(vkey, logicHash, opts = {}) {
42
+ const { requireTrue = true } = opts;
43
+ const clauses = [
44
+ { groth16_verify: [vkey, { var: 'witness.publicValues' }, { var: 'witness.proof' }] },
45
+ { '==': [pvWord(0), logicHash] },
46
+ ];
47
+ if (requireTrue) {
48
+ clauses.push({ '==': [pvWord(2), KECCAK_TRUE] });
49
+ // ok bit: the final hex pair of word 3 (offset 256) must be "01" — the JLVM run did not error.
50
+ clauses.push({ '==': [{ substr: [{ var: 'witness.publicValues' }, wordOffset(3) + 62, 2] }, '01'] });
51
+ }
52
+ return { and: clauses };
53
+ }
54
+ /**
55
+ * Locally run the SAME `groth16_verify` pairing the chain runs (the verifier opcode, not the prover).
56
+ * Returns `false` — never throws — on an invalid/garbage bundle, mirroring the guard's graceful deny.
57
+ */
58
+ export function verifyGroth16Bundle(bundle) {
59
+ try {
60
+ return jsonLogic.apply({ groth16_verify: [bundle.vkey, bundle.publicValues, bundle.proof] }, {}) === true;
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ }
66
+ /**
67
+ * Check the binding words WITHOUT the pairing: `exprHash == expectedLogicHash`, `ok` set, and
68
+ * (when `requireTrue`) `outputHash == keccak256("true")`. Pair with {@link verifyGroth16Bundle} for
69
+ * the full client-side mirror of the on-chain guard (see {@link verifySemiPrivate}).
70
+ */
71
+ export function checkSemiPrivateBinding(bundle, expectedLogicHash, opts = {}) {
72
+ const { requireTrue = true } = opts;
73
+ const decoded = decodeJlvmPublicValues(bundle.publicValues);
74
+ const eq = (a, b) => a.toLowerCase() === b.toLowerCase();
75
+ const reasons = [];
76
+ if (!eq(decoded.exprHash, expectedLogicHash))
77
+ reasons.push(`exprHash ${decoded.exprHash} != expected logicHash ${expectedLogicHash}`);
78
+ if (!decoded.ok)
79
+ reasons.push('ok bit is false (guest evaluation errored)');
80
+ if (requireTrue && !eq(decoded.outputHash, KECCAK_TRUE))
81
+ reasons.push(`outputHash ${decoded.outputHash} != keccak256("true") ${KECCAK_TRUE}`);
82
+ return { ok: reasons.length === 0, reasons, decoded };
83
+ }
84
+ /**
85
+ * The full client-side mirror of the on-chain guard: the proof verifies AND its words bind to the
86
+ * pinned rule (and, by default, to a `true` result). Run this before submitting to fail fast instead
87
+ * of paying for an on-chain rejection.
88
+ */
89
+ export function verifySemiPrivate(bundle, expectedLogicHash, opts = {}) {
90
+ const binding = checkSemiPrivateBinding(bundle, expectedLogicHash, opts);
91
+ if (!verifyGroth16Bundle(bundle)) {
92
+ return {
93
+ ok: false,
94
+ reasons: ['groth16_verify failed (invalid proof for this vkey/publicValues)', ...binding.reasons],
95
+ decoded: binding.decoded,
96
+ };
97
+ }
98
+ return binding;
99
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `@ottochain/sdk/zk` — the client-side semi-private tier.
3
+ *
4
+ * Public-by-default, private-by-opt-in: a holder proves (off-chain, via SP1 zk-jlvm) that some
5
+ * PRIVATE data satisfies a PUBLIC, pinned JLVM rule, and carries only `{publicValues, proof}` on a
6
+ * signed op. A policy's {@link semiPrivateGuard} re-verifies the proof and binds it to the pinned
7
+ * rule on-chain — the value stays hidden, the predicate stays legible.
8
+ *
9
+ * The ONE canonical invariant tying it together: the prover is fed `canonicalForSigning(expr)` /
10
+ * `canonicalForSigning(data)`, so a proof's `exprHash` word equals {@link exprHash}`(rule)` equals a
11
+ * policy's `logicHash` — see {@link proverPreimage} and docs/design/client-side-private-data.md §3.0.
12
+ */
13
+ export { canonicalForSigning, proverPreimage, exprHash, dataHash, KECCAK_TRUE } from './preimage';
14
+ export { R, toFr, frToHex, randomSalt, poseidonCommit, poseidonCommitN, openCommitment, } from './commit';
15
+ export { decodeJlvmPublicValues } from './types';
16
+ export { groth16Witness, pmtWitness } from './witness';
17
+ export { ExprRegistry, boundRule, atLeast, atMost } from './registry';
18
+ export { wordOffset, semiPrivateGuard, verifyGroth16Bundle, checkSemiPrivateBinding, verifySemiPrivate, } from './guard';
19
+ export { SubprocessProver, parseGroth16Stdout, } from './prover';
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The ONE canonical path for the semi-private tier.
3
+ *
4
+ * metakit-sdk 1.8.x `canonicalize` = `serializeJcs(dropNullFields)` — it drops null object-fields
5
+ * before RFC 8785 (server-aligned), so it is the EXACT byte string the chain signs over. Feeding
6
+ * the zk-jlvm prover this same string makes the proof's keccak preimage equal the signature
7
+ * preimage — the determinism binding (see docs/design/client-side-private-data.md §3.0).
8
+ *
9
+ * Note the two hashes differ by design: signing is sha256 over the canonical bytes (+ Constellation
10
+ * prefix), the zk-jlvm guest is keccak256 over the canonical bytes — they bind the SAME canonical
11
+ * string, which is the load-bearing invariant.
12
+ */
13
+ import { canonicalize } from '@constellation-network/metagraph-sdk';
14
+ import { keccak_256 } from '@noble/hashes/sha3.js';
15
+ const utf8 = (s) => new TextEncoder().encode(s);
16
+ const toHex = (b) => Array.from(b, (x) => x.toString(16).padStart(2, '0')).join('');
17
+ /** The single canonical string both the signer and the prover-feed share (JCS ∘ dropNullFields). */
18
+ export function canonicalForSigning(x) {
19
+ return canonicalize(x);
20
+ }
21
+ /** keccak256 of the canonical bytes — the value the zk-jlvm guest commits as exprHash / dataHash. */
22
+ export function proverPreimage(x) {
23
+ return `0x${toHex(keccak_256(utf8(canonicalForSigning(x))))}`;
24
+ }
25
+ /** exprHash of a published JLVM rule (canonicalized + keccak'd). Equals the chain's `logicHash`. */
26
+ export const exprHash = (rule) => proverPreimage(rule);
27
+ /** dataHash of a private data context — kept private; only its hash is public. */
28
+ export const dataHash = (data) => proverPreimage(data);
29
+ /** `keccak256(canonicalize(true))` — the `outputHash` a guard binds for a "rule returned true" proof. */
30
+ export const KECCAK_TRUE = proverPreimage(true);