From 06712a7832f7e3b3149728573814e6a84d0b83a2 Mon Sep 17 00:00:00 2001 From: sarah-obasi-analytics Date: Fri, 25 Sep 2026 17:08:56 +0100 Subject: [PATCH 1/4] feat(creator-keys): restrict early access toggle to creator and emit event set_early_access_mode is now creator-only and emits EarlyAccessModeChangedEvent with the new state. Tests cover the admin rejection and the toggle event. Co-Authored-By: Claude Sonnet 5 --- creator-keys/src/events.rs | 13 +++++++++++++ creator-keys/src/lib.rs | 11 +++++++++-- creator-keys/src/test_new_features.rs | 21 +++++++++++++++++++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/creator-keys/src/events.rs b/creator-keys/src/events.rs index 17dfdb7c..076f2898 100644 --- a/creator-keys/src/events.rs +++ b/creator-keys/src/events.rs @@ -821,6 +821,7 @@ pub const ADDRESS_WHITELISTED_EVENT_NAME: Symbol = symbol_short!("wl_add"); pub const ADDRESS_REMOVED_EVENT_NAME: Symbol = symbol_short!("wl_rem"); pub const HOLDING_CAP_UPDATED_EVENT_NAME: Symbol = symbol_short!("hold_cap"); pub const WHITELIST_UPDATED_EVENT_NAME: Symbol = symbol_short!("wl_upd"); +pub const EARLY_ACCESS_MODE_CHANGED_EVENT_NAME: Symbol = symbol_short!("ea_mode"); pub const REFERRAL_REGISTERED_EVENT_NAME: Symbol = symbol_short!("ref_reg"); pub const REFERRAL_REWARD_ALLOCATED_EVENT_NAME: Symbol = symbol_short!("ref_rwd"); pub const REFERRAL_REWARDS_CLAIMED_EVENT_NAME: Symbol = symbol_short!("ref_clm"); @@ -926,6 +927,18 @@ pub fn whitelist_updated_topics(creator: &Address) -> (Symbol, Address) { (WHITELIST_UPDATED_EVENT_NAME, creator.clone()) } +/// Emitted when a creator toggles early-access mode. +#[derive(Clone, Debug, Eq, PartialEq)] +#[contracttype] +pub struct EarlyAccessModeChangedEvent { + pub creator: Address, + pub enabled: bool, +} + +pub fn early_access_mode_changed_topics(creator: &Address) -> (Symbol, Address) { + (EARLY_ACCESS_MODE_CHANGED_EVENT_NAME, creator.clone()) +} + #[derive(Clone, Debug, Eq, PartialEq)] #[contracttype] pub struct ReferralRegisteredEvent { diff --git a/creator-keys/src/lib.rs b/creator-keys/src/lib.rs index a7796d74..6ced7de7 100644 --- a/creator-keys/src/lib.rs +++ b/creator-keys/src/lib.rs @@ -8819,7 +8819,7 @@ impl CreatorKeysContract { .get(&constants::storage::max_keys_per_wallet(&creator)) } - /// Creator or admin toggles early-access mode. While on, only whitelisted + /// Creator toggles early-access mode. While on, only whitelisted /// wallets may buy; turning it off opens trading to everyone. Shares the /// flag used by `enable_whitelist` / `disable_whitelist`. pub fn set_early_access_mode( @@ -8829,10 +8829,17 @@ impl CreatorKeysContract { enabled: bool, ) -> Result<(), ContractError> { caller.require_auth(); - assert_creator_or_admin(&env, &caller, &creator)?; + read_registered_creator_profile(&env, &creator)?; + if caller != creator { + return Err(ContractError::Unauthorized); + } let mode_key = constants::storage::whitelist_mode(&creator); env.storage().persistent().set(&mode_key, &enabled); extend_key_ttl_to_full_window(&env, &mode_key); + env.events().publish( + events::early_access_mode_changed_topics(&creator), + events::EarlyAccessModeChangedEvent { creator, enabled }, + ); Ok(()) } diff --git a/creator-keys/src/test_new_features.rs b/creator-keys/src/test_new_features.rs index ad93fc84..e5912973 100644 --- a/creator-keys/src/test_new_features.rs +++ b/creator-keys/src/test_new_features.rs @@ -5,7 +5,10 @@ use crate::{ RegisterCreatorParams, METADATA_AVATAR_URI_MAX_LEN, METADATA_BIO_MAX_LEN, METADATA_NAME_MAX_LEN, }; -use soroban_sdk::{testutils::Address as _, Address, Env, String}; +use soroban_sdk::{ + testutils::{Address as _, Events as _}, + Address, Env, String, +}; fn setup_test() -> (Env, CreatorKeysContractClient<'static>, Address, Address) { let env = Env::default(); @@ -540,10 +543,24 @@ fn test_early_access_mode_whitelist_and_permissions() { client.try_buy_key(&creator, &wallet, &1000i128, &None), Err(Ok(ContractError::NotWhitelisted)) ); - client.set_early_access_mode(&admin, &creator, &false); + assert_eq!( + client.try_set_early_access_mode(&admin, &creator, &false), + Err(Ok(ContractError::Unauthorized)) + ); + client.set_early_access_mode(&creator, &creator, &false); assert_eq!(client.buy_key(&creator, &wallet, &1000i128, &None), 2); } +#[test] +fn test_early_access_mode_toggle_emits_event() { + let (env, client, _admin, _treasury) = setup_test(); + let creator = Address::generate(&env); + register_creator(&env, &client, &creator); + + client.set_early_access_mode(&creator, &creator, &true); + assert_eq!(env.events().all().len(), 1); +} + #[test] fn test_registered_referral_first_trade_fee_and_claim() { let (env, client, admin, _treasury) = setup_test(); From aed7915b0582ef52fab4e5e60c79bf242954d8e0 Mon Sep 17 00:00:00 2001 From: sarah-obasi-analytics Date: Tue, 29 Sep 2026 18:45:36 +0100 Subject: [PATCH 2/4] chore: add dummy file to trigger CI run Co-Authored-By: Claude Sonnet 5 --- ci-trigger.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 ci-trigger.txt diff --git a/ci-trigger.txt b/ci-trigger.txt new file mode 100644 index 00000000..1678d26c --- /dev/null +++ b/ci-trigger.txt @@ -0,0 +1 @@ +dummy file to trigger CI run From 71b35b05d13e0ddfdc8836b8d5a8cad343b337a3 Mon Sep 17 00:00:00 2001 From: sarah-obasi-analytics Date: Thu, 1 Oct 2026 04:02:32 +0100 Subject: [PATCH 3/4] fix: close unclosed if-block in set_early_access_mode cargo fmt --check was failing with an unclosed-delimiter parse error. The if caller != creator block was missing its closing brace, with a leftover assert_creator_or_admin call bleeding into it from before the creator-only restriction was added. Tests already assert admin gets Unauthorized here, so the fix removes that stray call and closes the brace rather than restoring admin access. Co-Authored-By: Claude Sonnet 5 --- creator-keys/src/lib.rs | 2 +- creator-keys/src/test_new_features.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/creator-keys/src/lib.rs b/creator-keys/src/lib.rs index 8f3f8487..9ea96c4b 100644 --- a/creator-keys/src/lib.rs +++ b/creator-keys/src/lib.rs @@ -12347,7 +12347,7 @@ impl CreatorKeysContract { read_registered_creator_profile(&env, &creator)?; if caller != creator { return Err(ContractError::Unauthorized); - assert_creator_or_admin(&env, &caller, &creator)?; + } if enabled && is_whitelist_permanently_disabled(&env, &creator) { return Err(ContractError::WhitelistPermanentlyDisabled); } diff --git a/creator-keys/src/test_new_features.rs b/creator-keys/src/test_new_features.rs index 6904cf23..3cd26737 100644 --- a/creator-keys/src/test_new_features.rs +++ b/creator-keys/src/test_new_features.rs @@ -8,7 +8,6 @@ use crate::{ }; use soroban_sdk::{ testutils::{Address as _, Events as _}, - Address, Env, String, testutils::{Address as _, Events}, Address, Env, IntoVal, String, Symbol, }; From 31daa056dca47779d67a98e07aa2f63a0f36c0d3 Mon Sep 17 00:00:00 2001 From: sarah-obasi-analytics Date: Thu, 1 Oct 2026 04:11:41 +0100 Subject: [PATCH 4/4] fix: remove duplicate testutils import in test_new_features Clippy with -D warnings failed on unused-import errors: the file had two separate testutils::{...} use blocks left over from a prior merge, making the second one's Address/Events bindings fully redundant with the first. Collapsed to a single import. Co-Authored-By: Claude Sonnet 5 --- creator-keys/src/test_new_features.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/creator-keys/src/test_new_features.rs b/creator-keys/src/test_new_features.rs index 3cd26737..0b266778 100644 --- a/creator-keys/src/test_new_features.rs +++ b/creator-keys/src/test_new_features.rs @@ -8,7 +8,6 @@ use crate::{ }; use soroban_sdk::{ testutils::{Address as _, Events as _}, - testutils::{Address as _, Events}, Address, Env, IntoVal, String, Symbol, };