Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion contracts/predict-iq/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod test_snapshot_voting;
mod test_resolution_state_machine;
mod test_multi_token;
mod test_cancellation;
mod test_referral;

use crate::types::{ConfigKey, CircuitBreakerState};
use crate::modules::admin;
Expand Down Expand Up @@ -63,8 +64,9 @@ impl PredictIQ {
outcome: u32,
amount: i128,
token_address: Address,
referrer: Option<Address>,
) -> Result<(), ErrorCode> {
crate::modules::bets::place_bet(&e, bettor, market_id, outcome, amount, token_address)
crate::modules::bets::place_bet(&e, bettor, market_id, outcome, amount, token_address, referrer)
}

pub fn claim_winnings(
Expand Down Expand Up @@ -101,6 +103,10 @@ impl PredictIQ {
crate::modules::fees::get_revenue(&e, token)
}

pub fn claim_referral_rewards(e: Env, address: Address, token: Address) -> Result<i128, ErrorCode> {
crate::modules::fees::claim_referral_rewards(&e, &address, &token)
}

pub fn set_oracle_result(e: Env, market_id: u64, outcome: u32) -> Result<(), ErrorCode> {
crate::modules::admin::require_admin(&e)?;
crate::modules::oracles::set_oracle_result(&e, market_id, outcome)
Expand Down
7 changes: 7 additions & 0 deletions contracts/predict-iq/src/modules/bets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub fn place_bet(
outcome: u32,
amount: i128,
token_address: Address,
referrer: Option<Address>,
) -> Result<(), ErrorCode> {
bettor.require_auth();

Expand Down Expand Up @@ -63,6 +64,12 @@ pub fn place_bet(
e.storage().persistent().set(&bet_key, &existing_bet);
markets::update_market(e, market);

// Process referral reward if referrer provided
if let Some(ref_addr) = referrer {
let fee = crate::modules::fees::calculate_fee(e, amount);
crate::modules::fees::add_referral_reward(e, &ref_addr, fee);
}

// Event format: (Topic, MarketID, SubjectAddr, Data)
e.events().publish(
(Symbol::new(e, "bet_placed"), market_id, bettor),
Expand Down
37 changes: 37 additions & 0 deletions contracts/predict-iq/src/modules/fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::errors::ErrorCode;
pub enum DataKey {
TotalFeesCollected,
FeeRevenue(Address), // token_address -> amount
ReferrerBalance(Address), // referrer_address -> amount
}

pub fn get_base_fee(e: &Env) -> i128 {
Expand Down Expand Up @@ -45,3 +46,39 @@ pub fn collect_fee(e: &Env, token: Address, amount: i128) {
pub fn get_revenue(e: &Env, token: Address) -> i128 {
e.storage().persistent().get(&DataKey::FeeRevenue(token)).unwrap_or(0)
}

pub fn add_referral_reward(e: &Env, referrer: &Address, fee_amount: i128) {
let reward = (fee_amount * 10) / 100; // 10% of fee
let key = DataKey::ReferrerBalance(referrer.clone());
let mut balance: i128 = e.storage().persistent().get(&key).unwrap_or(0);
balance += reward;
e.storage().persistent().set(&key, &balance);

e.events().publish(
(Symbol::new(e, "referral_reward"), referrer),
reward,
);
}

pub fn claim_referral_rewards(e: &Env, address: &Address, token: &Address) -> Result<i128, ErrorCode> {
address.require_auth();

let key = DataKey::ReferrerBalance(address.clone());
let balance: i128 = e.storage().persistent().get(&key).unwrap_or(0);

if balance == 0 {
return Err(ErrorCode::InsufficientBalance);
}

e.storage().persistent().set(&key, &0);

let client = soroban_sdk::token::Client::new(e, token);
client.transfer(&e.current_contract_address(), address, &balance);

e.events().publish(
(Symbol::new(e, "referral_claimed"), address),
balance,
);

Ok(balance)
}
10 changes: 5 additions & 5 deletions contracts/predict-iq/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ fn test_claim_winnings_three_winners() {

let market_id = client.create_market(&creator, &description, &options, &100, &200, &oracle_config, &token_address);

client.place_bet(&bettor1, &market_id, &0, &1000, &token_address);
client.place_bet(&bettor2, &market_id, &0, &2000, &token_address);
client.place_bet(&bettor3, &market_id, &0, &3000, &token_address);
client.place_bet(&loser, &market_id, &1, &4000, &token_address);
client.place_bet(&bettor1, &market_id, &0, &1000, &token_address, &None);
client.place_bet(&bettor2, &market_id, &0, &2000, &token_address, &None);
client.place_bet(&bettor3, &market_id, &0, &3000, &token_address, &None);
client.place_bet(&loser, &market_id, &1, &4000, &token_address, &None);

client.resolve_market(&market_id, &0);

Expand Down Expand Up @@ -151,7 +151,7 @@ fn test_claim_winnings_double_claim() {
};

let market_id = client.create_market(&creator, &description, &options, &100, &200, &oracle_config, &token_address);
client.place_bet(&bettor, &market_id, &0, &1000, &token_address);
client.place_bet(&bettor, &market_id, &0, &1000, &token_address, &None);
client.resolve_market(&market_id, &0);

client.claim_winnings(&bettor, &market_id);
Expand Down
12 changes: 6 additions & 6 deletions contracts/predict-iq/src/test_cancellation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn test_admin_cancel_market() {
&token_address,
);

client.place_bet(&user1, &market_id, &0, &1000, &token_address);
client.place_bet(&user1, &market_id, &0, &1000, &token_address, &None);

client.cancel_market_admin(&market_id);

Expand Down Expand Up @@ -83,8 +83,8 @@ fn test_withdraw_refund_full_amount() {
let bet_amount_user1 = 1000i128;
let bet_amount_user2 = 2000i128;

client.place_bet(&user1, &market_id, &0, &bet_amount_user1, &token_address);
client.place_bet(&user2, &market_id, &1, &bet_amount_user2, &token_address);
client.place_bet(&user1, &market_id, &0, &bet_amount_user1, &token_address, &None);
client.place_bet(&user2, &market_id, &1, &bet_amount_user2, &token_address, &None);

let user1_balance_before = token_client.balance(&user1);
let user2_balance_before = token_client.balance(&user2);
Expand Down Expand Up @@ -122,7 +122,7 @@ fn test_refund_no_fee_collected() {
&token_address,
);

client.place_bet(&user1, &market_id, &0, &1000, &token_address);
client.place_bet(&user1, &market_id, &0, &1000, &token_address, &None);

let revenue_before = client.get_revenue(&token_address);

Expand Down Expand Up @@ -156,7 +156,7 @@ fn test_refund_only_on_cancelled_market() {
&token_address,
);

client.place_bet(&user1, &market_id, &0, &1000, &token_address);
client.place_bet(&user1, &market_id, &0, &1000, &token_address, &None);

client.withdraw_refund(&user1, &market_id);
}
Expand All @@ -183,7 +183,7 @@ fn test_refund_only_once() {
&token_address,
);

client.place_bet(&user1, &market_id, &0, &1000, &token_address);
client.place_bet(&user1, &market_id, &0, &1000, &token_address, &None);

client.cancel_market_admin(&market_id);
client.withdraw_refund(&user1, &market_id);
Expand Down
22 changes: 11 additions & 11 deletions contracts/predict-iq/src/test_multi_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ fn test_usdc_market_6_decimals() {
usdc_client.mint(&bettor2, &500_000_000);

// Place bets
client.place_bet(&bettor1, &market_id, &0, &1_000_000_000, &usdc_address);
client.place_bet(&bettor2, &market_id, &1, &500_000_000, &usdc_address);
client.place_bet(&bettor1, &market_id, &0, &1_000_000_000, &usdc_address, &None);
client.place_bet(&bettor2, &market_id, &1, &500_000_000, &usdc_address, &None);

// Verify total_staked
let market = client.get_market(&market_id).unwrap();
Expand Down Expand Up @@ -107,8 +107,8 @@ fn test_xlm_market_7_decimals() {
xlm_client.mint(&bettor2, &10_000_000_000);

// Place bets
client.place_bet(&bettor1, &market_id, &0, &20_000_000_000, &xlm_address);
client.place_bet(&bettor2, &market_id, &1, &10_000_000_000, &xlm_address);
client.place_bet(&bettor1, &market_id, &0, &20_000_000_000, &xlm_address, &None);
client.place_bet(&bettor2, &market_id, &1, &10_000_000_000, &xlm_address, &None);

// Verify total_staked
let market = client.get_market(&market_id).unwrap();
Expand Down Expand Up @@ -167,9 +167,9 @@ fn test_usdc_payout_precision() {
usdc_client.mint(&loser, &500_000_000); // 500 USDC

// Place bets
client.place_bet(&winner1, &market_id, &0, &1_000_000_000, &usdc_address);
client.place_bet(&winner2, &market_id, &0, &500_000_000, &usdc_address);
client.place_bet(&loser, &market_id, &1, &500_000_000, &usdc_address);
client.place_bet(&winner1, &market_id, &0, &1_000_000_000, &usdc_address, &None);
client.place_bet(&winner2, &market_id, &0, &500_000_000, &usdc_address, &None);
client.place_bet(&loser, &market_id, &1, &500_000_000, &usdc_address, &None);

// Resolve market
e.ledger().with_mut(|li| li.timestamp = 2500);
Expand Down Expand Up @@ -237,9 +237,9 @@ fn test_xlm_payout_precision() {
xlm_client.mint(&loser, &50_000_000_0); // 5.0 XLM

// Place bets
client.place_bet(&winner1, &market_id, &0, &33_333_333_3, &xlm_address);
client.place_bet(&winner2, &market_id, &0, &16_666_666_7, &xlm_address);
client.place_bet(&loser, &market_id, &1, &50_000_000_0, &xlm_address);
client.place_bet(&winner1, &market_id, &0, &33_333_333_3, &xlm_address, &None);
client.place_bet(&winner2, &market_id, &0, &16_666_666_7, &xlm_address, &None);
client.place_bet(&loser, &market_id, &1, &50_000_000_0, &xlm_address, &None);

// Resolve market
e.ledger().with_mut(|li| li.timestamp = 2500);
Expand Down Expand Up @@ -299,7 +299,7 @@ fn test_wrong_token_rejected() {
let xlm_address = xlm_id.address();

let bettor = Address::generate(&e);
let result = client.try_place_bet(&bettor, &market_id, &0, &1_000_000, &xlm_address);
let result = client.try_place_bet(&bettor, &market_id, &0, &1_000_000, &xlm_address, &None);

// Should fail with InvalidBetAmount error
assert_eq!(result, Err(Ok(ErrorCode::InvalidBetAmount)));
Expand Down
Loading