diff --git a/contracts/governor/src/lib.rs b/contracts/governor/src/lib.rs index 5f4e6cc..7a1cb25 100644 --- a/contracts/governor/src/lib.rs +++ b/contracts/governor/src/lib.rs @@ -50,6 +50,17 @@ fn assert_not_paused(env: &Env) -> Result<(), Error> { } } +/// The zero Stellar account is represented by an Ed25519 public key +/// consisting entirely of zero bytes. +fn is_zero_stellar_account(env: &Env, address: &Address) -> bool { + let zero_account = Address::from_string(&soroban_sdk::String::from_str( + env, + "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + )); + + address == &zero_account +} + #[contract] pub struct DripGovernor; @@ -391,6 +402,9 @@ impl DripGovernor { pub fn set_fee_recipient(env: Env, caller: Address, recipient: Address) -> Result<(), Error> { assert_not_paused(&env)?; role::require_role_or_admin(&env, &caller, Role::FeeManager)?; + if is_zero_stellar_account(&env, &recipient) { + return Err(Error::InvalidParam); + } ttl::bump(&env); let old_recipient: Address = env .storage() diff --git a/tests/governor_config.rs b/tests/governor_config.rs index 54f95d7..63dc821 100644 --- a/tests/governor_config.rs +++ b/tests/governor_config.rs @@ -358,6 +358,24 @@ fn set_fee_recipient_extends_instance_ttl() { assert_eq!(ttl, 200_000); } +#[test] +fn set_fee_recipient_rejects_zero_address() { + let env = Env::default(); + env.mock_all_auths(); + + let (client, authority, fee_recipient) = deploy_governor(&env); + let zero_account = Address::from_string(&soroban_sdk::String::from_str( + &env, + "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF", + )); + + let result = client.try_set_fee_recipient(&authority, &zero_account); + assert_eq!(result, Err(Ok(Error::InvalidParam))); + + // The rejected call must not have mutated state. + assert_eq!(client.config().fee_recipient, fee_recipient); +} + #[test] fn set_min_duration_extends_instance_ttl() { let env = Env::default();