From 274109d6643806a57d799897b94a94a5367a7852 Mon Sep 17 00:00:00 2001 From: ZeeTech374 Date: Tue, 20 Oct 2026 11:44:50 +0100 Subject: [PATCH] Contracts: Fix factory/treasury integration and state synchronization - #1351: Expose MarketFactory::get_market_address(market_id) -> Option
for Treasury market authorization, and update all Treasury deposits/payouts to handle Option return - #1352: Remove factory's call to non-existent Treasury::register_market (C-93 supersedes with factory registry) - #1353: Keep ProtocolConfig.admin in sync when accept_admin rotates the ADMIN key (single source of truth for authorized admin functions) - #1354: Unify pause state: pause_factory/unpause_factory now update both PAUSED key and ProtocolConfig.paused so emergency_drain is reachable after pause Closes #1351, #1352, #1353, #1354 --- contracts/market_factory/src/lib.rs | 29 +++++++++++++++++++++++------ contracts/treasury/src/lib.rs | 27 +++++++++++++++++++++------ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/contracts/market_factory/src/lib.rs b/contracts/market_factory/src/lib.rs index 01b15cde..cbf7c871 100644 --- a/contracts/market_factory/src/lib.rs +++ b/contracts/market_factory/src/lib.rs @@ -206,12 +206,6 @@ impl MarketFactory { ], )?; - let _: () = env.invoke_contract( - &treasury, - &Symbol::new(&env, "register_market"), - soroban_sdk::vec![&env, market_address.clone().into()], - ); - let info = MarketInfo { market_id: market_id.clone(), market_address, @@ -254,6 +248,12 @@ impl MarketFactory { .get(&(key_prefix, key_suffix)) } + /// Returns the address of a registered market, or None if not found. + /// Treasury uses this to authorize deposits and payouts against registered markets. + pub fn get_market_address(env: Env, market_id: Bytes) -> Option
{ + Self::get_market(env, market_id).map(|info| info.market_address) + } + /// Returns a bounded, stably-ordered (creation order) page of all markets /// ever created. `limit` is capped at `MAX_PAGE_SIZE` regardless of the /// value requested, to bound gas. @@ -369,6 +369,11 @@ impl MarketFactory { env.storage().persistent().set(&ADMIN, &new_admin); env.storage().persistent().remove(&PENDING_ADMIN); + // Update ProtocolConfig.admin to keep it in sync + let mut config = Self::get_config(&env)?; + config.admin = new_admin.clone(); + env.storage().persistent().set(&CONFIG_KEY, &config); + events::emit_admin_transferred(&env, old_admin, new_admin); Ok(()) } @@ -432,6 +437,12 @@ impl MarketFactory { pub fn pause_factory(env: Env, admin: Address) -> Result<(), ContractError> { Self::require_admin(&env, &admin)?; env.storage().persistent().set(&PAUSED, &true); + + // Update ProtocolConfig.paused to keep it in sync + let mut config = Self::get_config(&env)?; + config.paused = true; + env.storage().persistent().set(&CONFIG_KEY, &config); + events::emit_protocol_paused(&env); Ok(()) } @@ -445,6 +456,12 @@ impl MarketFactory { pub fn unpause_factory(env: Env, admin: Address) -> Result<(), ContractError> { Self::require_admin(&env, &admin)?; env.storage().persistent().set(&PAUSED, &false); + + // Update ProtocolConfig.paused to keep it in sync + let mut config = Self::get_config(&env)?; + config.paused = false; + env.storage().persistent().set(&CONFIG_KEY, &config); + events::emit_protocol_unpaused(&env); Ok(()) } diff --git a/contracts/treasury/src/lib.rs b/contracts/treasury/src/lib.rs index 3fc99c9d..0fb5cdfb 100644 --- a/contracts/treasury/src/lib.rs +++ b/contracts/treasury/src/lib.rs @@ -232,12 +232,12 @@ impl Treasury { .get(&key_factory(&env)) .expect("not initialized"); - let registered: Address = env.invoke_contract( + let registered: Option
= env.invoke_contract( &factory, &Symbol::new(&env, "get_market_address"), soroban_sdk::vec![&env, market_id.to_val()], ); - if registered != from_market { + if registered != Some(from_market.clone()) { return Err(ContractError::MarketNotApproved); } @@ -289,12 +289,12 @@ impl Treasury { let caller = env.current_contract_address(); - let registered: Address = env.invoke_contract( + let registered: Option
= env.invoke_contract( &factory, &Symbol::new(&env, "get_market_address"), soroban_sdk::vec![&env, market_id.to_val()], ); - if registered != caller { + if registered != Some(caller.clone()) { return Err(ContractError::MarketNotApproved); } @@ -354,12 +354,12 @@ impl Treasury { .get(&key_factory(&env)) .expect("not initialized"); - let registered: Address = env.invoke_contract( + let registered: Option
= env.invoke_contract( &factory, &Symbol::new(&env, "get_market_address"), soroban_sdk::vec![&env, market_id.to_val()], ); - if registered != from_market { + if registered != Some(from_market.clone()) { panic!("unauthorized: caller is not a registered market"); } @@ -462,6 +462,21 @@ impl Treasury { pub fn release_winnings(env: Env, from_market: Address, market_id: Bytes, recipient: Address, amount: i128) { from_market.require_auth(); + let factory: Address = env + .storage() + .persistent() + .get(&key_factory(&env)) + .expect("not initialized"); + + let registered: Option
= env.invoke_contract( + &factory, + &Symbol::new(&env, "get_market_address"), + soroban_sdk::vec![&env, market_id.to_val()], + ); + if registered != Some(from_market.clone()) { + panic!("unauthorized: caller is not a registered market"); + } + let balance: i128 = env.storage().persistent().get(&key_balance(&env)).unwrap_or(0); if amount > balance { panic!("insufficient escrow for payout");