Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"settings": {
"evmVersion": "cancun",
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
},
"sources": {
"project/contracts/Hackathon.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./StakeSystem.sol";
import "./VotingSystem.sol";
import "./JudgingSystem.sol";
contract Hackathon is StakeSystem, VotingSystem, JudgingSystem {
struct Submission {
address participant;
string projectName;
string projectUrl;
uint256 submissionTime;
uint256 score;
bool isEvaluated;
}
struct Sponsor {
address sponsorAddress;
uint256 contribution;
bool isActive;
}
uint256 public hackathonId;
uint256 public startTime;
uint256 public endTime;
uint256 public prizePool;
address public organizer;
bool public isActive;
address public factory;
uint256 public participantCount;
uint256 public minimumSponsorContribution;
uint256 public judgingDuration;
uint256 public votingStartTime;
uint256 public claimingStartTime;
mapping(address => Submission) public submissions;
mapping(address => bool) public isRegistered;
mapping(address => bool) public hasSubmitted;
uint256 public totalSubmissions;
mapping(address => Sponsor) public sponsors;
address[] public sponsorList;
uint256 public totalSponsorContributions;
event ParticipantRegistered(
address indexed participant
);
event SubmissionMade(
address indexed participant,
string projectName
);
event PrizeDistributed(
address indexed winner,
uint256 amount
);
event HackathonEnded();
event SponsorAdded(
address indexed sponsor,
uint256 contribution
);
event SubmissionScored(
address indexed participant,
uint256 score
);
modifier onlyOrganizer() {
require(
msg.sender == organizer,
"Only organizer can call this function"
);
_;
}
modifier hackathonActive() {
require(
isActive,
"Hackathon is not active"
);
require(
block.timestamp >= startTime,
"Hackathon has not started yet"
);
require(
block.timestamp <= endTime,
"Hackathon has ended"
);
_;
}
modifier onlyRegistered() {
require(
isRegistered[msg.sender],
"Not registered for this hackathon"
);
_;
}
modifier onlySponsor() {
require(
sponsors[msg.sender].isActive,
"Only sponsors can call this function"
);
_;
}
/**
* @dev Internal function to initialize hackathon parameters (for cloning)
* @notice This function is called by the implementation contract during initialization
*/
function _initializeHackathon(
uint256 _hackathonId,
uint256 _startTime,
uint256 _endTime,
uint256[] memory _prizeDistribution,
uint256 _stakeAmount,
uint256 _prizeClaimCooldown,
uint256 _judgingDuration,
uint256 _judgeRewardPercentage,
address[] memory _selectedJudges
)
internal
{
require(
_startTime > block.timestamp,
"Start time must be in the future"
);
require(
_endTime > _startTime,
"End time must be after start time"
);
require(
_prizeDistribution.length > 0,
"Must have at least 1 winner"
);
require(
_prizeClaimCooldown > 0,
"Prize claim cooldown must be greater than 0"
);
require(
_judgingDuration >= 2 hours && _judgingDuration <= 2 days,
"Judging duration must be between 2 hours and 2 days"
);
// Validate prize distribution
uint256 totalDistribution = 0;
for (uint256 i = 0; i < _prizeDistribution.length; i++) {
require(_prizeDistribution[i] > 0, "Each prize distribution must be greater than 0");
totalDistribution += _prizeDistribution[i];
}
// Initialize all hackathon state variables
hackathonId = _hackathonId;
startTime = _startTime;
endTime = _endTime;
isActive = true;
participantCount = 0;
totalSponsorContributions = 0;
stakeAmount = _stakeAmount;
prizeClaimCooldown = _prizeClaimCooldown;
judgingDuration = _judgingDuration;
pointsPerJudge = 100;
votingOpen = false;
totalStakes = 0;
// Calculate all phase timestamps upfront
votingStartTime = _endTime; // Voting starts when hackathon ends
votingEndTime = votingStartTime + _judgingDuration;
claimingStartTime = votingEndTime + _prizeClaimCooldown;
// Initialize inherited systems
// StakeSystem initialization
stakeAmount = _stakeAmount;
// VotingSystem initialization
prizeDistribution = _prizeDistribution;
prizeClaimCooldown = _prizeClaimCooldown;
pointsPerJudge = 100;
maxWinners = _prizeDistribution.length;
prizePool = totalDistribution;
// Initialize judging system
judgeRewardPercentage = _judgeRewardPercentage;
// Add selected judges
for (uint256 i = 0; i < _selectedJudges.length; i++) {
require(_selectedJudges[i] != address(0), "Invalid judge address");
isJudge[_selectedJudges[i]] = true;
judgeList.push(_selectedJudges[i]);
}
}
/**
* @dev Initialize the cloned hackathon with actual parameters
* @notice This function is called once after cloning to set the actual hackathon parameters
* @notice Only the factory can call this function
*/
function initialize(
address _organizer,
uint256 _hackathonId,
uint256 _startTime,
uint256 _endTime,
uint256 _minimumSponsorContribution,
uint256 _stakeAmount,
uint256[] memory _prizeDistribution,
address _factory,
address[] memory _selectedJudges
)
external
payable
{
// Only factory can call initialize
require(
msg.sender == _factory,
"Only factory can initialize"
);
// Prevent multiple initialization
require(
organizer == address(0),
"Already initialized"
);
// Set the organizer and factory
organizer = _organizer;
factory = _factory;
// Set parameters
minimumSponsorContribution = _minimumSponsorContribution;
uint256 _prizeClaimCooldown = 1 days; // Default prize claim cooldown
uint256 _judgingDuration = 1 days; // Default judging duration
uint256 _judgeRewardPercentage = 50; // Default 0.5% judge reward
// Initialize the hackathon with the provided parameters
_initializeHackathon(
_hackathonId,
_startTime,
_endTime,
_prizeDistribution,
_stakeAmount,
_prizeClaimCooldown,
_judgingDuration,
_judgeRewardPercentage,
_selectedJudges
);
}
/**
* @dev Registers a participant for this hackathon
*/
function register()
external
payable
{
require(
isActive,
"Hackathon is not active"
);
require(
block.timestamp < startTime,
"Registration closed - hackathon has started"
);
require(
isRegistered[msg.sender] == false,
"Already registered"
);
isRegistered[msg.sender] = true;
participantCount++;
// Use inherited stake system
_depositStake(
msg.sender
);
emit ParticipantRegistered(
msg.sender
);
}
/**
* @dev Submits a project for this hackathon
* @param _projectName Name of the project
* @param _projectUrl URL of the project repository or demo
*/
function submitProject(
string memory _projectName,
string memory _projectUrl
)
external
onlyDuringSubmission
onlyRegistered
{
require(
!hasSubmitted[msg.sender],
"Already submitted"
);
submissions[msg.sender] = Submission({
participant: msg.sender,
projectName: _projectName,
projectUrl: _projectUrl,
submissionTime: block.timestamp,
score: 0,
isEvaluated: false
});
hasSubmitted[msg.sender] = true;
totalSubmissions++;
// Return stake to participant
uint256 stake = participantStakes[msg.sender];
if (stake > 0) {
participantStakes[msg.sender] = 0;
totalStakes -= stake;
payable(msg.sender).transfer(stake);
emit StakeReturned(
msg.sender, stake);
}
emit SubmissionMade(
msg.sender,
_projectName
);
}
/**
* @dev Checks if an address is registered for this hackathon
* @param _participant Address to check
*/
function isParticipantRegistered(
address _participant
)
external
view
returns (bool)
{
return isRegistered[
_participant
];
}
/**
* @dev Gets submission details for a participant
* @param _participant Address of the participant
*/
function getSubmission(
address _participant
)
external
view
returns (
address participant,
string memory projectName,
string memory projectUrl,
uint256 submissionTime,
uint256 score,
bool isEvaluated
)
{
Submission storage submission = submissions[
_participant
];
return (
submission.participant,
submission.projectName,
submission.projectUrl,
submission.submissionTime,
submission.score,
submission.isEvaluated
);
}
/**
* @dev Distributes prize to winner (only organizer or sponsors)
* @param _winner Address of the winner
* @param _amount Amount to distribute
*/
function distributePrize(
address _winner,
uint256 _amount
)
external
{
require(
msg.sender == organizer || sponsors[msg.sender].isActive,
"Only organizer or sponsors can distribute prizes"
);
require(!isActive || block.timestamp > endTime, "Hackathon is still active");
require(_amount <= prizePool, "Amount exceeds prize pool");
require(_amount > 0, "Amount must be greater than 0");
prizePool -= _amount;
payable(_winner).transfer(
_amount
);
emit PrizeDistributed(
_winner,
_amount
);
}
/**
* @dev Gets hackathon details
*/
function getHackathonDetails()
external
view
returns (
uint256 _hackathonId,
uint256 _startTime,
uint256 _endTime,
uint256 _prizePool,
address _organizer,
bool _isActive,
uint256 _participantCount
)
{
return (
hackathonId,
startTime,
endTime,
prizePool,
organizer,
isActive,
participantCount
);
}
/**
* @dev Checks if hackathon is currently accepting registrations
*/
function isRegistrationOpen()
external
view
returns (bool)
{
return isActive && block.timestamp < startTime;
}
/**
* @dev Checks if hackathon is currently accepting submissions
*/
function isSubmissionOpen()
external
view
returns (bool)
{
return isActive && block.timestamp >= startTime && block.timestamp <= endTime;
}
modifier onlyDuringSubmission() {
require(isActive && block.timestamp >= startTime && block.timestamp <= endTime, "Not during submission phase");
_;
}
modifier onlyDuringVoting() {
require(block.timestamp >= votingStartTime && block.timestamp <= votingEndTime, "Not during voting phase");
_;
}
modifier onlyDuringClaiming() {
require(block.timestamp >= claimingStartTime, "Not during claiming phase");
_;
}
/**
* @dev Check if hackathon is currently active based on timestamps
*/
function _updateActiveStatus() internal {
isActive = block.timestamp >= startTime && block.timestamp <= endTime;
}
/**
* @dev Allows anyone to become a sponsor by contributing the minimum amount
*/
function becomeSponsor()
external
payable
{
require(
msg.value >= minimumSponsorContribution,
"Contribution below minimum required"
);
require(
sponsors[msg.sender].isActive == false,
"Already a sponsor"
);
sponsors[msg.sender] = Sponsor({
sponsorAddress: msg.sender,
contribution: msg.value,
isActive: true
});
sponsorList.push(msg.sender);
totalSponsorContributions += msg.value;
// Add judge rewards from sponsor contribution
uint256 judgeReward = msg.value * getJudgeRewardPercentage() / 10000;
judgeRewardPool += judgeReward;
emit SponsorAdded(
msg.sender,
msg.value
);
}
/**
* @dev Adds a judge to the hackathon (only organizer can call)
* @param _judge Address of the judge
*/
function addJudge(
address _judge
)
public
override
onlyOrganizer
{
require(isActive, "Cannot add judges to inactive hackathon");
require(block.timestamp < endTime, "Cannot add judges after hackathon ends");
// Call inherited function directly
JudgingSystem.addJudge(_judge);
}
/**
* @dev Allows a judge to score a submission
* @param _participant Address of the participant
* @param _score Score to assign (0-100)
*/
function scoreSubmission(
address _participant,
uint256 _score
)
external
{
require(
isJudgeOrDelegate(msg.sender),
"Only judges can score"
);
require(
hasSubmitted[_participant],
"No submission found"
);
require(_score <= 100, "Score must be between 0 and 100");
require(!submissions[_participant].isEvaluated, "Submission already evaluated");
submissions[_participant].score = _score;
submissions[_participant].isEvaluated = true;
emit SubmissionScored(
_participant,
_score
);
}
/**
* @dev Gets all sponsors
*/
function getSponsors()
external
view
returns (address[] memory)
{
return sponsorList;
}
/**
* @dev Gets sponsor contribution amount
* @param _sponsor Address of the sponsor
*/
function getSponsorContribution(
address _sponsor
)
external
view
returns (uint256)
{
return sponsors[_sponsor].contribution;
}
/**
* @dev Claim judge reward (hackathon-specific)
*/
function claimJudgeReward()
external
{
require(isJudgeOrDelegate(msg.sender), "Only judges or their delegates can claim rewards");
address actualJudge = isJudge[msg.sender] ? msg.sender : delegateToJudge[msg.sender];
require(!hasReceivedJudgeReward[actualJudge], "Already claimed judge reward");
require(judgeList.length > 0, "No judges to distribute rewards to");
uint256 rewardPerJudge = judgeRewardPool / judgeList.length;
require(rewardPerJudge > 0, "Insufficient reward per judge");
hasReceivedJudgeReward[actualJudge] = true;
payable(msg.sender).transfer(rewardPerJudge);
emit JudgeRewardDistributed(actualJudge, rewardPerJudge);
}
/**
* @dev Gets total prize pool including sponsor contributions
*/
function getTotalPrizePool()
external
view
returns (uint256)
{
return prizePool;
}
/**
* @dev Gets minimum sponsor contribution required
*/
function getMinimumSponsorContribution()
external
view
returns (uint256)
{
return minimumSponsorContribution;
}
// ========== Voting System Functions ==========
/**
* @dev Allows a judge to vote on submissions by allocating points
* @param _participant Address of the participant to vote for
* @param _points Points to allocate (0-100)
*/
function voteForSubmission(
address _participant,
uint256 _points
)
external
onlyDuringVoting
{
_updateActiveStatus();
require(
isJudgeOrDelegate(msg.sender),
"Only judges can vote"
);
require(
hasSubmitted[_participant],
"Participant has not submitted"
);
require(
_points <= pointsPerJudge,
"Cannot allocate more points than allowed"
);
require(
hasVoted[msg.sender] == false,
"Judge has already voted"
);
// Check if judge has already allocated points to this participant
uint256 currentPoints = judgeVotes[msg.sender][_participant];
require(
currentPoints == 0,
"Judge has already voted for this participant"
);
judgeVotes[msg.sender][_participant] = _points;
totalPoints[_participant] += _points;
emit JudgeVoted(
msg.sender,
_participant,
_points
);
}
/**
* @dev Allows winners to claim their prize after cooldown period
*/
function claimPrize()
external
onlyDuringClaiming
{
require(
hasSubmitted[msg.sender],
"Must have submitted a project"
);
_claimPrize(
msg.sender,
prizePool
);
}
/**
* @notice Get prize amount for a participant
* @dev Uses VotingSystem's internal function to calculate prize amount
* @param _participant Address of the participant
* @return Prize amount for the participant
*/
function getPrizeAmount(
address _participant
)
external
view
returns (uint256)
{
return _getPrizeAmount(
_participant
);
}
/**
* @notice Check if a participant is a winner
* @dev Public function to check if a participant is a winner
* @param _participant Address of the participant
* @return True if the participant is a winner, false otherwise
*/
function isWinner(
address _participant
)
external
view
returns (bool)
{
return _isWinner(
_participant
);
}
/**
* @notice Check if a participant has claimed their prize
* @dev Public function to check if a participant has claimed their prize
* @param _participant Address of the participant
* @return True if prize has been claimed, false otherwise
*/
function getHasClaimedPrize(
address _participant
)
external
view
returns (bool)
{
return hasClaimedPrize[
_participant
];
}
}
"
},
"project/contracts/JudgingSystem.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* @title JudgingSystem
* @dev Handles all judge-related functionality for hackathons
* @notice This contract manages judges, their delegation, scoring, and rewards
*/
contract JudgingSystem {
// Judge management
mapping(address => bool) public isJudge;
address[] public judgeList;
// Judge delegation
mapping(address => address) public judgeDelegates;
mapping(address => address) public delegateToJudge;
// Judge rewards
uint256 public judgeRewardPercentage;
uint256 public judgeRewardPool;
mapping(address => bool) public hasReceivedJudgeReward;
// Events
event JudgeAdded(address indexed judge);
event JudgeRemoved(address indexed judge);
event JudgeDelegated(address indexed judge, address indexed delegate);
event JudgeRewardDistributed(address indexed judge, uint256 amount);
modifier onlyJudge() {
require(
isJudge[msg.sender],
"Only judges can call this function"
);
_;
}
/**
* @dev Add a judge to the system
* @param _judge Address of the judge to add
*/
function addJudge(address _judge) public virtual {
require(_judge != address(0), "Invalid judge address");
require(!isJudge[_judge], "Judge already added");
isJudge[_judge] = true;
judgeList.push(_judge);
emit JudgeAdded(_judge);
}
/**
* @dev Remove a judge from the system
* @param _judge Address of the judge to remove
*/
function removeJudge(address _judge) public {
require(isJudge[_judge], "Old judge not found");
isJudge[_judge] = false;
// Remove from array
for (uint256 i = 0; i < judgeList.length; i++) {
if (judgeList[i] == _judge) {
judgeList[i] = judgeList[judgeList.length - 1];
judgeList.pop();
break;
}
}
emit JudgeRemoved(_judge);
}
/**
* @dev Delegate judge responsibilities to another address
* @param _delegate Address to delegate to
*/
function delegateToAgent(address _delegate) external {
require(isJudge[msg.sender], "Only judges can delegate");
require(_delegate != address(0), "Invalid delegate address");
require(_delegate != msg.sender, "Cannot delegate to yourself");
require(judgeDelegates[msg.sender] == address(0), "Already has a delegate");
judgeDelegates[msg.sender] = _delegate;
delegateToJudge[_delegate] = msg.sender;
emit JudgeDelegated(msg.sender, _delegate);
}
/**
* @dev Revoke delegation
*/
function revokeDelegation() external {
require(isJudge[msg.sender], "Only judges can revoke delegation");
require(judgeDelegates[msg.sender] != address(0), "No delegation to revoke");
address delegate = judgeDelegates[msg.sender];
judgeDelegates[msg.sender] = address(0);
delegateToJudge[delegate] = address(0);
emit JudgeDelegated(msg.sender, address(0));
}
/**
* @dev Add funds to judge reward pool
*/
function addJudgeRewards() public payable {
require(msg.value > 0, "Must send ETH");
judgeRewardPool += msg.value;
}
/**
* @dev Distribute judge rewards
*/
function distributeJudgeRewards() external {
require(judgeRewardPool > 0, "No judge rewards available");
require(judgeList.length > 0, "No judges to reward");
uint256 rewardPerJudge = judgeRewardPool / judgeList.length;
require(rewardPerJudge > 0, "Insufficient reward per judge");
for (uint256 i = 0; i < judgeList.length; i++) {
address judge = judgeList[i];
if (!hasReceivedJudgeReward[judge]) {
hasReceivedJudgeReward[judge] = true;
payable(judge).transfer(rewardPerJudge);
emit JudgeRewardDistributed(judge, rewardPerJudge);
}
}
judgeRewardPool = 0;
}
/**
* @dev Check if an address is a judge or delegate
* @param _address Address to check
* @return True if the address is a judge or delegate
*/
function isJudgeOrDelegate(address _address) public view returns (bool) {
return isJudge[_address] || delegateToJudge[_address] != address(0);
}
/**
* @dev Get all judges
* @return Array of judge addresses
*/
function getJudges() external view returns (address[] memory) {
return judgeList;
}
/**
* @dev Get judge reward pool amount
* @return Amount in the reward pool
*/
function getJudgeRewardPool() external view returns (uint256) {
return judgeRewardPool;
}
/**
* @dev Get reward per judge
* @return Amount each judge would receive
*/
function getRewardPerJudge() external view returns (uint256) {
if (judgeList.length == 0) return 0;
return judgeRewardPool / judgeList.length;
}
/**
* @dev Get judge reward percentage
* @return Percentage of prize pool for judges
*/
function getJudgeRewardPercentage() public view returns (uint256) {
return judgeRewardPercentage;
}
/**
* @dev Get delegate for a judge
* @param _judge Address of the judge
* @return Address of the delegate
*/
function getJudgeDelegate(address _judge) external view returns (address) {
return judgeDelegates[_judge];
}
/**
* @dev Get judge for a delegate
* @param _delegate Address of the delegate
* @return Address of the judge
*/
function getDelegateJudge(address _delegate) external view returns (address) {
return delegateToJudge[_delegate];
}
}
"
},
"project/contracts/StakeSystem.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* @title StakeSystem
* @dev Handles stake management for hackathon participants
* @notice This contract manages the stake system where participants must deposit funds
*/
contract StakeSystem {
uint256 public stakeAmount;
uint256 public totalStakes;
mapping(address => uint256) public participantStakes;
event StakeDeposited(
address indexed participant,
uint256 amount
);
event StakeReturned(
address indexed participant,
uint256 amount
);
/**
* @notice Deposit stake for a participant
* @dev Internal function to handle stake deposits and emit events
* @param _participant Address of the participant depositing the stake
*/
function _depositStake(
address _participant
)
internal
{
require(
msg.value == stakeAmount,
"Must deposit exact stake amount"
);
participantStakes[_participant] = msg.value;
totalStakes += msg.value;
emit StakeDeposited(
_participant,
msg.value
);
}
/**
* @notice Return stake to a participant
* @dev Internal function to return stake to participant and emit events
* @param _participant Address of the participant to return stake to
*/
function _returnStake(
address _participant
)
internal
{
uint256 stake = participantStakes[
_participant
];
if (stake > 0) {
participantStakes[_participant] = 0;
totalStakes -= stake;
payable(_participant).transfer(
stake
);
emit StakeReturned(
_participant,
stake
);
}
}
/**
* @notice Get the stake amount for a specific participant
* @dev Returns the stake amount deposited by a participant
* @param _participant Address of the participant to check
* @return Stake amount deposited by the participant
*/
function getParticipantStake(
address _participant
)
external
view
returns (uint256)
{
return participantStakes[
_participant
];
}
/**
* @notice Get the total stakes collected from all participants
* @dev Returns the sum of all stakes deposited by participants
* @return Total amount of stakes collected
*/
function getTotalStakes()
external
view
returns (uint256)
{
return totalStakes;
}
/**
* @notice Get the required stake amount for hackathon participation
* @dev Returns the amount that participants must deposit when joining
* @return Required stake amount for participation
*/
function getStakeAmount()
external
view
returns (uint256)
{
return stakeAmount;
}
}
"
},
"project/contracts/VotingSystem.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract VotingSystem {
uint256[] public prizeDistribution;
uint256 public pointsPerJudge;
mapping(address => bool) public hasVoted;
mapping(address => uint256) public totalPoints;
mapping(address => mapping(address => uint256)) public judgeVotes;
uint256 public votingDeadline;
bool public votingOpen;
uint256 public votingEndTime;
uint256 public prizeClaimCooldown;
mapping(address => bool) public hasClaimedPrize;
// Dynamic winner tracking - updated on each vote
mapping(address => uint256) public winnerPosition; // 0 = not a winner, 1+ = position (1-indexed)
address[] public winners; // Array of winners in order (1st, 2nd, 3rd, etc.)
uint256 public maxWinners; // Maximum number of winners (from prize distribution length)
event JudgeVoted(
address indexed judge,
address indexed participant,
uint256 points
);
event VotingOpened(
uint256 deadline
);
event PrizeClaimed(
address indexed winner,
uint256 amount
);
event WinnerAdded(
address indexed participant,
uint256 position
);
event WinnerRemoved(
address indexed participant
);
/**
* @notice Vote for a participant's submission
* @dev Internal function for judges to allocate points to participants and update winner list
* @param _participant Address of the participant to vote for
* @param _points Number of points to allocate (max 100 per judge)
*/
function _voteForSubmission(
address _participant,
uint256 _points
)
internal
{
require(
votingOpen,
"Voting is not open"
);
require(
block.timestamp <= votingDeadline,
"Voting deadline has passed"
);
require(
_points <= pointsPerJudge,
"Cannot allocate more than 100 points"
);
require(
hasVoted[msg.sender] == false,
"Judge has already voted"
);
// Store old points for comparison
uint256 oldPoints = totalPoints[_participant];
// Add points to participant's total
totalPoints[_participant] += _points;
judgeVotes[msg.sender][_participant] = _points;
hasVoted[msg.sender] = true;
// Update winner list based on new points
_updateWinnerList(
_participant,
oldPoints,
totalPoints[_participant]
);
emit JudgeVoted(
msg.sender,
_participant,
_points
);
}
/**
* @notice Update winner list dynamically based on participant's new points
* @dev Maintains a sorted list of top performers without sorting all participants
* @param _participant Address of the participant whose points changed
* @param _oldPoints Previous total points
* @param _newPoints New total points
*/
function _updateWinnerList(
address _participant,
uint256 _oldPoints,
uint256 _newPoints
)
internal
{
uint256 currentPosition = winnerPosition[
_participant
];
// If participant was already a winner
if (currentPosition > 0) {
// Check if they should be moved up or down in rankings
_adjustWinnerPosition(
_participant,
_oldPoints,
_newPoints
);
return;
}
if (_newPoints > 0 && (winners.length < maxWinners || _newPoints > totalPoints[_getLowestWinner()])) {
_addNewWinner(
_participant
);
}
}
/**
* @notice Adjust position of existing winner based on new points
* @dev Moves winner up or down in the rankings as needed
*/
function _adjustWinnerPosition(
address _participant,
uint256 _oldPoints,
uint256 _newPoints
)
internal
{
uint256 currentPosition = winnerPosition[
_participant
];
// If points increased, try to move up
if (_newPoints > _oldPoints) {
_moveWinnerUp(
_participant,
currentPosition
);
return;
}
if (_newPoints < _oldPoints) {
_moveWinnerDown(
_participant,
currentPosition
);
}
}
/**
* @notice Move winner up in rankings
*/
function _moveWinnerUp(
address _participant,
uint256 currentPosition
)
internal
{
uint256 newPosition = currentPosition;
// Find the correct position by comparing with winners above
for (uint256 i = currentPosition - 1; i > 0; i--) {
if (totalPoints[_participant] > totalPoints[winners[i - 1]]) {
newPosition = i;
} else {
break;
}
}
if (newPosition != currentPosition) {
_swapWinners(
_participant,
currentPosition,
newPosition
);
}
}
/**
* @notice Move winner down in rankings or remove if no longer in top
*/
function _moveWinnerDown(address _participant, uint256 currentPosition) internal {
uint256 newPosition = currentPosition;
// Find the correct position by comparing with winners below
for (uint256 i = currentPosition; i < winners.length; i++) {
if (totalPoints[_participant] < totalPoints[winners[i]]) {
newPosition = i + 1;
} else {
break;
}
}
// If moved beyond max winners, remove from winners
if (newPosition > maxWinners) {
_removeWinner(_participant, currentPosition);
} else if (newPosition != currentPosition) {
_swapWinners(_participant, currentPosition, newPosition);
}
}
/**
* @notice Add new winner to the list
*/
function _addNewWinner(address _participant) internal {
if (winners.length < maxWinners) {
// Add to end if we have space
winners.push(_participant);
winnerPosition[_participant] = winners.length;
_moveWinnerUp(_participant, winners.length);
} else {
// Replace the lowest winner
address lowestWinner = _getLowestWinner();
uint256 lowestPosition = winnerPosition[lowestWinner];
_removeWinner(lowestWinner, lowestPosition);
_addNewWinner(_participant);
}
}
/**
* @notice Remove winner from the list
*/
function _removeWinner(address _participant, uint256 position) internal {
// Move last winner to this position
if (winners.length > 1) {
address lastWinner = winners[winners.length - 1];
winners[position - 1] = lastWinner;
winnerPosition[lastWinner] = position;
}
// Remove from array
winners.pop();
winnerPosition[_participant] = 0;
emit WinnerRemoved(
_participant
);
}
/**
* @notice Swap two winners in the list
*/
function _swapWinners(address _participant, uint256 fromPosition, uint256 toPosition) internal {
address otherParticipant = winners[toPosition - 1];
// Swap in array
winners[fromPosition - 1] = otherParticipant;
winners[toPosition - 1] = _participant;
// Update positions
winnerPosition[_participant] = toPosition;
winnerPosition[otherParticipant] = fromPosition;
emit WinnerAdded(_participant, toPosition);
}
/**
* @notice Get the winner with the lowest points
*/
function _getLowestWinner()
internal
view
returns (address)
{
require(
winners.length > 0,
"No winners"
);
return winners[
winners.length - 1
];
}
/**
* @notice Check if voting has ended (either manually closed or deadline passed)
* @dev Automatically determines if voting should be considered ended
* @return True if voting has ended, false otherwise
*/
function _isVotingEnded()
internal
view
returns (bool)
{
// If manually closed, voting has ended
if (!votingOpen) {
return true;
}
// If deadline has passed, voting has ended
if (block.timestamp > votingDeadline) {
return true;
}
return false;
}
/**
* @notice Get the actual voting end time
* @dev Returns the time when voting actually ended (manual close or deadline)
* @return Timestamp when voting ended
*/
function _getVotingEndTime() internal view returns (uint256) {
// If manually closed, return the recorded end time
if (!votingOpen && votingEndTime > 0) {
return votingEndTime;
}
// If deadline passed but not manually closed, return deadline
if (block.timestamp > votingDeadline) {
return votingDeadline;
}
// Voting is still active
return 0;
}
/**
* @notice Check if a participant is a winner
* @dev Uses pre-determined winners for O(1) lookup
* @param _participant Address of the participant to check
* @return True if the participant is a winner, false otherwise
*/
function _isWinner(
address _participant
)
internal
view
returns (bool)
{
return winnerPosition[_participant] > 0;
}
/**
* @notice Get prize amount for a participant based on their ranking
* @dev Determines the participant's position and returns the exact prize amount from the distribution array
* @param _participant Address of the participant
* @return Prize amount for the participant
*/
function _getPrizeAmount(
address _participant
)
internal
view
returns (uint256)
{
uint256 position = winnerPosition[
_participant
];
if (position == 0) {
return 0; // Not a winner
}
// Convert to 0-indexed for array access
uint256 arrayIndex = position - 1;
// Check if position is within prize distribution range
if (arrayIndex >= prizeDistribution.length) {
return 0;
}
// Return exact prize amount from the distribution array
return prizeDistribution[
arrayIndex
];
}
/**
* @notice Claim prize for a winner
* @dev Internal function to handle prize claiming with cooldown and winner validation
* @param _participant Address of the participant claiming the prize
*/
function _claimPrize(
address _participant,
uint256 /* _totalPrizePool */
)
internal
{
require(!hasClaimedPrize[_participant], "Prize already claimed");
require(_isWinner(_participant), "Not a winner");
uint256 prizeAmount = _getPrizeAmount(_participant);
hasClaimedPrize[_participant] = true;
payable(_participant).transfer(prizeAmount);
emit PrizeClaimed(_participant, prizeAmount);
}
// Getter functions
/**
* @notice Get the maximum number of winners based on prize distribution
* @return Maximum number of winners that can be selected
*/
function getMaxWinners()
external
view
returns (uint256)
{
return maxWinners;
}
/**
* @notice Get the prize distribution array
* @return Array defining how the prize pool is distributed among winners
*/
function getPrizeDistribution()
external
view
returns (uint256[] memory)
{
return prizeDistribution;
}
/**
* @notice Get the points each judge can distribute
* @return Maximum points each judge can allocate
*/
function getPointsPerJudge()
external
view
returns (uint256)
{
return pointsPerJudge;
}
/**
* @notice Get total points received by a participant
* @param _participant Address of the participant
* @return Total points received by the participant
*/
function getTotalPoints(
address _participant
)
external
view
returns (uint256)
{
return totalPoints[
_participant
];
}
/**
* @notice Get all winners in order
* @return Array of winner addresses in ranking order
*/
function getWinners()
external
view
returns (address[] memory)
{
return winners;
}
/**
* @notice Get winner position for a participant
* @param _participant Address of the participant
* @return Position (1-indexed, 0 if not a winner)
*/
function getWinnerPosition(
address _participant
)
external
view
returns (uint256)
{
return winnerPosition[
_participant
];
}
/**
* @notice Get the voting deadline timestamp
* @return Timestamp when voting period ends
*/
function getVotingDeadline()
external
view
returns (uint256)
{
return votingDeadline;
}
/**
* @notice Get the voting end time timestamp
* @return Timestamp when voting actually ended (manual close or deadline)
*/
function getVotingEndTime()
external
view
returns (uint256)
{
return _getVotingEndTime();
}
/**
* @notice Check if voting is currently open
* @return True if voting is open and deadline not passed, false otherwise
*/
function isVotingOpen()
external
view
returns (bool)
{
return votingOpen && block.timestamp <= votingDeadline;
}
/**
* @notice Get the prize claim cooldown period
* @return Cooldown period in seconds
*/
function getPrizeClaimCooldown()
external
view
returns (uint256)
{
return prizeClaimCooldown;
}
/**
* @notice Check if a participant has claimed their prize
* @param _participant Address of the participant
* @return True if prize has been claimed, false otherwise
*/
function hasParticipantClaimedPrize(
address _participant
)
external
view
returns (bool)
{
return hasClaimedPrize[_participant];
}
}
"
}
}
}}
Submitted on: 2025-10-23 19:10:26
Comments
Log in to comment.
No comments yet.