Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
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/"
]
},
"sources": {
"npm/@openzeppelin/contracts@5.4.0/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"npm/@openzeppelin/contracts@5.4.0/proxy/Clones.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (proxy/Clones.sol)
pragma solidity ^0.8.20;
import {Create2} from "../utils/Create2.sol";
import {Errors} from "../utils/Errors.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*/
library Clones {
error CloneArgumentsTooLong();
/**
* @dev Deploys and returns the address of a clone that mimics the behavior of `implementation`.
*
* This function uses the create opcode, which should never revert.
*
* WARNING: This function does not check if `implementation` has code. A clone that points to an address
* without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
* have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
*/
function clone(address implementation) internal returns (address instance) {
return clone(implementation, 0);
}
/**
* @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
* to the new contract.
*
* WARNING: This function does not check if `implementation` has code. A clone that points to an address
* without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
* have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function clone(address implementation, uint256 value) internal returns (address instance) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
assembly ("memory-safe") {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(value, 0x09, 0x37)
}
if (instance == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Deploys and returns the address of a clone that mimics the behavior of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple times will revert, since
* the clones cannot be deployed twice at the same address.
*
* WARNING: This function does not check if `implementation` has code. A clone that points to an address
* without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
* have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
*/
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
return cloneDeterministic(implementation, salt, 0);
}
/**
* @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
* a `value` parameter to send native currency to the new contract.
*
* WARNING: This function does not check if `implementation` has code. A clone that points to an address
* without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
* have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function cloneDeterministic(
address implementation,
bytes32 salt,
uint256 value
) internal returns (address instance) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
assembly ("memory-safe") {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(value, 0x09, 0x37, salt)
}
if (instance == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := and(keccak256(add(ptr, 0x43), 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
/**
* @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
* immutable arguments. These are provided through `args` and cannot be changed after deployment. To
* access the arguments within the implementation, use {fetchCloneArgs}.
*
* This function uses the create opcode, which should never revert.
*
* WARNING: This function does not check if `implementation` has code. A clone that points to an address
* without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
* have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
*/
function cloneWithImmutableArgs(address implementation, bytes memory args) internal returns (address instance) {
return cloneWithImmutableArgs(implementation, args, 0);
}
/**
* @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value`
* parameter to send native currency to the new contract.
*
* WARNING: This function does not check if `implementation` has code. A clone that points to an address
* without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
* have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function cloneWithImmutableArgs(
address implementation,
bytes memory args,
uint256 value
) internal returns (address instance) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
assembly ("memory-safe") {
instance := create(value, add(bytecode, 0x20), mload(bytecode))
}
if (instance == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
* immutable arguments. These are provided through `args` and cannot be changed after deployment. To
* access the arguments within the implementation, use {fetchCloneArgs}.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same
* `implementation`, `args` and `salt` multiple times will revert, since the clones cannot be deployed twice
* at the same address.
*
* WARNING: This function does not check if `implementation` has code. A clone that points to an address
* without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
* have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
*/
function cloneDeterministicWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
return cloneDeterministicWithImmutableArgs(implementation, args, salt, 0);
}
/**
* @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs],
* but with a `value` parameter to send native currency to the new contract.
*
* WARNING: This function does not check if `implementation` has code. A clone that points to an address
* without code cannot be initialized. Initialization calls may appear to be successful when, in reality, they
* have no effect and leave the clone uninitialized, allowing a third party to initialize it later.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function cloneDeterministicWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt,
uint256 value
) internal returns (address instance) {
bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
return Create2.deploy(value, salt, bytecode);
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
*/
function predictDeterministicAddressWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
return Create2.computeAddress(salt, keccak256(bytecode), deployer);
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
*/
function predictDeterministicAddressWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddressWithImmutableArgs(implementation, args, salt, address(this));
}
/**
* @dev Get the immutable args attached to a clone.
*
* - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this
* function will return an empty array.
* - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or
* `cloneDeterministicWithImmutableArgs`, this function will return the args array used at
* creation.
* - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This
* function should only be used to check addresses that are known to be clones.
*/
function fetchCloneArgs(address instance) internal view returns (bytes memory) {
bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short
assembly ("memory-safe") {
extcodecopy(instance, add(result, 32), 45, mload(result))
}
return result;
}
/**
* @dev Helper that prepares the initcode of the proxy with immutable args.
*
* An assembly variant of this function requires copying the `args` array, which can be efficiently done using
* `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using
* abi.encodePacked is more expensive but also more portable and easier to review.
*
* NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes.
* With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes.
*/
function _cloneCodeWithImmutableArgs(
address implementation,
bytes memory args
) private pure returns (bytes memory) {
if (args.length > 24531) revert CloneArgumentsTooLong();
return
abi.encodePacked(
hex"61",
uint16(args.length + 45),
hex"3d81600a3d39f3363d3d373d3d3d363d73",
implementation,
hex"5af43d82803e903d91602b57fd5bf3",
args
);
}
}
"
},
"npm/@openzeppelin/contracts@5.4.0/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
"
},
"npm/@openzeppelin/contracts@5.4.0/utils/Create2.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/
library Create2 {
/**
* @dev There's no code to deploy.
*/
error Create2EmptyBytecode();
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
if (bytecode.length == 0) {
revert Create2EmptyBytecode();
}
assembly ("memory-safe") {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
// if no address was created, and returndata is not empty, bubble revert
if and(iszero(addr), not(iszero(returndatasize()))) {
let p := mload(0x40)
returndatacopy(p, 0, returndatasize())
revert(p, returndatasize())
}
}
if (addr == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
assembly ("memory-safe") {
let ptr := mload(0x40) // Get free memory pointer
// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |
// |-------------------|---------------------------------------------------------------------------|
// | bytecodeHash | CCCCCCCCCCCCC...CC |
// | salt | BBBBBBBBBBBBB...BB |
// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |
// | 0xFF | FF |
// |-------------------|---------------------------------------------------------------------------|
// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
// | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
mstore(add(ptr, 0x40), bytecodeHash)
mstore(add(ptr, 0x20), salt)
mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
mstore8(start, 0xff)
addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff)
}
}
}
"
},
"npm/@openzeppelin/contracts@5.4.0/utils/Errors.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}
"
},
"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/HackathonFactory.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./Hackathon.sol";
import "./JudgeCouncil.sol";
import "./VotingTypes.sol";
import "./OpenVoting.sol";
import "./RevealCommitVoting.sol";
import "./ZKVotingSystem.sol";
import "./QuadraticVoting.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
/**
* @title HackathonFactory
* @dev Factory contract for creating new hackathon instances using cloning
* @notice This contract serves as a factory for deploying individual hackathon contracts using the clone pattern for gas efficiency.
* @notice This contract also manages global judge governance.
*/
contract HackathonFactory is JudgeCouncil {
mapping(address => uint256) public organizerHackathonCount;
mapping(address => mapping(uint256 => address)) public organizerHackathons;
uint256 public totalHackathons;
uint256 public constant MAX_PRIZE_CLAIM_COOLDOWN = 7 days;
// Implementation contract addresses
address public immutable implementation;
// Voting system implementation addresses
address public immutable openVotingImplementation;
address public immutable RevealCommitVotingImplementation;
address public immutable zkVotingImplementation;
address public immutable qvWrapperImplementation;
event HackathonCreated(
address indexed hackathonAddress,
uint256 hackathonId,
address indexed organizer,
uint256 prizePool,
VotingSystemType votingSystem,
bool useQuadraticVoting
);
event VotingSystemDeployed(
address indexed votingContract,
VotingSystemType systemType,
bool useQuadraticVoting
);
/**
* @dev Constructor that sets the implementation contract address and initializes judge governance
* @param _implementation Address of the HackathonImplementation contract
*/
constructor(
address _implementation
)
JudgeCouncil(address(this))
{
require(
_implementation != address(0),
"Invalid implementation address"
);
implementation = _implementation;
// Deploy voting system implementations (initial)
openVotingImplementation = address(
new OpenVoting()
);
RevealCommitVotingImplementation = address(
new RevealCommitVoting()
);
zkVotingImplementation = address(
new ZKVotingSystem()
);
qvWrapperImplementation = address(
new QVWrapper()
);
_addFirstGlobalJudge(
msg.sender
);
}
/**
* @notice Creates a new hackathon contract
* @dev Deploys a new Hackathon contract with the specified parameters and tracks it
* @param _hackathonId Unique identifier for the hackathon
* @param _startTime Start time in Unix timestamp
* @param _endTime End time in Unix timestamp
* @param _minimumSponsorContribution Minimum contribution required to become a sponsor
* @param _stakeAmount Amount participants must stake when joining
* @param _prizeDistribution Array defining how the prize pool is distributed among winners
* @param _selectedJudges Array of judge addresses to assign to this hackathon
* @param _votingConfig Voting system configuration
* @return hackathonAddress Address of the newly created hackathon
*/
function createHackathon(
uint256 _hackathonId,
uint256 _startTime,
uint256 _endTime,
uint256 _minimumSponsorContribution,
uint256 _stakeAmount,
uint256[] memory _prizeDistribution,
address[] memory _selectedJudges,
VotingConfig memory _votingConfig
)
external
payable
returns (address hackathonAddress)
{
// Basic validation
require(_startTime > block.timestamp, "Start time must be in the future");
require(_endTime > _startTime, "End time must be after start time");
require(
msg.value > 0,
"Prize pool must be greater than 0"
);
// Validate that all selected judges are in the global registry
for (uint256 i = 0; i < _selectedJudges.length; i++) {
require(
isJudgeOrDelegate(_selectedJudges[i]),
"Selected judge is not in global registry"
);
}
// Deploy voting system
_deployVotingSystem(_votingConfig, _selectedJudges);
// Clone the implementation contract
hackathonAddress = Clones.clone(implementation);
// Initialize the cloned contract
Hackathon(hackathonAddress).initialize{
value: msg.value
}(
msg.sender, // organizer
_hackathonId,
_startTime,
_endTime,
_minimumSponsorContribution,
_stakeAmount,
_prizeDistribution,
address(this), // factory
_selectedJudges
);
// Store the hackathon address
uint256 organizerIndex = organizerHackathonCount[msg.sender];
totalHackathons++;
organizerHackathonCount[msg.sender]++;
organizerHackathons[msg.sender][organizerIndex] = hackathonAddress;
emit HackathonCreated(
hackathonAddress,
_hackathonId,
msg.sender,
msg.value,
_votingConfig.systemType,
_votingConfig.useQuadraticVoting
);
return hackathonAddress;
}
/**
* @dev Deploy voting system based on configuration using clone pattern
* @param _votingConfig Voting system configuration
* @param _judges Array of judge addresses
* @return votingContract Address of the deployed voting contract
*/
function _deployVotingSystem(
VotingConfig memory _votingConfig,
address[] memory _judges
) internal returns (address votingContract) {
// Clone base voting system
if (_votingConfig.systemType == VotingSystemType.OPEN) {
votingContract = Clones.clone(openVotingImplementation);
} else if (_votingConfig.systemType == VotingSystemType.COMMIT_REVEAL) {
votingContract = Clones.clone(RevealCommitVotingImplementation);
} else if (_votingConfig.systemType == VotingSystemType.ZK_SNARK) {
votingContract = Clones.clone(zkVotingImplementation);
} else if (_votingConfig.systemType == VotingSystemType.QUADRATIC) {
// Quadratic voting uses the same OpenVoting but with quadratic validation
votingContract = Clones.clone(openVotingImplementation);
} else {
revert("Unsupported voting system type");
}
// Initialize the voting system
IVotingSystem(votingContract).initialize(
_votingConfig.votingPowerPerJudge,
_votingConfig.maxWinners,
_judges
);
// Wrap with quadratic voting if enabled
if (_votingConfig.useQuadraticVoting) {
// Clone QVWrapper for gas efficiency
address qvWrapper = Clones.clone(qvWrapperImplementation);
// Initialize the QVWrapper
QVWrapper(qvWrapper).initialize(votingContract, _votingConfig.votingPowerPerJudge);
votingContract = qvWrapper;
}
emit VotingSystemDeployed(
votingContract,
_votingConfig.systemType,
_votingConfig.useQuadraticVoting
);
return votingContract;
}
/**
* @dev Gets the total number of hackathons created
*/
function getHackathonCount()
external
view
returns (uint256)
{
return totalHackathons;
}
/**
* @dev Gets the number of hackathons created by a specific organizer
* @param _organizer Address of the organizer
*/
function getOrganizerHackathonCount(
address _organizer
)
external
view
returns (uint256)
{
return organizerHackathonCount[
_organizer
];
}
/**
* @dev Gets a specific hackathon created by an organizer
* @param _organizer Address of the organizer
* @param _index Index of the hackathon
*/
function getOrganizerHackathon(
address _organizer,
uint256 _index
)
external
view
returns (address)
{
require(
_index < organizerHackathonCount[_organizer],
"Index out of bounds"
);
return organizerHackathons[_organizer][_index];
}
}
"
},
"project/contracts/IVotingSystem.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* @title IVotingSystem
* @dev Interface for all voting systems in DeHack platform
* @notice Supports batch voting for multiple participants in one transaction
*/
interface IVotingSystem {
// ============ EVENTS ============
event VotesSubmitted(
address indexed judge,
address[] participants,
uint256[] points
);
event WinnerUpdated(
address indexed participant,
uint256 oldPosition,
uint256 newPosition
);
// ============ CORE FUNCTIONS ============
/**
* @dev Submit votes for multiple participants in one transaction
* @param _participants Array of participant addresses
* @param _points Array of points allocated to each participant
*/
function submitVotes(
address[] calldata _participants,
uint256[] calldata _points
)
external;
/**
* @dev Get current winners in order (1st, 2nd, 3rd, etc.)
* @return Array of winner addresses in ranking order
*/
function getWinners()
external
view
returns (address[] memory);
/**
* @dev Get participant's current total points
* @param _participant Address of the participant
* @return Total points received
*/
function getParticipantPoints(
address _participant
)
external
view
returns (uint256);
/**
* @dev Get participant's current ranking position
* @param _participant Address of the participant
* @return Ranking position (0 = not in top N, 1+ = position)
*/
function getParticipantRanking(
address _participant
)
external
view
returns (uint256);
/**
* @dev Check if a judge has voted
* @param _judge Address of the judge
* @return True if judge has voted
*/
function hasJudgeVoted(
address _judge
)
external
view
returns (bool);
/**
* @dev Get voting statistics
* @return _totalJudges Total number of judges
* @return _votedJudges Number of judges who have voted
* @return _totalParticipants Number of participants with votes
*/
function getVotingStats()
external
view
returns (
uint256 _totalJudges,
uint256 _votedJudges,
uint256 _totalParticipants
);
// ============ CONFIGURATION ============
/**
* @dev Initialize the voting system
* @param _pointsPerJudge Maximum points each judge can allocate
* @param _maxWinners Maximum number of winners to track
* @param _judges Array of judge addresses
*/
function initialize(
uint256 _pointsPerJudge,
uint256 _maxWinners,
address[] calldata _judges
)
external;
/**
* @dev Add a judge to the voting system
* @param _judge Address of the judge to add
*/
function addJudge(
address _judge
)
external;
/**
* @dev Remove a judge from the voting system
* @param _judge Address of the judge to remove
*/
function removeJudge(
address _judge
)
external;
// ============ WINNER MANAGEMENT ============
/**
* @dev Get the number of current winners
* @return Number of winners currently tracked
*/
function getWinnerCount()
external
view
returns (uint256);
/**
* @dev Get winner at specific position
* @param _position Position in rankings (1-indexed)
* @return Address of winner at position
*/
function getWinnerAtPosition(
uint256 _position
)
external
view
returns (address);
/**
* @dev Check if participant is currently a winner
* @param _participant Address of the participant
* @return True if participant is in top N winners
*/
function isWinner(
address _participant
)
external
view
returns (bool);
}
"
},
"project/contracts/JudgeCouncil.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* @title JudgeCouncil
* @dev Standalone service for managing judges and their rewards
* @notice This contract can be used by hackathons to manage judge-related functionality
*/
contract JudgeCouncil {
// Global judge registry
mapping(address => bool) public isGlobalJudge;
address[] public globalJudges;
// Judge rewards tracking
mapping(address => uint256) public judgeRewards;
mapping(address => bool) public hasClaimedReward;
// Judge delegation
mapping(address => address) public judgeDelegates;
mapping(address => address) public delegateToJudge;
// Governance voting system
uint256 public votesRequired = 1; // Minimum votes required for governance actions
mapping(bytes32 => mapping(address => bool)) public hasVoted; // proposalId => judge => hasVoted
mapping(bytes32 => uint256) public proposalVotes; // proposalId => vote count
mapping(bytes32 => bool) public proposalExecuted; // proposalId => executed
// Events
event GlobalJudgeAdded(
address indexed judge
);
event GlobalJudgeRemoved(
address indexed judge
);
event JudgeDelegated(
address indexed judge,
address indexed delegate
);
event JudgeRewardAdded(
address indexed judge,
uint256 amount
);
event JudgeRewardClaimed(
address indexed judge,
uint256 amount
);
event ProposalCreated(
bytes32 indexed proposalId,
string proposalType,
address indexed proposer
);
event VoteCast(
bytes32 indexed proposalId,
address indexed voter,
bool support
);
event ProposalExecuted(
bytes32 indexed proposalId
);
event VotesRequiredChanged(
uint256 oldVotesRequired,
uint256 newVotesRequired
);
modifier onlyGlobalJudge() {
require(
isGlobalJudge[msg.sender],
"Only global judges can call this function"
);
_;
}
modifier onlyFactory() {
require(
msg.sender == factory,
"Only factory can call this function"
);
_;
}
address public immutable factory;
/**
* @dev Constructor to set the factory address
* @param _factory Address of the factory contract
*/
constructor(
address _factory
) {
factory = _factory;
}
/**
* @dev Add the first global judge (only factory can call this)
* @param _judge Address of the first judge to add
*/
function _addFirstGlobalJudge(
address _judge
)
internal
{
require(_judge != address(0), "Invalid judge address");
require(!isGlobalJudge[_judge], "Judge already exists");
require(globalJudges.length == 0, "First judge already added");
isGlobalJudge[_judge] = true;
globalJudges.push(_judge);
emit GlobalJudgeAdded(_judge);
}
/**
* @dev Delegate judge responsibilities to another address
* @param _delegate Address to delegate to
*/
function delegateToAgent(
address _delegate
)
external
onlyGlobalJudge
{
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
onlyGlobalJudge
{
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 reward for a judge (called by hackathons)
* @param _judge Address of the judge
* @param _amount Amount to add
*/
function addJudgeReward(
address _judge,
uint256 _amount
)
external
payable
{
require(_judge != address(0), "Invalid judge address");
require(_amount > 0, "Amount must be greater than 0");
require(msg.value >= _amount, "Insufficient payment");
judgeRewards[_judge] += _amount;
emit JudgeRewardAdded(
_judge,
_amount
);
}
/**
* @dev Claim accumulated judge rewards
*/
function claimJudgeReward()
external
{
address judge = isGlobalJudge[msg.sender]
? msg.sender
: delegateToJudge[msg.sender];
require(judgeRewards[judge] > 0, "No rewards to claim");
require(!hasClaimedReward[judge], "Already claimed rewards");
uint256 amount = judgeRewards[judge];
hasClaimedReward[judge] = true;
payable(msg.sender).transfer(
amount
);
emit JudgeRewardClaimed(
judge,
amount
);
}
/**
* @dev Check if an address is a judge or delegate
* @param _address Address t
Submitted on: 2025-10-23 19:10:42
Comments
Log in to comment.
No comments yet.