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
200 changes: 200 additions & 0 deletions contract/contracts/predifi-contract/src/integration_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
#![cfg(test)]

use super::*;
use crate::test_utils::TokenTestContext;
use soroban_sdk::{testutils::Address as _, Address, Env};

mod dummy_access_control {
use soroban_sdk::{contract, contractimpl, Address, Env, Symbol};

#[contract]
pub struct DummyAccessControl;

#[contractimpl]
impl DummyAccessControl {
pub fn grant_role(env: Env, user: Address, role: u32) {
let key = (Symbol::new(&env, "role"), user, role);
env.storage().instance().set(&key, &true);
}

pub fn has_role(env: Env, user: Address, role: u32) -> bool {
let key = (Symbol::new(&env, "role"), user, role);
env.storage().instance().get(&key).unwrap_or(false)
}
}
}

const ROLE_ADMIN: u32 = 0;
const ROLE_OPERATOR: u32 = 1;

fn setup_integration(env: &Env) -> (
PredifiContractClient<'static>,
TokenTestContext,
Address, // Admin
Address, // Operator
Address, // Treasury
) {
let admin = Address::generate(env);
let operator = Address::generate(env);
let treasury = Address::generate(env);

let ac_id = env.register(dummy_access_control::DummyAccessControl, ());
let ac_client = dummy_access_control::DummyAccessControlClient::new(env, &ac_id);
ac_client.grant_role(&admin, &ROLE_ADMIN);
ac_client.grant_role(&operator, &ROLE_OPERATOR);

let contract_id = env.register(PredifiContract, ());
let client = PredifiContractClient::new(env, &contract_id);
client.init(&ac_id, &treasury, &0u32);

let token_ctx = TokenTestContext::deploy(env, &admin);

(client, token_ctx, admin, operator, treasury)
}

#[test]
fn test_full_market_lifecycle() {
let env = Env::default();
env.mock_all_auths();

let (client, token_ctx, _admin, operator, _treasury) = setup_integration(&env);

let user1 = Address::generate(&env);
let user2 = Address::generate(&env);
let user3 = Address::generate(&env);

token_ctx.mint(&user1, 1000);
token_ctx.mint(&user2, 1000);
token_ctx.mint(&user3, 1000);

// 1. Create Pool
let end_time = 1000u64;
let pool_id = client.create_pool(&end_time, &token_ctx.token_address);

// 2. Place Predictions
client.place_prediction(&user1, &pool_id, &100, &1); // User 1 bets 100 on Outcome 1
client.place_prediction(&user2, &pool_id, &200, &2); // User 2 bets 200 on Outcome 2
client.place_prediction(&user3, &pool_id, &300, &1); // User 3 bets 300 on Outcome 1 (Total Outcome 1 = 400)

// Total stake = 100 + 200 + 300 = 600
assert_eq!(token_ctx.token.balance(&client.address), 600);

// 3. Resolve Pool
client.resolve_pool(&operator, &pool_id, &1u32); // Outcome 1 wins

// 4. Claim Winnings
// User 1 Winnings: (100 / 400) * 600 = 150
let w1 = client.claim_winnings(&user1, &pool_id);
assert_eq!(w1, 150);
assert_eq!(token_ctx.token.balance(&user1), 1050); // 1000 - 100 + 150

// User 3 Winnings: (300 / 400) * 600 = 450
let w3 = client.claim_winnings(&user3, &pool_id);
assert_eq!(w3, 450);
assert_eq!(token_ctx.token.balance(&user3), 1150); // 1000 - 300 + 450

// User 2 Winnings: 0 (loser)
let w2 = client.claim_winnings(&user2, &pool_id);
assert_eq!(w2, 0);
assert_eq!(token_ctx.token.balance(&user2), 800); // 1000 - 200

// Contract balance should be 0
assert_eq!(token_ctx.token.balance(&client.address), 0);
}

#[test]
fn test_multi_user_betting_and_balance_verification() {
let env = Env::default();
env.mock_all_auths();

let (client, token_ctx, _admin, operator, _treasury) = setup_integration(&env);

// 5 users
let users: soroban_sdk::Vec<Address> = soroban_sdk::Vec::from_array(&env, [
Address::generate(&env),
Address::generate(&env),
Address::generate(&env),
Address::generate(&env),
Address::generate(&env),
]);

for user in users.iter() {
token_ctx.mint(&user, 5000);
}

let pool_id = client.create_pool(&2000u64, &token_ctx.token_address);

// Bets:
// U0: 500 on 1
// U1: 1000 on 2
// U2: 500 on 1
// U3: 1500 on 3
// U4: 500 on 1
// Total 1: 1500, Total 2: 1000, Total 3: 1500. Total Stake: 4000.

client.place_prediction(&users.get(0).unwrap(), &pool_id, &500, &1);
client.place_prediction(&users.get(1).unwrap(), &pool_id, &1000, &2);
client.place_prediction(&users.get(2).unwrap(), &pool_id, &500, &1);
client.place_prediction(&users.get(3).unwrap(), &pool_id, &1500, &3);
client.place_prediction(&users.get(4).unwrap(), &pool_id, &500, &1);

assert_eq!(token_ctx.token.balance(&client.address), 4000);

// Resolve to Outcome 3
client.resolve_pool(&operator, &pool_id, &3u32);

// Winner: U3
// Winnings: (1500 / 1500) * 4000 = 4000
let w3 = client.claim_winnings(&users.get(3).unwrap(), &pool_id);
assert_eq!(w3, 4000);
assert_eq!(token_ctx.token.balance(&users.get(3).unwrap()), 7500); // 5000 - 1500 + 4000

// Losers check
let w0 = client.claim_winnings(&users.get(0).unwrap(), &pool_id);
assert_eq!(w0, 0);
assert_eq!(token_ctx.token.balance(&users.get(0).unwrap()), 4500); // 5000 - 500

assert_eq!(token_ctx.token.balance(&client.address), 0);
}

#[test]
fn test_market_resolution_multiple_winners() {
let env = Env::default();
env.mock_all_auths();

let (client, token_ctx, _admin, operator, _treasury) = setup_integration(&env);

let user1 = Address::generate(&env);
let user2 = Address::generate(&env);
let user3 = Address::generate(&env);

token_ctx.mint(&user1, 1000);
token_ctx.mint(&user2, 1000);
token_ctx.mint(&user3, 1000);

let pool_id = client.create_pool(&1500u64, &token_ctx.token_address);

// Bets:
// U1: 200 on 1
// U2: 300 on 1
// U3: 500 on 2
// Total 1: 500, Total 2: 500. Total Stake: 1000.

client.place_prediction(&user1, &pool_id, &200, &1);
client.place_prediction(&user2, &pool_id, &300, &1);
client.place_prediction(&user3, &pool_id, &500, &2);

client.resolve_pool(&operator, &pool_id, &1u32); // Outcome 1 wins

// U1 Winnings: (200 / 500) * 1000 = 400
// U2 Winnings: (300 / 500) * 1000 = 600

let w1 = client.claim_winnings(&user1, &pool_id);
let w2 = client.claim_winnings(&user2, &pool_id);

assert_eq!(w1, 400);
assert_eq!(w2, 600);
assert_eq!(token_ctx.token.balance(&user1), 1200); // 1000 - 200 + 400
assert_eq!(token_ctx.token.balance(&user2), 1300); // 1000 - 300 + 600
assert_eq!(token_ctx.token.balance(&client.address), 0);
}
2 changes: 2 additions & 0 deletions contract/contracts/predifi-contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,3 +324,5 @@ impl PredifiContract {
}

mod test;
mod test_utils;
mod integration_test;
14 changes: 7 additions & 7 deletions contract/contracts/predifi-contract/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ const ROLE_OPERATOR: u32 = 1;
fn setup(
env: &Env,
) -> (
dummy_access_control::DummyAccessControlClient,
PredifiContractClient,
dummy_access_control::DummyAccessControlClient<'_>,
PredifiContractClient<'_>,
Address, // token_address
token::Client,
token::StellarAssetClient,
token::Client<'_>,
token::StellarAssetClient<'_>,
Address, // treasury
Address, // operator
) {
Expand Down Expand Up @@ -73,8 +73,8 @@ fn test_claim_winnings() {
let env = Env::default();
env.mock_all_auths();

let (_, client, token_address, token, token_admin_client, _, operator) = setup(&env);
let contract_id = env.register(PredifiContract, ()); // get contract address for balance check
let (_, client, token_address, _token, token_admin_client, _, operator) = setup(&env);
let _contract_id = env.register(PredifiContract, ()); // get contract address for balance check
// Re-derive contract address from client
let contract_addr = client.address.clone();

Expand Down Expand Up @@ -262,7 +262,7 @@ fn test_multiple_pools_independent() {
let env = Env::default();
env.mock_all_auths();

let (_, client, token_address, token, token_admin_client, _, operator) = setup(&env);
let (_, client, token_address, _token, token_admin_client, _, operator) = setup(&env);

let user1 = Address::generate(&env);
let user2 = Address::generate(&env);
Expand Down
27 changes: 27 additions & 0 deletions contract/contracts/predifi-contract/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#![cfg(test)]

use soroban_sdk::{token, Address, Env};

pub struct TokenTestContext {
pub token_address: Address,
pub token: token::Client<'static>,
pub admin: token::StellarAssetClient<'static>,
}

impl TokenTestContext {
pub fn deploy(env: &Env, admin: &Address) -> Self {
let token_contract = env.register_stellar_asset_contract_v2(admin.clone());
let token = token::Client::new(env, &token_contract);
let token_admin = token::StellarAssetClient::new(env, &token_contract);

Self {
token_address: token_contract,
token,
admin: token_admin,
}
}

pub fn mint(&self, to: &Address, amount: i128) {
self.admin.mint(to, &amount);
}
}
Loading