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.
- package/LICENSE +30 -0
- package/LICENSE-APACHE2.0 +165 -0
- package/README.md +398 -0
- package/RULES.md +490 -0
- package/SECURITY.md +347 -0
- package/dist/circuits/activityClass/activityclass.circom +59 -0
- package/dist/circuits/activityClass/activityclass_final.zkey +0 -0
- package/dist/circuits/activityClass/activityclass_js/activityclass.wasm +0 -0
- package/dist/circuits/activityClass/activityclass_js/generate_witness.js +20 -0
- package/dist/circuits/activityClass/activityclass_js/witness_calculator.js +337 -0
- package/dist/circuits/activityClass/verification_key.json +109 -0
- package/dist/circuits/cooldown/cooldown.circom +33 -0
- package/dist/circuits/cooldown/cooldown_final.zkey +0 -0
- package/dist/circuits/cooldown/cooldown_js/cooldown.wasm +0 -0
- package/dist/circuits/cooldown/cooldown_js/generate_witness.js +20 -0
- package/dist/circuits/cooldown/cooldown_js/witness_calculator.js +337 -0
- package/dist/circuits/cooldown/verification_key.json +94 -0
- package/dist/circuits/manifest.json +32 -0
- package/dist/circuits/minActivity/minactivity.circom +31 -0
- package/dist/circuits/minActivity/minactivity_final.zkey +0 -0
- package/dist/circuits/minActivity/minactivity_js/generate_witness.js +20 -0
- package/dist/circuits/minActivity/minactivity_js/minactivity.wasm +0 -0
- package/dist/circuits/minActivity/minactivity_js/witness_calculator.js +337 -0
- package/dist/circuits/minActivity/verification_key.json +94 -0
- package/dist/circuits/tokenHold/tokenhold.circom +31 -0
- package/dist/circuits/tokenHold/tokenhold_final.zkey +0 -0
- package/dist/circuits/tokenHold/tokenhold_js/generate_witness.js +20 -0
- package/dist/circuits/tokenHold/tokenhold_js/tokenhold.wasm +0 -0
- package/dist/circuits/tokenHold/tokenhold_js/witness_calculator.js +337 -0
- package/dist/circuits/tokenHold/verification_key.json +94 -0
- package/dist/circuits/walletAge/verification_key.json +94 -0
- package/dist/circuits/walletAge/walletage.circom +28 -0
- package/dist/circuits/walletAge/walletage_final.zkey +0 -0
- package/dist/circuits/walletAge/walletage_js/generate_witness.js +20 -0
- package/dist/circuits/walletAge/walletage_js/walletage.wasm +0 -0
- package/dist/circuits/walletAge/walletage_js/witness_calculator.js +337 -0
- package/dist/cli/commands/listRules.d.ts +2 -0
- package/dist/cli/commands/listRules.d.ts.map +1 -0
- package/dist/cli/commands/listRules.js +11 -0
- package/dist/cli/commands/listRules.js.map +1 -0
- package/dist/cli/commands/prove.d.ts +2 -0
- package/dist/cli/commands/prove.d.ts.map +1 -0
- package/dist/cli/commands/prove.js +39 -0
- package/dist/cli/commands/prove.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +2 -0
- package/dist/cli/commands/verify.d.ts.map +1 -0
- package/dist/cli/commands/verify.js +38 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +57 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/constants/limits.d.ts +7 -0
- package/dist/constants/limits.d.ts.map +1 -0
- package/dist/constants/limits.js +10 -0
- package/dist/constants/limits.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/alchemy.d.ts +6 -0
- package/dist/providers/alchemy.d.ts.map +1 -0
- package/dist/providers/alchemy.js +119 -0
- package/dist/providers/alchemy.js.map +1 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +67 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/infura.d.ts +4 -0
- package/dist/providers/infura.d.ts.map +1 -0
- package/dist/providers/infura.js +13 -0
- package/dist/providers/infura.js.map +1 -0
- package/dist/providers/types.d.ts +8 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +3 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/rules/activityClass/activityClass.d.ts +12 -0
- package/dist/rules/activityClass/activityClass.d.ts.map +1 -0
- package/dist/rules/activityClass/activityClass.js +39 -0
- package/dist/rules/activityClass/activityClass.js.map +1 -0
- package/dist/rules/activityClass/config.d.ts +3 -0
- package/dist/rules/activityClass/config.d.ts.map +1 -0
- package/dist/rules/activityClass/config.js +3 -0
- package/dist/rules/activityClass/config.js.map +1 -0
- package/dist/rules/cooldown/config.d.ts +4 -0
- package/dist/rules/cooldown/config.d.ts.map +1 -0
- package/dist/rules/cooldown/config.js +3 -0
- package/dist/rules/cooldown/config.js.map +1 -0
- package/dist/rules/cooldown/cooldown.d.ts +12 -0
- package/dist/rules/cooldown/cooldown.d.ts.map +1 -0
- package/dist/rules/cooldown/cooldown.js +65 -0
- package/dist/rules/cooldown/cooldown.js.map +1 -0
- package/dist/rules/minActivity/config.d.ts +4 -0
- package/dist/rules/minActivity/config.d.ts.map +1 -0
- package/dist/rules/minActivity/config.js +3 -0
- package/dist/rules/minActivity/config.js.map +1 -0
- package/dist/rules/minActivity/minActivity.d.ts +12 -0
- package/dist/rules/minActivity/minActivity.d.ts.map +1 -0
- package/dist/rules/minActivity/minActivity.js +61 -0
- package/dist/rules/minActivity/minActivity.js.map +1 -0
- package/dist/rules/tokenHold/config.d.ts +5 -0
- package/dist/rules/tokenHold/config.d.ts.map +1 -0
- package/dist/rules/tokenHold/config.js +3 -0
- package/dist/rules/tokenHold/config.js.map +1 -0
- package/dist/rules/tokenHold/tokenHold.d.ts +12 -0
- package/dist/rules/tokenHold/tokenHold.d.ts.map +1 -0
- package/dist/rules/tokenHold/tokenHold.js +68 -0
- package/dist/rules/tokenHold/tokenHold.js.map +1 -0
- package/dist/rules/walletAge/config.d.ts +4 -0
- package/dist/rules/walletAge/config.d.ts.map +1 -0
- package/dist/rules/walletAge/config.js +3 -0
- package/dist/rules/walletAge/config.js.map +1 -0
- package/dist/rules/walletAge/walletAge.d.ts +12 -0
- package/dist/rules/walletAge/walletAge.d.ts.map +1 -0
- package/dist/rules/walletAge/walletAge.js +76 -0
- package/dist/rules/walletAge/walletAge.js.map +1 -0
- package/dist/sdk/ZKEligibiltySDK.d.ts +13 -0
- package/dist/sdk/ZKEligibiltySDK.d.ts.map +1 -0
- package/dist/sdk/ZKEligibiltySDK.js +57 -0
- package/dist/sdk/ZKEligibiltySDK.js.map +1 -0
- package/dist/sdk/errors.d.ts +10 -0
- package/dist/sdk/errors.d.ts.map +1 -0
- package/dist/sdk/errors.js +23 -0
- package/dist/sdk/errors.js.map +1 -0
- package/dist/sdk/executeRule.d.ts +4 -0
- package/dist/sdk/executeRule.d.ts.map +1 -0
- package/dist/sdk/executeRule.js +38 -0
- package/dist/sdk/executeRule.js.map +1 -0
- package/dist/sdk/generateProof.d.ts +3 -0
- package/dist/sdk/generateProof.d.ts.map +1 -0
- package/dist/sdk/generateProof.js +14 -0
- package/dist/sdk/generateProof.js.map +1 -0
- package/dist/sdk/index.d.ts +6 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +27 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/ruleRegistry.d.ts +11 -0
- package/dist/sdk/ruleRegistry.d.ts.map +1 -0
- package/dist/sdk/ruleRegistry.js +41 -0
- package/dist/sdk/ruleRegistry.js.map +1 -0
- package/dist/sdk/types.d.ts +24 -0
- package/dist/sdk/types.d.ts.map +1 -0
- package/dist/sdk/types.js +3 -0
- package/dist/sdk/types.js.map +1 -0
- package/dist/sdk/verifyProof.d.ts +9 -0
- package/dist/sdk/verifyProof.d.ts.map +1 -0
- package/dist/sdk/verifyProof.js +21 -0
- package/dist/sdk/verifyProof.js.map +1 -0
- package/dist/types/ErrorCode.d.ts +12 -0
- package/dist/types/ErrorCode.d.ts.map +1 -0
- package/dist/types/ErrorCode.js +16 -0
- package/dist/types/ErrorCode.js.map +1 -0
- package/dist/types/Errors.d.ts +13 -0
- package/dist/types/Errors.d.ts.map +1 -0
- package/dist/types/Errors.js +32 -0
- package/dist/types/Errors.js.map +1 -0
- package/dist/types/Proof.d.ts +8 -0
- package/dist/types/Proof.d.ts.map +1 -0
- package/dist/types/Proof.js +3 -0
- package/dist/types/Proof.js.map +1 -0
- package/dist/types/Provider.d.ts +8 -0
- package/dist/types/Provider.d.ts.map +1 -0
- package/dist/types/Provider.js +3 -0
- package/dist/types/Provider.js.map +1 -0
- package/dist/types/Rule.d.ts +6 -0
- package/dist/types/Rule.d.ts.map +1 -0
- package/dist/types/Rule.js +3 -0
- package/dist/types/Rule.js.map +1 -0
- package/dist/zk/cache.d.ts +7 -0
- package/dist/zk/cache.d.ts.map +1 -0
- package/dist/zk/cache.js +28 -0
- package/dist/zk/cache.js.map +1 -0
- package/dist/zk/generateProof.d.ts +10 -0
- package/dist/zk/generateProof.d.ts.map +1 -0
- package/dist/zk/generateProof.js +49 -0
- package/dist/zk/generateProof.js.map +1 -0
- package/dist/zk/generateWitness.d.ts +8 -0
- package/dist/zk/generateWitness.d.ts.map +1 -0
- package/dist/zk/generateWitness.js +14 -0
- package/dist/zk/generateWitness.js.map +1 -0
- package/dist/zk/runCmd.d.ts +2 -0
- package/dist/zk/runCmd.d.ts.map +1 -0
- package/dist/zk/runCmd.js +32 -0
- package/dist/zk/runCmd.js.map +1 -0
- package/dist/zk/verifyProof.d.ts +2 -0
- package/dist/zk/verifyProof.d.ts.map +1 -0
- package/dist/zk/verifyProof.js +33 -0
- package/dist/zk/verifyProof.js.map +1 -0
- 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).**
|