zklicensing 0.1.0-beta.2 → 0.1.0-beta.21

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 (317) hide show
  1. package/README.md +417 -201
  2. package/build/agent.d.ts +371 -0
  3. package/build/agent.d.ts.map +1 -0
  4. package/build/agent.js +553 -0
  5. package/build/agent.js.map +1 -0
  6. package/build/agentActions.d.ts +841 -0
  7. package/build/agentActions.d.ts.map +1 -0
  8. package/build/agentActions.js +581 -0
  9. package/build/agentActions.js.map +1 -0
  10. package/build/agentRecord.d.ts +83 -0
  11. package/build/agentRecord.d.ts.map +1 -0
  12. package/build/agentRecord.js +69 -0
  13. package/build/agentRecord.js.map +1 -0
  14. package/build/agentVendor.d.ts +220 -0
  15. package/build/agentVendor.d.ts.map +1 -0
  16. package/build/agentVendor.js +306 -0
  17. package/build/agentVendor.js.map +1 -0
  18. package/build/appsLookup.d.ts +14 -0
  19. package/build/appsLookup.d.ts.map +1 -0
  20. package/build/appsLookup.js +12 -0
  21. package/build/appsLookup.js.map +1 -0
  22. package/build/archiveBootstrap.d.ts +31 -5
  23. package/build/archiveBootstrap.d.ts.map +1 -0
  24. package/build/archiveBootstrap.js +140 -47
  25. package/build/archiveBootstrap.js.map +1 -0
  26. package/build/atomicJson.d.ts +5 -0
  27. package/build/atomicJson.d.ts.map +1 -0
  28. package/build/atomicJson.js +55 -0
  29. package/build/atomicJson.js.map +1 -0
  30. package/build/bindClient.d.ts +85 -0
  31. package/build/bindClient.d.ts.map +1 -0
  32. package/build/bindClient.js +187 -0
  33. package/build/bindClient.js.map +1 -0
  34. package/build/buyClient.d.ts +27 -4
  35. package/build/buyClient.d.ts.map +1 -0
  36. package/build/buyClient.js +72 -26
  37. package/build/buyClient.js.map +1 -0
  38. package/build/chainHead.d.ts +61 -0
  39. package/build/chainHead.d.ts.map +1 -0
  40. package/build/chainHead.js +88 -0
  41. package/build/chainHead.js.map +1 -0
  42. package/build/compactMerkleMap.d.ts +72 -0
  43. package/build/compactMerkleMap.d.ts.map +1 -0
  44. package/build/compactMerkleMap.js +224 -0
  45. package/build/compactMerkleMap.js.map +1 -0
  46. package/build/contractInterface.d.ts +191 -51
  47. package/build/contractInterface.d.ts.map +1 -0
  48. package/build/contractInterface.js +234 -43
  49. package/build/contractInterface.js.map +1 -0
  50. package/build/contractTypes.d.ts +9 -0
  51. package/build/contractTypes.d.ts.map +1 -0
  52. package/build/contractTypes.js +28 -0
  53. package/build/contractTypes.js.map +1 -0
  54. package/build/countryStats.d.ts +12 -3
  55. package/build/countryStats.d.ts.map +1 -0
  56. package/build/countryStats.js +87 -23
  57. package/build/countryStats.js.map +1 -0
  58. package/build/deployClient.d.ts +138 -24
  59. package/build/deployClient.d.ts.map +1 -0
  60. package/build/deployClient.js +91 -31
  61. package/build/deployClient.js.map +1 -0
  62. package/build/deviceIdentity.d.ts +68 -0
  63. package/build/deviceIdentity.d.ts.map +1 -0
  64. package/build/deviceIdentity.js +209 -0
  65. package/build/deviceIdentity.js.map +1 -0
  66. package/build/freezeClient.d.ts +26 -6
  67. package/build/freezeClient.d.ts.map +1 -0
  68. package/build/freezeClient.js +30 -6
  69. package/build/freezeClient.js.map +1 -0
  70. package/build/generationClient.d.ts +6 -3
  71. package/build/generationClient.d.ts.map +1 -0
  72. package/build/generationClient.js +25 -38
  73. package/build/generationClient.js.map +1 -0
  74. package/build/holderSecret.d.ts +70 -0
  75. package/build/holderSecret.d.ts.map +1 -0
  76. package/build/holderSecret.js +76 -0
  77. package/build/holderSecret.js.map +1 -0
  78. package/build/index.d.ts +38 -38
  79. package/build/index.d.ts.map +1 -0
  80. package/build/index.js +97 -121
  81. package/build/index.js.map +1 -0
  82. package/build/keysClient.d.ts +142 -0
  83. package/build/keysClient.d.ts.map +1 -0
  84. package/build/keysClient.js +109 -0
  85. package/build/keysClient.js.map +1 -0
  86. package/build/licenseLeaf.d.ts +76 -0
  87. package/build/licenseLeaf.d.ts.map +1 -0
  88. package/build/licenseLeaf.js +189 -0
  89. package/build/licenseLeaf.js.map +1 -0
  90. package/build/licenseRecord.d.ts +68 -5
  91. package/build/licenseRecord.d.ts.map +1 -0
  92. package/build/licenseRecord.js +26 -1
  93. package/build/licenseRecord.js.map +1 -0
  94. package/build/licenseStore.d.ts +81 -4
  95. package/build/licenseStore.d.ts.map +1 -0
  96. package/build/licenseStore.js +400 -30
  97. package/build/licenseStore.js.map +1 -0
  98. package/build/licenseTrustless.d.ts +43 -0
  99. package/build/licenseTrustless.d.ts.map +1 -0
  100. package/build/licenseTrustless.js +180 -0
  101. package/build/licenseTrustless.js.map +1 -0
  102. package/build/manifestVerify.d.ts +29 -1
  103. package/build/manifestVerify.d.ts.map +1 -0
  104. package/build/manifestVerify.js +46 -4
  105. package/build/manifestVerify.js.map +1 -0
  106. package/build/minaPriceHistory.d.ts +9 -0
  107. package/build/minaPriceHistory.d.ts.map +1 -0
  108. package/build/minaPriceHistory.js +152 -0
  109. package/build/minaPriceHistory.js.map +1 -0
  110. package/build/onChainRoot.d.ts +60 -0
  111. package/build/onChainRoot.d.ts.map +1 -0
  112. package/build/onChainRoot.js +74 -0
  113. package/build/onChainRoot.js.map +1 -0
  114. package/build/ownershipLicenseHash.d.ts +25 -0
  115. package/build/ownershipLicenseHash.d.ts.map +1 -0
  116. package/build/ownershipLicenseHash.js +29 -0
  117. package/build/ownershipLicenseHash.js.map +1 -0
  118. package/build/ownershipSignature.d.ts +153 -0
  119. package/build/ownershipSignature.d.ts.map +1 -0
  120. package/build/ownershipSignature.js +297 -0
  121. package/build/ownershipSignature.js.map +1 -0
  122. package/build/permitCheckFlow.d.ts +155 -0
  123. package/build/permitCheckFlow.d.ts.map +1 -0
  124. package/build/permitCheckFlow.js +196 -0
  125. package/build/permitCheckFlow.js.map +1 -0
  126. package/build/permitDescribe.d.ts +124 -0
  127. package/build/permitDescribe.d.ts.map +1 -0
  128. package/build/permitDescribe.js +324 -0
  129. package/build/permitDescribe.js.map +1 -0
  130. package/build/permitInterface.d.ts +85 -0
  131. package/build/permitInterface.d.ts.map +1 -0
  132. package/build/permitInterface.js +121 -0
  133. package/build/permitInterface.js.map +1 -0
  134. package/build/permitMessages.d.ts +313 -0
  135. package/build/permitMessages.d.ts.map +1 -0
  136. package/build/permitMessages.js +462 -0
  137. package/build/permitMessages.js.map +1 -0
  138. package/build/permitProof.d.ts +108 -0
  139. package/build/permitProof.d.ts.map +1 -0
  140. package/build/permitProof.js +202 -0
  141. package/build/permitProof.js.map +1 -0
  142. package/build/permitRegistryRebuild.d.ts +166 -0
  143. package/build/permitRegistryRebuild.d.ts.map +1 -0
  144. package/build/permitRegistryRebuild.js +347 -0
  145. package/build/permitRegistryRebuild.js.map +1 -0
  146. package/build/permitTree.d.ts +88 -0
  147. package/build/permitTree.d.ts.map +1 -0
  148. package/build/permitTree.js +230 -0
  149. package/build/permitTree.js.map +1 -0
  150. package/build/permitsClient.d.ts +680 -0
  151. package/build/permitsClient.d.ts.map +1 -0
  152. package/build/permitsClient.js +597 -0
  153. package/build/permitsClient.js.map +1 -0
  154. package/build/proveErrors.d.ts +50 -0
  155. package/build/proveErrors.d.ts.map +1 -0
  156. package/build/proveErrors.js +76 -0
  157. package/build/proveErrors.js.map +1 -0
  158. package/build/receipt.d.ts +95 -0
  159. package/build/receipt.d.ts.map +1 -0
  160. package/build/receipt.js +84 -0
  161. package/build/receipt.js.map +1 -0
  162. package/build/refundClient.d.ts +7 -12
  163. package/build/refundClient.d.ts.map +1 -0
  164. package/build/refundClient.js +52 -21
  165. package/build/refundClient.js.map +1 -0
  166. package/build/registryTrustVerify.d.ts +78 -0
  167. package/build/registryTrustVerify.d.ts.map +1 -0
  168. package/build/registryTrustVerify.js +129 -0
  169. package/build/registryTrustVerify.js.map +1 -0
  170. package/build/releaseClient.d.ts +26 -0
  171. package/build/releaseClient.d.ts.map +1 -0
  172. package/build/releaseClient.js +65 -0
  173. package/build/releaseClient.js.map +1 -0
  174. package/build/renewClient.d.ts +28 -2
  175. package/build/renewClient.d.ts.map +1 -0
  176. package/build/renewClient.js +31 -27
  177. package/build/renewClient.js.map +1 -0
  178. package/build/seatName.d.ts +8 -0
  179. package/build/seatName.d.ts.map +1 -0
  180. package/build/seatName.js +136 -0
  181. package/build/seatName.js.map +1 -0
  182. package/build/slotConstants.d.ts +15 -0
  183. package/build/slotConstants.d.ts.map +1 -0
  184. package/build/slotConstants.js +17 -0
  185. package/build/slotConstants.js.map +1 -0
  186. package/build/statsClient.d.ts +61 -7
  187. package/build/statsClient.d.ts.map +1 -0
  188. package/build/statsClient.js +44 -5
  189. package/build/statsClient.js.map +1 -0
  190. package/build/successorChain.d.ts +35 -0
  191. package/build/successorChain.d.ts.map +1 -0
  192. package/build/successorChain.js +69 -0
  193. package/build/successorChain.js.map +1 -0
  194. package/build/taxId.d.ts +44 -0
  195. package/build/taxId.d.ts.map +1 -0
  196. package/build/taxId.js +212 -0
  197. package/build/taxId.js.map +1 -0
  198. package/build/trustAnchorVerify.d.ts +49 -0
  199. package/build/trustAnchorVerify.d.ts.map +1 -0
  200. package/build/trustAnchorVerify.js +432 -0
  201. package/build/trustAnchorVerify.js.map +1 -0
  202. package/build/trustAnchorVerifyCli.d.ts +45 -0
  203. package/build/trustAnchorVerifyCli.d.ts.map +1 -0
  204. package/build/trustAnchorVerifyCli.js +235 -0
  205. package/build/trustAnchorVerifyCli.js.map +1 -0
  206. package/build/txBroadcastClient.d.ts +26 -0
  207. package/build/txBroadcastClient.d.ts.map +1 -0
  208. package/build/txBroadcastClient.js +20 -0
  209. package/build/txBroadcastClient.js.map +1 -0
  210. package/build/unbindClient.d.ts +77 -0
  211. package/build/unbindClient.d.ts.map +1 -0
  212. package/build/unbindClient.js +169 -0
  213. package/build/unbindClient.js.map +1 -0
  214. package/build/units.d.ts +28 -0
  215. package/build/units.d.ts.map +1 -0
  216. package/build/units.js +65 -0
  217. package/build/units.js.map +1 -0
  218. package/build/vatRates.d.ts +54 -0
  219. package/build/vatRates.d.ts.map +1 -0
  220. package/build/vatRates.js +82 -0
  221. package/build/vatRates.js.map +1 -0
  222. package/build/vatRatesData.d.ts +5 -0
  223. package/build/vatRatesData.d.ts.map +1 -0
  224. package/build/vatRatesData.js +42 -0
  225. package/build/vatRatesData.js.map +1 -0
  226. package/build/vendorAccountClient.d.ts +66 -0
  227. package/build/vendorAccountClient.d.ts.map +1 -0
  228. package/build/vendorAccountClient.js +47 -0
  229. package/build/vendorAccountClient.js.map +1 -0
  230. package/build/verifyCore.d.ts +4 -1
  231. package/build/verifyCore.d.ts.map +1 -0
  232. package/build/verifyCore.js +21 -10
  233. package/build/verifyCore.js.map +1 -0
  234. package/build/verifyOffline.d.ts +85 -0
  235. package/build/verifyOffline.d.ts.map +1 -0
  236. package/build/verifyOffline.js +139 -0
  237. package/build/verifyOffline.js.map +1 -0
  238. package/build/verifyService.d.ts.map +1 -0
  239. package/build/verifyService.js +571 -501
  240. package/build/verifyService.js.map +1 -0
  241. package/build/verifyWithAncestors.d.ts +19 -0
  242. package/build/verifyWithAncestors.d.ts.map +1 -0
  243. package/build/verifyWithAncestors.js +127 -0
  244. package/build/verifyWithAncestors.js.map +1 -0
  245. package/build/zipToState.d.ts +2 -0
  246. package/build/zipToState.d.ts.map +1 -0
  247. package/build/zipToState.js +94 -0
  248. package/build/zipToState.js.map +1 -0
  249. package/package.json +141 -16
  250. package/src/agent.ts +682 -0
  251. package/src/agentActions.ts +637 -0
  252. package/src/agentRecord.ts +121 -0
  253. package/src/agentVendor.ts +356 -0
  254. package/src/appsLookup.ts +48 -0
  255. package/src/archiveBootstrap.ts +386 -0
  256. package/src/atomicJson.ts +53 -0
  257. package/src/bindClient.ts +297 -0
  258. package/src/buyClient.ts +344 -0
  259. package/src/chainHead.ts +106 -0
  260. package/src/compactMerkleMap.ts +222 -0
  261. package/src/contractInterface.ts +465 -0
  262. package/src/contractTypes.ts +31 -0
  263. package/src/countryStats.ts +200 -0
  264. package/src/deployClient.ts +517 -0
  265. package/src/deviceIdentity.ts +249 -0
  266. package/src/freezeClient.ts +162 -0
  267. package/src/generationClient.ts +145 -0
  268. package/src/holderSecret.ts +91 -0
  269. package/src/index.ts +362 -0
  270. package/src/keysClient.ts +135 -0
  271. package/src/licenseLeaf.ts +221 -0
  272. package/src/licenseRecord.ts +190 -0
  273. package/src/licenseStore.ts +767 -0
  274. package/src/licenseTrustless.ts +207 -0
  275. package/src/manifestVerify.ts +164 -0
  276. package/src/minaPriceHistory.ts +166 -0
  277. package/src/onChainRoot.ts +90 -0
  278. package/src/ownershipLicenseHash.ts +31 -0
  279. package/src/ownershipSignature.ts +421 -0
  280. package/src/permitCheckFlow.ts +227 -0
  281. package/src/permitDescribe.ts +362 -0
  282. package/src/permitInterface.ts +146 -0
  283. package/src/permitMessages.ts +572 -0
  284. package/src/permitProof.ts +230 -0
  285. package/src/permitRegistryRebuild.ts +423 -0
  286. package/src/permitTree.ts +232 -0
  287. package/src/permitsClient.ts +746 -0
  288. package/src/proveErrors.ts +98 -0
  289. package/src/receipt.ts +156 -0
  290. package/src/refundClient.ts +177 -0
  291. package/src/registryTrustVerify.ts +192 -0
  292. package/src/releaseClient.ts +101 -0
  293. package/src/renewClient.ts +234 -0
  294. package/src/seatName.ts +165 -0
  295. package/src/slotConstants.ts +18 -0
  296. package/src/statsClient.ts +249 -0
  297. package/src/successorChain.ts +87 -0
  298. package/src/taxId.ts +227 -0
  299. package/src/trustAnchorVerify.ts +550 -0
  300. package/src/trustAnchorVerifyCli.ts +234 -0
  301. package/src/txBroadcastClient.ts +48 -0
  302. package/src/unbindClient.ts +233 -0
  303. package/src/units.ts +74 -0
  304. package/src/vatRates.ts +100 -0
  305. package/src/vatRatesData.ts +43 -0
  306. package/src/vendorAccountClient.ts +120 -0
  307. package/src/verifyCore.ts +100 -0
  308. package/src/verifyOffline.ts +212 -0
  309. package/src/verifyService.ts +982 -0
  310. package/src/verifyWithAncestors.ts +163 -0
  311. package/src/zipToState.ts +94 -0
  312. package/build/licenseProof.d.ts +0 -26
  313. package/build/licenseProof.js +0 -41
  314. package/build/migrateClient.d.ts +0 -43
  315. package/build/migrateClient.js +0 -47
  316. package/build/ownership.d.ts +0 -34
  317. package/build/ownership.js +0 -147
@@ -0,0 +1,572 @@
1
+ // Copyright (c) 2025-2026 zkLicensing project developers
2
+ // SPDX-License-Identifier: BUSL-1.1
3
+
4
+ /**
5
+ * permitMessages.ts — `zklicensing/permits/messages`
6
+ *
7
+ * The single definition of everything signed or hashed for zkPermits: the
8
+ * permit leaf, subject derivation, packed fees and policy, the authority
9
+ * key-set commitment, and the message each signer signs. The PermitRegistry
10
+ * circuit imports this module, so an off-chain signature (authority console,
11
+ * operator page, agent) always hashes to exactly what the circuit verifies.
12
+ * Test vectors: permitMessages.vectors.json.
13
+ *
14
+ * Every function works both off-chain and inside a circuit (o1js types
15
+ * only). Messages are short Field arrays; wallets sign them with
16
+ * `signFields` (Auro, WalletConnect `mina_signFields`), and o1js
17
+ * `Signature.verify` accepts those signatures in-circuit.
18
+ *
19
+ * Wallet field signatures are NOT bound to a network by the signer, so
20
+ * every message carries the network, the registry address and a
21
+ * per-message nonce itself (see `authorityMessage`).
22
+ */
23
+
24
+ import { Bool, Field, Poseidon, PrivateKey, Provable, PublicKey, Signature, Struct, UInt32, UInt64 } from 'o1js';
25
+
26
+ // ------------------------------------------------------------
27
+ // Domain separation
28
+ // ------------------------------------------------------------
29
+
30
+ /** Deterministic Field from a short ASCII label (≤ 31 bytes). */
31
+ function labelField(label: string): Field {
32
+ if (label.length > 31) throw new Error(`label too long: ${label}`);
33
+ let v = 0n;
34
+ for (const ch of label) {
35
+ const c = ch.charCodeAt(0);
36
+ if (c > 0x7f) throw new Error('ASCII labels only');
37
+ v = (v << 8n) | BigInt(c);
38
+ }
39
+ return Field(v);
40
+ }
41
+
42
+ /** One tag per message type. Never reuse or renumber. */
43
+ export const PERMIT_TAGS: Readonly<Record<
44
+ 'INIT' | 'GRANT' | 'ACCEPT' | 'SUSPEND' | 'REINSTATE' | 'REVOKE' | 'SURRENDER' | 'SET_FEES' | 'ROTATE' | 'LEAF' | 'SUBJECT' | 'KEYS'
45
+ | 'APPLICATION' | 'APPLY' | 'APPROVE' | 'REJECT' | 'WITHDRAW' | 'RETIRE' | 'PLATFORM_RETIRE' | 'PLATFORM_CANCEL'
46
+ | 'PAYOUT' | 'PAYOUT_CANCEL' | 'REKEY',
47
+ Field
48
+ >> = {
49
+ INIT: labelField('zkPermits/v1/init'),
50
+ GRANT: labelField('zkPermits/v1/grant'),
51
+ ACCEPT: labelField('zkPermits/v1/accept'),
52
+ SUSPEND: labelField('zkPermits/v1/suspend'),
53
+ REINSTATE: labelField('zkPermits/v1/reinstate'),
54
+ REVOKE: labelField('zkPermits/v1/revoke'),
55
+ SURRENDER: labelField('zkPermits/v1/surrender'),
56
+ SET_FEES: labelField('zkPermits/v1/setFees'),
57
+ ROTATE: labelField('zkPermits/v1/rotate'),
58
+ LEAF: labelField('zkPermits/v1/leaf'),
59
+ SUBJECT: labelField('zkPermits/v1/subject'),
60
+ KEYS: labelField('zkPermits/v1/keys'),
61
+ // Application escrow (PERMITS_PLAN.md §4.2).
62
+ APPLICATION: labelField('zkPermits/v1/application'),
63
+ APPLY: labelField('zkPermits/v1/apply'),
64
+ APPROVE: labelField('zkPermits/v1/approve'),
65
+ REJECT: labelField('zkPermits/v1/reject'),
66
+ WITHDRAW: labelField('zkPermits/v1/withdraw'),
67
+ // Generations: the authority retires a registry before redeploying it.
68
+ RETIRE: labelField('zkPermits/v1/retire'),
69
+ // ... or the platform does (one signature by the platform key).
70
+ PLATFORM_RETIRE: labelField('zkPermits/v1/platformRetire'),
71
+ // Before retiring, the platform may cancel an open application (full refund).
72
+ PLATFORM_CANCEL: labelField('zkPermits/v1/platformCancel'),
73
+ // Key changes: a new payout address (threshold, after a delay), a permit moving to a new holder key.
74
+ PAYOUT: labelField('zkPermits/v1/payout'),
75
+ PAYOUT_CANCEL: labelField('zkPermits/v1/payoutCancel'),
76
+ REKEY: labelField('zkPermits/v1/rekey'),
77
+ };
78
+
79
+ /** Network in signed messages. 0 is never valid. */
80
+ export const PERMIT_NETWORK: Readonly<{ devnet: Field; mainnet: Field }> = { devnet: Field(1), mainnet: Field(2) };
81
+ export type PermitNetwork = keyof typeof PERMIT_NETWORK;
82
+
83
+ export function networkField(network: PermitNetwork): Field {
84
+ const f = PERMIT_NETWORK[network];
85
+ if (!f) throw new Error(`unknown network ${network}`);
86
+ return f;
87
+ }
88
+
89
+ // ------------------------------------------------------------
90
+ // Permit leaf
91
+ // ------------------------------------------------------------
92
+
93
+ /** MOVED: the permit moved to a new holder key (rekeyPermit), under a new subject; this leaf is closed. */
94
+ export const PERMIT_STATUS = { ACTIVE: 0, SUSPENDED: 1, REVOKED: 2, MOVED: 3 } as const;
95
+ export const PERMIT_STATUS_ACTIVE = UInt32.from(0);
96
+ export const PERMIT_STATUS_SUSPENDED = UInt32.from(1);
97
+ export const PERMIT_STATUS_REVOKED = UInt32.from(2);
98
+ export const PERMIT_STATUS_MOVED = UInt32.from(3);
99
+
100
+ /** Expiry value meaning "does not expire" (only if the policy allows it). */
101
+ export const NO_EXPIRY_SLOT = UInt32.MAXINT();
102
+
103
+ // `const X: any = Struct(...)` — same pattern as licenseLeaf.ts: the inferred
104
+ // Struct class type references o1js internals outside this package's rootDir.
105
+ export const PermitLeaf: any = Struct({
106
+ subject: Field,
107
+ holderKey: PublicKey,
108
+ scope: Field,
109
+ validFrom: UInt32,
110
+ expirySlot: UInt32,
111
+ status: UInt32,
112
+ seq: UInt32,
113
+ docHash: Field,
114
+ });
115
+ export type PermitLeaf = any;
116
+
117
+ /** Hash stored in the permits MerkleMap. An empty map slot is Field(0). */
118
+ export function permitLeafHash(leaf: PermitLeaf): Field {
119
+ return Poseidon.hash([PERMIT_TAGS.LEAF, ...PermitLeaf.toFields(leaf)]);
120
+ }
121
+
122
+ /** Tree key of an operator: hides the key behind a salt. */
123
+ export function permitSubject(holderKey: PublicKey, salt: Field): Field {
124
+ return Poseidon.hash([PERMIT_TAGS.SUBJECT, ...holderKey.toFields(), salt]);
125
+ }
126
+
127
+ /** Leaf value for a subject that has never held a permit. */
128
+ export function emptyPermitLeaf(subject: Field): PermitLeaf {
129
+ return new PermitLeaf({
130
+ subject,
131
+ holderKey: PublicKey.empty(),
132
+ scope: Field(0),
133
+ validFrom: UInt32.zero,
134
+ expirySlot: UInt32.zero,
135
+ status: PERMIT_STATUS_ACTIVE,
136
+ seq: UInt32.zero,
137
+ docHash: Field(0),
138
+ });
139
+ }
140
+
141
+ // ------------------------------------------------------------
142
+ // Packed fees and policy
143
+ // ------------------------------------------------------------
144
+
145
+ const TWO_64 = 1n << 64n;
146
+
147
+ export type PermitFees = { grantFee: UInt64; renewalFee: UInt64; minFee: UInt64 };
148
+
149
+ /** grantFee | renewalFee << 64 | minFee << 128 (nanomina). */
150
+ export function packFees(f: PermitFees): Field {
151
+ return f.grantFee.value
152
+ .add(f.renewalFee.value.mul(Field(TWO_64)))
153
+ .add(f.minFee.value.mul(Field(TWO_64 * TWO_64)));
154
+ }
155
+
156
+ /** Inverse of packFees; range-checks every part (safe in-circuit). */
157
+ export function unpackFees(packed: Field): PermitFees {
158
+ const bits = packed.toBits(192);
159
+ return {
160
+ grantFee: UInt64.Unsafe.fromField(Field.fromBits(bits.slice(0, 64))),
161
+ renewalFee: UInt64.Unsafe.fromField(Field.fromBits(bits.slice(64, 128))),
162
+ minFee: UInt64.Unsafe.fromField(Field.fromBits(bits.slice(128, 192))),
163
+ };
164
+ }
165
+
166
+ /** Default minimum permit fee: 10 MINA. Configurable per registry at initialize. */
167
+ export const DEFAULT_MIN_PERMIT_FEE = UInt64.from(10_000_000_000n);
168
+
169
+ /** Platform share of a fee: 2%, rounded down (rounding favours the authority). */
170
+ export const PERMIT_PLATFORM_FEE_BASIS = UInt64.from(50);
171
+ export function platformShare(fee: UInt64): UInt64 {
172
+ return fee.div(PERMIT_PLATFORM_FEE_BASIS);
173
+ }
174
+
175
+ export type PermitPolicy = {
176
+ gracePeriod: UInt32; // slots after expiry still reported as valid
177
+ maxTerm: UInt32; // max expirySlot - validFrom; 0 = unlimited
178
+ singleKeySuspend: Bool; // any one authority key may suspend
179
+ holderSigRequired: Bool; // grant needs the holder's countersignature
180
+ };
181
+
182
+ /** gracePeriod | maxTerm << 32 | singleKeySuspend << 64 | holderSigRequired << 65 */
183
+ export function packPolicy(p: PermitPolicy): Field {
184
+ return p.gracePeriod.value
185
+ .add(p.maxTerm.value.mul(Field(1n << 32n)))
186
+ .add(p.singleKeySuspend.toField().mul(Field(1n << 64n)))
187
+ .add(p.holderSigRequired.toField().mul(Field(1n << 65n)));
188
+ }
189
+
190
+ export function unpackPolicy(packed: Field): PermitPolicy {
191
+ const bits = packed.toBits(66);
192
+ return {
193
+ gracePeriod: UInt32.Unsafe.fromField(Field.fromBits(bits.slice(0, 32))),
194
+ maxTerm: UInt32.Unsafe.fromField(Field.fromBits(bits.slice(32, 64))),
195
+ singleKeySuspend: bits[64],
196
+ holderSigRequired: bits[65],
197
+ };
198
+ }
199
+
200
+ // ------------------------------------------------------------
201
+ // Application terms
202
+ // ------------------------------------------------------------
203
+
204
+ export type ApplicationTerms = {
205
+ processingFee: UInt64; // kept on rejection or withdrawal (nanomina)
206
+ minProcessingFee: UInt64; // platform minimum, fixed for the generation
207
+ decisionWindow: UInt32; // slots from filing to the decision deadline
208
+ };
209
+
210
+ /** processingFee | minProcessingFee << 64 | decisionWindow << 128 */
211
+ export function packApplicationTerms(t: ApplicationTerms): Field {
212
+ return t.processingFee.value
213
+ .add(t.minProcessingFee.value.mul(Field(TWO_64)))
214
+ .add(t.decisionWindow.value.mul(Field(TWO_64 * TWO_64)));
215
+ }
216
+
217
+ export function unpackApplicationTerms(packed: Field): ApplicationTerms {
218
+ const bits = packed.toBits(160);
219
+ return {
220
+ processingFee: UInt64.Unsafe.fromField(Field.fromBits(bits.slice(0, 64))),
221
+ minProcessingFee: UInt64.Unsafe.fromField(Field.fromBits(bits.slice(64, 128))),
222
+ decisionWindow: UInt32.Unsafe.fromField(Field.fromBits(bits.slice(128, 160))),
223
+ };
224
+ }
225
+
226
+ /** Default minimum processing fee: 1 MINA. Configurable per registry at initialize. */
227
+ export const DEFAULT_MIN_PROCESSING_FEE = UInt64.from(1_000_000_000n);
228
+ /** Default decision window: 14 days of 90 s slots (as licensing's refund window). */
229
+ export const DEFAULT_DECISION_WINDOW = UInt32.from(13_440);
230
+ /** Longest decision window an authority may set: one year. */
231
+ export const MAX_DECISION_WINDOW = UInt32.from(350_640);
232
+
233
+ /**
234
+ * An application with its fee in escrow. Stored as `applicationHash` in the
235
+ * registry's escrow MerkleMap under `subject`; emitted in full when filed.
236
+ * `baseSeq` is the permit leaf's `seq` it applies to (0 = new permit);
237
+ * `fee` is the whole amount escrowed, `processingFee` the part kept on
238
+ * rejection or withdrawal; both are locked at filing. `filing` is the
239
+ * registry's filing number (its filingCount when filed), so two filings
240
+ * never share a hash and a signature settling one can't settle another.
241
+ */
242
+ export const Application: any = Struct({
243
+ subject: Field,
244
+ holderKey: PublicKey,
245
+ payer: PublicKey,
246
+ baseSeq: UInt32,
247
+ scope: Field,
248
+ validFrom: UInt32,
249
+ expirySlot: UInt32,
250
+ docHash: Field,
251
+ fee: UInt64,
252
+ processingFee: UInt64,
253
+ deadline: UInt32,
254
+ filing: UInt32,
255
+ });
256
+ export type Application = any;
257
+
258
+ export function applicationHash(app: Application): Field {
259
+ return Poseidon.hash([PERMIT_TAGS.APPLICATION, ...Application.toFields(app)]);
260
+ }
261
+
262
+ /**
263
+ * What the holder agrees to when applying: everything in the application
264
+ * except the payer (the transaction sender), the deadline (set by the
265
+ * contract from the filing slot) and the filing number (the registry's next).
266
+ */
267
+ export function applicationTermsHash(app: Application): Field {
268
+ return Poseidon.hash([
269
+ PERMIT_TAGS.APPLICATION, app.subject, ...app.holderKey.toFields(), app.baseSeq.value, app.scope,
270
+ app.validFrom.value, app.expirySlot.value, app.docHash, app.fee.value, app.processingFee.value,
271
+ ]);
272
+ }
273
+
274
+ /** The permit leaf an approved application becomes. */
275
+ export function leafFromApplication(app: Application): PermitLeaf {
276
+ return new PermitLeaf({
277
+ subject: app.subject,
278
+ holderKey: app.holderKey,
279
+ scope: app.scope,
280
+ validFrom: app.validFrom,
281
+ expirySlot: app.expirySlot,
282
+ status: PERMIT_STATUS_ACTIVE,
283
+ seq: app.baseSeq.add(1),
284
+ docHash: app.docHash,
285
+ });
286
+ }
287
+
288
+ /** Outcome codes in `applicationSettled` events. */
289
+ export const APPLICATION_OUTCOME = { APPROVED: 1, REJECTED: 2, WITHDRAWN: 3, RECLAIMED: 4, CANCELLED: 5 } as const;
290
+
291
+ // ------------------------------------------------------------
292
+ // Authority key set
293
+ // ------------------------------------------------------------
294
+
295
+ export const MAX_AUTHORITY_KEYS = 3;
296
+
297
+ // Fixed-length key set. `count` keys are in use (slots 0..count-1); unused
298
+ // slots hold PublicKey.empty() and never count toward a threshold, whatever
299
+ // signature is supplied for them.
300
+ export const AuthorityKeys: any = Struct({
301
+ keys: Provable.Array(PublicKey, MAX_AUTHORITY_KEYS),
302
+ count: UInt32,
303
+ threshold: UInt32,
304
+ });
305
+ export type AuthorityKeys = any;
306
+
307
+ export function authorityKeys(keys: PublicKey[], threshold: number): AuthorityKeys {
308
+ if (keys.length < 1 || keys.length > MAX_AUTHORITY_KEYS) throw new Error(`1..${MAX_AUTHORITY_KEYS} authority keys`);
309
+ if (!Number.isInteger(threshold) || threshold < 1 || threshold > keys.length) throw new Error('threshold must be 1..number of keys');
310
+ const b58 = keys.map(k => k.toBase58());
311
+ if (new Set(b58).size !== b58.length) throw new Error('authority keys must be distinct');
312
+ const padded = [...keys];
313
+ while (padded.length < MAX_AUTHORITY_KEYS) padded.push(PublicKey.empty());
314
+ return new AuthorityKeys({ keys: padded, count: UInt32.from(keys.length), threshold: UInt32.from(threshold) });
315
+ }
316
+
317
+ // One optional signature per key slot. `present[i]` false → slot i ignored.
318
+ export const AuthoritySigs: any = Struct({
319
+ sigs: Provable.Array(Signature, MAX_AUTHORITY_KEYS),
320
+ present: Provable.Array(Bool, MAX_AUTHORITY_KEYS),
321
+ });
322
+ export type AuthoritySigs = any;
323
+
324
+ // Placeholder for absent slots: a well-formed signature over an unrelated
325
+ // one-field message. Absent slots are never counted anyway.
326
+ let placeholder: Signature | null = null;
327
+ function placeholderSignature(): Signature {
328
+ placeholder ??= Signature.create(PrivateKey.fromBigInt(1n), [Field(0)]);
329
+ return placeholder;
330
+ }
331
+
332
+ /** Builds the signature bundle from `{ slotIndex: signature }` (base58 or Signature). */
333
+ export function authoritySigs(bySlot: Record<number, Signature | string>): AuthoritySigs {
334
+ const sigs: Signature[] = [];
335
+ const present: Bool[] = [];
336
+ for (let i = 0; i < MAX_AUTHORITY_KEYS; i++) {
337
+ const s = bySlot[i];
338
+ if (s === undefined) { sigs.push(placeholderSignature()); present.push(Bool(false)); continue; }
339
+ sigs.push(typeof s === 'string' ? Signature.fromBase58(s) : s);
340
+ present.push(Bool(true));
341
+ }
342
+ return new AuthoritySigs({ sigs, present });
343
+ }
344
+
345
+ /**
346
+ * Number of valid signatures over `message` from distinct, in-use key
347
+ * slots. Works in-circuit. Absent or unused slots count zero; a present
348
+ * signature that doesn't verify also counts zero.
349
+ */
350
+ export function countAuthoritySignatures(keys: AuthorityKeys, sigs: AuthoritySigs, message: Field[]): UInt32 {
351
+ let n = UInt32.zero;
352
+ for (let i = 0; i < MAX_AUTHORITY_KEYS; i++) {
353
+ const inUse = UInt32.from(i).lessThan(keys.count);
354
+ // An unused slot's key is PublicKey.empty(); verify against a real key
355
+ // instead so the check stays well-formed. The result is masked by inUse.
356
+ const key = Provable.if(inUse, PublicKey, keys.keys[i], keys.keys[0]);
357
+ const ok = sigs.sigs[i].verify(key, message);
358
+ const counted = inUse.and(sigs.present[i]).and(ok);
359
+ n = n.add(Provable.if(counted, UInt32.from(1), UInt32.zero));
360
+ }
361
+ return n;
362
+ }
363
+
364
+ /**
365
+ * In-circuit validity of a key set: 1 ≤ count ≤ MAX, 1 ≤ threshold ≤ count,
366
+ * in-use keys pairwise distinct, unused slots empty.
367
+ */
368
+ export function assertValidAuthorityKeys(keys: AuthorityKeys): void {
369
+ keys.count.assertGreaterThanOrEqual(UInt32.from(1));
370
+ keys.count.assertLessThanOrEqual(UInt32.from(MAX_AUTHORITY_KEYS));
371
+ keys.threshold.assertGreaterThanOrEqual(UInt32.from(1));
372
+ keys.threshold.assertLessThanOrEqual(keys.count);
373
+ for (let i = 0; i < MAX_AUTHORITY_KEYS; i++) {
374
+ const inUse = UInt32.from(i).lessThan(keys.count);
375
+ // Unused slots must be empty so the commitment is canonical.
376
+ inUse.or(keys.keys[i].equals(PublicKey.empty())).assertTrue();
377
+ // In-use keys are real keys, and distinct from every other in-use key.
378
+ inUse.not().or(keys.keys[i].equals(PublicKey.empty()).not()).assertTrue();
379
+ for (let j = i + 1; j < MAX_AUTHORITY_KEYS; j++) {
380
+ const bothInUse = UInt32.from(j).lessThan(keys.count);
381
+ bothInUse.not().or(keys.keys[i].equals(keys.keys[j]).not()).assertTrue();
382
+ }
383
+ }
384
+ }
385
+
386
+ /** Commitment stored on-chain as `authorityKeysHash`. */
387
+ export function authorityKeysHash(k: AuthorityKeys): Field {
388
+ return Poseidon.hash([PERMIT_TAGS.KEYS, ...AuthorityKeys.toFields(k)]);
389
+ }
390
+
391
+ // ------------------------------------------------------------
392
+ // Messages
393
+ // ------------------------------------------------------------
394
+
395
+ /**
396
+ * Message an authority key signs:
397
+ * [tag, network, registry.x, registry.isOdd, epoch, nonce, payloadHash]
398
+ * `nonce` is the leaf's `seq` for per-permit actions and the registry's
399
+ * `actionNonce` for registry-wide ones (setFees, rotate, init = 0).
400
+ */
401
+ export function authorityMessage(
402
+ tag: Field, network: Field, registry: PublicKey, epoch: UInt32, nonce: UInt32, payload: Field[],
403
+ ): Field[] {
404
+ return [tag, network, ...registry.toFields(), epoch.value, nonce.value, Poseidon.hash(payload)];
405
+ }
406
+
407
+ export function initMessage(a: {
408
+ network: Field; registry: PublicKey; keysHash: Field; policyPacked: Field; feesPacked: Field;
409
+ appTermsPacked: Field; metadataHash: Field; payout: PublicKey; forkActivationSlot: UInt32;
410
+ predecessor: PublicKey; initialPermitsRoot: Field;
411
+ }): Field[] {
412
+ // On a redeploy, the registry this one continues and the frozen permits
413
+ // tree it starts with (the chain checks both); PublicKey.empty() and the
414
+ // empty root for a first generation.
415
+ return authorityMessage(PERMIT_TAGS.INIT, a.network, a.registry, UInt32.zero, UInt32.zero,
416
+ [a.keysHash, a.policyPacked, a.feesPacked, a.appTermsPacked, a.metadataHash, ...a.payout.toFields(), a.forkActivationSlot.value, ...a.predecessor.toFields(), a.initialPermitsRoot]);
417
+ }
418
+
419
+ /** Authority approval of a leaf change (issue, renew, scope change). Nonce = oldLeaf.seq. */
420
+ export function grantMessage(a: {
421
+ network: Field; registry: PublicKey; epoch: UInt32; oldLeaf: PermitLeaf; newLeaf: PermitLeaf;
422
+ }): Field[] {
423
+ return authorityMessage(PERMIT_TAGS.GRANT, a.network, a.registry, a.epoch, a.oldLeaf.seq,
424
+ [permitLeafHash(a.oldLeaf), permitLeafHash(a.newLeaf)]);
425
+ }
426
+
427
+ /** The operator's acceptance of a grant, including the fee it pays. */
428
+ export function acceptMessage(a: { network: Field; registry: PublicKey; newLeaf: PermitLeaf; fee: UInt64 }): Field[] {
429
+ return [PERMIT_TAGS.ACCEPT, a.network, ...a.registry.toFields(), permitLeafHash(a.newLeaf), a.fee.value];
430
+ }
431
+
432
+ export type StatusAction = 'suspend' | 'reinstate' | 'revoke';
433
+ const STATUS_TAG: Record<StatusAction, Field> = {
434
+ suspend: PERMIT_TAGS.SUSPEND, reinstate: PERMIT_TAGS.REINSTATE, revoke: PERMIT_TAGS.REVOKE,
435
+ };
436
+
437
+ /** Authority status change of a permit. Nonce = leaf.seq. */
438
+ export function statusMessage(action: StatusAction, a: {
439
+ network: Field; registry: PublicKey; epoch: UInt32; leaf: PermitLeaf; reasonCode: Field;
440
+ }): Field[] {
441
+ return authorityMessage(STATUS_TAG[action], a.network, a.registry, a.epoch, a.leaf.seq,
442
+ [permitLeafHash(a.leaf), a.reasonCode]);
443
+ }
444
+
445
+ /** The holder giving up their permit. */
446
+ export function surrenderMessage(a: { network: Field; registry: PublicKey; leaf: PermitLeaf }): Field[] {
447
+ return [PERMIT_TAGS.SURRENDER, a.network, ...a.registry.toFields(), permitLeafHash(a.leaf), a.leaf.seq.value];
448
+ }
449
+
450
+ /** New fees and application terms (processing fee, decision window). */
451
+ export function setFeesMessage(a: {
452
+ network: Field; registry: PublicKey; epoch: UInt32; actionNonce: UInt32; newFeesPacked: Field; newAppTermsPacked: Field;
453
+ }): Field[] {
454
+ return authorityMessage(PERMIT_TAGS.SET_FEES, a.network, a.registry, a.epoch, a.actionNonce,
455
+ [a.newFeesPacked, a.newAppTermsPacked]);
456
+ }
457
+
458
+ /**
459
+ * The authority retires the registry, freezing its permits at `permitsRoot`
460
+ * (the root the redeployed registry's ancestorsRoot commits to). Bound to the
461
+ * epoch and action nonce like the other registry-wide actions.
462
+ */
463
+ export function retireMessage(a: {
464
+ network: Field; registry: PublicKey; epoch: UInt32; actionNonce: UInt32; permitsRoot: Field;
465
+ /** The one registry that may start from these permits (stored on chain). */
466
+ successor: PublicKey;
467
+ }): Field[] {
468
+ return authorityMessage(PERMIT_TAGS.RETIRE, a.network, a.registry, a.epoch, a.actionNonce, [a.permitsRoot, ...a.successor.toFields()]);
469
+ }
470
+
471
+ /**
472
+ * The platform's retirement of a registry (`retireByPlatform`): what the
473
+ * platform key signs, the fields of retireMessage under its own tag (so
474
+ * neither signature can stand in for the other) plus `force`: retire with
475
+ * application fees still in escrow, which then stay on the retired registry.
476
+ */
477
+ export function platformRetireMessage(a: {
478
+ network: Field; registry: PublicKey; epoch: UInt32; actionNonce: UInt32; permitsRoot: Field; successor: PublicKey; force: Bool;
479
+ }): Field[] {
480
+ return authorityMessage(PERMIT_TAGS.PLATFORM_RETIRE, a.network, a.registry, a.epoch, a.actionNonce, [a.permitsRoot, ...a.successor.toFields(), a.force.toField()]);
481
+ }
482
+
483
+ /**
484
+ * authorityKeysHash of a retired registry: no key set hashes to it, so no
485
+ * authority action can run again, and filing and surrender check
486
+ * it. (0 means not yet initialized.)
487
+ */
488
+ export const RETIRED_KEYS_HASH: Field = Field(1);
489
+
490
+ export function rotateMessage(a: {
491
+ network: Field; registry: PublicKey; epoch: UInt32; actionNonce: UInt32; newKeysHash: Field;
492
+ }): Field[] {
493
+ return authorityMessage(PERMIT_TAGS.ROTATE, a.network, a.registry, a.epoch, a.actionNonce, [a.newKeysHash]);
494
+ }
495
+
496
+ // ------------------------------------------------------------
497
+ // Application escrow messages
498
+ // ------------------------------------------------------------
499
+
500
+ /** The holder's application: the terms and the fee they put in escrow. */
501
+ export function applyMessage(a: { network: Field; registry: PublicKey; application: Application }): Field[] {
502
+ return [PERMIT_TAGS.APPLY, a.network, ...a.registry.toFields(), applicationTermsHash(a.application)];
503
+ }
504
+
505
+ /** Authority approval of an application exactly as filed. Nonce = baseSeq. */
506
+ export function approveMessage(a: { network: Field; registry: PublicKey; epoch: UInt32; application: Application }): Field[] {
507
+ return authorityMessage(PERMIT_TAGS.APPROVE, a.network, a.registry, a.epoch, a.application.baseSeq,
508
+ [applicationHash(a.application)]);
509
+ }
510
+
511
+ /** Authority rejection of an application. Nonce = baseSeq. */
512
+ export function rejectMessage(a: {
513
+ network: Field; registry: PublicKey; epoch: UInt32; application: Application; reasonCode: Field;
514
+ }): Field[] {
515
+ return authorityMessage(PERMIT_TAGS.REJECT, a.network, a.registry, a.epoch, a.application.baseSeq,
516
+ [applicationHash(a.application), a.reasonCode]);
517
+ }
518
+
519
+ /**
520
+ * The platform cancelling the open applications of a registry before it
521
+ * retires it (`cancelApplication`, e.g. ahead of a hard fork): every
522
+ * application filed before filing number `cutoff` (the registry's filing
523
+ * count when signed) may be cancelled, its whole fee back to its payer. One
524
+ * signature covers them all; anyone may send the cancellations. Later
525
+ * filings it never covers.
526
+ */
527
+ export function cancelMessage(a: { network: Field; registry: PublicKey; cutoff: UInt32 }): Field[] {
528
+ return [PERMIT_TAGS.PLATFORM_CANCEL, a.network, ...a.registry.toFields(), a.cutoff.value];
529
+ }
530
+
531
+ /**
532
+ * A new payout address for the registry (threshold, registry-wide nonce):
533
+ * it takes effect PAYOUT_DELAY_SLOTS_N after it is proposed, unless the keys
534
+ * cancel it (payoutCancelMessage) meanwhile.
535
+ */
536
+ export function payoutMessage(a: { network: Field; registry: PublicKey; epoch: UInt32; actionNonce: UInt32; payout: PublicKey }): Field[] {
537
+ return authorityMessage(PERMIT_TAGS.PAYOUT, a.network, a.registry, a.epoch, a.actionNonce, a.payout.toFields());
538
+ }
539
+
540
+ export function payoutCancelMessage(a: { network: Field; registry: PublicKey; epoch: UInt32; actionNonce: UInt32 }): Field[] {
541
+ return authorityMessage(PERMIT_TAGS.PAYOUT_CANCEL, a.network, a.registry, a.epoch, a.actionNonce, []);
542
+ }
543
+
544
+ /**
545
+ * A permit moving to a new holder key (rekeyPermit), with the same terms,
546
+ * under the new subject permitSubject(newHolderKey, newSalt): signed by the
547
+ * current holder key and the new one, over the permit as it is now. The old
548
+ * leaf is closed (MOVED), so the signatures can't be used twice.
549
+ */
550
+ export function rekeyMessage(a: { network: Field; registry: PublicKey; leaf: PermitLeaf; newSubject: Field; newHolderKey: PublicKey }): Field[] {
551
+ return [PERMIT_TAGS.REKEY, a.network, ...a.registry.toFields(), permitLeafHash(a.leaf), a.newSubject, ...a.newHolderKey.toFields()];
552
+ }
553
+
554
+ /** The holder withdrawing their application before a decision. */
555
+ export function withdrawMessage(a: { network: Field; registry: PublicKey; application: Application }): Field[] {
556
+ return [PERMIT_TAGS.WITHDRAW, a.network, ...a.registry.toFields(), applicationHash(a.application)];
557
+ }
558
+
559
+ /** What both wallets sign (signMessage) to move an authority account to a new wallet. */
560
+ export function authorityMoveText(from: string, to: string): string {
561
+ return [
562
+ 'zkPermits: move my authority account to a new wallet',
563
+ `from: ${from}`,
564
+ `to: ${to}`,
565
+ 'Both wallets sign this. The account and its registries move to the new wallet; registry keys change by a rotation.',
566
+ ].join('\n');
567
+ }
568
+
569
+ /** Decimal strings for a wallet's `signFields({ message })`. */
570
+ export function toSignableFields(message: Field[]): string[] {
571
+ return message.map(f => f.toString());
572
+ }