HackathonFactory

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

Tags:
Multisig, Swap, Voting, Upgradeable, Multi-Signature, Factory|addr:0x553db01f160771df2b483f6e9bb5ad173b040151|verified:true|block:23632051|tx:0x5effcabd50c7d245ec490b4dbf54095531816343fa56912983a5bcb8dda3abb4|first_check:1761239440

Submitted on: 2025-10-23 19:10:42

Comments

Log in to comment.

No comments yet.