Skip to content

Commit 8fd99ff

Browse files
mohammadfawazMohammad Fawaz
authored andcommitted
add quadratic_vote example showcasing library packages
1 parent aa0597a commit 8fd99ff

21 files changed

Lines changed: 1687 additions & 1 deletion

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
.idea/
2-
**/README.md

quadratic_vote/README.md

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# Quadratic Vote
2+
3+
## Summary
4+
5+
`quadratic_vote` is a governance example that demonstrates Leo **library packages**.
6+
7+
Two reusable libraries handle all the math:
8+
9+
| Package | Kind | What it provides |
10+
|---------|------|-----------------|
11+
| `sqrt_math` | library | `isqrt` — integer square root via Newton's method |
12+
| `vote_math` | library | QV weights, credit costs, voter profiles (depends on `sqrt_math`) |
13+
| `governance.aleo` | program | Private vote records, proposal outcome checks |
14+
15+
Under **Quadratic Voting (QV)**, a participant's vote weight equals ⌊√(token\_balance)⌋ and
16+
casting *k* votes costs ** credits. This makes influence grow with the square root of
17+
wealth rather than linearly — fairer to smaller holders.
18+
19+
| Token balance | Vote weight |
20+
|---------------|-------------|
21+
| 1 | 1 |
22+
| 100 | 10 |
23+
| 225 | 15 |
24+
| 1 000 | 31 |
25+
| 10 000 | 100 (cap) |
26+
27+
This example is inspired by the [Quadratic Voting](https://en.wikipedia.org/wiki/Quadratic_voting)
28+
mechanism used in decentralised governance systems such as Gitcoin Grants.
29+
30+
## Noteworthy Features
31+
32+
**Library packages**`sqrt_math` and `vote_math` are pure libraries: they have no
33+
`.aleo` program name, are never deployed on their own, and only take effect when included
34+
by a program package.
35+
36+
**Library submodules** — each extra `.leo` file under `src/` becomes a named module. No
37+
declaration is needed; the filename is the module name. Items are accessed as
38+
`lib_name::module_name::item`.
39+
40+
**Cross-submodule calls within a library**`vote_math::cost::build_profile` calls
41+
`vote_math::weight::compute` internally; `sqrt_math::checks::is_perfect_square` calls
42+
`sqrt_math::newton::isqrt`.
43+
44+
**Cross-library submodule calls**`vote_math::weight::compute` reaches directly into
45+
`sqrt_math::newton::isqrt`, demonstrating the full dependency chain
46+
(`governance → vote_math::cost → vote_math::weight → sqrt_math::newton`).
47+
48+
**Top-level library constants**`vote_math::MAX_WEIGHT`, `vote_math::MIN_BALANCE`, and
49+
`sqrt_math::ISQRT_MAX` are defined in each library's `lib.leo` and referenced by name from
50+
within submodules.
51+
52+
**Library struct export**`vote_math::cost::VoterProfile` is defined in a submodule (field:
53+
`weight: u32`) and used directly as a variable type inside `governance.aleo`.
54+
55+
**Private vote receipt** — the `Vote` record is private; the token balance that drove the
56+
weight computation is never revealed on-chain.
57+
58+
## How to Build
59+
60+
### Option A — clone this repo and build in place
61+
62+
```bash
63+
cd quadratic_vote
64+
65+
cd sqrt_math && leo build && cd ..
66+
cd vote_math && leo build && cd ..
67+
cd governance && leo build && cd ..
68+
```
69+
70+
### Option B — create from scratch with the Leo CLI
71+
72+
```bash
73+
# Create the two libraries and the program.
74+
leo new --library sqrt_math
75+
leo new --library vote_math
76+
leo new governance
77+
78+
# Wire up dependencies.
79+
cd vote_math && leo add sqrt_math --local ../sqrt_math && cd ..
80+
cd governance && leo add sqrt_math --local ../sqrt_math && cd ..
81+
cd governance && leo add vote_math --local ../vote_math && cd ..
82+
83+
# Copy source files, then build.
84+
cd sqrt_math && leo build && cd ..
85+
cd vote_math && leo build && cd ..
86+
cd governance && leo build && cd ..
87+
```
88+
89+
`leo new --library` creates a library package — `program.json` has no `.aleo` suffix and the
90+
source lives in `src/lib.leo` instead of `src/main.leo`. `leo add --local` records the
91+
dependency in `program.json` and lets the compiler resolve cross-package symbols.
92+
93+
## How to Run
94+
95+
All `leo run` commands are issued from inside the `governance/` directory.
96+
97+
### Cast a vote
98+
99+
A participant's token balance is kept private. The program derives their QV weight and
100+
returns a private `Vote` receipt.
101+
102+
```
103+
leo run cast_vote <proposal_id: field> <token_balance: u32> <approve: bool>
104+
```
105+
106+
**Alice (1 000 tokens, approval):**
107+
```bash
108+
leo run cast_vote 1field 1000u32 true
109+
```
110+
111+
Output:
112+
```
113+
{
114+
owner: aleo1...private,
115+
proposal_id: 1field.private,
116+
weight: 31u32.private,
117+
approve: true.private,
118+
_nonce: ...group.public,
119+
_version: 1u8.public
120+
}
121+
```
122+
123+
**Bob (225 tokens, rejection):**
124+
```bash
125+
leo run cast_vote 1field 225u32 false
126+
```
127+
128+
**Carol (10 000 tokens — hits the `MAX_WEIGHT` cap of 100):**
129+
```bash
130+
leo run cast_vote 1field 10000u32 true
131+
```
132+
133+
### Query the credit cost of casting votes
134+
135+
```
136+
leo run qv_cost <votes: u32>
137+
```
138+
139+
How much does it cost to cast 5 votes?
140+
141+
```bash
142+
leo run qv_cost 5u32
143+
# Output: 25u32 (= 5²)
144+
```
145+
146+
### Check whether a number is a perfect square
147+
148+
```
149+
leo run has_perfect_sqrt <token_balance: u32>
150+
```
151+
152+
This function bypasses `vote_math` entirely and calls `sqrt_math::checks::is_perfect_square`
153+
directly, illustrating that a program can reach any library submodule independently.
154+
155+
```bash
156+
leo run has_perfect_sqrt 1024u32 # 1024 = 32² → true
157+
leo run has_perfect_sqrt 1000u32 # 31² = 961, 32² = 1024 → false
158+
```
159+
160+
### Check whether a proposal is passing
161+
162+
```
163+
leo run is_passing <approvals: u32> <rejections: u32>
164+
```
165+
166+
After tallying Alice (31) + Carol (100) = 131 approvals vs Bob's 15 rejections:
167+
168+
```bash
169+
leo run is_passing 131u32 15u32
170+
# Output: true
171+
```
172+
173+
## Run the unit tests
174+
175+
From inside `governance/`:
176+
177+
```bash
178+
leo test
179+
```
180+
181+
18 tests covering `cast_vote`, `qv_cost`, `has_perfect_sqrt`, and `is_passing`.
182+
183+
## Run the full demo
184+
185+
```bash
186+
bash run.sh
187+
```
188+
189+
## Project structure
190+
191+
Each library is split into **submodules** — separate `.leo` files under `src/` that are
192+
automatically discovered by the compiler. No explicit `module` keyword is needed; the
193+
filename becomes the module name.
194+
195+
```
196+
quadratic_vote/
197+
├── sqrt_math/ # Library: integer square root
198+
│ ├── program.json # "program": "sqrt_math" (no .aleo suffix)
199+
│ └── src/
200+
│ ├── lib.leo # const ISQRT_MAX
201+
│ ├── newton.leo # fn isqrt(n) — Newton's method [module: newton]
202+
│ └── checks.leo # fn is_perfect_square(n) [module: checks]
203+
204+
├── vote_math/ # Library: QV weight & cost formulas
205+
│ ├── program.json # depends on sqrt_math
206+
│ └── src/
207+
│ ├── lib.leo # const MAX_WEIGHT, MIN_BALANCE
208+
│ ├── weight.leo # fn compute(balance), fn is_eligible(balance) [module: weight]
209+
│ └── cost.leo # struct VoterProfile { weight }, [module: cost]
210+
│ # fn credits_for_votes, fn build_profile(balance)
211+
212+
├── governance/ # Program: private vote receipts
213+
│ ├── program.json # depends on sqrt_math + vote_math
214+
│ ├── inputs/governance.in
215+
│ ├── src/main.leo # record Vote
216+
│ │ # fn cast_vote — uses vote_math::weight + vote_math::cost
217+
│ │ # fn qv_cost — uses vote_math::cost
218+
│ │ # fn has_perfect_sqrt — uses sqrt_math::checks directly
219+
│ │ # fn is_passing
220+
│ └── tests/
221+
│ └── test_governance.leo # @test fns covering cast_vote, qv_cost, has_perfect_sqrt,
222+
│ # is_passing (18 test cases)
223+
224+
├── run.sh
225+
└── README.md
226+
```
227+
228+
### Call graph
229+
230+
```
231+
governance.aleo
232+
├── vote_math::weight::is_eligible(token_balance)
233+
│ └── vote_math::MIN_BALANCE (top-level lib constant)
234+
├── vote_math::cost::build_profile(token_balance)
235+
│ └── vote_math::weight::compute(balance) (cross-submodule call within vote_math)
236+
│ ├── sqrt_math::newton::isqrt(n) (cross-library submodule call)
237+
│ │ └── sqrt_math::ISQRT_MAX (top-level lib constant, safety assert)
238+
│ └── vote_math::MAX_WEIGHT (top-level lib constant)
239+
├── vote_math::cost::credits_for_votes(votes)
240+
└── sqrt_math::checks::is_perfect_square(n) (direct cross-library submodule call)
241+
└── sqrt_math::newton::isqrt(n) (cross-submodule call within sqrt_math)
242+
└── sqrt_math::ISQRT_MAX (top-level lib constant, safety assert)
243+
```

0 commit comments

Comments
 (0)