Skip to content

Commit 030f2b7

Browse files
feat: implement betting engine
1 parent 17c8985 commit 030f2b7

5 files changed

Lines changed: 358 additions & 6 deletions

File tree

contracts/src/betting.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
use soroban_sdk::contracttype;
1+
use soroban_sdk::{contracttype, Address};
22

33
#[contracttype]
44
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
55
pub enum BetSide {
66
Yes,
77
No,
88
}
9+
10+
#[contracttype]
11+
#[derive(Clone, Debug, Eq, PartialEq)]
12+
pub struct Bet {
13+
pub market_id: u64,
14+
pub bettor: Address,
15+
pub outcome_index: u32,
16+
pub amount: i128,
17+
}

contracts/src/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,11 @@ pub enum PredictXError {
1010
InvalidOutcomeCount = 4,
1111
InvalidEndTime = 5,
1212
MarketNotFound = 6,
13+
MarketResolved = 7,
14+
MarketEnded = 8,
15+
InvalidBetAmount = 9,
16+
InvalidOutcomeIndex = 10,
17+
DuplicateBet = 11,
18+
BetNotFound = 12,
19+
MarketPoolOverflow = 13,
1320
}

contracts/src/lib.rs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@ pub mod rewards;
99
pub mod storage;
1010

1111
use crate::{
12+
betting::Bet,
1213
errors::PredictXError,
1314
market::Market,
14-
storage::{get_market as load_market, get_market_count, get_next_market_id, save_market},
15+
storage::{
16+
get_bet as load_bet, get_market as load_market, get_market_count,
17+
get_market_pool as load_market_pool, get_next_market_id, save_bet, save_market,
18+
save_market_pool,
19+
},
1520
};
16-
use soroban_sdk::{contract, contractimpl, Env, String, Vec};
21+
use soroban_sdk::{contract, contractimpl, Address, Env, String, Vec};
1722

1823
#[contract]
1924
pub struct PredictXContract;
@@ -79,6 +84,61 @@ impl PredictXContract {
7984

8085
markets
8186
}
87+
88+
pub fn place_bet(
89+
env: Env,
90+
market_id: u64,
91+
bettor: Address,
92+
outcome_index: u32,
93+
amount: i128,
94+
) -> Result<(), PredictXError> {
95+
bettor.require_auth();
96+
97+
let market = load_market(&env, market_id).ok_or(PredictXError::MarketNotFound)?;
98+
99+
if market.resolved {
100+
return Err(PredictXError::MarketResolved);
101+
}
102+
103+
if env.ledger().timestamp() >= market.end_time {
104+
return Err(PredictXError::MarketEnded);
105+
}
106+
107+
if amount <= 0 {
108+
return Err(PredictXError::InvalidBetAmount);
109+
}
110+
111+
if outcome_index >= market.outcomes.len() {
112+
return Err(PredictXError::InvalidOutcomeIndex);
113+
}
114+
115+
if load_bet(&env, market_id, &bettor).is_some() {
116+
return Err(PredictXError::DuplicateBet);
117+
}
118+
119+
let bet = Bet {
120+
market_id,
121+
bettor,
122+
outcome_index,
123+
amount,
124+
};
125+
let market_pool = load_market_pool(&env, market_id)
126+
.checked_add(amount)
127+
.ok_or(PredictXError::MarketPoolOverflow)?;
128+
129+
save_bet(&env, &bet);
130+
save_market_pool(&env, market_id, market_pool);
131+
132+
Ok(())
133+
}
134+
135+
pub fn get_bet(env: Env, market_id: u64, bettor: Address) -> Result<Bet, PredictXError> {
136+
load_bet(&env, market_id, &bettor).ok_or(PredictXError::BetNotFound)
137+
}
138+
139+
pub fn get_market_pool(env: Env, market_id: u64) -> i128 {
140+
load_market_pool(&env, market_id)
141+
}
82142
}
83143

84144
#[cfg(test)]

contracts/src/storage.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
use crate::market::Market;
2-
use soroban_sdk::{contracttype, Env};
1+
use crate::{betting::Bet, market::Market};
2+
use soroban_sdk::{contracttype, Address, Env};
33

44
#[contracttype]
55
#[derive(Clone, Debug, Eq, PartialEq)]
66
pub enum DataKey {
77
Market(u64),
88
MarketCounter,
9+
Bet(u64, Address),
10+
MarketPool(u64),
911
}
1012

1113
pub fn get_next_market_id(env: &Env) -> u64 {
@@ -39,3 +41,28 @@ pub fn save_market(env: &Env, market: &Market) {
3941
pub fn get_market(env: &Env, market_id: u64) -> Option<Market> {
4042
env.storage().persistent().get(&DataKey::Market(market_id))
4143
}
44+
45+
pub fn save_bet(env: &Env, bet: &Bet) {
46+
env.storage()
47+
.persistent()
48+
.set(&DataKey::Bet(bet.market_id, bet.bettor.clone()), bet);
49+
}
50+
51+
pub fn get_bet(env: &Env, market_id: u64, bettor: &Address) -> Option<Bet> {
52+
env.storage()
53+
.persistent()
54+
.get(&DataKey::Bet(market_id, bettor.clone()))
55+
}
56+
57+
pub fn get_market_pool(env: &Env, market_id: u64) -> i128 {
58+
env.storage()
59+
.persistent()
60+
.get(&DataKey::MarketPool(market_id))
61+
.unwrap_or(0_i128)
62+
}
63+
64+
pub fn save_market_pool(env: &Env, market_id: u64, amount: i128) {
65+
env.storage()
66+
.persistent()
67+
.set(&DataKey::MarketPool(market_id), &amount);
68+
}

0 commit comments

Comments
 (0)