diff --git a/COMEBACKHERE-contracts/contracts/compliance/src/lib.rs b/COMEBACKHERE-contracts/contracts/compliance/src/lib.rs index 262e17f..e117d66 100644 --- a/COMEBACKHERE-contracts/contracts/compliance/src/lib.rs +++ b/COMEBACKHERE-contracts/contracts/compliance/src/lib.rs @@ -122,6 +122,7 @@ impl ComplianceContract { } pub fn accept_admin(e: Env, new_admin: Address) -> Result<(), ContractError> { + check_not_paused(&e)?; new_admin.require_auth(); let pending: Address = e .storage() @@ -163,14 +164,28 @@ impl ComplianceContract { Ok(()) } - pub fn pause(e: Env, admin: Address) { + pub fn pause(e: Env, admin: Address) -> Result<(), ContractError> { admin.require_auth(); + let stored_admin: Address = e.storage().instance().get(&DataKey::Admin).unwrap(); + if admin != stored_admin { + return Err(ContractError::Unauthorized); + } e.storage().instance().set(&DataKey::Paused, &true); + e.events() + .publish((Symbol::new(&e, "contract_paused"),), ()); + Ok(()) } - pub fn unpause(e: Env, admin: Address) { + pub fn unpause(e: Env, admin: Address) -> Result<(), ContractError> { admin.require_auth(); + let stored_admin: Address = e.storage().instance().get(&DataKey::Admin).unwrap(); + if admin != stored_admin { + return Err(ContractError::Unauthorized); + } e.storage().instance().set(&DataKey::Paused, &false); + e.events() + .publish((Symbol::new(&e, "contract_unpaused"),), ()); + Ok(()) } } @@ -223,4 +238,135 @@ mod tests { c.allow_address(&admin, &addr); assert!(c.is_allowed(&addr)); } + + // ── pause / unpause and admin guard tests ───────────────────────────────── + + #[test] + fn test_pause_emits_event() { + let e = Env::default(); + e.mock_all_auths(); + let contract_id = e.register(ComplianceContract, ()); + let admin = Address::generate(&e); + ComplianceContractClient::new(&e, &contract_id).initialize(&admin); + let c = ComplianceContractClient::new(&e, &contract_id); + + c.pause(&admin); + + let all_events = e.events().all(); + assert!( + all_events.iter().any(|ev| ev.0 == (contract_id, "contract_paused".into())), + "contract_paused event should be emitted" + ); + } + + #[test] + fn test_unpause_emits_event() { + let e = Env::default(); + e.mock_all_auths(); + let contract_id = e.register(ComplianceContract, ()); + let admin = Address::generate(&e); + ComplianceContractClient::new(&e, &contract_id).initialize(&admin); + let c = ComplianceContractClient::new(&e, &contract_id); + + c.pause(&admin); + c.unpause(&admin); + + let all_events = e.events().all(); + assert!( + all_events.iter().any(|ev| ev.0 == (contract_id, "contract_unpaused".into())), + "contract_unpaused event should be emitted" + ); + } + + #[test] + fn test_pause_unauthorized_fails() { + let e = Env::default(); + e.mock_all_auths(); + let contract_id = e.register(ComplianceContract, ()); + let admin = Address::generate(&e); + let non_admin = Address::generate(&e); + ComplianceContractClient::new(&e, &contract_id).initialize(&admin); + let c = ComplianceContractClient::new(&e, &contract_id); + + let res = c.try_pause(&non_admin); + assert_eq!(res, Err(Ok(ContractError::Unauthorized))); + } + + #[test] + fn test_unpause_unauthorized_fails() { + let e = Env::default(); + e.mock_all_auths(); + let contract_id = e.register(ComplianceContract, ()); + let admin = Address::generate(&e); + let non_admin = Address::generate(&e); + ComplianceContractClient::new(&e, &contract_id).initialize(&admin); + let c = ComplianceContractClient::new(&e, &contract_id); + + c.pause(&admin); + let res = c.try_unpause(&non_admin); + assert_eq!(res, Err(Ok(ContractError::Unauthorized))); + } + + #[test] + fn test_accept_admin_when_paused_fails() { + let e = Env::default(); + e.mock_all_auths(); + let contract_id = e.register(ComplianceContract, ()); + let admin = Address::generate(&e); + let new_admin = Address::generate(&e); + ComplianceContractClient::new(&e, &contract_id).initialize(&admin); + let c = ComplianceContractClient::new(&e, &contract_id); + + c.pause(&admin); + let res = c.try_accept_admin(&new_admin); + assert_eq!(res, Err(Ok(ContractError::ContractPaused))); + } + + #[test] + fn test_mutating_entrypoints_blocked_when_paused() { + let e = Env::default(); + e.mock_all_auths(); + let contract_id = e.register(ComplianceContract, ()); + let admin = Address::generate(&e); + let addr = Address::generate(&e); + ComplianceContractClient::new(&e, &contract_id).initialize(&admin); + let c = ComplianceContractClient::new(&e, &contract_id); + + c.pause(&admin); + + assert_eq!( + c.try_allow_address(&admin, &addr), + Err(Ok(ContractError::ContractPaused)) + ); + assert_eq!( + c.try_block_address(&admin, &addr), + Err(Ok(ContractError::ContractPaused)) + ); + assert_eq!( + c.try_allow_address_until(&admin, &addr, &1000u64), + Err(Ok(ContractError::ContractPaused)) + ); + assert_eq!( + c.try_clear_address(&admin, &addr), + Err(Ok(ContractError::ContractPaused)) + ); + } + + #[test] + fn test_readonly_entrypoints_work_when_paused() { + let e = Env::default(); + e.mock_all_auths(); + let contract_id = e.register(ComplianceContract, ()); + let admin = Address::generate(&e); + let addr = Address::generate(&e); + ComplianceContractClient::new(&e, &contract_id).initialize(&admin); + let c = ComplianceContractClient::new(&e, &contract_id); + + c.allow_address(&admin, &addr); + c.pause(&admin); + + assert!(c.is_allowed(&addr)); + let status = c.get_address_status(&addr); + assert!(matches!(status, AddressStatus::Allowed)); + } } diff --git a/COMEBACKHERE-contracts/contracts/invoice/src/lib.rs b/COMEBACKHERE-contracts/contracts/invoice/src/lib.rs index 5aacd6f..7fb97ae 100644 --- a/COMEBACKHERE-contracts/contracts/invoice/src/lib.rs +++ b/COMEBACKHERE-contracts/contracts/invoice/src/lib.rs @@ -475,6 +475,7 @@ impl InvoiceContract { /// # Errors /// - [`ContractError::Unauthorized`] if `caller` is not the admin. pub fn set_treasury(env: Env, caller: Address, treasury: Address) -> Result<(), ContractError> { + check_not_paused(&env)?; check_admin(&env, &caller)?; env.storage() .persistent() diff --git a/COMEBACKHERE-contracts/contracts/treasury/src/lib.rs b/COMEBACKHERE-contracts/contracts/treasury/src/lib.rs index 85896ae..8e09031 100644 --- a/COMEBACKHERE-contracts/contracts/treasury/src/lib.rs +++ b/COMEBACKHERE-contracts/contracts/treasury/src/lib.rs @@ -351,6 +351,10 @@ impl TreasuryContract { pub fn pause(e: Env, admin: Address) -> Result<(), TreasuryError> { Self::check_admin(&e, &admin)?; e.storage().instance().set(&DataKey::Paused, &true); + e.events().publish( + (Symbol::new(&e, "contract_paused"),), + (), + ); Ok(()) } @@ -365,6 +369,10 @@ impl TreasuryContract { pub fn unpause(e: Env, admin: Address) -> Result<(), TreasuryError> { Self::check_admin(&e, &admin)?; e.storage().instance().set(&DataKey::Paused, &false); + e.events().publish( + (Symbol::new(&e, "contract_unpaused"),), + (), + ); Ok(()) }