zk-eligibility-sdk 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/LICENSE +30 -0
  2. package/LICENSE-APACHE2.0 +165 -0
  3. package/README.md +398 -0
  4. package/RULES.md +490 -0
  5. package/SECURITY.md +347 -0
  6. package/dist/circuits/activityClass/activityclass.circom +59 -0
  7. package/dist/circuits/activityClass/activityclass_final.zkey +0 -0
  8. package/dist/circuits/activityClass/activityclass_js/activityclass.wasm +0 -0
  9. package/dist/circuits/activityClass/activityclass_js/generate_witness.js +20 -0
  10. package/dist/circuits/activityClass/activityclass_js/witness_calculator.js +337 -0
  11. package/dist/circuits/activityClass/verification_key.json +109 -0
  12. package/dist/circuits/cooldown/cooldown.circom +33 -0
  13. package/dist/circuits/cooldown/cooldown_final.zkey +0 -0
  14. package/dist/circuits/cooldown/cooldown_js/cooldown.wasm +0 -0
  15. package/dist/circuits/cooldown/cooldown_js/generate_witness.js +20 -0
  16. package/dist/circuits/cooldown/cooldown_js/witness_calculator.js +337 -0
  17. package/dist/circuits/cooldown/verification_key.json +94 -0
  18. package/dist/circuits/manifest.json +32 -0
  19. package/dist/circuits/minActivity/minactivity.circom +31 -0
  20. package/dist/circuits/minActivity/minactivity_final.zkey +0 -0
  21. package/dist/circuits/minActivity/minactivity_js/generate_witness.js +20 -0
  22. package/dist/circuits/minActivity/minactivity_js/minactivity.wasm +0 -0
  23. package/dist/circuits/minActivity/minactivity_js/witness_calculator.js +337 -0
  24. package/dist/circuits/minActivity/verification_key.json +94 -0
  25. package/dist/circuits/tokenHold/tokenhold.circom +31 -0
  26. package/dist/circuits/tokenHold/tokenhold_final.zkey +0 -0
  27. package/dist/circuits/tokenHold/tokenhold_js/generate_witness.js +20 -0
  28. package/dist/circuits/tokenHold/tokenhold_js/tokenhold.wasm +0 -0
  29. package/dist/circuits/tokenHold/tokenhold_js/witness_calculator.js +337 -0
  30. package/dist/circuits/tokenHold/verification_key.json +94 -0
  31. package/dist/circuits/walletAge/verification_key.json +94 -0
  32. package/dist/circuits/walletAge/walletage.circom +28 -0
  33. package/dist/circuits/walletAge/walletage_final.zkey +0 -0
  34. package/dist/circuits/walletAge/walletage_js/generate_witness.js +20 -0
  35. package/dist/circuits/walletAge/walletage_js/walletage.wasm +0 -0
  36. package/dist/circuits/walletAge/walletage_js/witness_calculator.js +337 -0
  37. package/dist/cli/commands/listRules.d.ts +2 -0
  38. package/dist/cli/commands/listRules.d.ts.map +1 -0
  39. package/dist/cli/commands/listRules.js +11 -0
  40. package/dist/cli/commands/listRules.js.map +1 -0
  41. package/dist/cli/commands/prove.d.ts +2 -0
  42. package/dist/cli/commands/prove.d.ts.map +1 -0
  43. package/dist/cli/commands/prove.js +39 -0
  44. package/dist/cli/commands/prove.js.map +1 -0
  45. package/dist/cli/commands/verify.d.ts +2 -0
  46. package/dist/cli/commands/verify.d.ts.map +1 -0
  47. package/dist/cli/commands/verify.js +38 -0
  48. package/dist/cli/commands/verify.js.map +1 -0
  49. package/dist/cli/index.d.ts +3 -0
  50. package/dist/cli/index.d.ts.map +1 -0
  51. package/dist/cli/index.js +57 -0
  52. package/dist/cli/index.js.map +1 -0
  53. package/dist/constants/limits.d.ts +7 -0
  54. package/dist/constants/limits.d.ts.map +1 -0
  55. package/dist/constants/limits.js +10 -0
  56. package/dist/constants/limits.js.map +1 -0
  57. package/dist/index.d.ts +7 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +25 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/providers/alchemy.d.ts +6 -0
  62. package/dist/providers/alchemy.d.ts.map +1 -0
  63. package/dist/providers/alchemy.js +119 -0
  64. package/dist/providers/alchemy.js.map +1 -0
  65. package/dist/providers/index.d.ts +4 -0
  66. package/dist/providers/index.d.ts.map +1 -0
  67. package/dist/providers/index.js +67 -0
  68. package/dist/providers/index.js.map +1 -0
  69. package/dist/providers/infura.d.ts +4 -0
  70. package/dist/providers/infura.d.ts.map +1 -0
  71. package/dist/providers/infura.js +13 -0
  72. package/dist/providers/infura.js.map +1 -0
  73. package/dist/providers/types.d.ts +8 -0
  74. package/dist/providers/types.d.ts.map +1 -0
  75. package/dist/providers/types.js +3 -0
  76. package/dist/providers/types.js.map +1 -0
  77. package/dist/rules/activityClass/activityClass.d.ts +12 -0
  78. package/dist/rules/activityClass/activityClass.d.ts.map +1 -0
  79. package/dist/rules/activityClass/activityClass.js +39 -0
  80. package/dist/rules/activityClass/activityClass.js.map +1 -0
  81. package/dist/rules/activityClass/config.d.ts +3 -0
  82. package/dist/rules/activityClass/config.d.ts.map +1 -0
  83. package/dist/rules/activityClass/config.js +3 -0
  84. package/dist/rules/activityClass/config.js.map +1 -0
  85. package/dist/rules/cooldown/config.d.ts +4 -0
  86. package/dist/rules/cooldown/config.d.ts.map +1 -0
  87. package/dist/rules/cooldown/config.js +3 -0
  88. package/dist/rules/cooldown/config.js.map +1 -0
  89. package/dist/rules/cooldown/cooldown.d.ts +12 -0
  90. package/dist/rules/cooldown/cooldown.d.ts.map +1 -0
  91. package/dist/rules/cooldown/cooldown.js +65 -0
  92. package/dist/rules/cooldown/cooldown.js.map +1 -0
  93. package/dist/rules/minActivity/config.d.ts +4 -0
  94. package/dist/rules/minActivity/config.d.ts.map +1 -0
  95. package/dist/rules/minActivity/config.js +3 -0
  96. package/dist/rules/minActivity/config.js.map +1 -0
  97. package/dist/rules/minActivity/minActivity.d.ts +12 -0
  98. package/dist/rules/minActivity/minActivity.d.ts.map +1 -0
  99. package/dist/rules/minActivity/minActivity.js +61 -0
  100. package/dist/rules/minActivity/minActivity.js.map +1 -0
  101. package/dist/rules/tokenHold/config.d.ts +5 -0
  102. package/dist/rules/tokenHold/config.d.ts.map +1 -0
  103. package/dist/rules/tokenHold/config.js +3 -0
  104. package/dist/rules/tokenHold/config.js.map +1 -0
  105. package/dist/rules/tokenHold/tokenHold.d.ts +12 -0
  106. package/dist/rules/tokenHold/tokenHold.d.ts.map +1 -0
  107. package/dist/rules/tokenHold/tokenHold.js +68 -0
  108. package/dist/rules/tokenHold/tokenHold.js.map +1 -0
  109. package/dist/rules/walletAge/config.d.ts +4 -0
  110. package/dist/rules/walletAge/config.d.ts.map +1 -0
  111. package/dist/rules/walletAge/config.js +3 -0
  112. package/dist/rules/walletAge/config.js.map +1 -0
  113. package/dist/rules/walletAge/walletAge.d.ts +12 -0
  114. package/dist/rules/walletAge/walletAge.d.ts.map +1 -0
  115. package/dist/rules/walletAge/walletAge.js +76 -0
  116. package/dist/rules/walletAge/walletAge.js.map +1 -0
  117. package/dist/sdk/ZKEligibiltySDK.d.ts +13 -0
  118. package/dist/sdk/ZKEligibiltySDK.d.ts.map +1 -0
  119. package/dist/sdk/ZKEligibiltySDK.js +57 -0
  120. package/dist/sdk/ZKEligibiltySDK.js.map +1 -0
  121. package/dist/sdk/errors.d.ts +10 -0
  122. package/dist/sdk/errors.d.ts.map +1 -0
  123. package/dist/sdk/errors.js +23 -0
  124. package/dist/sdk/errors.js.map +1 -0
  125. package/dist/sdk/executeRule.d.ts +4 -0
  126. package/dist/sdk/executeRule.d.ts.map +1 -0
  127. package/dist/sdk/executeRule.js +38 -0
  128. package/dist/sdk/executeRule.js.map +1 -0
  129. package/dist/sdk/generateProof.d.ts +3 -0
  130. package/dist/sdk/generateProof.d.ts.map +1 -0
  131. package/dist/sdk/generateProof.js +14 -0
  132. package/dist/sdk/generateProof.js.map +1 -0
  133. package/dist/sdk/index.d.ts +6 -0
  134. package/dist/sdk/index.d.ts.map +1 -0
  135. package/dist/sdk/index.js +27 -0
  136. package/dist/sdk/index.js.map +1 -0
  137. package/dist/sdk/ruleRegistry.d.ts +11 -0
  138. package/dist/sdk/ruleRegistry.d.ts.map +1 -0
  139. package/dist/sdk/ruleRegistry.js +41 -0
  140. package/dist/sdk/ruleRegistry.js.map +1 -0
  141. package/dist/sdk/types.d.ts +24 -0
  142. package/dist/sdk/types.d.ts.map +1 -0
  143. package/dist/sdk/types.js +3 -0
  144. package/dist/sdk/types.js.map +1 -0
  145. package/dist/sdk/verifyProof.d.ts +9 -0
  146. package/dist/sdk/verifyProof.d.ts.map +1 -0
  147. package/dist/sdk/verifyProof.js +21 -0
  148. package/dist/sdk/verifyProof.js.map +1 -0
  149. package/dist/types/ErrorCode.d.ts +12 -0
  150. package/dist/types/ErrorCode.d.ts.map +1 -0
  151. package/dist/types/ErrorCode.js +16 -0
  152. package/dist/types/ErrorCode.js.map +1 -0
  153. package/dist/types/Errors.d.ts +13 -0
  154. package/dist/types/Errors.d.ts.map +1 -0
  155. package/dist/types/Errors.js +32 -0
  156. package/dist/types/Errors.js.map +1 -0
  157. package/dist/types/Proof.d.ts +8 -0
  158. package/dist/types/Proof.d.ts.map +1 -0
  159. package/dist/types/Proof.js +3 -0
  160. package/dist/types/Proof.js.map +1 -0
  161. package/dist/types/Provider.d.ts +8 -0
  162. package/dist/types/Provider.d.ts.map +1 -0
  163. package/dist/types/Provider.js +3 -0
  164. package/dist/types/Provider.js.map +1 -0
  165. package/dist/types/Rule.d.ts +6 -0
  166. package/dist/types/Rule.d.ts.map +1 -0
  167. package/dist/types/Rule.js +3 -0
  168. package/dist/types/Rule.js.map +1 -0
  169. package/dist/zk/cache.d.ts +7 -0
  170. package/dist/zk/cache.d.ts.map +1 -0
  171. package/dist/zk/cache.js +28 -0
  172. package/dist/zk/cache.js.map +1 -0
  173. package/dist/zk/generateProof.d.ts +10 -0
  174. package/dist/zk/generateProof.d.ts.map +1 -0
  175. package/dist/zk/generateProof.js +49 -0
  176. package/dist/zk/generateProof.js.map +1 -0
  177. package/dist/zk/generateWitness.d.ts +8 -0
  178. package/dist/zk/generateWitness.d.ts.map +1 -0
  179. package/dist/zk/generateWitness.js +14 -0
  180. package/dist/zk/generateWitness.js.map +1 -0
  181. package/dist/zk/runCmd.d.ts +2 -0
  182. package/dist/zk/runCmd.d.ts.map +1 -0
  183. package/dist/zk/runCmd.js +32 -0
  184. package/dist/zk/runCmd.js.map +1 -0
  185. package/dist/zk/verifyProof.d.ts +2 -0
  186. package/dist/zk/verifyProof.d.ts.map +1 -0
  187. package/dist/zk/verifyProof.js +33 -0
  188. package/dist/zk/verifyProof.js.map +1 -0
  189. package/package.json +39 -0
package/RULES.md ADDED
@@ -0,0 +1,490 @@
1
+ # Eligibility Rules Specification
2
+
3
+ **This document defines all eligibility rules supported by the ZK Eligibility SDK.**
4
+
5
+ Each rule is independently verifiable, privacy-preserving, and composable with other rules.
6
+
7
+ All rules are deterministic:
8
+ the same inputs always produce the same proof and public signals.
9
+
10
+ ---
11
+
12
+ ## General Rule Model
13
+
14
+ All rules follow the same cryptographic pattern:
15
+
16
+ 1. **Public blockchain data** is fetched from chain (via RPC)
17
+ 2. **Private witness** is generated (never shared)
18
+ 3. **ZK proof** is computed using witness + constraints
19
+ 4. **Minimal public signals** are revealed (minimal public signals -> 1-4 values per rule)
20
+
21
+ **Key insight:** Transaction history, exact metrics, and identifiers are never revealed—only eligibility status.
22
+
23
+ ---
24
+
25
+ ## Rule 1: WALLET_AGE
26
+
27
+ ### Purpose
28
+ Cryptographically prove that a wallet's first outbound transaction occurred before a specified block number.
29
+
30
+ ### Inputs
31
+ | Name | Type | Description |
32
+ |------|------|-------------|
33
+ | `walletAddress` | `string` | Ethereum address |
34
+ | `thresholdBlock` | `number` | Block number threshold |
35
+
36
+ ### Statement Proven
37
+ ```
38
+ The wallet's first outbound transaction block < thresholdBlock
39
+ ```
40
+
41
+ ### Public Signals
42
+ | Signal | Value | Meaning |
43
+ |--------|-------|---------|
44
+ | `isValid` | 0 or 1 | 1 if statement is true, 0 otherwise |
45
+
46
+ ### EXAMPLE API USAGE
47
+
48
+ import { ZKEligibilitySDK } from "zk-eligibility-sdk";
49
+
50
+ const wallet = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
51
+
52
+ const result = await ZKEligibilitySDK.prove(
53
+ "WALLET_AGE",
54
+ wallet,
55
+ {
56
+ thresholdBlock: 15_000_000
57
+ }
58
+ );
59
+
60
+ if (result.isValid) {
61
+ console.log("Wallet is old enough ✅");
62
+ } else {
63
+ console.log("Not eligible:", result.reason);
64
+ }
65
+
66
+ ### EXAMPLE CLI USAGE
67
+
68
+ npx zkesdk prove WALLET_AGE 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 "{\"thresholdBlock\": 1000000}"
69
+
70
+ ### What Is Hidden
71
+ - ✅ Exact wallet creation time
72
+ - ✅ Exact first transaction block
73
+ - ✅ Transaction hash or amounts
74
+ - ✅ Sender/receiver identities
75
+
76
+ ### Edge Cases Handled
77
+ - Wallets with no outbound transactions → `isValid = 0`
78
+ - Negative thresholds → rejected at SDK level
79
+ - Block reorgs → proof reflects RPC provider's canonical view at generation time
80
+
81
+ ### Use Cases
82
+
83
+ ✅ Sybil resistance (new wallets spam)
84
+ ✅ Legacy user eligibility (early Ethereum participants)
85
+ ✅ Loyalty tiers (long-term community members)
86
+ ✅ Governance access (established accounts only)
87
+
88
+ ### When NOT to Use
89
+
90
+ ❌ Identity verification (doesn't prove ownership)
91
+ ❌ Real-time guarantees (depends on chain state)
92
+ ❌ Contract wallets (no outbound tx requirement)
93
+
94
+ ---
95
+
96
+ ## Rule 2: MIN_ACTIVITY
97
+
98
+ ### Purpose
99
+ Cryptographically prove that a wallet has executed at least N outbound transactions.
100
+
101
+ ### Inputs
102
+ | Name | Type | Description |
103
+ |------|------|-------------|
104
+ | `walletAddress` | `string` | Ethereum address |
105
+ | `minTx` | `number` | Minimum transaction count |
106
+
107
+ ### Statement Proven
108
+ ```
109
+ Wallet's outbound transaction count >= minTx
110
+ ```
111
+
112
+ ### Public Signals
113
+ | Signal | Value | Meaning |
114
+ |--------|-------|---------|
115
+ | `isValid` | 0 or 1 | 1 if statement is true, 0 otherwise |
116
+
117
+ ### What Is Hidden
118
+ - ✅ Exact transaction count
119
+ - ✅ Transaction hashes or amounts
120
+ - ✅ Timing of activity
121
+ - ✅ Recipient addresses
122
+
123
+ ### Edge Cases Handled
124
+ - `minTx > chain's total transactions` → rejected
125
+ - `minTx = 0` → always true (rejected as pointless)
126
+ - Contract wallets with high tx counts → processed normally
127
+
128
+ ### EXAMPLE API USAGE
129
+
130
+ import { ZKEligibilitySDK } from "zk-eligibility-sdk";
131
+
132
+ const wallet = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
133
+
134
+ const result = await ZKEligibilitySDK.prove(
135
+ "MIN_ACTIVITY",
136
+ wallet,
137
+ {
138
+ minTx: 10
139
+ }
140
+ );
141
+
142
+ if (result.isValid) {
143
+ console.log("Wallet is active ✅");
144
+ }
145
+
146
+ ### EXAMPLE CLI USAGE
147
+
148
+ npx zkesdk prove MIN_ACTIVITY 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 '{"minTx":10}'
149
+
150
+ ### Use Cases
151
+
152
+ ✅ Sybil resistance (filter one-time accounts)
153
+ ✅ Activity-based gating (active users only)
154
+ ✅ Airdrop eligibility (engaged community)
155
+ ✅ Bot detection (humans have patterns)
156
+
157
+ ### When NOT to Use
158
+
159
+ ❌ Identity verification (activity ≠ identity)
160
+ ❌ Fine-grained reputation scoring
161
+ ❌ Balance-based distribution (different rule needed)
162
+
163
+ ---
164
+
165
+ ## Rule 3: COOLDOWN
166
+
167
+ ### Purpose
168
+ Cryptographically prove that a wallet has been inactive for a minimum number of blocks.
169
+
170
+ ### Inputs
171
+ | Name | Type | Description |
172
+ |------|------|-------------|
173
+ | `walletAddress` | `string` | Ethereum address |
174
+ | `cooldownBlocks` | `number` | Minimum blocks of inactivity |
175
+
176
+ ### Statement Proven
177
+ ```
178
+ Current block - Last outbound tx block >= cooldownBlocks
179
+ ```
180
+
181
+ ### Public Signals
182
+ | Signal | Value | Meaning |
183
+ |--------|-------|---------|
184
+ | `isValid` | 0 or 1 | 1 if statement is true, 0 otherwise |
185
+
186
+ ### What Is Hidden
187
+ - ✅ Exact last activity block
188
+ - ✅ Transaction amounts or hashes
189
+ - ✅ Recent activity patterns
190
+ - ✅ Time between transactions
191
+
192
+ ### Edge Cases Handled
193
+ - Wallets with no Outbound transactions -> rule fails by default
194
+ - `cooldownBlocks > chain age` → `isValid = 0`
195
+ - Block reorgs → latest canonical state
196
+
197
+ ### EXAMPLE API USAGE
198
+
199
+ import { ZKEligibilitySDK } from "zk-eligibility-sdk";
200
+
201
+ const wallet = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
202
+
203
+ const result = await ZKEligibilitySDK.prove(
204
+ "COOLDOWN",
205
+ wallet,
206
+ {
207
+ cooldownBlocks: 50_000
208
+ }
209
+ );
210
+
211
+ if (result.isValid) {
212
+ console.log("Cooldown satisfied ✅");
213
+ }
214
+
215
+ ### EXAMPLE CLI USAGE
216
+
217
+ npx zkesdk prove COOLDOWN 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 '{"cooldownBlocks":50000}'
218
+
219
+ ### Use Cases
220
+
221
+ ✅ Rate limiting (prevent rapid repeat access)
222
+ ✅ Cooldown-based mints (NFTs with waiting periods)
223
+ ✅ Anti-bot measures (human-like behavior)
224
+ ✅ Temporal access control (time-gated rewards)
225
+
226
+ ### When NOT to Use
227
+
228
+ ❌ Time-critical enforcement (chain reorgs unpredictable)
229
+ ❌ High-frequency trading (not designed for it)
230
+
231
+ ---
232
+
233
+ ## Rule 4: TOKEN_HOLD
234
+
235
+ ### Purpose
236
+ Cryptographically prove that a wallet has held a specific token for at least N blocks.
237
+
238
+ ### Inputs
239
+ | Name | Type | Description |
240
+ |------|------|-------------|
241
+ | `walletAddress` | `string` | Ethereum address |
242
+ | `tokenAddress` | `string` | ERC-20/721/1155 contract |
243
+ | `minHoldBlocks` | `number` | Minimum holding duration |
244
+
245
+ ### Statement Proven
246
+ ```
247
+ First token receipt block + minHoldBlocks <= current block
248
+ ```
249
+
250
+ ### Public Signals
251
+ | Signal | Value | Meaning |
252
+ |--------|-------|---------|
253
+ | `isValid` | 0 or 1 | 1 if statement is true, 0 otherwise |
254
+
255
+ ### What Is Hidden
256
+ - ✅ Token balance (current or historical)
257
+ - ✅ Exact acquisition time
258
+ - ✅ Transfer history
259
+ - ✅ Amount held
260
+ - ✅ Recipient addresses in transfers
261
+
262
+ ### Edge Cases Handled
263
+ - Wallet never received token → `isValid = 0`
264
+ - Token transfers out are **not** tracked (holding duration only)
265
+ - Token burning/loss doesn't affect proof
266
+
267
+ ### EXAMPLE API USAGE
268
+
269
+ import { ZKEligibilitySDK } from "zk-eligibility-sdk";
270
+
271
+ const wallet = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
272
+
273
+ const result = await ZKEligibilitySDK.prove(
274
+ "TOKEN_HOLD",
275
+ wallet,
276
+ {
277
+ tokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
278
+ minHoldBlocks: 100_000
279
+ }
280
+ );
281
+
282
+ if (result.isValid) {
283
+ console.log("Token held long enough ✅");
284
+ }
285
+
286
+ ### EXAMPLE CLI USAGE
287
+
288
+ npx zkesdk prove TOKEN_HOLD 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 '{"tokenAddress":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","minHoldBlocks":100000}'
289
+
290
+ ### Use Cases
291
+
292
+ ✅ Holder privileges (rewards for loyal holders)
293
+ ✅ Loyalty programs (time-weighted access)
294
+ ✅ Fair distribution (prevent pump-and-dump gaming)
295
+ ✅ Community governance (long-term stakeholders)
296
+
297
+ ### When NOT to Use
298
+
299
+ ❌ Balance-based gating (this proves duration, not amount)
300
+ ❌ Real-time balance checks (state can change)
301
+
302
+ ⚠️ This rule proves holding duration based on first receipt only.
303
+ It does NOT prove current ownership or balance.
304
+
305
+ ---
306
+
307
+ ## Rule 5: ACTIVITY_CLASS
308
+
309
+ ### Purpose
310
+ Classify a wallet into a coarse activity band without revealing exact metrics.
311
+
312
+ ### Inputs
313
+ | Name | Type | Description |
314
+ |------|------|-------------|
315
+ | `walletAddress` | `string` | Ethereum address |
316
+
317
+ ### Classification Output
318
+ | Class | Activity Level | Transaction Range |
319
+ |-------|-----------------|-------------------|
320
+ | `0` | Very low | <5 tx |
321
+ | `1` | Low | 5-49 tx |
322
+ | `2` | Medium | 51-500 tx |
323
+ | `3` | High | 500+ tx |
324
+
325
+ ### Public Signals
326
+ | Signal | Value | Meaning |
327
+ |--------|-------|---------|
328
+ | `classId` | 0, 1, 2, or 3 | Activity band |
329
+
330
+ ### What Is Hidden
331
+ - ✅ Exact transaction count
332
+ - ✅ Transaction history
333
+ - ✅ Time distribution of activity
334
+ - ✅ Amounts transferred
335
+
336
+ ### Edge Cases Handled
337
+ - No outbound tx → `classId = 0`
338
+ - Classification thresholds are hardcoded in v1.0
339
+ - Classes are coarse by design (privacy + usability)
340
+
341
+ ### EXAMPLE API USAGE
342
+
343
+ import { ZKEligibilitySDK } from "zk-eligibility-sdk";
344
+
345
+ const wallet = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
346
+
347
+ const result = await ZKEligibilitySDK.prove(
348
+ "ACTIVITY_CLASS",
349
+ wallet,
350
+ {}
351
+ );
352
+
353
+ if (result.classId !== undefined) {
354
+ console.log("Activity class:", result.classId);
355
+ }
356
+
357
+ ### EXAMPLE CLI USAGE
358
+
359
+ npx zkesdk prove ACTIVITY_CLASS 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 '{}'
360
+
361
+ ### Use Cases
362
+
363
+ ✅ Reputation tiers (tier-based rewards)
364
+ ✅ Fair distribution (prevent whales from dominating)
365
+ ✅ Anti-sybil heuristics (filter inactive accounts)
366
+ ✅ Matchmaking (pair users of similar activity)
367
+
368
+ ### When NOT to Use
369
+
370
+ ❌ Detailed scoring systems
371
+ ❌ Fine-grained ranking
372
+ ❌ Identity inference (not designed for it)
373
+
374
+ ---
375
+
376
+ ## Composability
377
+
378
+ ### Combining Rules
379
+
380
+ Rules are **fully independent**. Combine them at the application layer:
381
+
382
+ ```typescript
383
+ // Proof 1: Wallet is old enough
384
+ const ageProof = await ZKEligibilitySDK.prove("WALLET_AGE", wallet, {
385
+ thresholdBlock: 15_000_000
386
+ });
387
+
388
+ // Proof 2: Wallet is active enough
389
+ const activityProof = await ZKEligibilitySDK.prove("MIN_ACTIVITY", wallet, {
390
+ minTx: 10
391
+ });
392
+
393
+ // Proof 3: Wallet held token long enough
394
+ const holdProof = await ZKEligibilitySDK.prove("TOKEN_HOLD", wallet, {
395
+ tokenAddress: "0x...",
396
+ minHoldBlocks: 100_000
397
+ });
398
+
399
+ // Application logic: all three must be true
400
+ if (ageProof.isValid && activityProof.isValid && holdProof.isValid) {
401
+ grantAccess();
402
+ }
403
+ ```
404
+
405
+ ### Important Notes
406
+
407
+ ⚠️ ZK guarantees apply **per rule**, not across rules
408
+ ⚠️ Misuse of rules may produce misleading results
409
+ ⚠️ Always combine at application layer (not ZK layer)
410
+
411
+ ---
412
+
413
+ ## Rule Semantics & Limitations
414
+
415
+ ### What All Rules Assume
416
+
417
+ ✅ RPC provider returns correct data
418
+ ✅ Chain state is finalized
419
+ ✅ No catastrophic reorgs
420
+
421
+ ### What All Rules Ignore
422
+
423
+ ❌ Smart contract interactions (DEX swaps, etc.)
424
+ ❌ Non-Ethereum chains (Bitcoin, Polygon, etc.)
425
+ ❌ Contract-to-contract calls
426
+ ❌ Intent or motivation
427
+
428
+ ### When Rules Disagree With Reality
429
+
430
+ If a rule proof doesn't match your expectations, check:
431
+
432
+ 1. **RPC provider data** — Is it returning correct values?
433
+ 2. **Rule semantics** — Are you using the right rule?
434
+ 3. **Parameter boundaries** — Are thresholds sensible?
435
+ 4. **Edge case handling** — See rule-specific edge cases above
436
+
437
+ ---
438
+
439
+ ## Verification & Proof Format
440
+
441
+ All proofs use **standard Groth16 format**:
442
+
443
+ ```json
444
+ {
445
+ "pi_a": [x, y, 1],
446
+ "pi_b": [[x1, x2], [y1, y2], [1, 0]],
447
+ "pi_c": [x, y, 1],
448
+ "protocol": "groth16",
449
+ "curve": "bn128"
450
+ }
451
+ ```
452
+
453
+ Public signals are simple arrays:
454
+
455
+ ```json
456
+ ["0"] // isValid = 0 (not eligible)
457
+ ["1"] // isValid = 1 (eligible)
458
+ ["2"] // classId = 2 (medium activity)
459
+ ```
460
+
461
+ ---
462
+
463
+ ## Version Guarantees
464
+
465
+ ### v1.x Locked
466
+
467
+ 🔒 These will **never change** in v1.x:
468
+ - Rule definitions
469
+ - Constraint systems
470
+ - Proof format
471
+ - Public signal layout
472
+
473
+ ### Breaking Changes → v2.0
474
+
475
+ Any change to rules or constraints triggers a major version bump.
476
+
477
+ ---
478
+
479
+ ## Final Design Principle
480
+
481
+ > **Each rule proves exactly one statement and nothing more.**
482
+
483
+ **Rules are:**
484
+ - ✅ Independent (don't depend on other rules)
485
+ - ✅ Minimal (reveal only what's necessary)
486
+ - ✅ Composable (combine at app layer)
487
+ - ✅ Conservative (fail safely, never default-true)
488
+
489
+ **For security assumptions, see [SECURITY.md](./SECURITY.md).
490
+ For SDK usage, see [README.md](./README.md).**