From b8d7a82bedfedefe1f2283c34a143b470a647cf0 Mon Sep 17 00:00:00 2001 From: Akeem813 Date: Wed, 29 Jul 2026 11:10:45 +0000 Subject: [PATCH] fix(market): guard execute_fee_rate_change with require_initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit execute_fee_rate_change was the only state-mutating contract entry point that did not call validation::require_initialized before operating on persistent storage. Every other mutating function (set_fee_rate, set_fee_cap, set_treasury, cancel_market, update_position, etc.) begins with require_initialized so that callers on an uninitialized contract receive ContractError::NotInitialized rather than silently reading or writing orphaned storage entries. Without the guard a caller could, in theory, write FeeRateBps storage via execute_fee_rate_change before the contract admin is set — producing a partially-configured contract state that is hard to reason about and inconsistent with the initialization invariant documented throughout the codebase. The timelock check itself (timestamp < effective_at → TimelockNotElapsed) is already correct and unchanged; this commit adds the missing initialization gate as the very first check in the function. Fixes: #496 --- contracts/market/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contracts/market/src/lib.rs b/contracts/market/src/lib.rs index 8eb30eb..ecdbdc2 100644 --- a/contracts/market/src/lib.rs +++ b/contracts/market/src/lib.rs @@ -1048,6 +1048,13 @@ impl MarketContract { /// time in [`Self::set_fee_rate`]) so a cap lowered by the admin while a /// change is in flight cannot let a stale, now-excessive rate through. pub fn execute_fee_rate_change(env: Env) -> Result { + // Guard: contract must be fully initialized before a pending fee-rate + // change can be applied. Without this check a caller could observe a + // PendingFeeRate entry that was somehow written before initialization + // completed and apply it, writing FeeRateBps storage before the admin + // is set and leaving the contract in an inconsistent state. + validation::require_initialized(&env)?; + let pending = storage::get_pending_fee_rate_change(&env) .ok_or(ContractError::NoPendingFeeChange)?; if env.ledger().timestamp() < pending.effective_at {