Skip to content

Commit 143e991

Browse files
feat: implement reward distribution
1 parent 5607e66 commit 143e991

4 files changed

Lines changed: 354 additions & 5 deletions

File tree

contracts/src/errors.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@ pub enum PredictXError {
1818
BetNotFound = 12,
1919
MarketPoolOverflow = 13,
2020
MarketNotEnded = 14,
21+
MarketNotResolved = 15,
22+
NotWinningBet = 16,
23+
RewardAlreadyClaimed = 17,
24+
RewardCalculationOverflow = 18,
2125
}

contracts/src/lib.rs

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ use crate::{
1414
market::Market,
1515
resolution::MarketResult,
1616
storage::{
17-
get_bet as load_bet, get_market as load_market, get_market_count,
18-
get_market_pool as load_market_pool, get_market_result as load_market_result,
19-
get_next_market_id, save_bet, save_market, save_market_pool, save_market_result,
17+
add_market_bettor, get_bet as load_bet, get_market as load_market, get_market_bettors,
18+
get_market_count, get_market_pool as load_market_pool,
19+
get_market_result as load_market_result, get_next_market_id, is_reward_claimed,
20+
mark_reward_claimed, save_bet, save_market, save_market_pool, save_market_result,
2021
},
2122
};
2223
use soroban_sdk::{contract, contractimpl, Address, Env, String, Vec};
@@ -128,6 +129,7 @@ impl PredictXContract {
128129
.ok_or(PredictXError::MarketPoolOverflow)?;
129130

130131
save_bet(&env, &bet);
132+
add_market_bettor(&env, market_id, &bet.bettor);
131133
save_market_pool(&env, market_id, market_pool);
132134

133135
Ok(())
@@ -184,6 +186,64 @@ impl PredictXContract {
184186
resolved: market.resolved,
185187
})
186188
}
189+
190+
pub fn calculate_reward(
191+
env: Env,
192+
market_id: u64,
193+
bettor: Address,
194+
) -> Result<i128, PredictXError> {
195+
let market = load_market(&env, market_id).ok_or(PredictXError::MarketNotFound)?;
196+
197+
if !market.resolved {
198+
return Err(PredictXError::MarketNotResolved);
199+
}
200+
201+
let result = load_market_result(&env, market_id).ok_or(PredictXError::MarketNotResolved)?;
202+
let winning_outcome = result
203+
.winning_outcome
204+
.ok_or(PredictXError::MarketNotResolved)?;
205+
let user_bet = load_bet(&env, market_id, &bettor).ok_or(PredictXError::BetNotFound)?;
206+
207+
if user_bet.outcome_index != winning_outcome {
208+
return Err(PredictXError::NotWinningBet);
209+
}
210+
211+
let bettors = get_market_bettors(&env, market_id);
212+
let mut total_winning_pool = 0_i128;
213+
214+
for index in 0..bettors.len() {
215+
let market_bettor = bettors.get(index).unwrap();
216+
if let Some(bet) = load_bet(&env, market_id, &market_bettor) {
217+
if bet.outcome_index == winning_outcome {
218+
total_winning_pool = total_winning_pool
219+
.checked_add(bet.amount)
220+
.ok_or(PredictXError::RewardCalculationOverflow)?;
221+
}
222+
}
223+
}
224+
225+
let total_market_pool = load_market_pool(&env, market_id);
226+
227+
user_bet
228+
.amount
229+
.checked_mul(total_market_pool)
230+
.ok_or(PredictXError::RewardCalculationOverflow)
231+
.map(|weighted_bet| weighted_bet / total_winning_pool)
232+
}
233+
234+
pub fn claim_reward(env: Env, market_id: u64, bettor: Address) -> Result<i128, PredictXError> {
235+
if is_reward_claimed(&env, market_id, &bettor) {
236+
return Err(PredictXError::RewardAlreadyClaimed);
237+
}
238+
239+
bettor.require_auth();
240+
241+
let reward = Self::calculate_reward(env.clone(), market_id, bettor.clone())?;
242+
243+
mark_reward_claimed(&env, market_id, &bettor);
244+
245+
Ok(reward)
246+
}
187247
}
188248

189249
#[cfg(test)]

contracts/src/storage.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{betting::Bet, market::Market, resolution::MarketResult};
2-
use soroban_sdk::{contracttype, Address, Env};
2+
use soroban_sdk::{contracttype, Address, Env, Vec};
33

44
#[contracttype]
55
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -9,6 +9,8 @@ pub enum DataKey {
99
Bet(u64, Address),
1010
MarketPool(u64),
1111
MarketResult(u64),
12+
MarketBettors(u64),
13+
RewardClaimed(u64, Address),
1214
}
1315

1416
pub fn get_next_market_id(env: &Env) -> u64 {
@@ -49,6 +51,15 @@ pub fn save_bet(env: &Env, bet: &Bet) {
4951
.set(&DataKey::Bet(bet.market_id, bet.bettor.clone()), bet);
5052
}
5153

54+
pub fn add_market_bettor(env: &Env, market_id: u64, bettor: &Address) {
55+
let mut bettors = get_market_bettors(env, market_id);
56+
bettors.push_back(bettor.clone());
57+
58+
env.storage()
59+
.persistent()
60+
.set(&DataKey::MarketBettors(market_id), &bettors);
61+
}
62+
5263
pub fn get_bet(env: &Env, market_id: u64, bettor: &Address) -> Option<Bet> {
5364
env.storage()
5465
.persistent()
@@ -79,3 +90,23 @@ pub fn get_market_result(env: &Env, market_id: u64) -> Option<MarketResult> {
7990
.persistent()
8091
.get(&DataKey::MarketResult(market_id))
8192
}
93+
94+
pub fn get_market_bettors(env: &Env, market_id: u64) -> Vec<Address> {
95+
env.storage()
96+
.persistent()
97+
.get(&DataKey::MarketBettors(market_id))
98+
.unwrap_or_else(|| Vec::new(env))
99+
}
100+
101+
pub fn mark_reward_claimed(env: &Env, market_id: u64, bettor: &Address) {
102+
env.storage()
103+
.persistent()
104+
.set(&DataKey::RewardClaimed(market_id, bettor.clone()), &true);
105+
}
106+
107+
pub fn is_reward_claimed(env: &Env, market_id: u64, bettor: &Address) -> bool {
108+
env.storage()
109+
.persistent()
110+
.get(&DataKey::RewardClaimed(market_id, bettor.clone()))
111+
.unwrap_or(false)
112+
}

0 commit comments

Comments
 (0)