Skip to content
Merged
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
10 changes: 9 additions & 1 deletion contracts/manage_hub/src/royalty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,17 @@ impl RoyaltyModule {
fn validate_config(recipients: &Vec<RoyaltyRecipient>) -> Result<u32, Error> {
let mut total_percentage: u32 = 0;

for recipient in recipients.iter() {
for (index, recipient) in recipients.iter().enumerate() {
if recipient.percentage == 0 {
return Err(Error::InvalidPaymentAmount);
}

for previous_index in 0..index {
if recipients.get(previous_index).unwrap().address == recipient.address {
return Err(Error::InvalidPaymentAmount);
}
}

total_percentage = total_percentage
.checked_add(recipient.percentage)
.ok_or(Error::InvalidPaymentAmount)?;
Expand Down Expand Up @@ -98,6 +104,8 @@ impl RoyaltyModule {
return Ok(0);
}

Self::validate_config(&cfg.recipients)?;

// Calculate royalties with deterministic rounding (CT-16)
// Ensure total_royalty <= sale_price
let mut total_royalty_amount: i128 = 0;
Expand Down
69 changes: 69 additions & 0 deletions contracts/manage_hub/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4234,6 +4234,75 @@ fn test_royalty_exact_full_allocation_accepted() {
assert_eq!(info.config.recipients.len(), 2);
}

#[test]
fn test_royalty_rejected_updates_preserve_existing_config() {
let env = Env::default();
env.mock_all_auths();

let contract_id = env.register(Contract, ());
let client = ContractClient::new(&env, &contract_id);

let admin = Address::generate(&env);
client.set_admin(&admin);

let token_id = BytesN::<32>::random(&env);
let owner = Address::generate(&env);
client.issue_token(&token_id, &owner, &(env.ledger().timestamp() + 1000));

let first = Address::generate(&env);
let second = Address::generate(&env);
let valid_recipients = vec![
&env,
types::RoyaltyRecipient {
address: first.clone(),
percentage: 3000,
},
types::RoyaltyRecipient {
address: second.clone(),
percentage: 2000,
},
];
client.set_royalty(&token_id, &valid_recipients);

let duplicate_recipients = vec![
&env,
types::RoyaltyRecipient {
address: first.clone(),
percentage: 1000,
},
types::RoyaltyRecipient {
address: first.clone(),
percentage: 1000,
},
];
assert_eq!(
client.try_set_royalty(&token_id, &duplicate_recipients),
Err(Ok(Error::InvalidPaymentAmount))
);
let info_after_duplicate = client.get_royalty_info(&token_id).unwrap();
assert_eq!(info_after_duplicate.config.recipients, valid_recipients);
assert_eq!(info_after_duplicate.total_percentage, 5000);

let excessive_recipients = vec![
&env,
types::RoyaltyRecipient {
address: first,
percentage: 6000,
},
types::RoyaltyRecipient {
address: second,
percentage: 5000,
},
];
assert_eq!(
client.try_set_royalty(&token_id, &excessive_recipients),
Err(Ok(Error::InvalidPaymentAmount))
);
let info_after_excessive = client.get_royalty_info(&token_id).unwrap();
assert_eq!(info_after_excessive.config.recipients, valid_recipients);
assert_eq!(info_after_excessive.total_percentage, 5000);
}

#[test]
fn test_transfer_with_royalty_events() {
let env = Env::default();
Expand Down
Loading