ReferralSystem

Description:

Smart contract deployed on Ethereum.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

/**
 *Submitted for verification at lineascan.build/ on 2025-05-17
*/

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ReferralSystem {
    address payable public owner;
    bool public paused;
    
    // Access control mechanism
    mapping(address => bool) public authorizedCallers;
    
    struct ReferralConfig {
        uint8 levels;
        mapping(uint8 => uint8) discounts;
        mapping(uint8 => uint8) rewards;
    }
    
    struct ReferralMilestone {
        uint256 referralCount;
        bool isPlanSpecific;
        uint256 planId;
        bool isCorePlan;
        uint256 rewardAmount;
        bool isActive;
    }
    
    struct ReferralStats {
        address[] referredUsers;
        mapping(uint256 => uint256) planReferralCounts;
        mapping(address => uint8) referralLevel;
        uint256 totalEarned;
        uint256 totalWithdrawn;
        uint256[] achievedMilestones;
        uint256 rank;
    }
    
    ReferralConfig public referralConfig;
    
    mapping(address => address) public userReferrer;
    
    mapping(address => uint256) public referralRewards;
    
    mapping(address => ReferralStats) private referralStats;
    
    mapping(uint256 => ReferralMilestone) public referralMilestones;
    uint256 private nextMilestoneId = 1;
    
    mapping(address => mapping(uint256 => bool)) public claimedMilestones;
    
    event ReferralPurchase(
        address indexed referrer, 
        address indexed user, 
        uint256 indexed planId,
        bool isCorePlan,
        uint256 purchaseId,
        uint256 referralLevel,
        uint256 rewardAmount
    );
    
    event ReferralRewardWithdrawn(
        address indexed referrer, 
        uint256 amount
    );
    
    event MilestoneAchieved(
        address indexed user,
        uint256 indexed milestoneId,
        uint256 referralCount,
        uint256 rewardAmount
    );
    
    event MilestoneRewardClaimed(
        address indexed user,
        uint256 indexed milestoneId,
        uint256 rewardAmount
    );
    
    event EmergencyStatusChanged(bool paused);
    event WithdrawnByOwner(uint256 amount);
    
    event AuthorizedCallerSet(address indexed caller, bool status);
    event RankUpdated(address indexed user, uint256 oldRank, uint256 newRank);
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function");
        _;
    }
    
    modifier whenNotPaused() {
        require(!paused, "Contract is paused");
        _;
    }
    
    /**
     * @dev Constructor initializes the contract with default settings
     */
    constructor() {
        owner = payable(msg.sender);
        
        referralConfig.levels = 2;
        referralConfig.discounts[0] = 10;
        referralConfig.discounts[1] = 5;
        referralConfig.rewards[0] = 10;
        referralConfig.rewards[1] = 2;
    }
    
    /**
     * @dev Change contract ownership
     * @param newOwner Address of the new owner
     */
    function transferOwnership(address payable newOwner) external onlyOwner {
        require(newOwner != address(0), "New owner cannot be zero address");
        owner = newOwner;
    }
    
    /**
     * @dev Toggle emergency pause status
     * @param _paused New pause status
     */
    function setPaused(bool _paused) external onlyOwner {
        paused = _paused;
        emit EmergencyStatusChanged(_paused);
    }
    
    /**
     * @dev Withdraw contract balance (owner only)
     */
    function withdraw() external onlyOwner {
        uint256 balance = address(this).balance;
        require(balance > 0, "No balance to withdraw");
        
        (bool success, ) = owner.call{value: balance}("");
        require(success, "Withdrawal failed");
        
        emit WithdrawnByOwner(balance);
    }
    
    /**
     * @dev Set authorized caller status
     * @param caller Caller address
     * @param status Authorization status
     */
    function setAuthorizedCaller(address caller, bool status) external onlyOwner {
        require(caller != address(0), "Invalid address");
        authorizedCallers[caller] = status;
        emit AuthorizedCallerSet(caller, status);
    }
    
    /**
     * @dev Check if caller is authorized
     */
    modifier onlyAuthorized() {
        require(msg.sender == owner || authorizedCallers[msg.sender], "Unauthorized caller");
        _;
    }
    
    // ============= Referral Configuration Functions =============
    /**
     * @dev Configure the referral system levels and percentages
     * @param _levels Number of referral levels
     * @param _discounts Array of discount percentages for each level
     * @param _rewards Array of reward percentages for each level
     */
    function configureReferralSystem(
        uint8 _levels,
        uint8[] calldata _discounts,
        uint8[] calldata _rewards
    ) 
        external 
        onlyOwner 
    {
        require(_levels > 0 && _levels <= 5, "Levels must be between 1 and 5");
        require(_discounts.length == _levels, "Discounts array length mismatch");
        require(_rewards.length == _levels, "Rewards array length mismatch");
        
        referralConfig.levels = _levels;
        
        for (uint8 i = 0; i < _levels; i++) {
            require(_discounts[i] <= 50, "Discount cannot exceed 50%");
            require(_rewards[i] <= 50, "Reward cannot exceed 50%");
            
            referralConfig.discounts[i] = _discounts[i];
            referralConfig.rewards[i] = _rewards[i];
        }
    }
    
    /**
     * @dev Create a new referral milestone
     * @param referralCount Number of referrals needed to achieve the milestone
     * @param rewardAmount Reward amount in wei
     * @param isPlanSpecific Whether this milestone is specific to a plan
     * @param planId Plan ID if plan-specific
     * @param isCorePlan Whether the plan is a core plan
     * @return milestoneId ID of the newly created milestone
     */
    function createReferralMilestone(
        uint256 referralCount,
        uint256 rewardAmount,
        bool isPlanSpecific,
        uint256 planId,
        bool isCorePlan
    ) 
        external 
        onlyOwner 
        returns (uint256 milestoneId) 
    {
        require(referralCount > 0, "Referral count must be greater than 0");
        require(rewardAmount > 0, "Reward amount must be greater than 0");
        
        milestoneId = nextMilestoneId++;
        
        referralMilestones[milestoneId] = ReferralMilestone({
            referralCount: referralCount,
            isPlanSpecific: isPlanSpecific,
            planId: planId,
            isCorePlan: isCorePlan,
            rewardAmount: rewardAmount,
            isActive: true
        });
    }
    
    /**
     * @dev Toggle milestone activation status
     * @param milestoneId ID of the milestone
     * @param isActive New active status
     */
    function setMilestoneStatus(uint256 milestoneId, bool isActive)
        external
        onlyOwner
    {
        require(milestoneId > 0 && milestoneId < nextMilestoneId, "Invalid milestone ID");
        referralMilestones[milestoneId].isActive = isActive;
    }
    
    /**
     * @dev Process referral reward for a purchase
     * @param referrer Referrer address
     * @param user User address
     * @param planId Plan ID
     * @param purchaseId Purchase ID
     * @param originalPrice Original price of the plan
     */
    function processReferralReward(
        address referrer,
        address user,
        uint256 planId,
        uint256 purchaseId,
        uint256 originalPrice
    ) 
        external 
        onlyAuthorized
    {
        address currentReferrer = referrer;
        bool isCorePlan = planId < 4; // Determine if it's a core plan based on ID
        
        uint8 level = 0;
        
        while (
            currentReferrer != address(0) && 
            level < referralConfig.levels
        ) {
            uint8 rewardPercentage = referralConfig.rewards[level];
            
            uint256 rewardAmount = (originalPrice * rewardPercentage) / 100;
            
            ReferralStats storage stats = referralStats[currentReferrer];
            
            bool isNewReferral = true;
            for (uint256 i = 0; i < stats.referredUsers.length; i++) {
                if (stats.referredUsers[i] == user) {
                    isNewReferral = false;
                    break;
                }
            }
            
            if (isNewReferral) {
                stats.referredUsers.push(user);
                stats.referralLevel[user] = level;
            }
            
            stats.planReferralCounts[planId]++;
            
            stats.totalEarned += rewardAmount;
            
            referralRewards[currentReferrer] += rewardAmount;
            
            _checkMilestones(currentReferrer, planId, isCorePlan);
            
            // Update referrer rank
            _updateReferrerRank(currentReferrer);
            
            emit ReferralPurchase(
                currentReferrer,
                user,
                planId,
                isCorePlan,
                purchaseId,
                level,
                rewardAmount
            );
            
            currentReferrer = userReferrer[currentReferrer];
            level++;
        }
    }
    
    /**
     * @dev Set referrer for a user
     * @param user User address
     * @param referrer Referrer address
     */
    function setReferrer(address user, address referrer) external {
        require(msg.sender == owner || userReferrer[user] == address(0), "Referrer already set");
        require(user != referrer, "Cannot refer yourself");
        require(referrer != address(0), "Invalid referrer address");
        
        userReferrer[user] = referrer;
    }
    
    /**
     * @dev Check if referrer has achieved any milestones
     * @param referrer Referrer address
     * @param planId Plan ID of the purchase
     * @param isCorePlan Whether the plan is a core plan
     */
    function _checkMilestones(
        address referrer,
        uint256 planId,
        bool isCorePlan
    ) 
        internal 
    {
        ReferralStats storage stats = referralStats[referrer];
        
        for (uint256 i = 1; i < nextMilestoneId; i++) {
            ReferralMilestone storage milestone = referralMilestones[i];
            
            if (!milestone.isActive) continue;
            
            bool alreadyAchieved = false;
            for (uint256 j = 0; j < stats.achievedMilestones.length; j++) {
                if (stats.achievedMilestones[j] == i) {
                    alreadyAchieved = true;
                    break;
                }
            }
            
            if (alreadyAchieved) continue;
            
            if (milestone.isPlanSpecific) {
                if (milestone.isCorePlan != isCorePlan || milestone.planId != planId) {
                    continue;
                }
                
                if (stats.planReferralCounts[planId] >= milestone.referralCount) {
                    stats.achievedMilestones.push(i);
                    emit MilestoneAchieved(referrer, i, stats.planReferralCounts[planId], milestone.rewardAmount);
                }
            } else {
                uint256 totalReferrals = stats.referredUsers.length;
                if (totalReferrals >= milestone.referralCount) {
                    stats.achievedMilestones.push(i);
                    emit MilestoneAchieved(referrer, i, totalReferrals, milestone.rewardAmount);
                }
            }
        }
    }
    
    /**
     * @dev Get referral discount percentage
     * @return Discount percentage
     */
    function getReferralDiscount(address /* referrer */) external view returns (uint8) {
        return referralConfig.discounts[0];
    }
    
    // ============= Referral Reward Functions =============
    /**
     * @dev Withdraw referral rewards
     */
    function withdrawReferralRewards() external whenNotPaused {
        uint256 amount = referralRewards[msg.sender];
        require(amount > 0, "No rewards to withdraw");
        
        referralRewards[msg.sender] = 0;
        
        referralStats[msg.sender].totalWithdrawn += amount;
        
        (bool success, ) = payable(msg.sender).call{value: amount}("");
        require(success, "Withdrawal failed");
        
        emit ReferralRewardWithdrawn(msg.sender, amount);
    }
    
    /**
     * @dev Claim milestone reward
     * @param milestoneId Milestone ID
     */
    function claimMilestoneReward(uint256 milestoneId) external whenNotPaused {
        require(milestoneId > 0 && milestoneId < nextMilestoneId, "Invalid milestone ID");
        require(!claimedMilestones[msg.sender][milestoneId], "Milestone already claimed");
        
        ReferralMilestone storage milestone = referralMilestones[milestoneId];
        require(milestone.isActive, "Milestone is not active");
        
        ReferralStats storage stats = referralStats[msg.sender];
        
        bool hasAchieved = false;
        for (uint256 i = 0; i < stats.achievedMilestones.length; i++) {
            if (stats.achievedMilestones[i] == milestoneId) {
                hasAchieved = true;
                break;
            }
        }
        
        require(hasAchieved, "Milestone not achieved");
        
        claimedMilestones[msg.sender][milestoneId] = true;
        
        uint256 rewardAmount = milestone.rewardAmount;
        
        stats.totalWithdrawn += rewardAmount;
        
        (bool success, ) = payable(msg.sender).call{value: rewardAmount}("");
        require(success, "Reward transfer failed");
        
        emit MilestoneRewardClaimed(msg.sender, milestoneId, rewardAmount);
    }
    
    // ============= Query Functions =============
    /**
     * @dev Get referral statistics for a user
     * @param user User address
     * @return totalReferrals Total number of referrals
     * @return totalEarned Total amount earned
     * @return totalWithdrawn Total amount withdrawn
     * @return referralCount Array of referral counts by plan
     */
    function getReferralStats(address user) 
        external 
        view 
        returns (
            uint256 totalReferrals,
            uint256 totalEarned,
            uint256 totalWithdrawn,
            uint256[] memory referralCount
        ) 
    {
        ReferralStats storage stats = referralStats[user];
        
        totalReferrals = stats.referredUsers.length;
        totalEarned = stats.totalEarned;
        totalWithdrawn = stats.totalWithdrawn;
        
        // Use a dynamic array since we don't know plan IDs in this contract
        uint256[] memory planIds = new uint256[](100); // Example size, adjust as needed
        uint256 count = 0;
        
        for (uint256 i = 0; i < 4; i++) {
            if (stats.planReferralCounts[i] > 0) {
                planIds[count++] = i;
            }
        }
        
        for (uint256 i = 100; i < 1000; i++) { // Assuming max custom plan ID is 999
            if (stats.planReferralCounts[i] > 0) {
                planIds[count++] = i;
            }
        }
        
        referralCount = new uint256[](count);
        for (uint256 i = 0; i < count; i++) {
            referralCount[i] = stats.planReferralCounts[planIds[i]];
        }
    }
    
    /**
     * @dev Get referral rewards balance
     * @param referrer Referrer address
     * @return Pending rewards amount
     */
    function getReferralRewards(address referrer) external view returns (uint256) {
        return referralRewards[referrer];
    }
    
    /**
     * @dev Get user's achieved milestones
     * @param user User address
     * @return achievedMilestoneIds Array of achieved milestone IDs
     * @return claimedStatus Array of whether each milestone has been claimed
     */
    function getUserMilestones(address user)
        external
        view
        returns (
            uint256[] memory achievedMilestoneIds,
            bool[] memory claimedStatus
        )
    {
        ReferralStats storage stats = referralStats[user];
        uint256[] memory milestoneIds = stats.achievedMilestones;
        
        achievedMilestoneIds = milestoneIds;
        claimedStatus = new bool[](milestoneIds.length);
        
        for (uint256 i = 0; i < milestoneIds.length; i++) {
            claimedStatus[i] = claimedMilestones[user][milestoneIds[i]];
        }
    }
    
    /**
     * @dev Update referrer rank based on total referrals and earnings
     * @param referrer Referrer address
     */
    function _updateReferrerRank(address referrer) internal {
        ReferralStats storage stats = referralStats[referrer];
        
        uint256 oldRank = stats.rank;
        uint256 newRank = 0;
        
        // Ranking based on number of referrals and earned amount
        uint256 referralCount = stats.referredUsers.length;
        
        if (referralCount >= 100) {
            newRank = 5; // Diamond
        } else if (referralCount >= 50) {
            newRank = 4; // Platinum
        } else if (referralCount >= 20) {
            newRank = 3; // Gold
        } else if (referralCount >= 10) {
            newRank = 2; // Silver
        } else if (referralCount >= 1) {
            newRank = 1; // Bronze
        }
        
        // If rank has changed, we update it
        if (newRank != oldRank) {
            stats.rank = newRank;
            emit RankUpdated(referrer, oldRank, newRank);
        }
    }
    
    /**
     * @dev Update all referrers ranks (for admin use)
     * @param referrers Array of referrer addresses
     */
    function updateReferrersRanks(address[] calldata referrers) external onlyOwner {
        for (uint256 i = 0; i < referrers.length; i++) {
            _updateReferrerRank(referrers[i]);
        }
    }
    
    /**
     * @dev Get referrer rank
     * @param referrer Referrer address
     * @return Referrer rank (0-5)
     */
    function getReferrerRank(address referrer) external view returns (uint256) {
        return referralStats[referrer].rank;
    }
    
    /**
     * @dev Receive function to accept ETH payments
     */
    receive() external payable {}
}

Tags:
addr:0x8a14c9791a15633b1dabb31c894f3d1c14fb44c8|verified:true|block:23668354|tx:0xdd8fe4fda610042d0dca226f3dfb09b97993e8c5227c93b1fa2163ef796807f2|first_check:1761567308

Submitted on: 2025-10-27 13:15:09

Comments

Log in to comment.

No comments yet.