Skip to content

Commit 519999f

Browse files
author
Esau
committed
fix
1 parent 4090ca6 commit 519999f

File tree

8 files changed

+6301
-1820
lines changed

8 files changed

+6301
-1820
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
target
22
.DS_Store
3-
ivc
3+
ivc
4+
node_modules

prediction_market_contract/Nargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ compiler_version = ">=0.25.0"
55
type = "contract"
66

77
[dependencies]
8-
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.4", directory = "noir-projects/aztec-nr/aztec" }
9-
uint_note = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.4", directory = "noir-projects/aztec-nr/uint-note" }
8+
aztec = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.5", directory = "noir-projects/aztec-nr/aztec" }
9+
uint_note = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v3.0.0-devnet.5", directory = "noir-projects/aztec-nr/uint-note" }

prediction_market_contract/README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@ A prediction market implementation on Aztec using a **Constant Sum Market Maker
55
## Overview
66

77
This contract allows users to:
8-
- **Vote on binary outcomes** (YES/NO) **anonymously** - no one knows WHO voted
8+
- **Bet on binary outcomes** (YES/NO) **anonymously** - no one knows WHO placed a bet
99
- **Hold fully private balances** - collateral and share holdings are all hidden
1010
- **Experience dynamic pricing** that adjusts based on market sentiment
1111
- **Get slippage protection** to prevent adverse price movements
12-
- **Single-transaction voting** using partial notes pattern
12+
- **Single-transaction betting** using partial notes pattern
1313

1414
## Privacy Model
1515

1616
### What's Private
1717

1818
| Data | Privacy | Notes |
1919
|------|---------|-------|
20-
| Voter identity | **PRIVATE** | Public function doesn't receive sender address |
20+
| Bettor identity | **PRIVATE** | Public function doesn't receive sender address |
2121
| Collateral balances | **PRIVATE** | Stored as private notes (UintNote) |
2222
| Share balances | **PRIVATE** | Stored as private notes (UintNote) |
23-
| Your vote (YES/NO) | **PRIVATE** | Hidden via partial notes |
23+
| Your bet (YES/NO) | **PRIVATE** | Hidden via partial notes |
2424

2525
### What's Public
2626

@@ -32,7 +32,7 @@ This contract allows users to:
3232

3333
### Privacy Architecture
3434

35-
The contract uses **partial notes** for private voting (like the [AMM contract](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts/amm_contract)):
35+
The contract uses **partial notes** for private betting (like the [AMM contract](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts/amm_contract)):
3636

3737
```
3838
1. buy_outcome() [PRIVATE]
@@ -55,7 +55,7 @@ Key privacy feature: The public `_process_buy()` function **does not receive the
5555

5656
## Usage
5757

58-
### Voting Flow (Single Transaction)
58+
### Betting Flow (Single Transaction)
5959

6060
```typescript
6161
// Deposit collateral privately
@@ -94,8 +94,8 @@ const balance = await market.methods.get_collateral_balance(myAddress).simulate(
9494
bash -i <(curl -s https://install.aztec.network)
9595
aztec-up 3.0.0-devnet.4
9696

97-
# Install npm dependencies
98-
npm install
97+
# Install dependencies
98+
yarn install
9999
```
100100

101101
### Building
@@ -129,10 +129,10 @@ Tests the CSMM pricing functions:
129129
aztec start --sandbox
130130

131131
# Run tests
132-
npm test
132+
yarn test
133133
```
134134

135-
Tests the full private voting flow:
135+
Tests the full private betting flow:
136136
- Contract deployment
137137
- Private deposit/withdraw
138138
- Private buy_outcome (single tx with partial notes)

prediction_market_contract/bun.lock

Lines changed: 0 additions & 1766 deletions
This file was deleted.

prediction_market_contract/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
"typescript": "^5.0.0"
2424
},
2525
"dependencies": {
26-
"@aztec/accounts": "3.0.0-devnet.4",
27-
"@aztec/aztec.js": "3.0.0-devnet.4",
28-
"@aztec/pxe": "3.0.0-devnet.4",
29-
"@aztec/test-wallet": "3.0.0-devnet.4"
26+
"@aztec/accounts": "3.0.0-devnet.5",
27+
"@aztec/aztec.js": "3.0.0-devnet.5",
28+
"@aztec/pxe": "3.0.0-devnet.5",
29+
"@aztec/test-wallet": "3.0.0-devnet.5"
3030
}
3131
}

prediction_market_contract/src/lib.nr

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,21 @@ pub fn calculate_shares_out(
6666
current_supply: u128,
6767
total_liquidity: u128,
6868
) -> (u128, u128) {
69-
assert(total_liquidity > 0 as u128, "ZERO_LIQUIDITY");
70-
assert(collateral_amount > 0 as u128, "ZERO_COLLATERAL");
69+
assert(total_liquidity > 0, "ZERO_LIQUIDITY");
70+
assert(collateral_amount > 0, "ZERO_COLLATERAL");
7171

7272
// Calculate current price as fixed-point
7373
// price = (current_supply * PRICE_PRECISION) / total_liquidity
7474
let current_price = (current_supply * PRICE_PRECISION) / total_liquidity;
7575

7676
// Prevent division by zero (price can't be zero unless supply is zero)
77-
assert(current_price > 0 as u128, "ZERO_PRICE");
77+
assert(current_price > 0, "ZERO_PRICE");
7878

7979
// Calculate shares out
8080
// shares = (collateral_amount * PRICE_PRECISION) / current_price
8181
let shares_out = (collateral_amount * PRICE_PRECISION) / current_price;
8282

83-
assert(shares_out > 0 as u128, "INSUFFICIENT_OUTPUT");
83+
assert(shares_out > 0, "INSUFFICIENT_OUTPUT");
8484

8585
let new_supply = current_supply + shares_out;
8686

@@ -92,7 +92,7 @@ pub fn calculate_shares_out(
9292
/// Formula: price = supply / total_supply
9393
/// Returns: price as fixed-point (price * PRICE_PRECISION)
9494
pub fn calculate_new_price(supply: u128, total_supply: u128) -> u128 {
95-
assert(total_supply > 0 as u128, "ZERO_TOTAL_SUPPLY");
95+
assert(total_supply > 0, "ZERO_TOTAL_SUPPLY");
9696

9797
// Return as fixed-point: (supply * PRICE_PRECISION) / total_supply
9898
(supply * PRICE_PRECISION) / total_supply
@@ -109,7 +109,7 @@ pub fn calculate_price_impact(
109109
) -> (u128, u128, u128) {
110110
let price_before = calculate_new_price(current_supply, total_liquidity);
111111

112-
let (shares_out, new_supply) = calculate_shares_out(
112+
let (_shares_out, new_supply) = calculate_shares_out(
113113
collateral_amount,
114114
current_supply,
115115
total_liquidity,
@@ -139,9 +139,9 @@ fn test_calculate_shares_out_at_50_50() {
139139
// At 50/50 odds: supply=500, liquidity=1000
140140
// Price = 500/1000 = 0.5
141141
// Buying 100 collateral should give 200 shares (100/0.5)
142-
let collateral = 100 as u128;
143-
let supply = 500 as u128;
144-
let liquidity = 1000 as u128;
142+
let collateral = 100;
143+
let supply = 500;
144+
let liquidity = 1000;
145145

146146
let (shares_out, new_supply) = calculate_shares_out(collateral, supply, liquidity);
147147

@@ -154,9 +154,9 @@ fn test_calculate_shares_out_at_different_price() {
154154
// At 70/30 odds: supply=700, liquidity=1000
155155
// Price = 700/1000 = 0.7
156156
// Buying 70 collateral should give 100 shares (70/0.7)
157-
let collateral = 70 as u128;
158-
let supply = 700 as u128;
159-
let liquidity = 1000 as u128;
157+
let collateral = 70;
158+
let supply = 700;
159+
let liquidity = 1000;
160160

161161
let (shares_out, new_supply) = calculate_shares_out(collateral, supply, liquidity);
162162

@@ -167,8 +167,8 @@ fn test_calculate_shares_out_at_different_price() {
167167
#[test]
168168
fn test_calculate_new_price_50_50() {
169169
// At 50/50: price should be 500000 (0.5 * PRICE_PRECISION)
170-
let supply = 500 as u128;
171-
let total = 1000 as u128;
170+
let supply = 500;
171+
let total = 1000;
172172

173173
let price = calculate_new_price(supply, total);
174174

@@ -178,8 +178,8 @@ fn test_calculate_new_price_50_50() {
178178
#[test]
179179
fn test_calculate_new_price_70_30() {
180180
// At 70/30: YES price should be 700000 (0.7 * PRICE_PRECISION)
181-
let supply = 700 as u128;
182-
let total = 1000 as u128;
181+
let supply = 700;
182+
let total = 1000;
183183

184184
let price = calculate_new_price(supply, total);
185185

@@ -189,9 +189,9 @@ fn test_calculate_new_price_70_30() {
189189
#[test]
190190
fn test_calculate_price_impact() {
191191
// Starting at 50/50, buying 100 should move price from 0.5 to ~0.636
192-
let collateral = 100 as u128;
193-
let supply = 500 as u128;
194-
let liquidity = 1000 as u128;
192+
let collateral = 100;
193+
let supply = 500;
194+
let liquidity = 1000;
195195

196196
let (price_before, price_after, impact) = calculate_price_impact(collateral, supply, liquidity);
197197

@@ -205,8 +205,8 @@ fn test_calculate_price_impact() {
205205
#[test]
206206
fn test_prices_sum_to_one() {
207207
// YES price + NO price should always equal PRICE_PRECISION (1.0)
208-
let yes_supply = 600 as u128;
209-
let no_supply = 400 as u128;
208+
let yes_supply = 600;
209+
let no_supply = 400;
210210
let total = yes_supply + no_supply;
211211

212212
let yes_price = calculate_new_price(yes_supply, total);
@@ -218,9 +218,9 @@ fn test_prices_sum_to_one() {
218218
#[test]
219219
fn test_large_purchase() {
220220
// Test with larger numbers to ensure no overflow issues
221-
let collateral = 10000 as u128;
222-
let supply = 50000 as u128;
223-
let liquidity = 100000 as u128;
221+
let collateral = 10000;
222+
let supply = 50000;
223+
let liquidity = 100000;
224224

225225
let (shares_out, new_supply) = calculate_shares_out(collateral, supply, liquidity);
226226

prediction_market_contract/src/main.nr

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use dep::aztec::macros::aztec;
66
/// All balances are private. Public function only sees trade amounts, not who traded.
77
#[aztec]
88
pub contract PredictionMarket {
9-
use crate::lib::{calculate_shares_out, calculate_new_price};
9+
use crate::lib::{calculate_shares_out, calculate_new_price, calculate_price_impact};
1010
use dep::aztec::{
1111
macros::{functions::{external, initializer, internal}, storage::storage},
1212
messages::message_delivery::MessageDelivery,
@@ -20,16 +20,12 @@ pub contract PredictionMarket {
2020
};
2121
use dep::uint_note::uint_note::{UintNote, PartialUintNote};
2222

23-
// Storage slots for deriving partial note locations
24-
global YES_BALANCES_SLOT: Field = 6;
25-
global NO_BALANCES_SLOT: Field = 7;
26-
2723
#[storage]
2824
struct Storage<Context> {
2925
collateral_balances: Map<AztecAddress, PrivateSet<UintNote, Context>, Context>,
3026
yes_supply: PublicMutable<u128, Context>,
3127
no_supply: PublicMutable<u128, Context>,
32-
liquidity_constant: PublicMutable<u128, Context>,
28+
total_liquidity: PublicMutable<u128, Context>,
3329
admin: PublicMutable<AztecAddress, Context>,
3430
yes_balances: Map<AztecAddress, PrivateSet<UintNote, Context>, Context>,
3531
no_balances: Map<AztecAddress, PrivateSet<UintNote, Context>, Context>,
@@ -48,7 +44,7 @@ pub contract PredictionMarket {
4844
let half = initial_liquidity / 2;
4945
storage.yes_supply.write(half);
5046
storage.no_supply.write(half);
51-
storage.liquidity_constant.write(initial_liquidity);
47+
storage.total_liquidity.write(initial_liquidity);
5248
}
5349

5450
/// Deposit collateral privately. Creates a private note for the sender.
@@ -115,7 +111,11 @@ pub contract PredictionMarket {
115111
}
116112

117113
// Create partial note for shares
118-
let base_slot = if is_yes { YES_BALANCES_SLOT } else { NO_BALANCES_SLOT };
114+
// Storage slots are required for deriving partial note locations.
115+
// This is because the partial note needs to be manually placed at that slot because we cannot use PrivateSet::insert().
116+
// We cannot use PrivateSet::insert() because the note does not exist yet, as it is not fully created in private.
117+
118+
let base_slot = if is_yes { PredictionMarket::storage_layout().yes_balances.slot } else { PredictionMarket::storage_layout().no_balances.slot };
119119
let storage_slot: Field = derive_storage_slot_in_map(base_slot, sender);
120120
let partial_note = UintNote::partial(sender, storage_slot, &mut context, sender, contract_address);
121121

@@ -131,7 +131,7 @@ pub contract PredictionMarket {
131131
fn _process_buy(is_yes: bool, collateral_amount: u128, min_shares_out: u128, partial_note: PartialUintNote) {
132132
let yes_supply = storage.yes_supply.read();
133133
let no_supply = storage.no_supply.read();
134-
let liquidity = storage.liquidity_constant.read();
134+
let liquidity = storage.total_liquidity.read();
135135

136136
let (shares_out, new_supply) = calculate_shares_out(
137137
collateral_amount,
@@ -146,7 +146,7 @@ pub contract PredictionMarket {
146146
} else {
147147
storage.no_supply.write(new_supply);
148148
}
149-
storage.liquidity_constant.write(liquidity + collateral_amount);
149+
storage.total_liquidity.write(liquidity + collateral_amount);
150150

151151
partial_note.complete(&mut context, context.this_address(), shares_out);
152152
}
@@ -159,18 +159,18 @@ pub contract PredictionMarket {
159159
calculate_new_price(if is_yes { yes_supply } else { no_supply }, yes_supply + no_supply)
160160
}
161161

162-
/// Get market state: (yes_supply, no_supply, liquidity_constant).
162+
/// Get market state: (yes_supply, no_supply, total_liquidity).
163163
#[external("utility")]
164164
unconstrained fn get_market_state() -> (u128, u128, u128) {
165-
(storage.yes_supply.read(), storage.no_supply.read(), storage.liquidity_constant.read())
165+
(storage.yes_supply.read(), storage.no_supply.read(), storage.total_liquidity.read())
166166
}
167167

168168
/// Quote shares out for a given collateral amount.
169169
#[external("utility")]
170170
unconstrained fn quote_buy(is_yes: bool, collateral_amount: u128) -> u128 {
171171
let yes_supply = storage.yes_supply.read();
172172
let no_supply = storage.no_supply.read();
173-
let liquidity = storage.liquidity_constant.read();
173+
let liquidity = storage.total_liquidity.read();
174174
let (shares_out, _) = calculate_shares_out(
175175
collateral_amount,
176176
if is_yes { yes_supply } else { no_supply },
@@ -179,6 +179,21 @@ pub contract PredictionMarket {
179179
shares_out
180180
}
181181

182+
/// Quote price impact for a purchase.
183+
/// Returns: (price_before, price_after, impact_percentage)
184+
/// All values use PRICE_PRECISION (1_000_000) as the decimal base.
185+
#[external("utility")]
186+
unconstrained fn quote_price_impact(is_yes: bool, collateral_amount: u128) -> (u128, u128, u128) {
187+
let yes_supply = storage.yes_supply.read();
188+
let no_supply = storage.no_supply.read();
189+
let liquidity = storage.total_liquidity.read();
190+
calculate_price_impact(
191+
collateral_amount,
192+
if is_yes { yes_supply } else { no_supply },
193+
liquidity,
194+
)
195+
}
196+
182197
/// Get user's private collateral balance.
183198
#[external("utility")]
184199
unconstrained fn get_collateral_balance(user: AztecAddress) -> u128 {

0 commit comments

Comments
 (0)