@poa-box/core 0.1.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 (220) hide show
  1. package/README.md +201 -0
  2. package/dist/abis/DirectDemocracyVotingNew.d.ts +749 -0
  3. package/dist/abis/DirectDemocracyVotingNew.js +961 -0
  4. package/dist/abis/ERC20.d.ts +90 -0
  5. package/dist/abis/ERC20.js +125 -0
  6. package/dist/abis/EducationHubNew.d.ts +590 -0
  7. package/dist/abis/EducationHubNew.js +762 -0
  8. package/dist/abis/EligibilityModuleNew.d.ts +1615 -0
  9. package/dist/abis/EligibilityModuleNew.js +2082 -0
  10. package/dist/abis/Executor.d.ts +619 -0
  11. package/dist/abis/Executor.js +796 -0
  12. package/dist/abis/HybridVotingNew.d.ts +898 -0
  13. package/dist/abis/HybridVotingNew.js +1149 -0
  14. package/dist/abis/ImplementationRegistry.d.ts +270 -0
  15. package/dist/abis/ImplementationRegistry.js +355 -0
  16. package/dist/abis/OrgDeployerNew.d.ts +1275 -0
  17. package/dist/abis/OrgDeployerNew.js +1630 -0
  18. package/dist/abis/OrgRegistry.d.ts +670 -0
  19. package/dist/abis/OrgRegistry.js +868 -0
  20. package/dist/abis/ParticipationToken.d.ts +1086 -0
  21. package/dist/abis/ParticipationToken.js +1415 -0
  22. package/dist/abis/PasskeyAccount.d.ts +788 -0
  23. package/dist/abis/PasskeyAccount.js +1019 -0
  24. package/dist/abis/PasskeyAccountFactory.d.ts +344 -0
  25. package/dist/abis/PasskeyAccountFactory.js +449 -0
  26. package/dist/abis/PaymasterHub.d.ts +1603 -0
  27. package/dist/abis/PaymasterHub.js +2048 -0
  28. package/dist/abis/PaymentManager.d.ts +520 -0
  29. package/dist/abis/PaymentManager.js +671 -0
  30. package/dist/abis/PoaManager.d.ts +344 -0
  31. package/dist/abis/PoaManager.js +449 -0
  32. package/dist/abis/QuickJoinNew.d.ts +855 -0
  33. package/dist/abis/QuickJoinNew.js +1098 -0
  34. package/dist/abis/TaskManagerNew.d.ts +1236 -0
  35. package/dist/abis/TaskManagerNew.js +1573 -0
  36. package/dist/abis/ToggleModule.d.ts +193 -0
  37. package/dist/abis/ToggleModule.js +255 -0
  38. package/dist/abis/UniversalAccountRegistry.d.ts +577 -0
  39. package/dist/abis/UniversalAccountRegistry.js +750 -0
  40. package/dist/abis/ZkEmailInvites.d.ts +823 -0
  41. package/dist/abis/ZkEmailInvites.js +1060 -0
  42. package/dist/abis/external/AaveGovernanceV2.d.ts +128 -0
  43. package/dist/abis/external/AaveGovernanceV2.js +177 -0
  44. package/dist/abis/external/AaveGovernanceV3.d.ts +160 -0
  45. package/dist/abis/external/AaveGovernanceV3.js +220 -0
  46. package/dist/abis/external/AragonVoting.d.ts +107 -0
  47. package/dist/abis/external/AragonVoting.js +150 -0
  48. package/dist/abis/external/CurveGaugeController.d.ts +95 -0
  49. package/dist/abis/external/CurveGaugeController.js +130 -0
  50. package/dist/abis/external/CurveVotingEscrow.d.ts +86 -0
  51. package/dist/abis/external/CurveVotingEscrow.js +116 -0
  52. package/dist/abis/external/GovernorAlpha.d.ts +172 -0
  53. package/dist/abis/external/GovernorAlpha.js +228 -0
  54. package/dist/abis/external/MakerDAOChief.d.ts +92 -0
  55. package/dist/abis/external/MakerDAOChief.js +129 -0
  56. package/dist/abis/external/OZGovernor.d.ts +227 -0
  57. package/dist/abis/external/OZGovernor.js +317 -0
  58. package/dist/abis/external/SolidlyVotingEscrow.d.ts +196 -0
  59. package/dist/abis/external/SolidlyVotingEscrow.js +260 -0
  60. package/dist/abis/index.d.ts +62 -0
  61. package/dist/abis/index.js +98 -0
  62. package/dist/chains.d.ts +150 -0
  63. package/dist/chains.js +320 -0
  64. package/dist/context.d.ts +48 -0
  65. package/dist/context.js +38 -0
  66. package/dist/contracts.d.ts +24 -0
  67. package/dist/contracts.js +41 -0
  68. package/dist/encoding.d.ts +58 -0
  69. package/dist/encoding.js +243 -0
  70. package/dist/env.d.ts +10 -0
  71. package/dist/env.js +4 -0
  72. package/dist/error-catalog.d.ts +41 -0
  73. package/dist/error-catalog.js +941 -0
  74. package/dist/errors.d.ts +27 -0
  75. package/dist/errors.js +60 -0
  76. package/dist/execute/ethers.d.ts +93 -0
  77. package/dist/execute/ethers.js +323 -0
  78. package/dist/execute/index.d.ts +7 -0
  79. package/dist/execute/index.js +23 -0
  80. package/dist/execute/sponsored.d.ts +68 -0
  81. package/dist/execute/sponsored.js +277 -0
  82. package/dist/exit-codes.d.ts +18 -0
  83. package/dist/exit-codes.js +21 -0
  84. package/dist/format.d.ts +21 -0
  85. package/dist/format.js +89 -0
  86. package/dist/graph/client.d.ts +298 -0
  87. package/dist/graph/client.js +702 -0
  88. package/dist/graph/documents/activity.d.ts +9 -0
  89. package/dist/graph/documents/activity.js +164 -0
  90. package/dist/graph/documents/beacons.d.ts +172 -0
  91. package/dist/graph/documents/beacons.js +345 -0
  92. package/dist/graph/documents/index.d.ts +21 -0
  93. package/dist/graph/documents/index.js +57 -0
  94. package/dist/graph/documents/infrastructure.d.ts +30 -0
  95. package/dist/graph/documents/infrastructure.js +34 -0
  96. package/dist/graph/documents/org.d.ts +15 -0
  97. package/dist/graph/documents/org.js +249 -0
  98. package/dist/graph/documents/paymaster.d.ts +95 -0
  99. package/dist/graph/documents/paymaster.js +101 -0
  100. package/dist/graph/documents/role.d.ts +6 -0
  101. package/dist/graph/documents/role.js +41 -0
  102. package/dist/graph/documents/roles.d.ts +50 -0
  103. package/dist/graph/documents/roles.js +112 -0
  104. package/dist/graph/documents/task.d.ts +84 -0
  105. package/dist/graph/documents/task.js +236 -0
  106. package/dist/graph/documents/token.d.ts +13 -0
  107. package/dist/graph/documents/token.js +126 -0
  108. package/dist/graph/documents/treasury.d.ts +39 -0
  109. package/dist/graph/documents/treasury.js +136 -0
  110. package/dist/graph/documents/user.d.ts +120 -0
  111. package/dist/graph/documents/user.js +302 -0
  112. package/dist/graph/documents/voting-classes.d.ts +69 -0
  113. package/dist/graph/documents/voting-classes.js +149 -0
  114. package/dist/graph/documents/voting.d.ts +28 -0
  115. package/dist/graph/documents/voting.js +163 -0
  116. package/dist/graph/documents/vouch.d.ts +79 -0
  117. package/dist/graph/documents/vouch.js +171 -0
  118. package/dist/graph/documents/zkemail.d.ts +63 -0
  119. package/dist/graph/documents/zkemail.js +188 -0
  120. package/dist/graph/index.d.ts +2 -0
  121. package/dist/graph/index.js +41 -0
  122. package/dist/index.d.ts +49 -0
  123. package/dist/index.js +92 -0
  124. package/dist/ipfs.d.ts +39 -0
  125. package/dist/ipfs.js +214 -0
  126. package/dist/label-aliases.d.ts +31 -0
  127. package/dist/label-aliases.js +70 -0
  128. package/dist/metadata/education.d.ts +30 -0
  129. package/dist/metadata/education.js +31 -0
  130. package/dist/metadata/index.d.ts +12 -0
  131. package/dist/metadata/index.js +48 -0
  132. package/dist/metadata/org.d.ts +92 -0
  133. package/dist/metadata/org.js +93 -0
  134. package/dist/metadata/proposal.d.ts +44 -0
  135. package/dist/metadata/proposal.js +45 -0
  136. package/dist/metadata/role.d.ts +38 -0
  137. package/dist/metadata/role.js +35 -0
  138. package/dist/metadata/task.d.ts +108 -0
  139. package/dist/metadata/task.js +81 -0
  140. package/dist/metadata/token.d.ts +32 -0
  141. package/dist/metadata/token.js +36 -0
  142. package/dist/metadata/user.d.ts +63 -0
  143. package/dist/metadata/user.js +70 -0
  144. package/dist/multicall.d.ts +32 -0
  145. package/dist/multicall.js +96 -0
  146. package/dist/payout.d.ts +59 -0
  147. package/dist/payout.js +96 -0
  148. package/dist/perms.d.ts +47 -0
  149. package/dist/perms.js +120 -0
  150. package/dist/preflight.d.ts +72 -0
  151. package/dist/preflight.js +269 -0
  152. package/dist/reads/education.d.ts +79 -0
  153. package/dist/reads/education.js +122 -0
  154. package/dist/reads/eligibility.d.ts +377 -0
  155. package/dist/reads/eligibility.js +687 -0
  156. package/dist/reads/index.d.ts +16 -0
  157. package/dist/reads/index.js +55 -0
  158. package/dist/reads/org.d.ts +258 -0
  159. package/dist/reads/org.js +220 -0
  160. package/dist/reads/paymaster.d.ts +147 -0
  161. package/dist/reads/paymaster.js +235 -0
  162. package/dist/reads/project.d.ts +33 -0
  163. package/dist/reads/project.js +62 -0
  164. package/dist/reads/resolve.d.ts +34 -0
  165. package/dist/reads/resolve.js +64 -0
  166. package/dist/reads/task.d.ts +286 -0
  167. package/dist/reads/task.js +280 -0
  168. package/dist/reads/token.d.ts +94 -0
  169. package/dist/reads/token.js +75 -0
  170. package/dist/reads/treasury.d.ts +222 -0
  171. package/dist/reads/treasury.js +241 -0
  172. package/dist/reads/user.d.ts +229 -0
  173. package/dist/reads/user.js +155 -0
  174. package/dist/reads/vote.d.ts +296 -0
  175. package/dist/reads/vote.js +392 -0
  176. package/dist/reads/zkemail.d.ts +117 -0
  177. package/dist/reads/zkemail.js +217 -0
  178. package/dist/similarity.d.ts +36 -0
  179. package/dist/similarity.js +67 -0
  180. package/dist/sponsorship-config.d.ts +47 -0
  181. package/dist/sponsorship-config.js +41 -0
  182. package/dist/stats.d.ts +11 -0
  183. package/dist/stats.js +27 -0
  184. package/dist/task-lens.d.ts +113 -0
  185. package/dist/task-lens.js +210 -0
  186. package/dist/tx/education.d.ts +169 -0
  187. package/dist/tx/education.js +344 -0
  188. package/dist/tx/eligibility.d.ts +511 -0
  189. package/dist/tx/eligibility.js +818 -0
  190. package/dist/tx/governance.d.ts +85 -0
  191. package/dist/tx/governance.js +87 -0
  192. package/dist/tx/index.d.ts +17 -0
  193. package/dist/tx/index.js +56 -0
  194. package/dist/tx/intent.d.ts +67 -0
  195. package/dist/tx/intent.js +48 -0
  196. package/dist/tx/org.d.ts +321 -0
  197. package/dist/tx/org.js +544 -0
  198. package/dist/tx/paymaster.d.ts +140 -0
  199. package/dist/tx/paymaster.js +251 -0
  200. package/dist/tx/project.d.ts +146 -0
  201. package/dist/tx/project.js +217 -0
  202. package/dist/tx/task.d.ts +480 -0
  203. package/dist/tx/task.js +1149 -0
  204. package/dist/tx/token.d.ts +96 -0
  205. package/dist/tx/token.js +165 -0
  206. package/dist/tx/treasury.d.ts +474 -0
  207. package/dist/tx/treasury.js +911 -0
  208. package/dist/tx/user.d.ts +177 -0
  209. package/dist/tx/user.js +292 -0
  210. package/dist/tx/vote.d.ts +332 -0
  211. package/dist/tx/vote.js +762 -0
  212. package/dist/tx/zkemail.d.ts +90 -0
  213. package/dist/tx/zkemail.js +210 -0
  214. package/dist/validation.d.ts +8 -0
  215. package/dist/validation.js +39 -0
  216. package/dist/version.d.ts +142 -0
  217. package/dist/version.js +257 -0
  218. package/dist/zkemail.d.ts +161 -0
  219. package/dist/zkemail.js +299 -0
  220. package/package.json +130 -0
@@ -0,0 +1,299 @@
1
+ "use strict";
2
+ /**
3
+ * ZK Email allowlist — build / parse / prove.
4
+ *
5
+ * Port of the frontend's `poa-app/src/lib/zkemail/allowlist.js`. That file names the
6
+ * CLI as an intended caller ("Applied for EVERY caller of buildAllowlist (editor, CLI,
7
+ * forge tooling)"), so this module reproduces it exactly rather than reinventing it.
8
+ *
9
+ * CRITICAL coordination — every one of these is load-bearing, and a mismatch produces a
10
+ * silently UNCLAIMABLE allowlist (fail-closed: no funds at risk, but a broken invite):
11
+ *
12
+ * - Merkle leaf == `ZkEmailInvites._leaf` == OpenZeppelin StandardMerkleTree over
13
+ * ['uint8','bytes32','uint256[]'] = [kind, id, hatIds], kind 0=domain, 1=email.
14
+ * We use @openzeppelin/merkle-tree rather than a hand-rolled tree because the doc
15
+ * carries a `treeDump` that the frontend reloads with StandardMerkleTree.load().
16
+ * - domain id = Poseidon(packBytes(ascii-lower(domain), 192)) == the circuit's
17
+ * `fromDomainHash` signal and the PoaDKIMRegistry key. NOT keccak.
18
+ * - email id = Poseidon(packBytes(ascii-lower(address), 192)) == PopRoleClaimV2.circom's
19
+ * `emailHash` public signal.
20
+ *
21
+ * Conformance is pinned by test/lib/zkemail.test.ts, which checks these helpers against the
22
+ * three Poseidon constants and the live allowlist root baked into the contracts repo's
23
+ * script/zkemail/CeremonyDeployTest6Gnosis.s.sol (verified equal to the deployed Gnosis root).
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.LEAF_KIND_EMAIL = exports.LEAF_KIND_DOMAIN = exports.LEAF_TYPES = exports.ALLOWLIST_SCHEMA = void 0;
27
+ exports.norm = norm;
28
+ exports.isPrintableAscii = isPrintableAscii;
29
+ exports.poseidonCommit = poseidonCommit;
30
+ exports.domainHash = domainHash;
31
+ exports.emailHash = emailHash;
32
+ exports.buildAllowlist = buildAllowlist;
33
+ exports.treeFromDoc = treeFromDoc;
34
+ exports.assertRootMatches = assertRootMatches;
35
+ exports.proofForDomain = proofForDomain;
36
+ exports.proofForEmailHash = proofForEmailHash;
37
+ exports.summarize = summarize;
38
+ exports.parseEntriesFile = parseEntriesFile;
39
+ exports.dkimSeededFromIndex = dkimSeededFromIndex;
40
+ const merkle_tree_1 = require("@openzeppelin/merkle-tree");
41
+ const poseidon_lite_1 = require("poseidon-lite");
42
+ const ethers_1 = require("ethers");
43
+ const errors_1 = require("./errors");
44
+ const exit_codes_1 = require("./exit-codes");
45
+ exports.ALLOWLIST_SCHEMA = 'poa.zkemail.allowlist/1';
46
+ exports.LEAF_TYPES = ['uint8', 'bytes32', 'uint256[]'];
47
+ /** == EMAX in PopRoleClaimV2.circom */
48
+ const EMAIL_PAD = 192;
49
+ /** ceil(192 / 31) */
50
+ const CHUNKS = 7;
51
+ exports.LEAF_KIND_DOMAIN = 0;
52
+ exports.LEAF_KIND_EMAIL = 1;
53
+ /**
54
+ * ASCII-only lowercase — EXACTLY mirrors the circuit's `ToLower` and the contract's `_lower`,
55
+ * both of which transform only bytes 0x41–0x5A. JS String.prototype.toLowerCase() is
56
+ * Unicode-aware and would additionally lowercase non-ASCII letters, producing a leaf the
57
+ * on-chain path can never reproduce. Trim, then ASCII-lowercase.
58
+ */
59
+ function norm(s) {
60
+ return String(s || '')
61
+ .trim()
62
+ .replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) + 32));
63
+ }
64
+ /**
65
+ * An identifier that isn't pure printable ASCII can never round-trip through the circuit
66
+ * (its From-address regex + PackBytes are ASCII) or the contract, so it would build an
67
+ * unclaimable leaf. Reject at build time so the operator learns immediately, not the claimer later.
68
+ */
69
+ function isPrintableAscii(s) {
70
+ return /^[\x20-\x7E]+$/.test(String(s));
71
+ }
72
+ /**
73
+ * Poseidon commitment = Poseidon(packBytes(ascii-lower(s) zero-padded to 192), 7).
74
+ *
75
+ * This is the exact commitment the circuits compute: `emailHash` over the From ADDRESS and
76
+ * `fromDomainHash` over the From DOMAIN. Both allowlist leaf ids use it, and each equals the
77
+ * corresponding on-chain proof signal / DKIM registry key.
78
+ *
79
+ * The 192-byte pad and little-endian 31-byte packing MUST equal EMAX/PackBytes in the circuit.
80
+ *
81
+ * The frontend calls circomlibjs `buildPoseidon()`; we use `poseidon-lite`, which is pure JS
82
+ * (no wasm, no worker threads — circomlibjs logs a worker TypeError to stderr on every call,
83
+ * which would corrupt `--json` output). Equivalence is not assumed: the conformance test pins
84
+ * this against the three production domain hashes and the live on-chain root.
85
+ * The fixed arity-7 entry point also enforces CHUNKS === 7.
86
+ *
87
+ * Synchronous, unlike the frontend's — theirs is async only because circomlibjs needs a wasm
88
+ * build step. The value is what has to match, not the signature.
89
+ */
90
+ function poseidonCommit(str) {
91
+ const bytes = new Uint8Array(EMAIL_PAD);
92
+ bytes.set(new TextEncoder().encode(norm(str)).slice(0, EMAIL_PAD));
93
+ const chunks = [];
94
+ for (let i = 0; i < CHUNKS; i++) {
95
+ let acc = 0n;
96
+ for (let j = 0; j < 31; j++) {
97
+ const idx = 31 * i + j;
98
+ acc += (idx < EMAIL_PAD ? BigInt(bytes[idx]) : 0n) << BigInt(8 * j);
99
+ }
100
+ chunks.push(acc);
101
+ }
102
+ return '0x' + (0, poseidon_lite_1.poseidon7)(chunks).toString(16).padStart(64, '0');
103
+ }
104
+ /** domainHash(domain) — domain-leaf id == the circuit's `fromDomainHash` / PoaDKIMRegistry key. */
105
+ function domainHash(domain) {
106
+ return poseidonCommit(domain);
107
+ }
108
+ /** emailHash(address) — email-leaf id == PopRoleClaimV2.circom's `emailHash` signal. */
109
+ function emailHash(address) {
110
+ return poseidonCommit(address);
111
+ }
112
+ /** Sort hat IDs ascending as bigints (leaf encoding is order-sensitive). */
113
+ function sortHats(hatIds) {
114
+ return hatIds.map((h) => BigInt(h)).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
115
+ }
116
+ /**
117
+ * Build the allowlist JSON + merkle tree.
118
+ *
119
+ * Returns the doc exactly as the frontend writes it, including `treeDump` so a claimant can
120
+ * reload the identical tree without recomputing Poseidon hashes.
121
+ */
122
+ async function buildAllowlist(args) {
123
+ const { orgId, entries } = args;
124
+ if (!entries || entries.length === 0) {
125
+ throw new errors_1.CliError('Allowlist must contain at least one entry.', exit_codes_1.EXIT.USAGE);
126
+ }
127
+ const rows = [];
128
+ for (const e of entries) {
129
+ if (e.type !== 'domain' && e.type !== 'email') {
130
+ throw new errors_1.CliError(`Allowlist entry type must be "domain" or "email", got "${e.type}".`, exit_codes_1.EXIT.USAGE);
131
+ }
132
+ if (!isPrintableAscii(e.identifier)) {
133
+ throw new errors_1.CliError(`Allowlist identifier "${e.identifier}" contains non-ASCII characters, which can never be `
134
+ + 'matched by an email proof on-chain. Use the plain ASCII domain/email.', exit_codes_1.EXIT.USAGE);
135
+ }
136
+ if (e.type === 'email' && !String(e.identifier).includes('@')) {
137
+ throw new errors_1.CliError(`Email allowlist entry "${e.identifier}" is not an email address.`, exit_codes_1.EXIT.USAGE);
138
+ }
139
+ if (e.type === 'domain' && String(e.identifier).includes('@')) {
140
+ throw new errors_1.CliError(`Domain allowlist entry "${e.identifier}" looks like an email address — use --type email, `
141
+ + 'or supply just the domain part.', exit_codes_1.EXIT.USAGE);
142
+ }
143
+ if (!e.hatIds || e.hatIds.length === 0) {
144
+ throw new errors_1.CliError(`Allowlist entry "${e.identifier}" grants no hat IDs.`, exit_codes_1.EXIT.USAGE);
145
+ }
146
+ const hatIds = sortHats(e.hatIds);
147
+ const id = e.type === 'domain' ? domainHash(e.identifier) : emailHash(e.identifier);
148
+ // Store the canonical (trimmed, ASCII-lowercased) identifier — exactly what was hashed
149
+ // into the leaf — so the displayed identifier can never drift from its committed id.
150
+ rows.push({ type: e.type, identifier: norm(e.identifier), id, hatIds, roleIndexes: e.roleIndexes });
151
+ }
152
+ const tree = merkle_tree_1.StandardMerkleTree.of(
153
+ // uint256[] values as decimal strings (BigInt isn't JSON-serializable in the tree dump).
154
+ rows.map((r) => [r.type === 'domain' ? exports.LEAF_KIND_DOMAIN : exports.LEAF_KIND_EMAIL, r.id, r.hatIds.map((h) => h.toString())]), exports.LEAF_TYPES);
155
+ const docEntries = rows.map((r) => ({
156
+ type: r.type,
157
+ identifier: r.identifier,
158
+ [r.type === 'email' ? 'emailHash' : 'domainHash']: r.id,
159
+ hatIds: r.hatIds.map((h) => '0x' + h.toString(16)),
160
+ roleIndexes: r.roleIndexes || [],
161
+ }));
162
+ const doc = {
163
+ schema: exports.ALLOWLIST_SCHEMA,
164
+ orgId,
165
+ root: tree.root,
166
+ leafTypes: exports.LEAF_TYPES,
167
+ entries: docEntries,
168
+ treeDump: tree.dump(),
169
+ };
170
+ return { doc, json: JSON.stringify(doc), tree, root: tree.root };
171
+ }
172
+ /** Reload the exact tree from a fetched allowlist doc (prefers the dumped tree). */
173
+ function treeFromDoc(doc) {
174
+ if (doc?.treeDump)
175
+ return merkle_tree_1.StandardMerkleTree.load(doc.treeDump);
176
+ throw new errors_1.CliError('Allowlist file is missing its merkle tree dump, so proofs cannot be derived from it.', exit_codes_1.EXIT.PRECONDITION);
177
+ }
178
+ /**
179
+ * Recompute the root from a doc and assert it matches `expectedRoot` (anti-swapped-CID guard).
180
+ *
181
+ * `label` names what `expectedRoot` is, because the two call sites mean different things:
182
+ * inspecting an active allowlist compares against the on-chain commit, whereas proposing one
183
+ * compares against a root that is not on-chain yet.
184
+ */
185
+ function assertRootMatches(doc, expectedRoot, label = 'active on-chain root') {
186
+ const tree = treeFromDoc(doc);
187
+ if (String(tree.root).toLowerCase() !== String(expectedRoot).toLowerCase()) {
188
+ throw new errors_1.CliError(`Allowlist file does not match the ${label}.\n`
189
+ + ` file root: ${tree.root}\n`
190
+ + ` expected root: ${expectedRoot}`, exit_codes_1.EXIT.PRECONDITION);
191
+ }
192
+ return tree;
193
+ }
194
+ /** Merkle proof + hatIds for a DOMAIN entry, or null if the domain is not in the allowlist. */
195
+ function proofForDomain(tree, domain) {
196
+ const dh = domainHash(domain).toLowerCase();
197
+ for (const [i, v] of tree.entries()) {
198
+ if (Number(v[0]) === exports.LEAF_KIND_DOMAIN && String(v[1]).toLowerCase() === dh) {
199
+ return { hatIds: v[2].map((h) => h.toString()), proof: tree.getProof(i) };
200
+ }
201
+ }
202
+ return null;
203
+ }
204
+ /** Merkle proof + hatIds for a SPECIFIC-address entry by its emailHash, or null. */
205
+ function proofForEmailHash(tree, eHash) {
206
+ const target = String(eHash).toLowerCase();
207
+ for (const [i, v] of tree.entries()) {
208
+ if (Number(v[0]) === exports.LEAF_KIND_EMAIL && String(v[1]).toLowerCase() === target) {
209
+ return { hatIds: v[2].map((h) => h.toString()), proof: tree.getProof(i) };
210
+ }
211
+ }
212
+ return null;
213
+ }
214
+ /** Human-readable summary: which domains + which specific addresses are invited. */
215
+ function summarize(doc) {
216
+ const entries = (doc && doc.entries) || [];
217
+ return {
218
+ domains: entries.filter((e) => e.type === 'domain').map((e) => e.identifier),
219
+ emails: entries.filter((e) => e.type === 'email').map((e) => e.identifier),
220
+ };
221
+ }
222
+ /**
223
+ * Parse the CLI's allowlist input format into entries.
224
+ *
225
+ * Accepts either a full allowlist doc (re-publish an existing one) or a bare array, and
226
+ * normalizes `hatIds` from hex strings / decimals / numbers.
227
+ */
228
+ function parseEntriesFile(raw, filePath) {
229
+ let parsed;
230
+ try {
231
+ parsed = JSON.parse(raw);
232
+ }
233
+ catch (e) {
234
+ throw new errors_1.CliError(`${filePath} is not valid JSON: ${e.message}`, exit_codes_1.EXIT.USAGE);
235
+ }
236
+ const list = Array.isArray(parsed) ? parsed : parsed?.entries;
237
+ if (!Array.isArray(list)) {
238
+ throw new errors_1.CliError(`${filePath} must be a JSON array of entries, or an allowlist doc with an "entries" array.`, exit_codes_1.EXIT.USAGE);
239
+ }
240
+ return list.map((e, i) => {
241
+ if (!e || typeof e !== 'object') {
242
+ throw new errors_1.CliError(`${filePath} entry ${i} is not an object.`, exit_codes_1.EXIT.USAGE);
243
+ }
244
+ const hatIds = e.hatIds ?? e.hats;
245
+ if (!Array.isArray(hatIds)) {
246
+ throw new errors_1.CliError(`${filePath} entry ${i} ("${e.identifier}") has no hatIds array.`, exit_codes_1.EXIT.USAGE);
247
+ }
248
+ return {
249
+ type: e.type,
250
+ identifier: e.identifier,
251
+ hatIds,
252
+ roleIndexes: Array.isArray(e.roleIndexes) ? e.roleIndexes : undefined,
253
+ };
254
+ });
255
+ }
256
+ /**
257
+ * Is any currently-usable DKIM key indexed for `domainLeafId`?
258
+ *
259
+ * Reproduces PoaDKIMRegistry.isKeyHashValid rather than trusting the indexed `valid` flag
260
+ * alone. That flag is the registry's boolean at last-event time and does NOT track a
261
+ * time-bounded expiry lapsing, because no event fires when one does — so a key can be
262
+ * `valid: true` on the index and already rejected on-chain.
263
+ *
264
+ * The sentinels are counter-intuitive and worth stating plainly:
265
+ * validUntil == 0 -> unset or REVOKED (never "permanent")
266
+ * validUntil == 2^256 - 1 -> NO_EXPIRY, valid until explicitly revoked
267
+ * anything else -> unix cut-off, compare against now
268
+ *
269
+ * 2^256-1 overflows a JS number, so every comparison goes through BigNumber.
270
+ *
271
+ * Returns null when the registry itself is not indexed (e.g. a chain where it is not
272
+ * configured), which must stay distinguishable from "indexed, and this domain has no key".
273
+ */
274
+ function dkimSeededFromIndex(registry, domainLeafId, nowSeconds) {
275
+ if (!registry)
276
+ return null;
277
+ const now = nowSeconds ?? Math.floor(Date.now() / 1000);
278
+ const target = String(domainLeafId).toLowerCase();
279
+ for (const k of registry.keys ?? []) {
280
+ if (String(k.domainHash).toLowerCase() !== target)
281
+ continue;
282
+ if (k.valid !== true)
283
+ continue;
284
+ let until;
285
+ try {
286
+ until = ethers_1.BigNumber.from(String(k.validUntil ?? 0));
287
+ }
288
+ catch {
289
+ continue;
290
+ }
291
+ if (until.isZero())
292
+ continue;
293
+ if (until.eq(ethers_1.constants.MaxUint256))
294
+ return true;
295
+ if (until.gte(now))
296
+ return true;
297
+ }
298
+ return false;
299
+ }
package/package.json ADDED
@@ -0,0 +1,130 @@
1
+ {
2
+ "name": "@poa-box/core",
3
+ "version": "0.1.0",
4
+ "description": "Browser-and-Node core for the POP (Proof of Participation) protocol: transaction intent builders, subgraph reads, canonical metadata, ABIs, and chain config. The stable layer the CLI, frontends, and integrators share.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./package.json": "./package.json",
17
+ "./abis": {
18
+ "types": "./dist/abis/index.d.ts",
19
+ "default": "./dist/abis/index.js"
20
+ },
21
+ "./graph": {
22
+ "types": "./dist/graph/index.d.ts",
23
+ "default": "./dist/graph/index.js"
24
+ },
25
+ "./graph/documents": {
26
+ "types": "./dist/graph/documents/index.d.ts",
27
+ "default": "./dist/graph/documents/index.js"
28
+ },
29
+ "./tx": {
30
+ "types": "./dist/tx/index.d.ts",
31
+ "default": "./dist/tx/index.js"
32
+ },
33
+ "./reads": {
34
+ "types": "./dist/reads/index.d.ts",
35
+ "default": "./dist/reads/index.js"
36
+ },
37
+ "./metadata": {
38
+ "types": "./dist/metadata/index.d.ts",
39
+ "default": "./dist/metadata/index.js"
40
+ },
41
+ "./execute": {
42
+ "types": "./dist/execute/index.d.ts",
43
+ "default": "./dist/execute/index.js"
44
+ },
45
+ "./*": {
46
+ "types": "./dist/*.d.ts",
47
+ "default": "./dist/*.js"
48
+ }
49
+ },
50
+ "typesVersions": {
51
+ "*": {
52
+ "dist/index.d.ts": [
53
+ "dist/index.d.ts"
54
+ ],
55
+ "abis": [
56
+ "dist/abis/index.d.ts"
57
+ ],
58
+ "graph": [
59
+ "dist/graph/index.d.ts"
60
+ ],
61
+ "graph/documents": [
62
+ "dist/graph/documents/index.d.ts"
63
+ ],
64
+ "tx": [
65
+ "dist/tx/index.d.ts"
66
+ ],
67
+ "reads": [
68
+ "dist/reads/index.d.ts"
69
+ ],
70
+ "metadata": [
71
+ "dist/metadata/index.d.ts"
72
+ ],
73
+ "execute": [
74
+ "dist/execute/index.d.ts"
75
+ ],
76
+ "*": [
77
+ "dist/*"
78
+ ]
79
+ }
80
+ },
81
+ "sideEffects": false,
82
+ "scripts": {
83
+ "build": "rm -rf dist && node scripts/gen-abis.mjs && tsc",
84
+ "prepublishOnly": "yarn build",
85
+ "test": "vitest run",
86
+ "test:watch": "vitest",
87
+ "lint": "node scripts/gen-abis.mjs && tsc --noEmit",
88
+ "api:check": "node scripts/check-api-surface.mjs",
89
+ "api:update": "node scripts/check-api-surface.mjs --update"
90
+ },
91
+ "dependencies": {
92
+ "@openzeppelin/merkle-tree": "^1.0.8",
93
+ "bs58": "^6.0.0",
94
+ "poseidon-lite": "^0.3.0"
95
+ },
96
+ "peerDependencies": {
97
+ "ethers": "^5.7.0",
98
+ "permissionless": "^0.3.0",
99
+ "viem": "^2.0.0"
100
+ },
101
+ "peerDependenciesMeta": {
102
+ "viem": {
103
+ "optional": true
104
+ },
105
+ "permissionless": {
106
+ "optional": true
107
+ }
108
+ },
109
+ "devDependencies": {
110
+ "@types/node": "^18.19.0",
111
+ "ethers": "5.7.2",
112
+ "permissionless": "^0.3.5",
113
+ "typescript": "^5.6.0",
114
+ "viem": "^2.47.12",
115
+ "vitest": "^1.6.0"
116
+ },
117
+ "engines": {
118
+ "node": ">=18.0.0"
119
+ },
120
+ "license": "MIT",
121
+ "repository": {
122
+ "type": "git",
123
+ "url": "git+https://github.com/poa-box/poa-cli.git",
124
+ "directory": "packages/core"
125
+ },
126
+ "homepage": "https://github.com/poa-box/poa-cli",
127
+ "bugs": {
128
+ "url": "https://github.com/poa-box/poa-cli/issues"
129
+ }
130
+ }