diff --git a/packages/starterpack/src/components/issuable.cairo b/packages/starterpack/src/components/issuable.cairo index 69d00d81..9481d8be 100644 --- a/packages/starterpack/src/components/issuable.cairo +++ b/packages/starterpack/src/components/issuable.cairo @@ -76,35 +76,40 @@ pub mod IssuableComponent { if base_price != 0 { let token_dispatcher = IERC20Dispatcher { contract_address: payment_token }; - // Calculate referral fee if referrer exists (included in base price) + // Calculate referral fee if referrer exists and is not the payer let referral_fee_amount = if let Option::Some(ref_addr) = referrer { - let ref_fee = base_price - * starterpack.referral_percentage.into() - / FEE_DENOMINATOR.into(); - - // Transfer referral fee - if ref_fee > 0 { - token_dispatcher.transfer_from(payer, ref_addr, ref_fee); - - // Track referral reward for individual referrer - let mut referral_reward = store.get_referral_reward(ref_addr); - if referral_reward.total_referrals == 0 { - referral_reward = ReferralRewardTrait::new(ref_addr); - } - referral_reward.add_referral(ref_fee); - store.set_referral_reward(@referral_reward); - - // Track group reward if referrer_group exists - if let Option::Some(group_id) = referrer_group { - let mut group_reward = store.get_group_reward(group_id); - if group_reward.total_referrals == 0 { - group_reward = GroupRewardTrait::new(group_id); + if ref_addr == payer { + // Skip self-referral + 0 + } else { + let ref_fee = base_price + * starterpack.referral_percentage.into() + / FEE_DENOMINATOR.into(); + + // Transfer referral fee + if ref_fee > 0 { + token_dispatcher.transfer_from(payer, ref_addr, ref_fee); + + // Track referral reward for individual referrer + let mut referral_reward = store.get_referral_reward(ref_addr); + if referral_reward.total_referrals == 0 { + referral_reward = ReferralRewardTrait::new(ref_addr); + } + referral_reward.add_referral(ref_fee); + store.set_referral_reward(@referral_reward); + + // Track group reward if referrer_group exists + if let Option::Some(group_id) = referrer_group { + let mut group_reward = store.get_group_reward(group_id); + if group_reward.total_referrals == 0 { + group_reward = GroupRewardTrait::new(group_id); + } + group_reward.add_referral(ref_fee); + store.set_group_reward(@group_reward); } - group_reward.add_referral(ref_fee); - store.set_group_reward(@group_reward); } + ref_fee } - ref_fee } else { 0 }; diff --git a/packages/starterpack/src/tests/test_fees.cairo b/packages/starterpack/src/tests/test_fees.cairo index 4491c94b..156e21fa 100644 --- a/packages/starterpack/src/tests/test_fees.cairo +++ b/packages/starterpack/src/tests/test_fees.cairo @@ -237,3 +237,66 @@ fn test_sp_free() { // [Assert] No payment made assert_eq!(systems.erc20.balance_of(context.spender), spender_initial, "No payment for free"); } + +#[test] +fn test_sp_fees_self_referral_ignored() { + // [Setup] + let (_world, systems, context) = spawn(); + + // [Initialize] + testing::set_contract_address(OWNER()); + testing::set_block_timestamp(1); + + // [Register] + testing::set_contract_address(context.creator); + let metadata = METADATA(); + let starterpack_id = systems + .starterpack + .register( + implementation: systems.starterpack_impl, + referral_percentage: REFERRAL_PERCENTAGE, + reissuable: false, + price: PRICE, + payment_token: systems.erc20.contract_address, + payment_receiver: Option::None, + metadata: metadata, + ); + + // [Record] Initial balances + let spender_initial = systems.erc20.balance_of(context.spender); + let creator_initial = systems.erc20.balance_of(context.creator); + let receiver_initial = systems.erc20.balance_of(context.receiver); + + // [Issue] With self as referrer (spender == referrer) + testing::set_contract_address(context.spender); + let protocol_fee_amount = PRICE * PROTOCOL_FEE.into() / FEE_DENOMINATOR.into(); + let total_cost = PRICE + protocol_fee_amount; + systems.erc20.approve(systems.starterpack.contract_address, total_cost); + systems + .starterpack + .issue( + recipient: PLAYER(), + starterpack_id: starterpack_id, + quantity: 1, + referrer: Option::Some(context.spender), // self-referral + referrer_group: Option::None, + ); + + // [Assert] Self-referral is ignored, behaves like no referrer + // Spender paid: base price + protocol fee + assert_eq!( + systems.erc20.balance_of(context.spender), spender_initial - total_cost, "Spender balance", + ); + + // Creator received: full base price (no referral deducted) + assert_eq!( + systems.erc20.balance_of(context.creator), creator_initial + PRICE, "Creator balance", + ); + + // Protocol receiver got: protocol fee only + assert_eq!( + systems.erc20.balance_of(context.receiver), + receiver_initial + protocol_fee_amount, + "Protocol receiver balance", + ); +}