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": [
"project/:@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.4.0/",
"project/:@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.4.0/",
"project/:@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.4.0/"
]
},
"sources": {
"npm/@openzeppelin/contracts@5.4.0/interfaces/IERC1363.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
pragma solidity >=0.6.2;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
"
},
"npm/@openzeppelin/contracts@5.4.0/interfaces/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";
"
},
"npm/@openzeppelin/contracts@5.4.0/interfaces/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
pragma solidity >=0.4.16;
import {IERC20} from "../token/ERC20/IERC20.sol";
"
},
"npm/@openzeppelin/contracts@5.4.0/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"npm/@openzeppelin/contracts@5.4.0/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}
"
},
"npm/@openzeppelin/contracts@5.4.0/utils/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
},
"project/contracts/Hackathon.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./StakeSystem.sol";
import "./VotingSystem.sol";
import "./JudgingSystem.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.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 distributedAmount; // Amount this sponsor has already distributed
bool isETHSponsor; // true if contributed ETH, false if contributed tokens
}
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;
// Token sponsor tracking
mapping(address => mapping(address => uint256)) public tokenContributions; // sponsor => token => amount
// Sponsor-specific prize pools
mapping(address => uint256) public sponsorPrizePools; // sponsor => amount available for distribution
mapping(address => address) public sponsorTokenAddresses; // sponsor => token address (for token sponsors)
mapping(address => uint256) public totalTokenContributions; // token => total amount
event ParticipantRegistered(
address indexed participant
);
event SubmissionMade(
address indexed participant,
string projectName
);
event PrizeDistributed(
address indexed winner,
uint256 amount
);
event EmergencyWithdrawal(
address indexed judge,
address indexed token,
uint256 amount
);
event HackathonEnded();
event SponsorAdded(
address indexed sponsor,
uint256 contribution
);
event SubmissionScored(
address indexed participant,
uint256 score
);
event TokenSponsorAdded(
address indexed sponsor,
address indexed token,
uint256 amount
);
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,
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
// Judge rewards are now handled via remaining ETH, not percentage
// 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,
address _pyusdToken
)
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 the PYUSD token
pyusdToken = IERC20(_pyusdToken);
_setPyusdToken(_pyusdToken);
// Set parameters
minimumSponsorContribution = _minimumSponsorContribution;
uint256 _prizeClaimCooldown = 1 days; // Default prize claim cooldown
uint256 _judgingDuration = 1 days; // Default judging duration
// Set judge reward pool with the remaining ETH sent to the contract
_setJudgeRewardPool(
msg.value
);
// Initialize the hackathon with the provided parameters
_initializeHackathon(
_hackathonId,
_startTime,
_endTime,
_prizeDistribution,
_stakeAmount,
_prizeClaimCooldown,
_judgingDuration,
_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] == false,
"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
onlySponsor
{
require(!isActive || block.timestamp > endTime, "Hackathon is still active");
require(_amount > 0, "Amount must be greater than 0");
// If organizer is distributing, use main prize pool
if (msg.sender == organizer) {
require(_amount <= prizePool, "Amount exceeds prize pool");
prizePool -= _amount;
payable(_winner).transfer(_amount);
} else {
// If sponsor is distributing, use their specific prize pool
require(_amount <= sponsorPrizePools[msg.sender], "Amount exceeds sponsor's available prize pool");
require(_amount <= (sponsors[msg.sender].contribution - sponsors[msg.sender].distributedAmount), "Amount exceeds sponsor's remaining contribution");
// Update sponsor's distributed amount
sponsors[msg.sender].distributedAmount += _amount;
sponsorPrizePools[msg.sender] -= _amount;
// Distribute based on sponsor type (ETH or token)
if (sponsors[msg.sender].isETHSponsor) {
payable(_winner).transfer(_amount);
} else {
// For token sponsors, transfer tokens
address tokenAddress = sponsorTokenAddresses[msg.sender];
IERC20(tokenAddress).transfer(_winner, _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,
distributedAmount: 0,
isETHSponsor: true
});
sponsorList.push(msg.sender);
totalSponsorContributions += msg.value;
// Set up sponsor-specific prize pool (ETH contribution)
sponsorPrizePools[msg.sender] = msg.value;
emit SponsorAdded(
msg.sender,
msg.value
);
}
/**
* @dev Allows anyone to become a sponsor by contributing tokens
* @param _tokenAddress Address of the ERC20 token to contribute
* @param _tokenAmount Amount of tokens to contribute
*/
function becomeSponsorWithToken(
address _tokenAddress,
uint256 _tokenAmount
)
external
{
require(
_tokenAmount >= minimumSponsorContribution,
"Contribution below minimum required"
);
require(
sponsors[msg.sender].isActive == false,
"Already a sponsor"
);
// Transfer tokens from sender to this contract
IERC20 token = IERC20(
_tokenAddress
);
token.transferFrom(
msg.sender,
address(this),
_tokenAmount
);
// Add sponsor to the list
sponsors[msg.sender] = Sponsor({
sponsorAddress: msg.sender,
contribution: _tokenAmount,
isActive: true,
distributedAmount: 0,
isETHSponsor: false
});
sponsorList.push(msg.sender);
totalSponsorContributions += _tokenAmount;
// Track token contributions
tokenContributions[msg.sender][_tokenAddress] = _tokenAmount;
sponsorTokenAddresses[msg.sender] = _tokenAddress;
// Set up sponsor-specific prize pool (token contribution)
sponsorPrizePools[msg.sender] = _tokenAmount;
totalTokenContributions[_tokenAddress] += _tokenAmount;
emit TokenSponsorAdded(
msg.sender,
_tokenAddress,
_tokenAmount
);
}
/**
* @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;
}
/**
* @dev Gets token contribution amount for a specific sponsor and token
* @param _sponsor Address of the sponsor
* @param _tokenAddress Address of the token
*/
function getTokenContribution(
address _sponsor,
address _tokenAddress
)
external
view
returns (uint256)
{
return tokenContributions[_sponsor][_tokenAddress];
}
/**
* @dev Gets sponsor's available prize pool for distribution
* @param _sponsor Address of the sponsor
* @return Available amount the sponsor can still distribute
*/
function getSponsorAvailablePrize(address _sponsor) external view returns (uint256) {
return sponsorPrizePools[_sponsor];
}
/**
* @dev Gets sponsor's total contribution
* @param _sponsor Address of the sponsor
* @return Total contribution amount
*/
function getSponsorTotalContribution(
address _sponsor
)
external
view
returns (uint256)
{
return sponsors[_sponsor].contribution;
}
/**
* @dev Gets sponsor's distributed amount
* @param _sponsor Address of the sponsor
* @return Amount already distributed by this sponsor
*/
function getSponsorDistributedAmount(
address _sponsor
)
external
view
returns (uint256)
{
return sponsors[_sponsor].distributedAmount;
}
/**
* @dev Gets sponsor's token address (for token sponsors)
* @param _sponsor Address of the sponsor
* @return Token address used by this sponsor
*/
function getSponsorTokenAddress(
address _sponsor
)
external
view
returns (address)
{
return sponsorTokenAddresses[
_sponsor
];
}
/**
* @dev Gets total token contributions for a specific token
* @param _tokenAddress Address of the token
*/
function getTotalTokenContributions(
address _tokenAddress
)
external
view
returns (uint256)
{
return totalTokenContributions[
_tokenAddress
];
}
// ========== 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
];
}
/**
* @notice Emergency withdrawal function for ETH - only judges can execute
* @dev Allows judges to withdraw ETH in emergency situations
* @param _amount Amount of ETH to withdraw
*/
function emergencyWithdrawETH(
uint256 _amount
)
external
onlyJudge
{
require(
_amount > 0,
"Amount must be greater than 0"
);
require(
_amount <= address(this).balance,
"Insufficient ETH balance"
);
// Transfer ETH to the judge
payable(msg.sender).transfer(_amount);
emit EmergencyWithdrawal(
msg.sender,
address(0),
_amount
);
}
/**
* @notice Emergency withdrawal function for PYUSD tokens - only judges can execute
* @dev Allows judges to withdraw PYUSD tokens in emergency situations
* @param _amount Amount of PYUSD tokens to withdraw
*/
function emergencyWithdrawPYUSD(
uint256 _amount
)
external
onlyJudge
{
require(
_amount > 0,
"Amount must be greater than 0"
);
require(
_amount <= pyusdToken.balanceOf(address(this)),
"Insufficient PYUSD balance"
);
// Transfer PYUSD tokens to the judge
pyusdToken.transfer(msg.sender, _amount);
emit EmergencyWithdrawal(
msg.sender,
address(pyusdToken),
_amount
);
}
}
"
},
"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];
}
/**
* @dev Set the judge reward pool (called during hackathon initialization)
* @param _rewardPool Amount of ETH available for judge rewards
*/
function _setJudgeRewardPool(
uint256 _rewardPool
)
internal
{
judgeRewardPool = _rewardPool;
}
}
"
},
"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;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract VotingSystem {
using SafeERC20 for IERC20;
uint256[] public prizeDistribution;
uint256 public pointsPerJudge;
// PYUSD token for prize distribution
IERC20 public pyusdToken;
/**
* @dev Set the PYUSD token address
* @param _pyusdToken Address of the PYUSD token
*/
function _setPyusdToken(
address _pyusdToken
)
internal
{
pyusdToken = IERC20(
_pyusdToken
);
}
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);
}
Submitted on: 2025-10-24 14:30:49
Comments
Log in to comment.
No comments yet.