diff --git a/contracts/game_contract/src/lib.rs b/contracts/game_contract/src/lib.rs index 7bcf9b3a..b27031eb 100644 --- a/contracts/game_contract/src/lib.rs +++ b/contracts/game_contract/src/lib.rs @@ -97,6 +97,8 @@ const MAX_STAKE: Symbol = symbol_short!("MAXSTAKE"); const FEE_BIPS: Symbol = symbol_short!("FEE_BIPS"); // u32 (0–1000, i.e. 0–10 %) const TREASURY_ADDR: Symbol = symbol_short!("TR_ADDR"); // Address const CONTRACT_ADMIN: Symbol = symbol_short!("CT_ADMIN"); // Address +const FEE_ADMINS: Symbol = symbol_short!("FEE_ADMS"); // Vec
+const FEE_THRESHOLD: Symbol = symbol_short!("FEE_THR"); // u32 // Dispute resolution system const DISPUTE_FEE: Symbol = symbol_short!("D_FEE"); // i128 - fee to file a dispute @@ -672,7 +674,12 @@ impl GameContract { env.storage().instance().set(&MAX_STAKE, &new_limit); } - pub fn configure_fees(env: Env, admin: Address, fee_bips: u32, treasury_address: Address) { + pub fn configure_fee_multisig( + env: Env, + caller: Address, + new_admins: Vec, + threshold: u32, + ) { let current_admin: Address = env .storage() .instance() @@ -680,9 +687,73 @@ impl GameContract { .expect("Not initialized"); current_admin.require_auth(); - if admin != current_admin { + if caller != current_admin { panic!("Unauthorized admin address"); } + if threshold == 0 || threshold > new_admins.len() as u32 { + panic!("Invalid threshold"); + } + + let mut unique_set = Vec::new(&env); + for i in 0..new_admins.len() { + let admin = new_admins.get(i).unwrap(); + if unique_set.contains(&admin) { + panic!("Duplicate admins in list"); + } + unique_set.push_back(admin); + } + + env.storage().instance().set(&FEE_ADMINS, &new_admins); + env.storage().instance().set(&FEE_THRESHOLD, &threshold); + } + + pub fn configure_fees( + env: Env, + admins: Vec, + fee_bips: u32, + treasury_address: Address, + ) { + let stored_admins: Vec = + env.storage() + .instance() + .get(&FEE_ADMINS) + .unwrap_or_else(|| { + let current_admin: Address = env + .storage() + .instance() + .get(&CONTRACT_ADMIN) + .expect("Not initialized"); + let mut v = Vec::new(&env); + v.push_back(current_admin); + v + }); + let threshold: u32 = env.storage().instance().get(&FEE_THRESHOLD).unwrap_or(1); + + if admins.len() < threshold { + panic!("Not enough admin approvals"); + } + + let mut unique_approvals = 0; + let mut approved_set = Vec::new(&env); + + for i in 0..admins.len() { + let admin = admins.get(i).unwrap(); + if stored_admins.contains(&admin) { + if approved_set.contains(&admin) { + panic!("Duplicate admin approvals"); + } + admin.require_auth(); + approved_set.push_back(admin.clone()); + unique_approvals += 1; + } else { + panic!("Unauthorized admin address"); + } + } + + if unique_approvals < threshold { + panic!("Not enough valid admin approvals"); + } + if fee_bips > 1000 { panic!("Fee bips must be between 0 and 1000"); } @@ -1069,6 +1140,39 @@ impl GameContract { .get(dispute.game_id) .ok_or(ContractError::GameNotFound)?; + // Update dispute status + dispute.status = DisputeStatus::Resolved; + dispute.resolution = Some(resolution.clone()); + let game_id = dispute.game_id; + disputes.set(dispute_id, dispute); + env.storage().instance().set(&DISPUTES, &disputes); + + // Update game state and process payout based on arbitrator's decision + if let Some(ref winner_addr) = winner { + // Winner takes all + let mut games: Map