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 {}
}
Submitted on: 2025-10-27 13:15:09
Comments
Log in to comment.
No comments yet.