diff --git a/pezcumulus/teyrchains/pezpallets/referral/src/lib.rs b/pezcumulus/teyrchains/pezpallets/referral/src/lib.rs index c2d1e8b7..702b61b7 100644 --- a/pezcumulus/teyrchains/pezpallets/referral/src/lib.rs +++ b/pezcumulus/teyrchains/pezpallets/referral/src/lib.rs @@ -239,6 +239,11 @@ pub mod pezpallet { let new_count = ReferralCount::::get(&referrer).saturating_add(1); ReferralCount::::insert(&referrer, new_count); + // Update referrer stats for direct responsibility tracking + ReferrerStatsStorage::::mutate(&referrer, |stats| { + stats.total_referrals = stats.total_referrals.saturating_add(1); + }); + // Create and store referral info let referral_info = ReferralInfo { referrer: referrer.clone(), @@ -343,21 +348,12 @@ pub mod pezpallet { fn get_referral_score(who: &T::AccountId) -> RawScore { let stats = ReferrerStatsStorage::::get(who); - // Calculate good referrals (total minus revoked) + // Step 1: "Haksız olanı geri alma" - Remove revoked referrals from count + // This is NOT a penalty, it's correcting the record to reflect reality let good_referrals = stats.total_referrals.saturating_sub(stats.revoked_referrals); - // BALANCED PENALTY SYSTEM (Gemini's suggestion): - // "Every 4 bad referrals = -10 points" - // This is equivalent to "1 bad = -2.5 points" - // Much fairer than "1 bad = 3 good deleted" - // - // Formula: penalty_points = (revoked_referrals / 4) * 10 - // Simplified: penalty_points = revoked_referrals * 10 / 4 = revoked_referrals * 2.5 - // Using integer math: penalty_points = (revoked_referrals * 10) / 4 - let penalty_points = (stats.revoked_referrals.saturating_mul(10)) / 4; - - // Calculate base score from good referrals - // Scoring system with max 500 points: + // Step 2: Calculate base score from good referrals + // Tiered scoring system with max 500 points: // 0 referrals = 0 points // 1-10 referrals = count * 10 points (10, 20, 30, ..., 100) // 11-50 referrals = 100 + ((count - 10) * 5) = 105, 110, ..., 300 @@ -371,8 +367,10 @@ pub mod pezpallet { _ => 500, }; - // Apply penalty (cannot go below 0) - base_score.saturating_sub(penalty_points) + // Step 3: "Cezalandırma" - Apply stored penalty from PenaltyPerRevocation + // Uses the pre-calculated penalty_score accumulated in on_citizenship_revoked() + // This is the actual punishment: "you should have been more careful" + base_score.saturating_sub(stats.penalty_score) } } diff --git a/pezcumulus/teyrchains/pezpallets/referral/src/mock.rs b/pezcumulus/teyrchains/pezpallets/referral/src/mock.rs index 709b41e9..6f5303f3 100644 --- a/pezcumulus/teyrchains/pezpallets/referral/src/mock.rs +++ b/pezcumulus/teyrchains/pezpallets/referral/src/mock.rs @@ -69,6 +69,7 @@ impl pezpallet_identity_kyc::Config for Test { type OnKycApproved = Referral; // Referral pezpallet handles KYC approval hook type OnCitizenshipRevoked = Referral; // Referral pezpallet handles revocation penalty type CitizenNftProvider = MockCitizenNftProvider; + type DefaultReferrer = DefaultReferrerAccount; type KycApplicationDeposit = KycApplicationDepositAmount; type MaxStringLength = MaxStringLen; type MaxCidLength = MaxCidLen; diff --git a/pezcumulus/teyrchains/pezpallets/referral/src/tests.rs b/pezcumulus/teyrchains/pezpallets/referral/src/tests.rs index cd8d2b97..0cf21a90 100644 --- a/pezcumulus/teyrchains/pezpallets/referral/src/tests.rs +++ b/pezcumulus/teyrchains/pezpallets/referral/src/tests.rs @@ -317,28 +317,31 @@ fn referral_score_with_balanced_penalty() { ReferrerStatsStorage::::mutate(REFERRER, |stats| { stats.total_referrals = 10; stats.revoked_referrals = 0; + stats.penalty_score = 0; }); assert_eq!(ReferralPallet::get_referral_score(&REFERRER), 100); // 10 total, 4 revoked = 6 good - // Penalty: (4 * 10) / 4 = 10 points deducted + // penalty_score: 4 * PenaltyPerRevocation(3) = 12 // Base score: 6 * 10 = 60 - // Final: 60 - 10 = 50 + // Final: 60 - 12 = 48 ReferrerStatsStorage::::mutate(REFERRER, |stats| { stats.total_referrals = 10; stats.revoked_referrals = 4; + stats.penalty_score = 4 * PenaltyPerRevocationAmount::get(); }); - assert_eq!(ReferralPallet::get_referral_score(&REFERRER), 50); + assert_eq!(ReferralPallet::get_referral_score(&REFERRER), 48); // 20 total, 8 revoked = 12 good (tier 2) - // Penalty: (8 * 10) / 4 = 20 points deducted + // penalty_score: 8 * PenaltyPerRevocation(3) = 24 // Base score: 100 + (2 * 5) = 110 - // Final: 110 - 20 = 90 + // Final: 110 - 24 = 86 ReferrerStatsStorage::::mutate(REFERRER, |stats| { stats.total_referrals = 20; stats.revoked_referrals = 8; + stats.penalty_score = 8 * PenaltyPerRevocationAmount::get(); }); - assert_eq!(ReferralPallet::get_referral_score(&REFERRER), 90); + assert_eq!(ReferralPallet::get_referral_score(&REFERRER), 86); }); } @@ -349,12 +352,13 @@ fn referral_score_cannot_go_negative() { new_test_ext().execute_with(|| { // Extreme case: All referrals revoked // 5 total, 5 revoked = 0 good - // Penalty: (5 * 10) / 4 = 12 points + // penalty_score: 5 * PenaltyPerRevocation(3) = 15 // Base score: 0 - // Final: 0 - 12 = 0 (saturating_sub) + // Final: 0 - 15 = 0 (saturating_sub) ReferrerStatsStorage::::mutate(REFERRER, |stats| { stats.total_referrals = 5; stats.revoked_referrals = 5; + stats.penalty_score = 5 * PenaltyPerRevocationAmount::get(); }); assert_eq!(ReferralPallet::get_referral_score(&REFERRER), 0); }); @@ -412,6 +416,12 @@ fn force_confirm_referral_works() { assert!(Referrals::::contains_key(REFERRED)); assert_eq!(Referrals::::get(REFERRED).unwrap().referrer, REFERRER); + // Verify ReferrerStats is updated (was missing before fix) + let stats = ReferrerStatsStorage::::get(REFERRER); + assert_eq!(stats.total_referrals, 1); + assert_eq!(stats.revoked_referrals, 0); + assert_eq!(stats.penalty_score, 0); + // Verify trait implementations assert_eq!(ReferralPallet::get_inviter(&REFERRED), Some(REFERRER)); });