Skip to content
6 changes: 6 additions & 0 deletions creator-keys/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ pub const PAUSE_EVENT_NAME: Symbol = symbol_short!("pause");
/// Event name for protocol unpause.
pub const UNPAUSE_EVENT_NAME: Symbol = symbol_short!("unpause");

/// Event name for a wallet being added to the admin blacklist.
pub const BLACKLIST_ADDED_EVENT_NAME: Symbol = symbol_short!("blk_add");

/// Event name for a wallet being removed from the admin blacklist.
pub const BLACKLIST_REMOVED_EVENT_NAME: Symbol = symbol_short!("blk_rem");

/// Event name for creator registration.
pub const REGISTER_EVENT_NAME: Symbol = symbol_short!("register");

Expand Down
63 changes: 63 additions & 0 deletions creator-keys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub enum ContractError {
InvalidReferrer = 34,
WalletCapExceeded = 35,
DiscountTierLimitExceeded = 36,
WalletBlacklisted = 37,
}

pub mod fee {
Expand Down Expand Up @@ -332,6 +333,10 @@ pub mod constants {
DataKey::Whitelist(creator.clone())
}

pub fn blacklisted(wallet: &Address) -> DataKey {
DataKey::Blacklisted(wallet.clone())
}

pub fn creator(creator: &Address) -> DataKey {
creator_key(creator)
}
Expand Down Expand Up @@ -608,6 +613,9 @@ pub enum DataKey {
/// decide whether to emit the TTL-extension event without a TTL read
/// (the Soroban SDK does not expose TTL reads to contract code).
CreatorTtlLiveUntil(Address),
/// Wallet addresses the protocol admin has barred from buying, selling,
/// or registering as a creator.
Blacklisted(Address),
}

/// Time-locked key allocation for creator self-vesting.
Expand Down Expand Up @@ -1004,6 +1012,20 @@ fn assert_not_paused(env: &Env) -> Result<(), ContractError> {
Ok(())
}

fn is_blacklisted(env: &Env, wallet: &Address) -> bool {
env.storage()
.persistent()
.get::<DataKey, bool>(&constants::storage::blacklisted(wallet))
.unwrap_or(false)
}

fn assert_not_blacklisted(env: &Env, wallet: &Address) -> Result<(), ContractError> {
if is_blacklisted(env, wallet) {
return Err(ContractError::WalletBlacklisted);
}
Ok(())
}

fn assert_is_admin(env: &Env, caller: &Address) -> Result<(), ContractError> {
let admin: Address = env
.storage()
Expand Down Expand Up @@ -1521,6 +1543,7 @@ impl CreatorKeysContract {

creator.require_auth();
assert_not_paused(&env)?;
assert_not_blacklisted(&env, &creator)?;

validate_creator_handle(&handle)?;
if let Some(config) = co_creator.as_ref() {
Expand Down Expand Up @@ -1684,6 +1707,7 @@ impl CreatorKeysContract {
) -> Result<u32, ContractError> {
buyer.require_auth();
assert_not_paused(&env)?;
assert_not_blacklisted(&env, &buyer)?;

if payment <= 0 {
return Err(ContractError::NotPositiveAmount);
Expand Down Expand Up @@ -1852,6 +1876,7 @@ impl CreatorKeysContract {
) -> Result<u32, ContractError> {
seller.require_auth();
assert_not_paused(&env)?;
assert_not_blacklisted(&env, &seller)?;

let mut profile: CreatorProfile = read_registered_creator_profile(&env, &creator)?;

Expand Down Expand Up @@ -2231,6 +2256,44 @@ impl CreatorKeysContract {
is_paused(&env)
}

/// Blocks a wallet from buying, selling, or registering as a creator.
///
/// Only the protocol admin may call this. Emits a `blacklist` event.
pub fn blacklist_wallet(env: Env, admin: Address, wallet: Address) -> Result<(), ContractError> {
admin.require_auth();
assert_is_admin(&env, &admin)?;
env.storage()
.persistent()
.set(&constants::storage::blacklisted(&wallet), &true);
env.events()
.publish((events::BLACKLIST_ADDED_EVENT_NAME, wallet), ());
Ok(())
}

/// Restores a previously blacklisted wallet's access to buy, sell, and
/// register as a creator.
///
/// Only the protocol admin may call this. Emits an `unblacklist` event.
pub fn remove_from_blacklist(
env: Env,
admin: Address,
wallet: Address,
) -> Result<(), ContractError> {
admin.require_auth();
assert_is_admin(&env, &admin)?;
env.storage()
.persistent()
.remove(&constants::storage::blacklisted(&wallet));
env.events()
.publish((events::BLACKLIST_REMOVED_EVENT_NAME, wallet), ());
Ok(())
}

/// Read-only view: returns whether `wallet` is currently blacklisted.
pub fn is_wallet_blacklisted(env: Env, wallet: Address) -> bool {
is_blacklisted(&env, &wallet)
}

pub fn get_key_balance(env: Env, creator: Address, wallet: Address) -> u32 {
let key = constants::storage::holder_balance_key(&creator, &wallet);
// Read-only callers get `0` for unseen balances to avoid sparse-map lookups failing.
Expand Down
Loading
Loading