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
128 changes: 80 additions & 48 deletions betting/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@
//! - `refund_bet` : user pulls a refund after the deadline if no settlement
//! - `claim_payout` : user pulls parimutuel winnings once the match is settled

use soroban_sdk::{
contract, contractimpl, contracttype, token, Address, BytesN, Env, Symbol, Vec,
};
use renaissance_core::PlatformError;
use soroban_sdk::{contract, contractimpl, contracttype, token, Address, BytesN, Env, Symbol, Vec};

// ── Outcomes ──────────────────────────────────────────────────────────────────

Expand Down Expand Up @@ -139,7 +137,12 @@ impl RenaissanceBettingContract {
}

fn ensure_not_paused(env: &Env) -> Result<(), PlatformError> {
if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
if env
.storage()
.instance()
.get(&DataKey::Paused)
.unwrap_or(false)
{
return Err(PlatformError::Paused);
}
Ok(())
Expand All @@ -155,16 +158,26 @@ impl RenaissanceBettingContract {
return Err(PlatformError::Unauthorized);
}
env.storage().instance().set(&DataKey::Admin, &admin);
env.storage().instance().set(&DataKey::PlatformAdmin, &admin);
env.storage().instance().set(&DataKey::SecurityAdmin, &admin);
env.storage().instance().set(&DataKey::TreasuryAdmin, &admin);
env.storage()
.instance()
.set(&DataKey::PlatformAdmin, &admin);
env.storage()
.instance()
.set(&DataKey::SecurityAdmin, &admin);
env.storage()
.instance()
.set(&DataKey::TreasuryAdmin, &admin);
env.storage().instance().set(&DataKey::Paused, &false);
env.events()
.publish((Symbol::new(&env, "ContractInitialized"),), admin);
Ok(())
}

pub fn upgrade(env: Env, caller: Address, new_wasm_hash: BytesN<32>) -> Result<(), PlatformError> {
pub fn upgrade(
env: Env,
caller: Address,
new_wasm_hash: BytesN<32>,
) -> Result<(), PlatformError> {
let platform: Address = env
.storage()
.instance()
Expand Down Expand Up @@ -196,21 +209,33 @@ impl RenaissanceBettingContract {
approvals.push_back(caller.clone());

if approvals.len() >= 2 {
env.storage().instance().set(&DataKey::WasmHash, &new_wasm_hash);
env.deployer().update_current_contract_wasm(new_wasm_hash.clone());
env.storage().instance().set(&DataKey::UpgradeApprovals, &Vec::<Address>::new(&env));
env.storage()
.instance()
.set(&DataKey::WasmHash, &new_wasm_hash);
env.deployer()
.update_current_contract_wasm(new_wasm_hash.clone());
env.storage()
.instance()
.set(&DataKey::UpgradeApprovals, &Vec::<Address>::new(&env));
env.events()
.publish((Symbol::new(&env, "Upgraded"),), new_wasm_hash);
} else {
env.storage().instance().set(&DataKey::UpgradeApprovals, &approvals);
env.storage()
.instance()
.set(&DataKey::UpgradeApprovals, &approvals);
}
Ok(())
}

pub fn pause(env: Env) -> Result<(), PlatformError> {
let admin = Self::require_admin(&env)?;
admin.require_auth();
if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
if env
.storage()
.instance()
.get(&DataKey::Paused)
.unwrap_or(false)
{
return Ok(());
}
env.storage().instance().set(&DataKey::Paused, &true);
Expand All @@ -222,12 +247,16 @@ impl RenaissanceBettingContract {
let admin = Self::require_admin(&env)?;
admin.require_auth();
env.storage().instance().set(&DataKey::Paused, &false);
env.events().publish((Symbol::new(&env, "Unpaused"),), admin);
env.events()
.publish((Symbol::new(&env, "Unpaused"),), admin);
Ok(())
}

pub fn is_paused(env: Env) -> bool {
env.storage().instance().get(&DataKey::Paused).unwrap_or(false)
env.storage()
.instance()
.get(&DataKey::Paused)
.unwrap_or(false)
}

/// Define a new match id with its authorized oracle, settlement token,
Expand Down Expand Up @@ -273,17 +302,15 @@ impl RenaissanceBettingContract {
.instance()
.set(&DataKey::Stats(match_id), &stats);

env.events()
.publish((Symbol::new(&env, "MatchRegistered"), admin), (match_id, oracle, token, deadline));
env.events().publish(
(Symbol::new(&env, "MatchRegistered"), admin),
(match_id, oracle, token, deadline),
);
Ok(())
}

/// Replace the oracle of an unsettled match. Admin only.
pub fn set_oracle(
env: Env,
match_id: u64,
new_oracle: Address,
) -> Result<(), PlatformError> {
pub fn set_oracle(env: Env, match_id: u64, new_oracle: Address) -> Result<(), PlatformError> {
Self::ensure_not_paused(&env)?;
let admin: Address = env
.storage()
Expand Down Expand Up @@ -370,9 +397,10 @@ impl RenaissanceBettingContract {
.ok_or(PlatformError::InternalError)?;
let idx = outcome.index();
let prev = stats.pools.get(idx).unwrap_or(0);
stats
.pools
.set(idx, prev.checked_add(amount).ok_or(PlatformError::Overflow)?);
stats.pools.set(
idx,
prev.checked_add(amount).ok_or(PlatformError::Overflow)?,
);
stats.bettors.push_back(user.clone());
env.storage()
.instance()
Expand Down Expand Up @@ -432,11 +460,7 @@ impl RenaissanceBettingContract {
/// ```
///
/// Emits `BetClaimed` with the paid amount.
pub fn claim_payout(
env: Env,
user: Address,
match_id: u64,
) -> Result<i128, PlatformError> {
pub fn claim_payout(env: Env, user: Address, match_id: u64) -> Result<i128, PlatformError> {
Self::ensure_not_paused(&env)?;
user.require_auth();
let key = DataKey::Bet(match_id, user.clone());
Expand Down Expand Up @@ -504,21 +528,15 @@ impl RenaissanceBettingContract {
let token_client = token::Client::new(&env, &m.token);
token_client.transfer(&env.current_contract_address(), &user, &payout);

env.events().publish(
(Symbol::new(&env, "BetClaimed"), user),
(match_id, payout),
);
env.events()
.publish((Symbol::new(&env, "BetClaimed"), user), (match_id, payout));
Ok(payout)
}

/// Refund the user's full bet amount if the match has passed its deadline
/// without being settled. Idempotent per `(user, match_id)`.
/// Emits `BetRefunded`.
pub fn refund_bet(
env: Env,
user: Address,
match_id: u64,
) -> Result<(), PlatformError> {
pub fn refund_bet(env: Env, user: Address, match_id: u64) -> Result<(), PlatformError> {
Self::ensure_not_paused(&env)?;
user.require_auth();
let key = DataKey::Bet(match_id, user.clone());
Expand Down Expand Up @@ -557,7 +575,12 @@ impl RenaissanceBettingContract {
/// Look up a user's bet on a match. Returns `None` if the bet does
/// not exist.
pub fn get_bet(env: Env, user: Address, match_id: u64) -> Option<Bet> {
if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
if env
.storage()
.instance()
.get(&DataKey::Paused)
.unwrap_or(false)
{
return None;
}
env.storage()
Expand All @@ -568,7 +591,12 @@ impl RenaissanceBettingContract {
/// Look up a match descriptor. Returns `None` if the match id is
/// not registered.
pub fn get_match(env: Env, match_id: u64) -> Option<Match> {
if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
if env
.storage()
.instance()
.get(&DataKey::Paused)
.unwrap_or(false)
{
return None;
}
env.storage().instance().get(&DataKey::Match(match_id))
Expand Down Expand Up @@ -618,9 +646,15 @@ mod test {
let approver_a = Address::generate(&env);
let approver_b = Address::generate(&env);
env.as_contract(&contract_id, || {
env.storage().instance().set(&DataKey::PlatformAdmin, &approver_a);
env.storage().instance().set(&DataKey::SecurityAdmin, &approver_b);
env.storage().instance().set(&DataKey::TreasuryAdmin, &admin);
env.storage()
.instance()
.set(&DataKey::PlatformAdmin, &approver_a);
env.storage()
.instance()
.set(&DataKey::SecurityAdmin, &approver_b);
env.storage()
.instance()
.set(&DataKey::TreasuryAdmin, &admin);
});

let new_hash = BytesN::from_array(&env, &[9; 32]);
Expand Down Expand Up @@ -763,8 +797,7 @@ mod test {
client.register_match(&8u64, &oracle, &token, &deadline_in(&env, 3_600));
let user = Address::generate(&env);
mint(&env, &token, &user, 100);
let res =
client.try_place_bet(&user, &8u64, &Outcome::HomeWin, &500i128);
let res = client.try_place_bet(&user, &8u64, &Outcome::HomeWin, &500i128);
match res {
Err(Ok(e)) => assert_eq!(e, PlatformError::InsufficientBalance),
_ => panic!("expected InsufficientBalance contract error"),
Expand All @@ -780,8 +813,7 @@ mod test {
mint(&env, &token, &user, 1_000);

env.ledger().set_timestamp(deadline_in(&env, 3_600) + 1);
let res =
client.try_place_bet(&user, &9u64, &Outcome::HomeWin, &100i128);
let res = client.try_place_bet(&user, &9u64, &Outcome::HomeWin, &100i128);
match res {
Err(Ok(e)) => assert_eq!(e, PlatformError::ExpiredDeadline),
_ => panic!("expected ExpiredDeadline contract error"),
Expand Down
Loading
Loading