Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions creator-keys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ pub enum ContractError {
HandleTooLong = 13,
InvalidHandleCharacter = 14,
ZeroAddress = 15,
InvalidMaxSupply = 16,
SupplyCapExceeded = 17,
}

pub mod fee {
Expand Down Expand Up @@ -226,6 +228,10 @@ pub mod constants {
pub fn key_balance(creator: &Address, holder: &Address) -> DataKey {
key_balance_key(creator, holder)
}

pub fn max_supply(creator: &Address) -> DataKey {
DataKey::MaxSupply(creator.clone())
}
}

fn creator_key(creator: &Address) -> DataKey {
Expand Down Expand Up @@ -363,6 +369,7 @@ pub enum DataKey {
ProtocolFeeRecipientBalance,
CreatorFeeBalance(Address),
ProtocolStateVersion,
MaxSupply(Address),
}

#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -646,11 +653,18 @@ impl CreatorKeysContract {
env: Env,
creator: Address,
handle: String,
max_supply: Option<u32>,
) -> Result<(), ContractError> {
creator.require_auth();

validate_creator_handle(&handle)?;

if let Some(cap) = max_supply {
if cap == 0 {
return Err(ContractError::InvalidMaxSupply);
}
}

let key = constants::storage::creator(&creator);
// Creator profile storage is a single source of truth keyed by creator address.
// Once written, this key's existence is the registration invariant.
Expand All @@ -675,6 +689,13 @@ impl CreatorKeysContract {
// Persist profile before event publication so indexers reading contract state
// after this tx observe the same registration payload that was emitted.
env.storage().persistent().set(&key, &profile);

if let Some(cap) = max_supply {
env.storage()
.persistent()
.set(&constants::storage::max_supply(&creator), &cap);
}

env.events().publish(
events::register_event_topics(&profile.creator),
events::CreatorRegisteredEvent {
Expand Down Expand Up @@ -714,6 +735,14 @@ impl CreatorKeysContract {

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

// Enforce supply cap if one was set at registration.
let cap_key = constants::storage::max_supply(&creator);
if let Some(cap) = env.storage().persistent().get::<DataKey, u32>(&cap_key) {
if profile.supply >= cap {
return Err(ContractError::SupplyCapExceeded);
}
}

let balance_key = constants::storage::key_balance(&creator, &buyer);
// Missing balance entries are treated as zero to keep storage sparse.
let current_balance: u32 = env.storage().persistent().get(&balance_key).unwrap_or(0);
Expand Down Expand Up @@ -960,6 +989,15 @@ impl CreatorKeysContract {
Ok(profile.supply)
}

/// Read-only view: returns the max supply cap for a creator, or `None` if uncapped.
///
/// The cap is immutable after registration. Returns `None` for unregistered creators.
pub fn get_max_supply(env: Env, creator: Address) -> Option<u32> {
env.storage()
.persistent()
.get(&constants::storage::max_supply(&creator))
}

/// Read-only view: returns the number of unique holders for a creator.
///
/// Returns `0` if the creator is not registered, avoiding panics for
Expand Down
36 changes: 18 additions & 18 deletions creator-keys/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ fn test_get_fee_config_persists_across_repeated_reads() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

// Repeatedly read the fee config and verify stability
for _ in 0..5 {
Expand All @@ -147,7 +147,7 @@ fn test_register_creator() {
let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");

client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let profile = client.get_creator(&creator);
assert_eq!(profile.handle, handle);
Expand All @@ -167,7 +167,7 @@ fn test_register_creator_persists_registration_metadata() {
let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");

client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let profile = client.get_creator(&creator);
assert_eq!(profile.creator, creator);
Expand All @@ -187,10 +187,10 @@ fn test_duplicate_registration_fails() {
let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");

client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

// Second registration should fail with AlreadyRegistered error
let result = client.try_register_creator(&creator, &handle);
let result = client.try_register_creator(&creator, &handle, &None);
assert_eq!(result, Err(Ok(ContractError::AlreadyRegistered)));
assert_no_events(&env);
}
Expand Down Expand Up @@ -225,7 +225,7 @@ fn test_buy_key_success() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let buyer = Address::generate(&env);
let supply = client.buy_key(&creator, &buyer, &100);
Expand All @@ -248,7 +248,7 @@ fn test_get_creator_holder_count_counts_unique_holders() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let holder_one = Address::generate(&env);
let holder_two = Address::generate(&env);
Expand Down Expand Up @@ -289,7 +289,7 @@ fn test_buy_key_insufficient_payment() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let buyer = Address::generate(&env);
let result = client.try_buy_key(&creator, &buyer, &99);
Expand Down Expand Up @@ -360,7 +360,7 @@ fn test_get_key_balance_returns_zero_for_unregistered_wallet() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let unregistered_wallet = Address::generate(&env);

Expand Down Expand Up @@ -459,7 +459,7 @@ fn test_get_buy_quote_success() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let quote = client.get_buy_quote(&creator);
assert_eq!(quote.price, 1000);
Expand All @@ -481,7 +481,7 @@ fn test_get_sell_quote_success() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let buyer = Address::generate(&env);
client.buy_key(&creator, &buyer, &1000);
Expand All @@ -506,7 +506,7 @@ fn test_get_sell_quote_fails_if_insufficient_balance() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let holder = Address::generate(&env); // Zero balance
let result = client.try_get_sell_quote(&creator, &holder);
Expand Down Expand Up @@ -541,7 +541,7 @@ fn test_get_quote_fails_if_fee_not_set() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let result = client.try_get_buy_quote(&creator);
assert_eq!(result, Err(Ok(ContractError::FeeConfigNotSet)));
Expand Down Expand Up @@ -571,7 +571,7 @@ fn test_get_creator_fee_recipient_success() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let recipient = client.get_creator_fee_recipient(&creator);
assert_eq!(recipient, creator);
Expand Down Expand Up @@ -603,7 +603,7 @@ fn test_quote_overflow_guards() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

// Buy quote: price + fees (will overflow)
let result = client.try_get_buy_quote(&creator);
Expand Down Expand Up @@ -680,7 +680,7 @@ fn test_register_event_field_order_is_stable() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "alice");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let all_events = env.events().all();
assert_eq!(
Expand Down Expand Up @@ -742,7 +742,7 @@ fn test_buy_event_topic_and_data_order_is_stable() {

let creator = Address::generate(&env);
let handle = String::from_str(&env, "bob");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let buyer = Address::generate(&env);
client.buy_key(&creator, &buyer, &500);
Expand Down Expand Up @@ -808,7 +808,7 @@ fn test_register_event_fee_adjacent_fields_are_zero_and_ordered_after_identity_f

let creator = Address::generate(&env);
let handle = String::from_str(&env, "carol");
client.register_creator(&creator, &handle);
client.register_creator(&creator, &handle, &None);

let all_events = env.events().all();
let (_contract_id, _topics, data): (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
},
{
"string": "creator3"
}
},
"void"
]
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
},
{
"string": "creator2"
}
},
"void"
]
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
},
{
"string": "creator1"
}
},
"void"
]
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
},
{
"string": "creator0"
}
},
"void"
]
}
},
Expand Down Expand Up @@ -121,7 +122,8 @@
},
{
"string": "creator1"
}
},
"void"
]
}
},
Expand Down Expand Up @@ -223,7 +225,8 @@
},
{
"string": "creator2"
}
},
"void"
]
}
},
Expand Down Expand Up @@ -325,7 +328,8 @@
},
{
"string": "creator3"
}
},
"void"
]
}
},
Expand Down
Loading
Loading