Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Create Hackathon | 23640951 | 94 days ago | IN | 0.00260455 ETH | 0.00007201 |
Latest 4 internal transactions
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60806040 | 23640923 | 94 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 23640923 | 94 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 23640923 | 94 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 23640923 | 94 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
HackathonFactory
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// Curve Finance Router interface
interface ICurveRouter {
function exchange(
address[11] calldata _route,
uint256[5][5] calldata _swap_params,
uint256 _amount,
uint256 _min_dy,
address[5] calldata _pools
) external payable returns (uint256);
function get_exchange_amount(
address[11] calldata _route,
uint256[5][5] calldata _swap_params,
uint256 _amount,
address[5] calldata _pools
) external view returns (uint256);
}
interface IWETH9 {
function deposit() external payable;
function withdraw(uint256 wad) external;
function transfer(address to, uint256 value) external returns (bool);
function balanceOf(address) external view returns (uint256);
}
/**
* @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 {
using SafeERC20 for IERC20;
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;
// Curve Finance Router and token addresses
address public immutable curveRouter;
address public immutable weth;
address public immutable pyusd;
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
);
event EmergencyWithdrawal(
address indexed judge,
address indexed token,
uint256 amount
);
event EthToPyusdConversion(
uint256 ethAmount,
uint256 pyusdAmount,
uint256 minPyusdOut
);
/**
* @dev Constructor that sets the implementation contract address and initializes judge governance
* @param _implementation Address of the HackathonImplementation contract
* @param _curveRouter Address of Curve router for ETH/PYUSD swaps
* @param _weth Address of WETH token
* @param _pyusd Address of PYUSD token
*/
constructor(
address _implementation,
address _curveRouter,
address _weth,
address _pyusd
)
JudgeCouncil(address(this))
{
implementation = _implementation;
curveRouter = _curveRouter;
weth = _weth;
pyusd = _pyusd;
// Deploy voting system implementations (initial)
openVotingImplementation = address(
new OpenVoting()
);
RevealCommitVotingImplementation = address(
new RevealCommitVoting()
);
zkVotingImplementation = address(
new ZKVotingSystem()
);
qvWrapperImplementation = address(
new QVWrapper()
);
_addFirstGlobalJudge(
msg.sender
);
}
/**
* @dev Converts ETH to PYUSD using Curve Finance
* @param _ethAmount Amount of ETH to convert
* @param _minPyusdOut Minimum PYUSD amount expected (for slippage protection)
* @return pyusdAmount Amount of PYUSD received
*/
function _convertEthToPyusd(
uint256 _ethAmount,
uint256 _minPyusdOut
)
internal
returns (uint256 pyusdAmount)
{
require(
_ethAmount > 0,
"ETH amount must be greater than 0"
);
// Execute the swap using Curve router
// Route: ETH -> USDC -> PYUSD
address[11] memory route = [
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, // ETH
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, // USDC
pyusd, // PYUSD
address(0), // Unused
address(0), // Unused
address(0), // Unused
address(0), // Unused
address(0), // Unused
address(0), // Unused
address(0), // Unused
address(0) // Unused
];
uint256[5][5] memory swapParams = [
[uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)], // Unused
[uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)], // Unused
[uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)], // Unused
[uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)], // Unused
[uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)] // Unused
];
address[5] memory pools = [
0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B, // ETH/USDC pool
0x383E6b4437b59fff47B619CBA855CA29342A8559, // USDC/PYUSD pool
address(0), // Unused
address(0), // Unused
address(0) // Unused
];
pyusdAmount = ICurveRouter(curveRouter).exchange{
value: _ethAmount
}(
route,
swapParams,
_ethAmount,
_minPyusdOut,
pools
);
// Emit conversion event
emit EthToPyusdConversion(
_ethAmount,
pyusdAmount,
_minPyusdOut
);
return pyusdAmount;
}
/**
* @dev Estimates how much ETH is needed to get the required PYUSD amount
* @param _pyusdAmount Required PYUSD amount
* @return Estimated ETH amount needed
*/
function _estimateEthForPyusd(
uint256 _pyusdAmount
)
internal
view
returns (uint256)
{
// Get the current exchange rate from Curve pool
uint256 estimatedPyusd = this.estimatePyusdOutput(1 ether);
// Calculate how much ETH is needed for the PYUSD amount
// Add 5% buffer for slippage
return (_pyusdAmount * 1 ether * 105) / (estimatedPyusd * 100);
}
/**
* @dev Helper function to create and initialize hackathon (reduces stack depth)
*/
function _createAndInitializeHackathon(
uint256 _hackathonId,
uint256 _startTime,
uint256 _endTime,
uint256 _minimumSponsorContribution,
uint256 _stakeAmount,
uint256[] memory _prizeDistribution,
address[] memory _selectedJudges,
uint256 _pyusdAmount,
uint256 _estimatedEthForPrizes
)
internal
returns (address hackathonAddress)
{
// Clone the implementation contract
hackathonAddress = Clones.clone(
implementation
);
// Transfer PYUSD to the hackathon contract
IERC20(pyusd).safeTransfer(
hackathonAddress,
_pyusdAmount
);
// Initialize the cloned contract with remaining ETH for judges
_initializeHackathon(
hackathonAddress,
_hackathonId,
_startTime,
_endTime,
_minimumSponsorContribution,
_stakeAmount,
_prizeDistribution,
_selectedJudges,
msg.value - _estimatedEthForPrizes
);
return hackathonAddress;
}
/**
* @dev Helper function to initialize hackathon (reduces stack depth)
*/
function _initializeHackathon(
address hackathonAddress,
uint256 _hackathonId,
uint256 _startTime,
uint256 _endTime,
uint256 _minimumSponsorContribution,
uint256 _stakeAmount,
uint256[] memory _prizeDistribution,
address[] memory _selectedJudges,
uint256 _remainingEth
)
internal
{
Hackathon(hackathonAddress).initialize{
value: _remainingEth
}(
msg.sender, // organizer
_hackathonId,
_startTime,
_endTime,
_minimumSponsorContribution,
_stakeAmount,
_prizeDistribution,
address(this), // factory
_selectedJudges,
pyusd // PYUSD token address
);
}
/**
* @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"
);
// Calculate total prize distribution
uint256 totalPrizeDistribution = 0;
for (uint256 i = 0; i < _prizeDistribution.length; i++) {
totalPrizeDistribution += _prizeDistribution[i];
}
// Estimate how much ETH is needed for the prize distribution
// This is a rough estimate - in practice, the organizer should send more ETH
uint256 estimatedEthForPrizes = _estimateEthForPyusd(
totalPrizeDistribution
);
// Convert only the prize distribution amount to PYUSD
uint256 pyusdAmount = _convertEthToPyusd(
estimatedEthForPrizes,
10000000 // @TODO: pass dynamically through constructor (6 decimals)
);
// Validate that the converted PYUSD amount matches the total prize distribution
require(
pyusdAmount >= totalPrizeDistribution,
"Insufficient PYUSD amount for prize distribution"
);
// 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
);
// Create and initialize hackathon
hackathonAddress = _createAndInitializeHackathon(
_hackathonId,
_startTime,
_endTime,
_minimumSponsorContribution,
_stakeAmount,
_prizeDistribution,
_selectedJudges,
pyusdAmount,
estimatedEthForPrizes
);
// Store the hackathon address
uint256 organizerIndex = organizerHackathonCount[msg.sender];
organizerHackathons[msg.sender][organizerIndex] = hackathonAddress;
organizerHackathonCount[msg.sender]++;
totalHackathons++;
emit HackathonCreated(
hackathonAddress,
_hackathonId,
msg.sender,
pyusdAmount,
_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);
}
// 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];
}
/**
* @dev Gets the current PYUSD balance of the factory
*/
function getPyusdBalance()
external
view
returns (uint256)
{
return IERC20(pyusd).balanceOf(
address(this)
);
}
/**
* @dev Gets the current WETH balance of the factory
*/
function getWethBalance()
external
view
returns (uint256)
{
return IERC20(weth).balanceOf(
address(this)
);
}
/**
* @dev Estimate PYUSD output for given ETH input using Curve pool
* @param _ethAmount Amount of ETH to convert
* @return Estimated PYUSD amount
*/
function estimatePyusdOutput(
uint256 _ethAmount
)
external
view
returns (uint256)
{
// Route: ETH -> USDC -> PYUSD
address[11] memory route = [
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, // ETH
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, // USDC
pyusd, // PYUSD
address(0), // Unused
address(0), // Unused
address(0), // Unused
address(0), // Unused
address(0), // Unused
address(0), // Unused
address(0), // Unused
address(0) // Unused
];
uint256[5][5] memory swapParams = [
[uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)], // Unused
[uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)], // Unused
[uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)], // Unused
[uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)], // Unused
[uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)] // Unused
];
address[5] memory pools = [
0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B, // ETH/USDC pool
0x383E6b4437b59fff47B619CBA855CA29342A8559, // USDC/PYUSD pool
address(0), // Unused
address(0), // Unused
address(0) // Unused
];
return ICurveRouter(curveRouter).get_exchange_amount(
route,
swapParams,
_ethAmount,
pools
);
}
/**
* @dev Get Curve router information
* @return router address, WETH address, PYUSD address
*/
function getCurveRouterInfo()
external
view
returns (
address,
address,
address
)
{
return (
curveRouter,
weth,
pyusd
);
}
/**
* @dev Emergency function to withdraw any remaining PYUSD (only global judges)
*/
function emergencyWithdrawPyusd()
external
onlyGlobalJudge
{
uint256 balance = IERC20(pyusd).balanceOf(
address(this)
);
if (balance > 0) {
IERC20(pyusd).safeTransfer(
msg.sender,
balance
);
}
}
/**
* @notice Emergency withdrawal function for ETH - only global judges can execute
* @dev Allows global judges to withdraw ETH in emergency situations
* @param _amount Amount of ETH to withdraw
*/
function emergencyWithdrawETH(
uint256 _amount
)
external
onlyGlobalJudge
{
// Transfer ETH to the global judge
payable(msg.sender).transfer(
_amount
);
emit EmergencyWithdrawal(
msg.sender,
address(0),
_amount
);
}
}// 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);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
pragma solidity >=0.6.2;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
pragma solidity >=0.4.16;
import {IERC20} from "../token/ERC20/IERC20.sol";// 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
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// 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;
}
}// 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)
}
}
}// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./StakeSystem.sol";
import "./VotingSystem.sol";
import "./JudgingSystem.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract Hackathon is StakeSystem, VotingSystem, JudgingSystem {
struct Submission {
address participant;
string projectName;
string projectUrl;
uint256 submissionTime;
uint256 score;
bool isEvaluated;
}
struct Sponsor {
address sponsorAddress;
uint256 contribution;
bool isActive;
uint256 distributedAmount; // Amount this sponsor has already distributed
bool isETHSponsor; // true if contributed ETH, false if contributed tokens
}
uint256 public hackathonId;
uint256 public startTime;
uint256 public endTime;
uint256 public prizePool;
address public organizer;
bool public isActive;
address public factory;
uint256 public participantCount;
uint256 public minimumSponsorContribution;
uint256 public judgingDuration;
uint256 public votingStartTime;
uint256 public claimingStartTime;
mapping(address => Submission) public submissions;
mapping(address => bool) public isRegistered;
mapping(address => bool) public hasSubmitted;
uint256 public totalSubmissions;
mapping(address => Sponsor) public sponsors;
address[] public sponsorList;
uint256 public totalSponsorContributions;
// Token sponsor tracking
mapping(address => mapping(address => uint256)) public tokenContributions; // sponsor => token => amount
// Sponsor-specific prize pools
mapping(address => uint256) public sponsorPrizePools; // sponsor => amount available for distribution
mapping(address => address) public sponsorTokenAddresses; // sponsor => token address (for token sponsors)
mapping(address => uint256) public totalTokenContributions; // token => total amount
event ParticipantRegistered(
address indexed participant
);
event SubmissionMade(
address indexed participant,
string projectName
);
event PrizeDistributed(
address indexed winner,
uint256 amount
);
event EmergencyWithdrawal(
address indexed judge,
address indexed token,
uint256 amount
);
event HackathonEnded();
event SponsorAdded(
address indexed sponsor,
uint256 contribution
);
event SubmissionScored(
address indexed participant,
uint256 score
);
event TokenSponsorAdded(
address indexed sponsor,
address indexed token,
uint256 amount
);
modifier onlyOrganizer() {
require(
msg.sender == organizer,
"Only organizer can call this function"
);
_;
}
modifier hackathonActive() {
require(
isActive,
"Hackathon is not active"
);
require(
block.timestamp >= startTime,
"Hackathon has not started yet"
);
require(
block.timestamp <= endTime,
"Hackathon has ended"
);
_;
}
modifier onlyRegistered() {
require(
isRegistered[msg.sender],
"Not registered for this hackathon"
);
_;
}
modifier onlySponsor() {
require(
sponsors[msg.sender].isActive,
"Only sponsors can call this function"
);
_;
}
/**
* @dev Internal function to initialize hackathon parameters (for cloning)
* @notice This function is called by the implementation contract during initialization
*/
function _initializeHackathon(
uint256 _hackathonId,
uint256 _startTime,
uint256 _endTime,
uint256[] memory _prizeDistribution,
uint256 _stakeAmount,
uint256 _prizeClaimCooldown,
uint256 _judgingDuration,
address[] memory _selectedJudges
)
internal
{
require(
_startTime > block.timestamp,
"Start time must be in the future"
);
require(
_endTime > _startTime,
"End time must be after start time"
);
require(
_prizeDistribution.length > 0,
"Must have at least 1 winner"
);
require(
_prizeClaimCooldown > 0,
"Prize claim cooldown must be greater than 0"
);
require(
_judgingDuration >= 2 hours && _judgingDuration <= 2 days,
"Judging duration must be between 2 hours and 2 days"
);
// Validate prize distribution
uint256 totalDistribution = 0;
for (uint256 i = 0; i < _prizeDistribution.length; i++) {
require(
_prizeDistribution[i] > 0,
"Each prize distribution must be greater than 0"
);
totalDistribution += _prizeDistribution[i];
}
// Initialize all hackathon state variables
hackathonId = _hackathonId;
startTime = _startTime;
endTime = _endTime;
isActive = true;
participantCount = 0;
totalSponsorContributions = 0;
stakeAmount = _stakeAmount;
prizeClaimCooldown = _prizeClaimCooldown;
judgingDuration = _judgingDuration;
pointsPerJudge = 100;
votingOpen = false;
totalStakes = 0;
// Calculate all phase timestamps upfront
votingStartTime = _endTime; // Voting starts when hackathon ends
votingEndTime = votingStartTime + _judgingDuration;
claimingStartTime = votingEndTime + _prizeClaimCooldown;
// Initialize inherited systems
// StakeSystem initialization
stakeAmount = _stakeAmount;
// VotingSystem initialization
prizeDistribution = _prizeDistribution;
prizeClaimCooldown = _prizeClaimCooldown;
pointsPerJudge = 100;
maxWinners = _prizeDistribution.length;
prizePool = totalDistribution;
// Initialize judging system
// Judge rewards are now handled via remaining ETH, not percentage
// Add selected judges
for (uint256 i = 0; i < _selectedJudges.length; i++) {
require(_selectedJudges[i] != address(0), "Invalid judge address");
isJudge[_selectedJudges[i]] = true;
judgeList.push(_selectedJudges[i]);
}
}
/**
* @dev Initialize the cloned hackathon with actual parameters
* @notice This function is called once after cloning to set the actual hackathon parameters
* @notice Only the factory can call this function
*/
function initialize(
address _organizer,
uint256 _hackathonId,
uint256 _startTime,
uint256 _endTime,
uint256 _minimumSponsorContribution,
uint256 _stakeAmount,
uint256[] memory _prizeDistribution,
address _factory,
address[] memory _selectedJudges,
address _pyusdToken
)
external
payable
{
// Only factory can call initialize
require(
msg.sender == _factory,
"Only factory can initialize"
);
// Prevent multiple initialization
require(
organizer == address(0),
"Already initialized"
);
// Set the organizer and factory
organizer = _organizer;
factory = _factory;
// Set the PYUSD token
pyusdToken = IERC20(_pyusdToken);
_setPyusdToken(_pyusdToken);
// Set parameters
minimumSponsorContribution = _minimumSponsorContribution;
uint256 _prizeClaimCooldown = 1 days; // Default prize claim cooldown
uint256 _judgingDuration = 1 days; // Default judging duration
// Set judge reward pool with the remaining ETH sent to the contract
_setJudgeRewardPool(
msg.value
);
// Initialize the hackathon with the provided parameters
_initializeHackathon(
_hackathonId,
_startTime,
_endTime,
_prizeDistribution,
_stakeAmount,
_prizeClaimCooldown,
_judgingDuration,
_selectedJudges
);
}
/**
* @dev Registers a participant for this hackathon
*/
function register()
external
payable
{
require(
isActive,
"Hackathon is not active"
);
require(
block.timestamp < startTime,
"Registration closed - hackathon has started"
);
require(
isRegistered[msg.sender] == false,
"Already registered"
);
isRegistered[msg.sender] = true;
participantCount++;
// Use inherited stake system
_depositStake(
msg.sender
);
emit ParticipantRegistered(
msg.sender
);
}
/**
* @dev Submits a project for this hackathon
* @param _projectName Name of the project
* @param _projectUrl URL of the project repository or demo
*/
function submitProject(
string memory _projectName,
string memory _projectUrl
)
external
onlyDuringSubmission
onlyRegistered
{
require(
hasSubmitted[msg.sender] == false,
"Already submitted"
);
submissions[msg.sender] = Submission({
participant: msg.sender,
projectName: _projectName,
projectUrl: _projectUrl,
submissionTime: block.timestamp,
score: 0,
isEvaluated: false
});
hasSubmitted[msg.sender] = true;
totalSubmissions++;
// Return stake to participant
uint256 stake = participantStakes[msg.sender];
if (stake > 0) {
participantStakes[msg.sender] = 0;
totalStakes -= stake;
payable(msg.sender).transfer(stake);
emit StakeReturned(
msg.sender, stake);
}
emit SubmissionMade(
msg.sender,
_projectName
);
}
/**
* @dev Checks if an address is registered for this hackathon
* @param _participant Address to check
*/
function isParticipantRegistered(
address _participant
)
external
view
returns (bool)
{
return isRegistered[
_participant
];
}
/**
* @dev Gets submission details for a participant
* @param _participant Address of the participant
*/
function getSubmission(
address _participant
)
external
view
returns (
address participant,
string memory projectName,
string memory projectUrl,
uint256 submissionTime,
uint256 score,
bool isEvaluated
)
{
Submission storage submission = submissions[
_participant
];
return (
submission.participant,
submission.projectName,
submission.projectUrl,
submission.submissionTime,
submission.score,
submission.isEvaluated
);
}
/**
* @dev Distributes prize to winner (only organizer or sponsors)
* @param _winner Address of the winner
* @param _amount Amount to distribute
*/
function distributePrize(
address _winner,
uint256 _amount
)
external
onlySponsor
{
require(!isActive || block.timestamp > endTime, "Hackathon is still active");
require(_amount > 0, "Amount must be greater than 0");
// If organizer is distributing, use main prize pool
if (msg.sender == organizer) {
require(_amount <= prizePool, "Amount exceeds prize pool");
prizePool -= _amount;
payable(_winner).transfer(_amount);
} else {
// If sponsor is distributing, use their specific prize pool
require(_amount <= sponsorPrizePools[msg.sender], "Amount exceeds sponsor's available prize pool");
require(_amount <= (sponsors[msg.sender].contribution - sponsors[msg.sender].distributedAmount), "Amount exceeds sponsor's remaining contribution");
// Update sponsor's distributed amount
sponsors[msg.sender].distributedAmount += _amount;
sponsorPrizePools[msg.sender] -= _amount;
// Distribute based on sponsor type (ETH or token)
if (sponsors[msg.sender].isETHSponsor) {
payable(_winner).transfer(_amount);
} else {
// For token sponsors, transfer tokens
address tokenAddress = sponsorTokenAddresses[msg.sender];
IERC20(tokenAddress).transfer(_winner, _amount);
}
}
emit PrizeDistributed(
_winner,
_amount
);
}
/**
* @dev Gets hackathon details
*/
function getHackathonDetails()
external
view
returns (
uint256 _hackathonId,
uint256 _startTime,
uint256 _endTime,
uint256 _prizePool,
address _organizer,
bool _isActive,
uint256 _participantCount
)
{
return (
hackathonId,
startTime,
endTime,
prizePool,
organizer,
isActive,
participantCount
);
}
/**
* @dev Checks if hackathon is currently accepting registrations
*/
function isRegistrationOpen()
external
view
returns (bool)
{
return isActive && block.timestamp < startTime;
}
/**
* @dev Checks if hackathon is currently accepting submissions
*/
function isSubmissionOpen()
external
view
returns (bool)
{
return isActive && block.timestamp >= startTime && block.timestamp <= endTime;
}
modifier onlyDuringSubmission() {
require(isActive && block.timestamp >= startTime && block.timestamp <= endTime, "Not during submission phase");
_;
}
modifier onlyDuringVoting() {
require(block.timestamp >= votingStartTime && block.timestamp <= votingEndTime, "Not during voting phase");
_;
}
modifier onlyDuringClaiming() {
require(block.timestamp >= claimingStartTime, "Not during claiming phase");
_;
}
/**
* @dev Check if hackathon is currently active based on timestamps
*/
function _updateActiveStatus() internal {
isActive = block.timestamp >= startTime && block.timestamp <= endTime;
}
/**
* @dev Allows anyone to become a sponsor by contributing the minimum amount
*/
function becomeSponsor()
external
payable
{
require(
msg.value >= minimumSponsorContribution,
"Contribution below minimum required"
);
require(
sponsors[msg.sender].isActive == false,
"Already a sponsor"
);
sponsors[msg.sender] = Sponsor({
sponsorAddress: msg.sender,
contribution: msg.value,
isActive: true,
distributedAmount: 0,
isETHSponsor: true
});
sponsorList.push(msg.sender);
totalSponsorContributions += msg.value;
// Set up sponsor-specific prize pool (ETH contribution)
sponsorPrizePools[msg.sender] = msg.value;
emit SponsorAdded(
msg.sender,
msg.value
);
}
/**
* @dev Allows anyone to become a sponsor by contributing tokens
* @param _tokenAddress Address of the ERC20 token to contribute
* @param _tokenAmount Amount of tokens to contribute
*/
function becomeSponsorWithToken(
address _tokenAddress,
uint256 _tokenAmount
)
external
{
require(
_tokenAmount >= minimumSponsorContribution,
"Contribution below minimum required"
);
require(
sponsors[msg.sender].isActive == false,
"Already a sponsor"
);
// Transfer tokens from sender to this contract
IERC20 token = IERC20(
_tokenAddress
);
token.transferFrom(
msg.sender,
address(this),
_tokenAmount
);
// Add sponsor to the list
sponsors[msg.sender] = Sponsor({
sponsorAddress: msg.sender,
contribution: _tokenAmount,
isActive: true,
distributedAmount: 0,
isETHSponsor: false
});
sponsorList.push(msg.sender);
totalSponsorContributions += _tokenAmount;
// Track token contributions
tokenContributions[msg.sender][_tokenAddress] = _tokenAmount;
sponsorTokenAddresses[msg.sender] = _tokenAddress;
// Set up sponsor-specific prize pool (token contribution)
sponsorPrizePools[msg.sender] = _tokenAmount;
totalTokenContributions[_tokenAddress] += _tokenAmount;
emit TokenSponsorAdded(
msg.sender,
_tokenAddress,
_tokenAmount
);
}
/**
* @dev Adds a judge to the hackathon (only organizer can call)
* @param _judge Address of the judge
*/
function addJudge(
address _judge
)
public
override
onlyOrganizer
{
require(isActive, "Cannot add judges to inactive hackathon");
require(block.timestamp < endTime, "Cannot add judges after hackathon ends");
// Call inherited function directly
JudgingSystem.addJudge(_judge);
}
/**
* @dev Allows a judge to score a submission
* @param _participant Address of the participant
* @param _score Score to assign (0-100)
*/
function scoreSubmission(
address _participant,
uint256 _score
)
external
{
require(
isJudgeOrDelegate(msg.sender),
"Only judges can score"
);
require(
hasSubmitted[_participant],
"No submission found"
);
require(_score <= 100, "Score must be between 0 and 100");
require(!submissions[_participant].isEvaluated, "Submission already evaluated");
submissions[_participant].score = _score;
submissions[_participant].isEvaluated = true;
emit SubmissionScored(
_participant,
_score
);
}
/**
* @dev Gets all sponsors
*/
function getSponsors()
external
view
returns (address[] memory)
{
return sponsorList;
}
/**
* @dev Gets sponsor contribution amount
* @param _sponsor Address of the sponsor
*/
function getSponsorContribution(
address _sponsor
)
external
view
returns (uint256)
{
return sponsors[_sponsor].contribution;
}
/**
* @dev Claim judge reward (hackathon-specific)
*/
function claimJudgeReward()
external
{
require(
isJudgeOrDelegate(msg.sender),
"Only judges or their delegates can claim rewards"
);
address actualJudge = isJudge[msg.sender]
? msg.sender
: delegateToJudge[msg.sender];
require(
!hasReceivedJudgeReward[actualJudge],
"Already claimed judge reward"
);
require(
judgeList.length > 0,
"No judges to distribute rewards to"
);
uint256 rewardPerJudge = judgeRewardPool / judgeList.length;
require(
rewardPerJudge > 0,
"Insufficient reward per judge"
);
hasReceivedJudgeReward[actualJudge] = true;
payable(msg.sender).transfer(
rewardPerJudge
);
emit JudgeRewardDistributed(
actualJudge,
rewardPerJudge
);
}
/**
* @dev Gets total prize pool including sponsor contributions
*/
function getTotalPrizePool()
external
view
returns (uint256)
{
return prizePool;
}
/**
* @dev Gets minimum sponsor contribution required
*/
function getMinimumSponsorContribution()
external
view
returns (uint256)
{
return minimumSponsorContribution;
}
/**
* @dev Gets token contribution amount for a specific sponsor and token
* @param _sponsor Address of the sponsor
* @param _tokenAddress Address of the token
*/
function getTokenContribution(
address _sponsor,
address _tokenAddress
)
external
view
returns (uint256)
{
return tokenContributions[_sponsor][_tokenAddress];
}
/**
* @dev Gets sponsor's available prize pool for distribution
* @param _sponsor Address of the sponsor
* @return Available amount the sponsor can still distribute
*/
function getSponsorAvailablePrize(address _sponsor) external view returns (uint256) {
return sponsorPrizePools[_sponsor];
}
/**
* @dev Gets sponsor's total contribution
* @param _sponsor Address of the sponsor
* @return Total contribution amount
*/
function getSponsorTotalContribution(
address _sponsor
)
external
view
returns (uint256)
{
return sponsors[_sponsor].contribution;
}
/**
* @dev Gets sponsor's distributed amount
* @param _sponsor Address of the sponsor
* @return Amount already distributed by this sponsor
*/
function getSponsorDistributedAmount(
address _sponsor
)
external
view
returns (uint256)
{
return sponsors[_sponsor].distributedAmount;
}
/**
* @dev Gets sponsor's token address (for token sponsors)
* @param _sponsor Address of the sponsor
* @return Token address used by this sponsor
*/
function getSponsorTokenAddress(
address _sponsor
)
external
view
returns (address)
{
return sponsorTokenAddresses[
_sponsor
];
}
/**
* @dev Gets total token contributions for a specific token
* @param _tokenAddress Address of the token
*/
function getTotalTokenContributions(
address _tokenAddress
)
external
view
returns (uint256)
{
return totalTokenContributions[
_tokenAddress
];
}
// ========== Voting System Functions ==========
/**
* @dev Allows a judge to vote on submissions by allocating points
* @param _participant Address of the participant to vote for
* @param _points Points to allocate (0-100)
*/
function voteForSubmission(
address _participant,
uint256 _points
)
external
onlyDuringVoting
{
_updateActiveStatus();
require(
isJudgeOrDelegate(msg.sender),
"Only judges can vote"
);
require(
hasSubmitted[_participant],
"Participant has not submitted"
);
require(
_points <= pointsPerJudge,
"Cannot allocate more points than allowed"
);
require(
hasVoted[msg.sender] == false,
"Judge has already voted"
);
// Check if judge has already allocated points to this participant
uint256 currentPoints = judgeVotes[msg.sender][_participant];
require(
currentPoints == 0,
"Judge has already voted for this participant"
);
judgeVotes[msg.sender][_participant] = _points;
totalPoints[_participant] += _points;
emit JudgeVoted(
msg.sender,
_participant,
_points
);
}
/**
* @dev Allows winners to claim their prize after cooldown period
*/
function claimPrize()
external
onlyDuringClaiming
{
require(
hasSubmitted[msg.sender],
"Must have submitted a project"
);
_claimPrize(
msg.sender,
prizePool
);
}
/**
* @notice Get prize amount for a participant
* @dev Uses VotingSystem's internal function to calculate prize amount
* @param _participant Address of the participant
* @return Prize amount for the participant
*/
function getPrizeAmount(
address _participant
)
external
view
returns (uint256)
{
return _getPrizeAmount(
_participant
);
}
/**
* @notice Check if a participant is a winner
* @dev Public function to check if a participant is a winner
* @param _participant Address of the participant
* @return True if the participant is a winner, false otherwise
*/
function isWinner(
address _participant
)
external
view
returns (bool)
{
return _isWinner(
_participant
);
}
/**
* @notice Check if a participant has claimed their prize
* @dev Public function to check if a participant has claimed their prize
* @param _participant Address of the participant
* @return True if prize has been claimed, false otherwise
*/
function getHasClaimedPrize(
address _participant
)
external
view
returns (bool)
{
return hasClaimedPrize[
_participant
];
}
/**
* @notice Emergency withdrawal function for ETH - only judges can execute
* @dev Allows judges to withdraw ETH in emergency situations
* @param _amount Amount of ETH to withdraw
*/
function emergencyWithdrawETH(
uint256 _amount
)
external
onlyJudge
{
require(
_amount > 0,
"Amount must be greater than 0"
);
require(
_amount <= address(this).balance,
"Insufficient ETH balance"
);
// Transfer ETH to the judge
payable(msg.sender).transfer(_amount);
emit EmergencyWithdrawal(
msg.sender,
address(0),
_amount
);
}
/**
* @notice Emergency withdrawal function for PYUSD tokens - only judges can execute
* @dev Allows judges to withdraw PYUSD tokens in emergency situations
* @param _amount Amount of PYUSD tokens to withdraw
*/
function emergencyWithdrawPYUSD(
uint256 _amount
)
external
onlyJudge
{
require(
_amount > 0,
"Amount must be greater than 0"
);
require(
_amount <= pyusdToken.balanceOf(address(this)),
"Insufficient PYUSD balance"
);
// Transfer PYUSD tokens to the judge
pyusdToken.transfer(msg.sender, _amount);
emit EmergencyWithdrawal(
msg.sender,
address(pyusdToken),
_amount
);
}
}// 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);
}// 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 to check
* @return True if the address is a judge or delegate
*/
function isJudgeOrDelegate(
address _address
)
public
view
returns (bool)
{
return isGlobalJudge[_address] || delegateToJudge[_address] != address(0);
}
/**
* @dev Get all global judges
* @return Array of global judge addresses
*/
function getGlobalJudges()
external
view
returns (address[] memory)
{
return globalJudges;
}
/**
* @dev Get judge rewards for an address
* @param _judge Address of the judge
* @return Amount of rewards
*/
function getJudgeRewards(
address _judge
)
external
view
returns (uint256)
{
return judgeRewards[_judge];
}
/**
* @dev Get delegate for a judge
* @param _judge Address of the judge
* @return Address of the delegate
*/
function getJudgeDelegate(
address _judge
)
external
view
returns (address)
{
return judgeDelegates[_judge];
}
/**
* @dev Get judge for a delegate
* @param _delegate Address of the delegate
* @return Address of the judge
*/
function getDelegateJudge(
address _delegate
)
external
view
returns (address)
{
return delegateToJudge[_delegate];
}
// ========== Governance Functions ==========
/**
* @dev Create a proposal to add a new judge
* @param _judge Address of the judge to add
* @return proposalId Unique identifier for the proposal
*/
function proposeAddJudge(
address _judge
)
external
onlyGlobalJudge
returns (bytes32)
{
require(_judge != address(0), "Invalid judge address");
require(!isGlobalJudge[_judge], "Judge already exists");
bytes32 proposalId = keccak256(abi.encodePacked("addJudge", _judge, block.timestamp));
require(!proposalExecuted[proposalId], "Proposal already executed");
emit ProposalCreated(
proposalId,
"addJudge",
msg.sender
);
return proposalId;
}
/**
* @dev Create a proposal to remove a judge
* @param _judge Address of the judge to remove
* @return proposalId Unique identifier for the proposal
*/
function proposeRemoveJudge(
address _judge
)
external
onlyGlobalJudge
returns (bytes32)
{
require(isGlobalJudge[_judge], "Judge does not exist");
require(_judge != msg.sender, "Cannot propose to remove yourself");
bytes32 proposalId = keccak256(abi.encodePacked("removeJudge", _judge, block.timestamp));
require(!proposalExecuted[proposalId], "Proposal already executed");
emit ProposalCreated(proposalId, "removeJudge", msg.sender);
return proposalId;
}
/**
* @dev Create a proposal to change the required votes
* @param _newVotesRequired New number of votes required
* @return proposalId Unique identifier for the proposal
*/
function proposeChangeVotesRequired(
uint256 _newVotesRequired
)
external
onlyGlobalJudge
returns (bytes32)
{
require(_newVotesRequired > 0, "Votes required must be greater than 0");
require(_newVotesRequired <= globalJudges.length, "Votes required cannot exceed total judges");
bytes32 proposalId = keccak256(abi.encodePacked("changeVotesRequired", _newVotesRequired, block.timestamp));
require(!proposalExecuted[proposalId], "Proposal already executed");
emit ProposalCreated(
proposalId,
"changeVotesRequired",
msg.sender
);
return proposalId;
}
/**
* @dev Vote on a proposal
* @param _proposalId Unique identifier for the proposal
* @param _support True to support the proposal, false to oppose
*/
function vote(
bytes32 _proposalId,
bool _support
)
external
onlyGlobalJudge
{
require(!hasVoted[_proposalId][msg.sender], "Already voted on this proposal");
require(!proposalExecuted[_proposalId], "Proposal already executed");
hasVoted[_proposalId][msg.sender] = true;
if (_support) {
proposalVotes[_proposalId]++;
}
emit VoteCast(
_proposalId,
msg.sender,
_support
);
}
/**
* @dev Execute a proposal if it has enough votes
* @param _proposalId Unique identifier for the proposal
* @param _judge Address of the judge (for add/remove judge proposals)
* @param _newVotesRequired New votes required (for change votes required proposals)
*/
function executeProposal(
bytes32 _proposalId,
address _judge,
uint256 _newVotesRequired
)
external
onlyGlobalJudge
{
require(!proposalExecuted[_proposalId], "Proposal already executed");
require(proposalVotes[_proposalId] >= votesRequired, "Insufficient votes");
proposalExecuted[_proposalId] = true;
// Determine proposal type and execute
if (_judge != address(0)) {
// Add or remove judge proposal
if (isGlobalJudge[_judge]) {
// Remove judge
isGlobalJudge[_judge] = false;
for (uint256 i = 0; i < globalJudges.length; i++) {
if (globalJudges[i] == _judge) {
globalJudges[i] = globalJudges[globalJudges.length - 1];
globalJudges.pop();
break;
}
}
emit GlobalJudgeRemoved(_judge);
} else {
// Add judge
isGlobalJudge[_judge] = true;
globalJudges.push(_judge);
emit GlobalJudgeAdded(_judge);
}
} else if (_newVotesRequired > 0) {
// Change votes required
uint256 oldVotesRequired = votesRequired;
votesRequired = _newVotesRequired;
emit VotesRequiredChanged(
oldVotesRequired,
_newVotesRequired
);
}
emit ProposalExecuted(
_proposalId
);
}
/**
* @dev Get the current vote count for a proposal
* @param _proposalId Unique identifier for the proposal
* @return Current vote count
*/
function getProposalVotes(
bytes32 _proposalId
)
external
view
returns (uint256)
{
return proposalVotes[_proposalId];
}
/**
* @dev Check if a judge has voted on a proposal
* @param _proposalId Unique identifier for the proposal
* @param _judge Address of the judge
* @return True if the judge has voted
*/
function hasJudgeVoted(
bytes32 _proposalId,
address _judge
)
external
view
returns (bool)
{
return hasVoted[_proposalId][_judge];
}
/**
* @dev Check if a proposal has been executed
* @param _proposalId Unique identifier for the proposal
* @return True if the proposal has been executed
*/
function isProposalExecuted(
bytes32 _proposalId
)
external
view
returns (bool)
{
return proposalExecuted[_proposalId];
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* @title JudgingSystem
* @dev Handles all judge-related functionality for hackathons
* @notice This contract manages judges, their delegation, scoring, and rewards
*/
contract JudgingSystem {
// Judge management
mapping(address => bool) public isJudge;
address[] public judgeList;
// Judge delegation
mapping(address => address) public judgeDelegates;
mapping(address => address) public delegateToJudge;
// Judge rewards
uint256 public judgeRewardPercentage;
uint256 public judgeRewardPool;
mapping(address => bool) public hasReceivedJudgeReward;
// Events
event JudgeAdded(address indexed judge);
event JudgeRemoved(address indexed judge);
event JudgeDelegated(address indexed judge, address indexed delegate);
event JudgeRewardDistributed(address indexed judge, uint256 amount);
modifier onlyJudge() {
require(
isJudge[msg.sender],
"Only judges can call this function"
);
_;
}
/**
* @dev Add a judge to the system
* @param _judge Address of the judge to add
*/
function addJudge(address _judge) public virtual {
require(_judge != address(0), "Invalid judge address");
require(!isJudge[_judge], "Judge already added");
isJudge[_judge] = true;
judgeList.push(_judge);
emit JudgeAdded(_judge);
}
/**
* @dev Remove a judge from the system
* @param _judge Address of the judge to remove
*/
function removeJudge(address _judge) public {
require(isJudge[_judge], "Old judge not found");
isJudge[_judge] = false;
// Remove from array
for (uint256 i = 0; i < judgeList.length; i++) {
if (judgeList[i] == _judge) {
judgeList[i] = judgeList[judgeList.length - 1];
judgeList.pop();
break;
}
}
emit JudgeRemoved(_judge);
}
/**
* @dev Delegate judge responsibilities to another address
* @param _delegate Address to delegate to
*/
function delegateToAgent(address _delegate) external {
require(isJudge[msg.sender], "Only judges can delegate");
require(_delegate != address(0), "Invalid delegate address");
require(_delegate != msg.sender, "Cannot delegate to yourself");
require(judgeDelegates[msg.sender] == address(0), "Already has a delegate");
judgeDelegates[msg.sender] = _delegate;
delegateToJudge[_delegate] = msg.sender;
emit JudgeDelegated(msg.sender, _delegate);
}
/**
* @dev Revoke delegation
*/
function revokeDelegation() external {
require(isJudge[msg.sender], "Only judges can revoke delegation");
require(judgeDelegates[msg.sender] != address(0), "No delegation to revoke");
address delegate = judgeDelegates[msg.sender];
judgeDelegates[msg.sender] = address(0);
delegateToJudge[delegate] = address(0);
emit JudgeDelegated(msg.sender, address(0));
}
/**
* @dev Add funds to judge reward pool
*/
function addJudgeRewards() public payable {
require(msg.value > 0, "Must send ETH");
judgeRewardPool += msg.value;
}
/**
* @dev Distribute judge rewards
*/
function distributeJudgeRewards() external {
require(judgeRewardPool > 0, "No judge rewards available");
require(judgeList.length > 0, "No judges to reward");
uint256 rewardPerJudge = judgeRewardPool / judgeList.length;
require(rewardPerJudge > 0, "Insufficient reward per judge");
for (uint256 i = 0; i < judgeList.length; i++) {
address judge = judgeList[i];
if (!hasReceivedJudgeReward[judge]) {
hasReceivedJudgeReward[judge] = true;
payable(judge).transfer(rewardPerJudge);
emit JudgeRewardDistributed(judge, rewardPerJudge);
}
}
judgeRewardPool = 0;
}
/**
* @dev Check if an address is a judge or delegate
* @param _address Address to check
* @return True if the address is a judge or delegate
*/
function isJudgeOrDelegate(address _address) public view returns (bool) {
return isJudge[_address] || delegateToJudge[_address] != address(0);
}
/**
* @dev Get all judges
* @return Array of judge addresses
*/
function getJudges() external view returns (address[] memory) {
return judgeList;
}
/**
* @dev Get judge reward pool amount
* @return Amount in the reward pool
*/
function getJudgeRewardPool() external view returns (uint256) {
return judgeRewardPool;
}
/**
* @dev Get reward per judge
* @return Amount each judge would receive
*/
function getRewardPerJudge() external view returns (uint256) {
if (judgeList.length == 0) return 0;
return judgeRewardPool / judgeList.length;
}
/**
* @dev Get judge reward percentage
* @return Percentage of prize pool for judges
*/
function getJudgeRewardPercentage() public view returns (uint256) {
return judgeRewardPercentage;
}
/**
* @dev Get delegate for a judge
* @param _judge Address of the judge
* @return Address of the delegate
*/
function getJudgeDelegate(address _judge) external view returns (address) {
return judgeDelegates[_judge];
}
/**
* @dev Get judge for a delegate
* @param _delegate Address of the delegate
* @return Address of the judge
*/
function getDelegateJudge(address _delegate) external view returns (address) {
return delegateToJudge[_delegate];
}
/**
* @dev Set the judge reward pool (called during hackathon initialization)
* @param _rewardPool Amount of ETH available for judge rewards
*/
function _setJudgeRewardPool(
uint256 _rewardPool
)
internal
{
judgeRewardPool = _rewardPool;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./IVotingSystem.sol";
import "./VotingTypes.sol";
/**
* @title OpenVoting
* @dev Transparent voting system with batch voting support
* @notice Judges can vote for multiple participants in one transaction
*/
contract OpenVoting is IVotingSystem {
// ============ STATE VARIABLES ============
// Voting configuration
uint256 public pointsPerJudge;
uint256 public maxWinners;
uint256 public votingDeadline;
// Judge management
mapping(address => bool) public isJudge;
mapping(address => bool) public hasVoted;
address[] public judges;
uint256 public totalJudges;
// Participant scoring
mapping(address => uint256) public totalPoints;
mapping(address => mapping(address => uint256)) public judgeVotes;
// Dynamic winner tracking - updated on each vote
mapping(address => uint256) public winnerPosition; // 0 = not a winner, 1+ = position (1-indexed)
address[] public winners; // Array of winners in order (1st, 2nd, 3rd, etc.)
// ============ EVENTS ============
event JudgeAdded(address indexed judge);
event JudgeRemoved(address indexed judge);
// ============ MODIFIERS ============
modifier onlyJudge() {
require(isJudge[msg.sender], "Only judges can perform this action");
_;
}
modifier onlyDuringVoting() {
require(block.timestamp <= votingDeadline, "Voting deadline has passed");
_;
}
// ============ INITIALIZATION ============
/**
* @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 override {
require(pointsPerJudge == 0, "Already initialized");
pointsPerJudge = _pointsPerJudge;
maxWinners = _maxWinners;
// Add judges
for (uint256 i = 0; i < _judges.length; i++) {
_addJudge(_judges[i]);
}
}
// ============ CORE VOTING 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 override onlyJudge onlyDuringVoting {
require(_participants.length == _points.length, "Arrays length mismatch");
require(_participants.length > 0, "Must vote for at least one participant");
require(!hasVoted[msg.sender], "Judge has already voted");
uint256 totalPointsUsed = 0;
// Process each vote
for (uint256 i = 0; i < _participants.length; i++) {
require(_points[i] > 0, "Points must be greater than 0");
totalPointsUsed += _points[i];
// Update participant's total points
uint256 oldPoints = totalPoints[_participants[i]];
totalPoints[_participants[i]] += _points[i];
// Store judge's vote
judgeVotes[msg.sender][_participants[i]] = _points[i];
// Update winner list (O(1) operation per participant)
_updateWinnerList(_participants[i], oldPoints, totalPoints[_participants[i]]);
}
require(totalPointsUsed <= pointsPerJudge, "Exceeds points per judge limit");
hasVoted[msg.sender] = true;
emit VotesSubmitted(msg.sender, _participants, _points);
}
// ============ JUDGE MANAGEMENT ============
/**
* @dev Add a judge to the voting system
* @param _judge Address of the judge to add
*/
function addJudge(address _judge) external override {
require(_judge != address(0), "Invalid judge address");
require(!isJudge[_judge], "Judge already exists");
_addJudge(_judge);
}
/**
* @dev Remove a judge from the voting system
* @param _judge Address of the judge to remove
*/
function removeJudge(address _judge) external override {
require(isJudge[_judge], "Judge does not exist");
require(!hasVoted[_judge], "Cannot remove judge who has voted");
_removeJudge(_judge);
}
/**
* @dev Internal function to add a judge
*/
function _addJudge(address _judge) internal {
isJudge[_judge] = true;
judges.push(_judge);
totalJudges++;
emit JudgeAdded(_judge);
}
/**
* @dev Internal function to remove a judge
*/
function _removeJudge(address _judge) internal {
isJudge[_judge] = false;
// Remove from judges array
for (uint256 i = 0; i < judges.length; i++) {
if (judges[i] == _judge) {
judges[i] = judges[judges.length - 1];
judges.pop();
break;
}
}
totalJudges--;
emit JudgeRemoved(_judge);
}
// ============ WINNER TRACKING (O(1) Operations) ============
/**
* @dev Update winner list dynamically based on participant's new points
* @param _participant Address of the participant whose points changed
* @param _oldPoints Previous total points
* @param _newPoints New total points
*/
function _updateWinnerList(
address _participant,
uint256 _oldPoints,
uint256 _newPoints
) internal {
uint256 currentPosition = winnerPosition[_participant];
// If participant was already a winner
if (currentPosition > 0) {
_adjustWinnerPosition(_participant, _oldPoints, _newPoints);
return;
}
// Check if participant should be added to winners
if (_newPoints > 0 && (winners.length < maxWinners || _newPoints > totalPoints[_getLowestWinner()])) {
_addNewWinner(_participant);
}
}
/**
* @dev Adjust position of existing winner based on new points
*/
function _adjustWinnerPosition(
address _participant,
uint256 _oldPoints,
uint256 _newPoints
) internal {
uint256 currentPosition = winnerPosition[_participant];
if (_newPoints > _oldPoints) {
_moveWinnerUp(_participant, currentPosition);
} else if (_newPoints < _oldPoints) {
_moveWinnerDown(_participant, currentPosition);
}
}
/**
* @dev Move winner up in rankings
*/
function _moveWinnerUp(address _participant, uint256 currentPosition) internal {
uint256 newPosition = currentPosition;
// Find correct position by comparing with winners above
for (uint256 i = currentPosition - 1; i > 0; i--) {
if (totalPoints[_participant] > totalPoints[winners[i - 1]]) {
newPosition = i;
} else {
break;
}
}
if (newPosition != currentPosition) {
_swapWinners(_participant, currentPosition, newPosition);
}
}
/**
* @dev Move winner down in rankings or remove if no longer in top
*/
function _moveWinnerDown(address _participant, uint256 currentPosition) internal {
uint256 newPosition = currentPosition;
// Find correct position by comparing with winners below
for (uint256 i = currentPosition; i < winners.length; i++) {
if (totalPoints[_participant] < totalPoints[winners[i]]) {
newPosition = i + 1;
} else {
break;
}
}
// If moved beyond max winners, remove from winners
if (newPosition > maxWinners) {
_removeWinner(_participant, currentPosition);
} else if (newPosition != currentPosition) {
_swapWinners(_participant, currentPosition, newPosition);
}
}
/**
* @dev Add new winner to the list
*/
function _addNewWinner(address _participant) internal {
if (winners.length < maxWinners) {
// Add to end if we have space
winners.push(_participant);
winnerPosition[_participant] = winners.length;
_moveWinnerUp(_participant, winners.length);
} else {
// Replace the lowest winner
address lowestWinner = _getLowestWinner();
uint256 lowestPosition = winnerPosition[lowestWinner];
_removeWinner(lowestWinner, lowestPosition);
_addNewWinner(_participant);
}
}
/**
* @dev Remove winner from the list
*/
function _removeWinner(address _participant, uint256 position) internal {
// Move last winner to this position
if (position < winners.length) {
address lastWinner = winners[winners.length - 1];
winners[position - 1] = lastWinner;
winnerPosition[lastWinner] = position;
winners.pop();
}
winnerPosition[_participant] = 0;
emit WinnerUpdated(_participant, position, 0);
}
/**
* @dev Swap two winners in the rankings
*/
function _swapWinners(address _participant, uint256 fromPosition, uint256 toPosition) internal {
if (fromPosition != toPosition) {
// Update positions
winnerPosition[_participant] = toPosition;
winnerPosition[winners[toPosition - 1]] = fromPosition;
// Swap in array
address temp = winners[fromPosition - 1];
winners[fromPosition - 1] = winners[toPosition - 1];
winners[toPosition - 1] = temp;
emit WinnerUpdated(_participant, fromPosition, toPosition);
}
}
/**
* @dev Get the lowest winner (last in winners array)
*/
function _getLowestWinner() internal view returns (address) {
if (winners.length == 0) return address(0);
return winners[winners.length - 1];
}
// ============ VIEW FUNCTIONS ============
/**
* @dev Get current winners in order
*/
function getWinners() external view override returns (address[] memory) {
return winners;
}
/**
* @dev Get participant's current total points
*/
function getParticipantPoints(address _participant) external view override returns (uint256) {
return totalPoints[_participant];
}
/**
* @dev Get participant's current ranking position
*/
function getParticipantRanking(address _participant) external view override returns (uint256) {
return winnerPosition[_participant];
}
/**
* @dev Check if a judge has voted
*/
function hasJudgeVoted(address _judge) external view override returns (bool) {
return hasVoted[_judge];
}
/**
* @dev Get voting statistics
*/
function getVotingStats() external view override returns (
uint256 _totalJudges,
uint256 _votedJudges,
uint256 _totalParticipants
) {
_totalJudges = totalJudges;
uint256 votedCount = 0;
uint256 participantsCount = 0;
for (uint256 i = 0; i < judges.length; i++) {
if (hasVoted[judges[i]]) {
votedCount++;
}
}
_votedJudges = votedCount;
// Count participants with points
for (uint256 i = 0; i < judges.length; i++) {
for (uint256 j = 0; j < judges.length; j++) {
if (judgeVotes[judges[i]][judges[j]] > 0) {
participantsCount++;
}
}
}
_totalParticipants = participantsCount;
}
/**
* @dev Get the number of current winners
*/
function getWinnerCount() external view override returns (uint256) {
return winners.length;
}
/**
* @dev Get winner at specific position
*/
function getWinnerAtPosition(uint256 _position) external view override returns (address) {
require(_position > 0 && _position <= winners.length, "Invalid position");
return winners[_position - 1];
}
/**
* @dev Check if participant is currently a winner
*/
function isWinner(address _participant) external view override returns (bool) {
return winnerPosition[_participant] > 0;
}
// ============ ADMIN FUNCTIONS ============
/**
* @dev Set voting deadline (called by hackathon contract)
*/
function setVotingDeadline(uint256 _deadline) external {
require(votingDeadline == 0, "Deadline already set");
votingDeadline = _deadline;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./IVotingSystem.sol";
/**
* @title QuadraticVoting
* @dev Library for quadratic voting calculations and validation
* @notice Implements quadratic voting where cost = votes²
*/
library QuadraticVoting {
// ============ ERRORS ============
error InvalidVoteAllocation();
error ExceedsCreditLimit();
error InvalidVoteValue();
// ============ FUNCTIONS ============
/**
* @dev Calculate the cost of a vote allocation
* @param _votes Array of votes for each participant
* @return Total cost in credits
*/
function calculateCost(uint256[] memory _votes) internal pure returns (uint256) {
uint256 totalCost = 0;
for (uint256 i = 0; i < _votes.length; i++) {
totalCost += _votes[i] * _votes[i];
}
return totalCost;
}
/**
* @dev Validate quadratic voting allocation
* @param _votes Array of votes for each participant
* @param _totalCredits Total credits available to judge
* @return True if allocation is valid
*/
function validateAllocation(
uint256[] memory _votes,
uint256 _totalCredits
) internal pure returns (bool) {
uint256 totalCost = calculateCost(_votes);
return totalCost <= _totalCredits;
}
/**
* @dev Calculate square root using Babylonian method
* @param _x Number to calculate square root of
* @return Square root of _x
*/
function sqrt(uint256 _x) internal pure returns (uint256) {
if (_x == 0) return 0;
uint256 z = (_x + 1) / 2;
uint256 y = _x;
while (z < y) {
y = z;
z = (_x / z + z) / 2;
}
return y;
}
/**
* @dev Convert credits to votes (sqrt function)
* @param _credits Number of credits
* @return Number of votes
*/
function creditsToVotes(uint256 _credits) internal pure returns (uint256) {
return sqrt(_credits);
}
/**
* @dev Convert votes to credits (squared function)
* @param _votes Number of votes
* @return Number of credits required
*/
function votesToCredits(uint256 _votes) internal pure returns (uint256) {
return _votes * _votes;
}
/**
* @dev Validate that all votes are non-negative
* @param _votes Array of votes
* @return True if all votes are valid
*/
function validateVotes(uint256[] memory _votes) internal pure returns (bool) {
for (uint256 i = 0; i < _votes.length; i++) {
if (_votes[i] < 0) {
return false;
}
}
return true;
}
/**
* @dev Calculate optimal vote distribution for given credits
* @param _participants Number of participants
* @param _totalCredits Total credits available
* @param _preferences Array of preference weights (0-100)
* @return Array of optimal votes for each participant
*/
function calculateOptimalDistribution(
uint256 _participants,
uint256 _totalCredits,
uint256[] memory _preferences
) internal pure returns (uint256[] memory) {
require(_participants == _preferences.length, "Preferences length mismatch");
require(_participants > 0, "Must have at least one participant");
uint256[] memory votes = new uint256[](_participants);
// Calculate total preference weight
uint256 totalWeight = 0;
for (uint256 i = 0; i < _participants; i++) {
totalWeight += _preferences[i];
}
if (totalWeight == 0) {
// If no preferences, distribute equally
uint256 equalVotes = sqrt(_totalCredits / _participants);
for (uint256 i = 0; i < _participants; i++) {
votes[i] = equalVotes;
}
} else {
// Distribute based on preferences
for (uint256 i = 0; i < _participants; i++) {
uint256 proportionalCredits = (_totalCredits * _preferences[i]) / totalWeight;
votes[i] = sqrt(proportionalCredits);
}
}
return votes;
}
}
/**
* @title QVWrapper
* @dev Wrapper contract that adds quadratic voting to any voting system
* @notice Converts quadratic votes to linear points for the base voting system
*/
contract QVWrapper is IVotingSystem {
// ============ STATE VARIABLES ============
IVotingSystem public baseVotingSystem;
uint256 public creditsPerJudge;
bool public initialized;
// ============ EVENTS ============
event QVVotesSubmitted(
address indexed judge,
address[] participants,
uint256[] votes,
uint256 totalCreditsUsed
);
// ============ MODIFIERS ============
modifier onlyInitialized() {
require(initialized, "Contract not initialized");
_;
}
// ============ INITIALIZATION ============
/**
* @dev Initialize the QVWrapper (for clone pattern)
* @param _baseVotingSystem Address of the base voting system
* @param _creditsPerJudge Number of credits per judge
*/
function initialize(address _baseVotingSystem, uint256 _creditsPerJudge) external {
require(!initialized, "Already initialized");
require(_baseVotingSystem != address(0), "Invalid base voting system");
require(_creditsPerJudge > 0, "Credits per judge must be greater than 0");
baseVotingSystem = IVotingSystem(_baseVotingSystem);
creditsPerJudge = _creditsPerJudge;
initialized = true;
}
// ============ CORE FUNCTIONS ============
/**
* @dev Submit quadratic votes for multiple participants
* @param _participants Array of participant addresses
* @param _votes Array of votes (not credits) for each participant
*/
function submitVotes(
address[] calldata _participants,
uint256[] calldata _votes
) external override onlyInitialized {
require(_participants.length == _votes.length, "Arrays length mismatch");
require(_participants.length > 0, "Must vote for at least one participant");
// Validate quadratic voting allocation
require(
QuadraticVoting.validateAllocation(_votes, creditsPerJudge),
"Invalid quadratic vote allocation"
);
// Calculate total credits used
uint256 totalCreditsUsed = QuadraticVoting.calculateCost(_votes);
// Convert votes to points for base system
// In QV, votes are the actual points (not credits)
uint256[] memory points = new uint256[](_votes.length);
for (uint256 i = 0; i < _votes.length; i++) {
points[i] = _votes[i];
}
// Submit to base voting system
baseVotingSystem.submitVotes(_participants, points);
emit QVVotesSubmitted(msg.sender, _participants, _votes, totalCreditsUsed);
}
// ============ DELEGATION TO BASE SYSTEM ============
function getWinners() external view override returns (address[] memory) {
return baseVotingSystem.getWinners();
}
function getParticipantPoints(address _participant) external view override returns (uint256) {
return baseVotingSystem.getParticipantPoints(_participant);
}
function getParticipantRanking(address _participant) external view override returns (uint256) {
return baseVotingSystem.getParticipantRanking(_participant);
}
function hasJudgeVoted(address _judge) external view override returns (bool) {
return baseVotingSystem.hasJudgeVoted(_judge);
}
function getVotingStats() external view override returns (
uint256 _totalJudges,
uint256 _votedJudges,
uint256 _totalParticipants
) {
return baseVotingSystem.getVotingStats();
}
function initialize(
uint256 _pointsPerJudge,
uint256 _maxWinners,
address[] calldata _judges
) external override {
// QV wrapper doesn't need initialization
// Base system should be initialized separately
}
function addJudge(address _judge) external override onlyInitialized {
// QV wrapper doesn't manage judges directly
// Judges are managed by the base voting system
// This function is here for interface compliance
}
function removeJudge(address _judge) external override onlyInitialized {
baseVotingSystem.removeJudge(_judge);
}
function getWinnerCount() external view override returns (uint256) {
return baseVotingSystem.getWinnerCount();
}
function getWinnerAtPosition(uint256 _position) external view override returns (address) {
return baseVotingSystem.getWinnerAtPosition(_position);
}
function isWinner(address _participant) external view override returns (bool) {
return baseVotingSystem.isWinner(_participant);
}
// ============ QV-SPECIFIC FUNCTIONS ============
/**
* @dev Calculate optimal vote distribution for a judge
* @param _participants Array of participant addresses
* @param _preferences Array of preference weights (0-100)
* @return Array of optimal votes for each participant
*/
function calculateOptimalVotes(
address[] calldata _participants,
uint256[] calldata _preferences
) external view returns (uint256[] memory) {
require(_participants.length == _preferences.length, "Arrays length mismatch");
return QuadraticVoting.calculateOptimalDistribution(
_participants.length,
creditsPerJudge,
_preferences
);
}
/**
* @dev Calculate cost of a vote allocation
* @param _votes Array of votes
* @return Total cost in credits
*/
function calculateVoteCost(uint256[] calldata _votes) external pure returns (uint256) {
return QuadraticVoting.calculateCost(_votes);
}
/**
* @dev Validate a vote allocation
* @param _votes Array of votes
* @return True if allocation is valid
*/
function validateVoteAllocation(uint256[] calldata _votes) external view returns (bool) {
return QuadraticVoting.validateAllocation(_votes, creditsPerJudge);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./IVotingSystem.sol";
import "./VotingTypes.sol";
/**
* @title RevealCommitVoting
* @dev Commit-reveal voting system with batch voting support
* @notice Judges commit votes first, then reveal them in a separate phase
*/
contract RevealCommitVoting is IVotingSystem {
// ============ STATE VARIABLES ============
// Voting configuration
uint256 public pointsPerJudge;
uint256 public maxWinners;
uint256 public votingDeadline;
// Judge management
mapping(address => bool) public isJudge;
mapping(address => bool) public hasCommitted;
mapping(address => bool) public hasRevealed;
address[] public judges;
uint256 public totalJudges;
// Commit phase
mapping(address => bytes32) public commitments;
// Reveal phase
mapping(address => address[]) public revealedParticipants;
mapping(address => uint256[]) public revealedPoints;
mapping(address => uint256) public revealedNonces;
// Participant scoring (only after reveal)
mapping(address => uint256) public totalPoints;
mapping(address => mapping(address => uint256)) public judgeVotes;
// Dynamic winner tracking
mapping(address => uint256) public winnerPosition;
address[] public winners;
// ============ EVENTS ============
event VotesCommitted(
address indexed judge,
bytes32 commitment
);
event VotesRevealed(
address indexed judge,
address[] participants,
uint256[] points
);
event JudgeAdded(address indexed judge);
event JudgeRemoved(address indexed judge);
// ============ MODIFIERS ============
modifier onlyJudge() {
require(isJudge[msg.sender], "Only judges can perform this action");
_;
}
modifier onlyDuringCommit() {
require(block.timestamp <= votingDeadline, "Voting deadline has passed");
_;
}
modifier onlyDuringReveal() {
require(block.timestamp > votingDeadline, "Still in commit phase");
_;
}
// ============ INITIALIZATION ============
/**
* @dev Initialize the voting system
*/
function initialize(
uint256 _pointsPerJudge,
uint256 _maxWinners,
address[] calldata _judges
) external override {
require(pointsPerJudge == 0, "Already initialized");
pointsPerJudge = _pointsPerJudge;
maxWinners = _maxWinners;
// Add judges
for (uint256 i = 0; i < _judges.length; i++) {
_addJudge(_judges[i]);
}
}
/**
* @dev Set voting deadline (called by Hackathon contract)
*/
function setVotingDeadline(uint256 _deadline) external {
require(votingDeadline == 0, "Deadline already set");
votingDeadline = _deadline;
}
// ============ COMMIT PHASE ============
/**
* @dev Commit votes for multiple participants
* @param _participants Array of participant addresses
* @param _points Array of points allocated to each participant
* @param _nonce Random nonce for commitment
*/
function commitBatchVotes(
address[] calldata _participants,
uint256[] calldata _points,
uint256 _nonce
) external onlyJudge onlyDuringCommit {
require(_participants.length == _points.length, "Arrays length mismatch");
require(_participants.length > 0, "Must vote for at least one participant");
require(!hasCommitted[msg.sender], "Judge has already committed");
// Validate points
uint256 totalPointsUsed = 0;
for (uint256 i = 0; i < _participants.length; i++) {
require(_points[i] > 0, "Points must be greater than 0");
totalPointsUsed += _points[i];
}
require(totalPointsUsed <= pointsPerJudge, "Exceeds points per judge limit");
// Create commitment
bytes32 commitment = keccak256(abi.encodePacked(
msg.sender,
_participants,
_points,
_nonce
));
commitments[msg.sender] = commitment;
hasCommitted[msg.sender] = true;
emit VotesCommitted(msg.sender, commitment);
}
// ============ REVEAL PHASE ============
/**
* @dev Reveal votes for multiple participants
* @param _participants Array of participant addresses
* @param _points Array of points allocated to each participant
* @param _nonce Nonce used in commitment
*/
function revealBatchVotes(
address[] calldata _participants,
uint256[] calldata _points,
uint256 _nonce
) external onlyJudge onlyDuringReveal {
require(hasCommitted[msg.sender], "Must commit votes first");
require(!hasRevealed[msg.sender], "Judge has already revealed");
require(_participants.length == _points.length, "Arrays length mismatch");
// Verify commitment
bytes32 expectedCommitment = keccak256(abi.encodePacked(
msg.sender,
_participants,
_points,
_nonce
));
require(commitments[msg.sender] == expectedCommitment, "Commitment mismatch");
// Process revealed votes
uint256 totalPointsUsed = 0;
for (uint256 i = 0; i < _participants.length; i++) {
require(_points[i] > 0, "Points must be greater than 0");
totalPointsUsed += _points[i];
// Update participant's total points
uint256 oldPoints = totalPoints[_participants[i]];
totalPoints[_participants[i]] += _points[i];
// Store judge's vote
judgeVotes[msg.sender][_participants[i]] = _points[i];
// Update winner list
_updateWinnerList(_participants[i], oldPoints, totalPoints[_participants[i]]);
}
require(totalPointsUsed <= pointsPerJudge, "Exceeds points per judge limit");
// Store revealed data
revealedParticipants[msg.sender] = _participants;
revealedPoints[msg.sender] = _points;
revealedNonces[msg.sender] = _nonce;
hasRevealed[msg.sender] = true;
emit VotesRevealed(msg.sender, _participants, _points);
}
/**
* @dev Reveal votes for multiple participants (interface requirement)
* @param _participants Array of participant addresses
* @param _points Array of points allocated to each participant
*/
function submitVotes(
address[] calldata _participants,
uint256[] calldata _points
) external override onlyJudge onlyDuringReveal {
// This function is used for reveal phase
require(hasCommitted[msg.sender], "Must commit votes first");
require(!hasRevealed[msg.sender], "Judge has already revealed");
require(_participants.length == _points.length, "Arrays length mismatch");
// Verify commitment
bytes32 expectedCommitment = keccak256(abi.encodePacked(
msg.sender,
_participants,
_points,
revealedNonces[msg.sender]
));
require(commitments[msg.sender] == expectedCommitment, "Commitment mismatch");
// Process revealed votes
uint256 totalPointsUsed = 0;
for (uint256 i = 0; i < _participants.length; i++) {
require(_points[i] > 0, "Points must be greater than 0");
totalPointsUsed += _points[i];
// Update participant's total points
uint256 oldPoints = totalPoints[_participants[i]];
totalPoints[_participants[i]] += _points[i];
// Store judge's vote
judgeVotes[msg.sender][_participants[i]] = _points[i];
// Update winner list
_updateWinnerList(_participants[i], oldPoints, totalPoints[_participants[i]]);
}
require(totalPointsUsed <= pointsPerJudge, "Exceeds points per judge limit");
// Store revealed data
revealedParticipants[msg.sender] = _participants;
revealedPoints[msg.sender] = _points;
hasRevealed[msg.sender] = true;
emit VotesRevealed(msg.sender, _participants, _points);
}
/**
* @dev Set nonce for reveal (must be called before submitVotes)
* @param _nonce Nonce used in original commitment
*/
function setRevealNonce(uint256 _nonce) external onlyJudge {
revealedNonces[msg.sender] = _nonce;
}
// ============ JUDGE MANAGEMENT ============
function addJudge(address _judge) external override {
require(_judge != address(0), "Invalid judge address");
require(!isJudge[_judge], "Judge already exists");
_addJudge(_judge);
}
function removeJudge(address _judge) external override {
require(isJudge[_judge], "Judge does not exist");
require(!hasCommitted[_judge], "Cannot remove judge who has committed");
_removeJudge(_judge);
}
function _addJudge(address _judge) internal {
isJudge[_judge] = true;
judges.push(_judge);
totalJudges++;
emit JudgeAdded(_judge);
}
function _removeJudge(address _judge) internal {
isJudge[_judge] = false;
for (uint256 i = 0; i < judges.length; i++) {
if (judges[i] == _judge) {
judges[i] = judges[judges.length - 1];
judges.pop();
break;
}
}
totalJudges--;
emit JudgeRemoved(_judge);
}
// ============ WINNER TRACKING (Same as OpenVoting) ============
function _updateWinnerList(
address _participant,
uint256 _oldPoints,
uint256 _newPoints
) internal {
uint256 currentPosition = winnerPosition[_participant];
if (currentPosition > 0) {
_adjustWinnerPosition(_participant, _oldPoints, _newPoints);
return;
}
if (_newPoints > 0 && (winners.length < maxWinners || _newPoints > totalPoints[_getLowestWinner()])) {
_addNewWinner(_participant);
}
}
function _adjustWinnerPosition(
address _participant,
uint256 _oldPoints,
uint256 _newPoints
) internal {
uint256 currentPosition = winnerPosition[_participant];
if (_newPoints > _oldPoints) {
_moveWinnerUp(_participant, currentPosition);
} else if (_newPoints < _oldPoints) {
_moveWinnerDown(_participant, currentPosition);
}
}
function _moveWinnerUp(address _participant, uint256 currentPosition) internal {
uint256 newPosition = currentPosition;
for (uint256 i = currentPosition - 1; i > 0; i--) {
if (totalPoints[_participant] > totalPoints[winners[i - 1]]) {
newPosition = i;
} else {
break;
}
}
if (newPosition != currentPosition) {
_swapWinners(_participant, currentPosition, newPosition);
}
}
function _moveWinnerDown(address _participant, uint256 currentPosition) internal {
uint256 newPosition = currentPosition;
for (uint256 i = currentPosition; i < winners.length; i++) {
if (totalPoints[_participant] < totalPoints[winners[i]]) {
newPosition = i + 1;
} else {
break;
}
}
if (newPosition > maxWinners) {
_removeWinner(_participant, currentPosition);
} else if (newPosition != currentPosition) {
_swapWinners(_participant, currentPosition, newPosition);
}
}
function _addNewWinner(address _participant) internal {
if (winners.length < maxWinners) {
winners.push(_participant);
winnerPosition[_participant] = winners.length;
_moveWinnerUp(_participant, winners.length);
} else {
address lowestWinner = _getLowestWinner();
uint256 lowestPosition = winnerPosition[lowestWinner];
_removeWinner(lowestWinner, lowestPosition);
_addNewWinner(_participant);
}
}
function _removeWinner(address _participant, uint256 position) internal {
if (position < winners.length) {
address lastWinner = winners[winners.length - 1];
winners[position - 1] = lastWinner;
winnerPosition[lastWinner] = position;
winners.pop();
}
winnerPosition[_participant] = 0;
emit WinnerUpdated(_participant, position, 0);
}
function _swapWinners(address _participant, uint256 fromPosition, uint256 toPosition) internal {
if (fromPosition != toPosition) {
winnerPosition[_participant] = toPosition;
winnerPosition[winners[toPosition - 1]] = fromPosition;
address temp = winners[fromPosition - 1];
winners[fromPosition - 1] = winners[toPosition - 1];
winners[toPosition - 1] = temp;
emit WinnerUpdated(_participant, fromPosition, toPosition);
}
}
function _getLowestWinner() internal view returns (address) {
if (winners.length == 0) return address(0);
return winners[winners.length - 1];
}
// ============ VIEW FUNCTIONS ============
function getWinners() external view override returns (address[] memory) {
return winners;
}
function getParticipantPoints(address _participant) external view override returns (uint256) {
return totalPoints[_participant];
}
function getParticipantRanking(address _participant) external view override returns (uint256) {
return winnerPosition[_participant];
}
function hasJudgeVoted(address _judge) external view override returns (bool) {
return hasRevealed[_judge];
}
function getVotingStats() external view override returns (
uint256 _totalJudges,
uint256 _votedJudges,
uint256 _totalParticipants
) {
_totalJudges = totalJudges;
uint256 votedCount = 0;
for (uint256 i = 0; i < judges.length; i++) {
if (hasRevealed[judges[i]]) {
votedCount++;
}
}
_votedJudges = votedCount;
_totalParticipants = 0; // Calculate based on revealed votes
}
function getWinnerCount() external view override returns (uint256) {
return winners.length;
}
function getWinnerAtPosition(uint256 _position) external view override returns (address) {
require(_position > 0 && _position <= winners.length, "Invalid position");
return winners[_position - 1];
}
function isWinner(address _participant) external view override returns (bool) {
return winnerPosition[_participant] > 0;
}
// ============ PHASE MANAGEMENT ============
// ============ COMMITMENT HELPERS ============
/**
* @dev Generate commitment hash for batch votes
* @param _judge Judge address
* @param _participants Array of participant addresses
* @param _points Array of points
* @param _nonce Random nonce
* @return Commitment hash
*/
function generateCommitment(
address _judge,
address[] calldata _participants,
uint256[] calldata _points,
uint256 _nonce
) external pure returns (bytes32) {
return keccak256(abi.encodePacked(_judge, _participants, _points, _nonce));
}
/**
* @dev Check if judge has committed votes
* @param _judge Judge address
* @return True if judge has committed
*/
function hasJudgeCommitted(address _judge) external view returns (bool) {
return hasCommitted[_judge];
}
/**
* @dev Get judge's commitment
* @param _judge Judge address
* @return Commitment hash
*/
function getJudgeCommitment(address _judge) external view returns (bytes32) {
return commitments[_judge];
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* @title StakeSystem
* @dev Handles stake management for hackathon participants
* @notice This contract manages the stake system where participants must deposit funds
*/
contract StakeSystem {
uint256 public stakeAmount;
uint256 public totalStakes;
mapping(address => uint256) public participantStakes;
event StakeDeposited(
address indexed participant,
uint256 amount
);
event StakeReturned(
address indexed participant,
uint256 amount
);
/**
* @notice Deposit stake for a participant
* @dev Internal function to handle stake deposits and emit events
* @param _participant Address of the participant depositing the stake
*/
function _depositStake(
address _participant
)
internal
{
require(
msg.value == stakeAmount,
"Must deposit exact stake amount"
);
participantStakes[_participant] = msg.value;
totalStakes += msg.value;
emit StakeDeposited(
_participant,
msg.value
);
}
/**
* @notice Return stake to a participant
* @dev Internal function to return stake to participant and emit events
* @param _participant Address of the participant to return stake to
*/
function _returnStake(
address _participant
)
internal
{
uint256 stake = participantStakes[
_participant
];
if (stake > 0) {
participantStakes[_participant] = 0;
totalStakes -= stake;
payable(_participant).transfer(
stake
);
emit StakeReturned(
_participant,
stake
);
}
}
/**
* @notice Get the stake amount for a specific participant
* @dev Returns the stake amount deposited by a participant
* @param _participant Address of the participant to check
* @return Stake amount deposited by the participant
*/
function getParticipantStake(
address _participant
)
external
view
returns (uint256)
{
return participantStakes[
_participant
];
}
/**
* @notice Get the total stakes collected from all participants
* @dev Returns the sum of all stakes deposited by participants
* @return Total amount of stakes collected
*/
function getTotalStakes()
external
view
returns (uint256)
{
return totalStakes;
}
/**
* @notice Get the required stake amount for hackathon participation
* @dev Returns the amount that participants must deposit when joining
* @return Required stake amount for participation
*/
function getStakeAmount()
external
view
returns (uint256)
{
return stakeAmount;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract VotingSystem {
using SafeERC20 for IERC20;
uint256[] public prizeDistribution;
uint256 public pointsPerJudge;
// PYUSD token for prize distribution
IERC20 public pyusdToken;
/**
* @dev Set the PYUSD token address
* @param _pyusdToken Address of the PYUSD token
*/
function _setPyusdToken(
address _pyusdToken
)
internal
{
pyusdToken = IERC20(
_pyusdToken
);
}
mapping(address => bool) public hasVoted;
mapping(address => uint256) public totalPoints;
mapping(address => mapping(address => uint256)) public judgeVotes;
uint256 public votingDeadline;
bool public votingOpen;
uint256 public votingEndTime;
uint256 public prizeClaimCooldown;
mapping(address => bool) public hasClaimedPrize;
// Dynamic winner tracking - updated on each vote
mapping(address => uint256) public winnerPosition; // 0 = not a winner, 1+ = position (1-indexed)
address[] public winners; // Array of winners in order (1st, 2nd, 3rd, etc.)
uint256 public maxWinners; // Maximum number of winners (from prize distribution length)
event JudgeVoted(
address indexed judge,
address indexed participant,
uint256 points
);
event VotingOpened(
uint256 deadline
);
event PrizeClaimed(
address indexed winner,
uint256 amount
);
event WinnerAdded(
address indexed participant,
uint256 position
);
event WinnerRemoved(
address indexed participant
);
/**
* @notice Vote for a participant's submission
* @dev Internal function for judges to allocate points to participants and update winner list
* @param _participant Address of the participant to vote for
* @param _points Number of points to allocate (max 100 per judge)
*/
function _voteForSubmission(
address _participant,
uint256 _points
)
internal
{
require(
votingOpen,
"Voting is not open"
);
require(
block.timestamp <= votingDeadline,
"Voting deadline has passed"
);
require(
_points <= pointsPerJudge,
"Cannot allocate more than 100 points"
);
require(
hasVoted[msg.sender] == false,
"Judge has already voted"
);
// Store old points for comparison
uint256 oldPoints = totalPoints[_participant];
// Add points to participant's total
totalPoints[_participant] += _points;
judgeVotes[msg.sender][_participant] = _points;
hasVoted[msg.sender] = true;
// Update winner list based on new points
_updateWinnerList(
_participant,
oldPoints,
totalPoints[_participant]
);
emit JudgeVoted(
msg.sender,
_participant,
_points
);
}
/**
* @notice Update winner list dynamically based on participant's new points
* @dev Maintains a sorted list of top performers without sorting all participants
* @param _participant Address of the participant whose points changed
* @param _oldPoints Previous total points
* @param _newPoints New total points
*/
function _updateWinnerList(
address _participant,
uint256 _oldPoints,
uint256 _newPoints
)
internal
{
uint256 currentPosition = winnerPosition[
_participant
];
// If participant was already a winner
if (currentPosition > 0) {
// Check if they should be moved up or down in rankings
_adjustWinnerPosition(
_participant,
_oldPoints,
_newPoints
);
return;
}
if (_newPoints > 0 && (winners.length < maxWinners || _newPoints > totalPoints[_getLowestWinner()])) {
_addNewWinner(
_participant
);
}
}
/**
* @notice Adjust position of existing winner based on new points
* @dev Moves winner up or down in the rankings as needed
*/
function _adjustWinnerPosition(
address _participant,
uint256 _oldPoints,
uint256 _newPoints
)
internal
{
uint256 currentPosition = winnerPosition[
_participant
];
// If points increased, try to move up
if (_newPoints > _oldPoints) {
_moveWinnerUp(
_participant,
currentPosition
);
return;
}
if (_newPoints < _oldPoints) {
_moveWinnerDown(
_participant,
currentPosition
);
}
}
/**
* @notice Move winner up in rankings
*/
function _moveWinnerUp(
address _participant,
uint256 currentPosition
)
internal
{
uint256 newPosition = currentPosition;
// Find the correct position by comparing with winners above
for (uint256 i = currentPosition - 1; i > 0; i--) {
if (totalPoints[_participant] > totalPoints[winners[i - 1]]) {
newPosition = i;
} else {
break;
}
}
if (newPosition != currentPosition) {
_swapWinners(
_participant,
currentPosition,
newPosition
);
}
}
/**
* @notice Move winner down in rankings or remove if no longer in top
*/
function _moveWinnerDown(address _participant, uint256 currentPosition) internal {
uint256 newPosition = currentPosition;
// Find the correct position by comparing with winners below
for (uint256 i = currentPosition; i < winners.length; i++) {
if (totalPoints[_participant] < totalPoints[winners[i]]) {
newPosition = i + 1;
} else {
break;
}
}
// If moved beyond max winners, remove from winners
if (newPosition > maxWinners) {
_removeWinner(_participant, currentPosition);
} else if (newPosition != currentPosition) {
_swapWinners(_participant, currentPosition, newPosition);
}
}
/**
* @notice Add new winner to the list
*/
function _addNewWinner(address _participant) internal {
if (winners.length < maxWinners) {
// Add to end if we have space
winners.push(_participant);
winnerPosition[_participant] = winners.length;
_moveWinnerUp(_participant, winners.length);
} else {
// Replace the lowest winner
address lowestWinner = _getLowestWinner();
uint256 lowestPosition = winnerPosition[lowestWinner];
_removeWinner(lowestWinner, lowestPosition);
_addNewWinner(_participant);
}
}
/**
* @notice Remove winner from the list
*/
function _removeWinner(address _participant, uint256 position) internal {
// Move last winner to this position
if (winners.length > 1) {
address lastWinner = winners[winners.length - 1];
winners[position - 1] = lastWinner;
winnerPosition[lastWinner] = position;
}
// Remove from array
winners.pop();
winnerPosition[_participant] = 0;
emit WinnerRemoved(
_participant
);
}
/**
* @notice Swap two winners in the list
*/
function _swapWinners(address _participant, uint256 fromPosition, uint256 toPosition) internal {
address otherParticipant = winners[toPosition - 1];
// Swap in array
winners[fromPosition - 1] = otherParticipant;
winners[toPosition - 1] = _participant;
// Update positions
winnerPosition[_participant] = toPosition;
winnerPosition[otherParticipant] = fromPosition;
emit WinnerAdded(_participant, toPosition);
}
/**
* @notice Get the winner with the lowest points
*/
function _getLowestWinner()
internal
view
returns (address)
{
require(
winners.length > 0,
"No winners"
);
return winners[
winners.length - 1
];
}
/**
* @notice Check if voting has ended (either manually closed or deadline passed)
* @dev Automatically determines if voting should be considered ended
* @return True if voting has ended, false otherwise
*/
function _isVotingEnded()
internal
view
returns (bool)
{
// If manually closed, voting has ended
if (!votingOpen) {
return true;
}
// If deadline has passed, voting has ended
if (block.timestamp > votingDeadline) {
return true;
}
return false;
}
/**
* @notice Get the actual voting end time
* @dev Returns the time when voting actually ended (manual close or deadline)
* @return Timestamp when voting ended
*/
function _getVotingEndTime() internal view returns (uint256) {
// If manually closed, return the recorded end time
if (!votingOpen && votingEndTime > 0) {
return votingEndTime;
}
// If deadline passed but not manually closed, return deadline
if (block.timestamp > votingDeadline) {
return votingDeadline;
}
// Voting is still active
return 0;
}
/**
* @notice Check if a participant is a winner
* @dev Uses pre-determined winners for O(1) lookup
* @param _participant Address of the participant to check
* @return True if the participant is a winner, false otherwise
*/
function _isWinner(
address _participant
)
internal
view
returns (bool)
{
return winnerPosition[_participant] > 0;
}
/**
* @notice Get prize amount for a participant based on their ranking
* @dev Determines the participant's position and returns the exact prize amount from the distribution array
* @param _participant Address of the participant
* @return Prize amount for the participant
*/
function _getPrizeAmount(
address _participant
)
internal
view
returns (uint256)
{
uint256 position = winnerPosition[
_participant
];
if (position == 0) {
return 0; // Not a winner
}
// Convert to 0-indexed for array access
uint256 arrayIndex = position - 1;
// Check if position is within prize distribution range
if (arrayIndex >= prizeDistribution.length) {
return 0;
}
// Return exact prize amount from the distribution array
return prizeDistribution[
arrayIndex
];
}
/**
* @notice Claim prize for a winner
* @dev Internal function to handle prize claiming with cooldown and winner validation
* @param _participant Address of the participant claiming the prize
*/
function _claimPrize(
address _participant,
uint256 /* _totalPrizePool */
)
internal
{
require(
hasClaimedPrize[_participant] == false,
"Prize already claimed"
);
require(
_isWinner(_participant),
"Not a winner"
);
uint256 prizeAmount = _getPrizeAmount(
_participant
);
hasClaimedPrize[_participant] = true;
pyusdToken.safeTransfer(
_participant,
prizeAmount
);
emit PrizeClaimed(
_participant,
prizeAmount
);
}
// Getter functions
/**
* @notice Get the maximum number of winners based on prize distribution
* @return Maximum number of winners that can be selected
*/
function getMaxWinners()
external
view
returns (uint256)
{
return maxWinners;
}
/**
* @notice Get the prize distribution array
* @return Array defining how the prize pool is distributed among winners
*/
function getPrizeDistribution()
external
view
returns (uint256[] memory)
{
return prizeDistribution;
}
/**
* @notice Get the points each judge can distribute
* @return Maximum points each judge can allocate
*/
function getPointsPerJudge()
external
view
returns (uint256)
{
return pointsPerJudge;
}
/**
* @notice Get total points received by a participant
* @param _participant Address of the participant
* @return Total points received by the participant
*/
function getTotalPoints(
address _participant
)
external
view
returns (uint256)
{
return totalPoints[
_participant
];
}
/**
* @notice Get all winners in order
* @return Array of winner addresses in ranking order
*/
function getWinners()
external
view
returns (address[] memory)
{
return winners;
}
/**
* @notice Get winner position for a participant
* @param _participant Address of the participant
* @return Position (1-indexed, 0 if not a winner)
*/
function getWinnerPosition(
address _participant
)
external
view
returns (uint256)
{
return winnerPosition[
_participant
];
}
/**
* @notice Get the voting deadline timestamp
* @return Timestamp when voting period ends
*/
function getVotingDeadline()
external
view
returns (uint256)
{
return votingDeadline;
}
/**
* @notice Get the voting end time timestamp
* @return Timestamp when voting actually ended (manual close or deadline)
*/
function getVotingEndTime()
external
view
returns (uint256)
{
return _getVotingEndTime();
}
/**
* @notice Check if voting is currently open
* @return True if voting is open and deadline not passed, false otherwise
*/
function isVotingOpen()
external
view
returns (bool)
{
return votingOpen && block.timestamp <= votingDeadline;
}
/**
* @notice Get the prize claim cooldown period
* @return Cooldown period in seconds
*/
function getPrizeClaimCooldown()
external
view
returns (uint256)
{
return prizeClaimCooldown;
}
/**
* @notice Check if a participant has claimed their prize
* @param _participant Address of the participant
* @return True if prize has been claimed, false otherwise
*/
function hasParticipantClaimedPrize(
address _participant
)
external
view
returns (bool)
{
return hasClaimedPrize[_participant];
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* @title VotingTypes
* @dev Common types and enums for all voting systems
*/
// ============ ENUMS ============
/**
* @dev Available voting system types
*/
enum VotingSystemType {
OPEN, // Transparent voting (current system)
COMMIT_REVEAL, // Hidden until reveal phase
ZK_SNARK, // Zero-knowledge proofs
QUADRATIC // Quadratic voting
}
// ============ STRUCTS ============
/**
* @dev Voting configuration for hackathon creation
*/
struct VotingConfig {
VotingSystemType systemType;
bool useQuadraticVoting;
uint256 votingPowerPerJudge; // Credits for quadratic voting, points for normal voting
uint256 maxWinners; // Number of winners to track
}
/**
* @dev Judge voting data for batch submissions
*/
struct JudgeVote {
address[] participants;
uint256[] points;
uint256 nonce; // For commit-reveal
bytes32 commitment; // For commit-reveal
bytes zkProof; // For ZK-SNARK
uint256[] quadraticCredits; // For quadratic voting
}
/**
* @dev Winner information
*/
struct WinnerInfo {
address participant;
uint256 totalPoints;
uint256 position; // 1-indexed position
bool isWinner; // True if in top N
}
/**
* @dev Voting statistics
*/
struct VotingStats {
uint256 totalJudges;
uint256 votedJudges;
uint256 totalParticipants;
uint256 totalPointsAllocated;
bool votingOpen;
uint256 votingDeadline;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title ZKProofVerifier
* @dev Verifies Groth16 Zero-Knowledge proofs for the DeHack voting system
* @notice Fast and efficient Groth16 proof verification for hackathon voting
*/
contract ZKProofVerifier is Ownable {
// ============ STRUCTS ============
struct G1Point {
uint256 x;
uint256 y;
}
struct G2Point {
uint256[2] x;
uint256[2] y;
}
struct Proof {
G1Point a;
G2Point b;
G1Point c;
}
// ============ STATE VARIABLES ============
// Groth16 verification key components
uint256[2] public vk_alpha1_x;
uint256[2] public vk_alpha1_y;
uint256[2] public vk_beta2_x;
uint256[2] public vk_beta2_y;
uint256 public vk_gamma2_x;
uint256 public vk_gamma2_y;
uint256[2] public vk_delta2_x;
uint256[2] public vk_delta2_y;
uint256[] public vk_ic_x;
uint256[] public vk_ic_y;
// Circuit parameters
uint256 public constant MAX_POINTS = 100;
uint256 public constant MIN_POINTS = 0;
// ============ EVENTS ============
event ProofVerified(address indexed judge, address indexed participant, uint256 points);
event VerificationKeyUpdated();
// ============ CONSTRUCTOR ============
constructor() Ownable(msg.sender) {
// Initialize with default verification key
// In production, this would be set during deployment
_initializeDefaultVerificationKey();
}
// ============ VERIFICATION FUNCTIONS ============
/**
* @dev Verify a Groth16 proof for vote validity
* @param _proof The Groth16 proof to verify
* @param _publicSignals Public signals from the proof
* @param _judge Judge address
* @param _participant Participant address
* @param _points Points allocated
* @return True if proof is valid
*/
function verifyGroth16Proof(
Proof memory _proof,
uint256[] memory _publicSignals,
address _judge,
address _participant,
uint256 _points
) external returns (bool) {
// Verify proof using Groth16 verification
bool proofValid = _verifyGroth16Proof(_proof, _publicSignals);
if (!proofValid) {
return false;
}
// Verify public signals match expected values
require(_publicSignals.length >= 3, "Invalid public signals length");
// Public signals should contain:
// [0] = commitment hash
// [1] = points
// [2] = judge address hash
// Verify points match
require(_publicSignals[1] == _points, "Points mismatch");
// Verify points are within valid range
require(_points >= MIN_POINTS && _points <= MAX_POINTS, "Invalid points range");
// Verify judge address hash
uint256 judgeHash = uint256(keccak256(abi.encodePacked(_judge)));
require(_publicSignals[2] == judgeHash, "Judge hash mismatch");
emit ProofVerified(_judge, _participant, _points);
return true;
}
/**
* @dev Verify a simplified proof (for testing/development)
* @param _proofHash Hash of the proof
* @param _judge Judge address
* @param _participant Participant address
* @param _points Points allocated
* @return True if proof is valid
*/
function verifySimplifiedProof(
bytes32 _proofHash,
address _judge,
address _participant,
uint256 _points
) external view returns (bool) {
// Verify points are within valid range
if (_points < MIN_POINTS || _points > MAX_POINTS) {
return false;
}
// For testing purposes, accept any non-zero proof hash
// In production, this would verify the actual ZK proof
return _proofHash != bytes32(0);
}
// ============ INTERNAL FUNCTIONS ============
/**
* @dev Verify Groth16 proof
* @param _proof The proof to verify
* @param _publicSignals Public signals
* @return True if proof is valid
*/
function _verifyGroth16Proof(
Proof memory _proof,
uint256[] memory _publicSignals
) internal pure returns (bool) {
// This is a simplified implementation
// In production, you would use a proper Groth16 verifier
// Verify proof components are non-zero
if (_proof.a.x == 0 || _proof.a.y == 0) {
return false;
}
if (_proof.b.x[0] == 0 || _proof.b.x[1] == 0) {
return false;
}
if (_proof.b.y[0] == 0 || _proof.b.y[1] == 0) {
return false;
}
if (_proof.c.x == 0 || _proof.c.y == 0) {
return false;
}
// Verify public signals are valid
if (_publicSignals.length == 0) {
return false;
}
// In a real implementation, this would perform the actual
// Groth16 verification using the verification key
// For now, we'll do a simplified check
// Verify that the proof components form a valid proof
// This is a placeholder - real verification would use elliptic curve operations
return true;
}
/**
* @dev Initialize default verification key
*/
function _initializeDefaultVerificationKey() internal {
// This would be set with the actual verification key from the trusted setup
// For now, we'll use placeholder values
vk_alpha1_x = [uint256(0), uint256(0)];
vk_alpha1_y = [uint256(0), uint256(0)];
vk_beta2_x = [uint256(0), uint256(0)];
vk_beta2_y = [uint256(0), uint256(0)];
vk_gamma2_x = 0;
vk_gamma2_y = 0;
vk_delta2_x = [uint256(0), uint256(0)];
vk_delta2_y = [uint256(0), uint256(0)];
// Initialize IC (Input Coefficients) array
vk_ic_x = new uint256[](4); // 4 public inputs
vk_ic_y = new uint256[](4);
for (uint256 i = 0; i < 4; i++) {
vk_ic_x[i] = 0;
vk_ic_y[i] = 0;
}
}
// ============ ADMIN FUNCTIONS ============
/**
* @dev Update verification key (only owner)
* @param _vk_alpha1_x New alpha1 x value
* @param _vk_alpha1_y New alpha1 y value
* @param _vk_beta2_x New beta2 x value
* @param _vk_beta2_y New beta2 y value
* @param _vk_gamma2_x New gamma2 x value
* @param _vk_gamma2_y New gamma2 y value
* @param _vk_delta2_x New delta2 x value
* @param _vk_delta2_y New delta2 y value
* @param _vk_ic_x New IC x values
* @param _vk_ic_y New IC y values
*/
function updateVerificationKey(
uint256[2] memory _vk_alpha1_x,
uint256[2] memory _vk_alpha1_y,
uint256[2] memory _vk_beta2_x,
uint256[2] memory _vk_beta2_y,
uint256 _vk_gamma2_x,
uint256 _vk_gamma2_y,
uint256[2] memory _vk_delta2_x,
uint256[2] memory _vk_delta2_y,
uint256[] memory _vk_ic_x,
uint256[] memory _vk_ic_y
) external onlyOwner {
vk_alpha1_x = _vk_alpha1_x;
vk_alpha1_y = _vk_alpha1_y;
vk_beta2_x = _vk_beta2_x;
vk_beta2_y = _vk_beta2_y;
vk_gamma2_x = _vk_gamma2_x;
vk_gamma2_y = _vk_gamma2_y;
vk_delta2_x = _vk_delta2_x;
vk_delta2_y = _vk_delta2_y;
vk_ic_x = _vk_ic_x;
vk_ic_y = _vk_ic_y;
emit VerificationKeyUpdated();
}
// ============ VIEW FUNCTIONS ============
/**
* @dev Get verification key
* @return alpha1_x, alpha1_y, beta2_x, beta2_y, gamma2_x, gamma2_y, delta2_x, delta2_y, ic_x, ic_y
*/
function getVerificationKey() external view returns (
uint256[2] memory,
uint256[2] memory,
uint256[2] memory,
uint256[2] memory,
uint256,
uint256,
uint256[2] memory,
uint256[2] memory,
uint256[] memory,
uint256[] memory
) {
return (vk_alpha1_x, vk_alpha1_y, vk_beta2_x, vk_beta2_y, vk_gamma2_x, vk_gamma2_y, vk_delta2_x, vk_delta2_y, vk_ic_x, vk_ic_y);
}
/**
* @dev Check if proof is valid format
* @param _proof The proof to check
* @return True if proof format is valid
*/
function isValidProofFormat(Proof memory _proof) external pure returns (bool) {
// Check that all proof components are non-zero
if (_proof.a.x == 0 || _proof.a.y == 0) {
return false;
}
if (_proof.b.x[0] == 0 || _proof.b.x[1] == 0) {
return false;
}
if (_proof.b.y[0] == 0 || _proof.b.y[1] == 0) {
return false;
}
if (_proof.c.x == 0 || _proof.c.y == 0) {
return false;
}
return true;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./IVotingSystem.sol";
import "./VotingTypes.sol";
import "./ZKProofVerifier.sol";
/**
* @title ZKVotingSystem
* @dev ZK voting system with batch voting support
* @notice Judges can vote for multiple participants in one transaction with ZK proofs
*/
contract ZKVotingSystem is IVotingSystem {
// ============ STATE VARIABLES ============
// Voting configuration
uint256 public pointsPerJudge;
uint256 public maxWinners;
uint256 public votingDeadline;
// Judge management
mapping(address => bool) public isJudge;
mapping(address => bool) public hasVoted;
address[] public judges;
uint256 public totalJudges;
// ZK proof verifier
ZKProofVerifier public proofVerifier;
// Batch vote commitments
mapping(address => bytes32) public batchCommitments;
mapping(address => bool) public hasCommitted;
// Batch vote reveals
mapping(address => address[]) public revealedParticipants;
mapping(address => uint256[]) public revealedPoints;
mapping(address => uint256) public revealedNonces;
mapping(address => bool) public hasRevealed;
// Participant scoring (only after reveal)
mapping(address => uint256) public totalPoints;
mapping(address => mapping(address => uint256)) public judgeVotes;
// Dynamic winner tracking
mapping(address => uint256) public winnerPosition;
address[] public winners;
// ============ EVENTS ============
event BatchVotesCommitted(
address indexed judge,
bytes32 commitment
);
event BatchVotesRevealed(
address indexed judge,
address[] participants,
uint256[] points,
bytes zkProof
);
event JudgeAdded(address indexed judge);
event JudgeRemoved(address indexed judge);
// ============ MODIFIERS ============
modifier onlyJudge() {
require(isJudge[msg.sender], "Only judges can perform this action");
_;
}
modifier onlyDuringVoting() {
require(block.timestamp <= votingDeadline, "Voting deadline has passed");
_;
}
// ============ INITIALIZATION ============
/**
* @dev Initialize the voting system
*/
function initialize(
uint256 _pointsPerJudge,
uint256 _maxWinners,
address[] calldata _judges
)
external
override
{
require(pointsPerJudge == 0, "Already initialized");
pointsPerJudge = _pointsPerJudge;
maxWinners = _maxWinners;
// Deploy ZK proof verifier
proofVerifier = new ZKProofVerifier();
// Add judges
for (uint256 i = 0; i < _judges.length; i++) {
_addJudge(_judges[i]);
}
}
/**
* @dev Set voting deadline (called by Hackathon contract)
*/
function setVotingDeadline(
uint256 _deadline
)
external
{
require(votingDeadline == 0, "Deadline already set");
votingDeadline = _deadline;
}
// ============ CORE VOTING FUNCTIONS ============
/**
* @dev Submit votes for multiple participants (interface requirement)
*/
function submitVotes(
address[] calldata /* _participants */,
uint256[] calldata /* _points */
)
external
pure
override
{
// This function signature is required by interface but ZK uses commit-reveal
revert("Use commitBatchVotes and revealBatchVotes for ZK voting");
}
/**
* @dev Commit batch votes for multiple participants
* @param _participants Array of participant addresses
* @param _points Array of points for each participant
* @param _nonce Random nonce for commitment
*/
function commitBatchVotes(
address[] calldata _participants,
uint256[] calldata _points,
uint256 _nonce
) external onlyJudge onlyDuringVoting {
require(_participants.length == _points.length, "Arrays length mismatch");
require(_participants.length > 0, "Must vote for at least one participant");
require(!hasCommitted[msg.sender], "Judge has already committed");
// Validate points
uint256 totalPointsUsed = 0;
for (uint256 i = 0; i < _participants.length; i++) {
require(_points[i] > 0, "Points must be greater than 0");
totalPointsUsed += _points[i];
}
require(totalPointsUsed <= pointsPerJudge, "Exceeds points per judge limit");
// Create batch commitment
bytes32 commitment = keccak256(abi.encodePacked(
msg.sender,
_participants,
_points,
_nonce
));
batchCommitments[msg.sender] = commitment;
hasCommitted[msg.sender] = true;
emit BatchVotesCommitted(
msg.sender,
commitment
);
}
/**
* @dev Reveal batch votes with ZK proof
* @param _participants Array of participant addresses
* @param _points Array of points for each participant
* @param _nonce Nonce used in commitment
* @param _zkProof ZK proof for batch vote validity
*/
function revealBatchVotes(
address[] calldata _participants,
uint256[] calldata _points,
uint256 _nonce,
bytes calldata _zkProof
)
external
onlyJudge
{
require(hasCommitted[msg.sender], "Must commit votes first");
require(!hasRevealed[msg.sender], "Judge has already revealed");
require(_participants.length == _points.length, "Arrays length mismatch");
// Verify commitment
bytes32 expectedCommitment = keccak256(abi.encodePacked(
msg.sender,
_participants,
_points,
_nonce
));
require(batchCommitments[msg.sender] == expectedCommitment, "Commitment mismatch");
// Verify ZK proof for batch vote validity
require(
_verifyBatchVoteProof(
_zkProof,
_participants,
_points
),
"Invalid ZK proof for batch vote"
);
// Process revealed votes
uint256 totalPointsUsed = 0;
for (uint256 i = 0; i < _participants.length; i++) {
require(_points[i] > 0, "Points must be greater than 0");
totalPointsUsed += _points[i];
// Update participant's total points
uint256 oldPoints = totalPoints[_participants[i]];
totalPoints[_participants[i]] += _points[i];
// Store judge's vote
judgeVotes[msg.sender][_participants[i]] = _points[i];
// Update winner list
_updateWinnerList(_participants[i], oldPoints, totalPoints[_participants[i]]);
}
require(totalPointsUsed <= pointsPerJudge, "Exceeds points per judge limit");
// Store revealed data
revealedParticipants[msg.sender] = _participants;
revealedPoints[msg.sender] = _points;
revealedNonces[msg.sender] = _nonce;
hasRevealed[msg.sender] = true;
emit BatchVotesRevealed(
msg.sender,
_participants,
_points,
_zkProof
);
}
// ============ ZK PROOF VERIFICATION ============
/**
* @dev Verify ZK proof for batch vote validity
* @param _zkProof ZK proof bytes
* @param _points Array of points for each participant
* @return True if proof is valid
*/
function _verifyBatchVoteProof(
bytes calldata _zkProof,
address[] calldata /* _participants */,
uint256[] calldata _points
)
internal
view
returns (bool)
{
// Verify that sum of points doesn't exceed limit
uint256 totalPointsSum = 0;
for (uint256 i = 0; i < _points.length; i++) {
totalPointsSum += _points[i];
}
if (totalPointsSum > pointsPerJudge) {
return false;
}
// Verify all points are positive
for (uint256 i = 0; i < _points.length; i++) {
if (_points[i] == 0) {
return false;
}
}
// Verify ZK proof using the proof verifier
// In a real implementation, this would verify the actual ZK proof
// For now, we'll use a simplified verification
return _verifySimplifiedBatchProof(
_zkProof,
_points
);
}
/**
* @dev Simplified batch proof verification (for demonstration)
*/
function _verifySimplifiedBatchProof(
bytes calldata _zkProof,
uint256[] calldata /* _points */
)
internal
pure
returns (bool)
{
// @TODO: Implement actual ZK proof verification
// Simplified verification - in production, this would verify actual ZK proof
// For now, just check that proof is not empty
return _zkProof.length > 0;
}
// ============ JUDGE MANAGEMENT ============
function addJudge(
address _judge
)
external
override
{
require(_judge != address(0), "Invalid judge address");
require(!isJudge[_judge], "Judge already exists");
_addJudge(_judge);
}
function removeJudge(
address _judge
)
external
override
{
require(isJudge[_judge], "Judge does not exist");
require(!hasCommitted[_judge], "Cannot remove judge who has committed");
_removeJudge(_judge);
}
function _addJudge(
address _judge
)
internal
{
isJudge[_judge] = true;
judges.push(_judge);
totalJudges++;
emit JudgeAdded(_judge);
}
function _removeJudge(
address _judge
)
internal
{
isJudge[_judge] = false;
for (uint256 i = 0; i < judges.length; i++) {
if (judges[i] == _judge) {
judges[i] = judges[judges.length - 1];
judges.pop();
break;
}
}
totalJudges--;
emit JudgeRemoved(_judge);
}
// ============ WINNER TRACKING (Same as OpenVoting) ============
function _updateWinnerList(
address _participant,
uint256 _oldPoints,
uint256 _newPoints
) internal {
uint256 currentPosition = winnerPosition[_participant];
if (currentPosition > 0) {
_adjustWinnerPosition(_participant, _oldPoints, _newPoints);
return;
}
if (_newPoints > 0 && (winners.length < maxWinners || _newPoints > totalPoints[_getLowestWinner()])) {
_addNewWinner(_participant);
}
}
function _adjustWinnerPosition(
address _participant,
uint256 _oldPoints,
uint256 _newPoints
) internal {
uint256 currentPosition = winnerPosition[_participant];
if (_newPoints > _oldPoints) {
_moveWinnerUp(_participant, currentPosition);
} else if (_newPoints < _oldPoints) {
_moveWinnerDown(_participant, currentPosition);
}
}
function _moveWinnerUp(address _participant, uint256 currentPosition) internal {
uint256 newPosition = currentPosition;
for (uint256 i = currentPosition - 1; i > 0; i--) {
if (totalPoints[_participant] > totalPoints[winners[i - 1]]) {
newPosition = i;
} else {
break;
}
}
if (newPosition != currentPosition) {
_swapWinners(_participant, currentPosition, newPosition);
}
}
function _moveWinnerDown(address _participant, uint256 currentPosition) internal {
uint256 newPosition = currentPosition;
for (uint256 i = currentPosition; i < winners.length; i++) {
if (totalPoints[_participant] < totalPoints[winners[i]]) {
newPosition = i + 1;
} else {
break;
}
}
if (newPosition > maxWinners) {
_removeWinner(_participant, currentPosition);
} else if (newPosition != currentPosition) {
_swapWinners(_participant, currentPosition, newPosition);
}
}
function _addNewWinner(address _participant) internal {
if (winners.length < maxWinners) {
winners.push(_participant);
winnerPosition[_participant] = winners.length;
_moveWinnerUp(_participant, winners.length);
} else {
address lowestWinner = _getLowestWinner();
uint256 lowestPosition = winnerPosition[lowestWinner];
_removeWinner(lowestWinner, lowestPosition);
_addNewWinner(_participant);
}
}
function _removeWinner(
address _participant,
uint256 position
)
internal
{
if (position < winners.length) {
address lastWinner = winners[winners.length - 1];
winners[position - 1] = lastWinner;
winnerPosition[lastWinner] = position;
winners.pop();
}
winnerPosition[_participant] = 0;
}
function _swapWinners(
address _participant,
uint256 fromPosition,
uint256 toPosition
)
internal
{
if (fromPosition != toPosition) {
winnerPosition[_participant] = toPosition;
winnerPosition[winners[toPosition - 1]] = fromPosition;
address temp = winners[fromPosition - 1];
winners[fromPosition - 1] = winners[toPosition - 1];
winners[toPosition - 1] = temp;
}
}
function _getLowestWinner()
internal
view
returns (address)
{
if (winners.length == 0) return address(0);
return winners[winners.length - 1];
}
// ============ VIEW FUNCTIONS ============
function getWinners()
external
view
override
returns (address[] memory)
{
return winners;
}
function getParticipantPoints(
address _participant
)
external
view
override
returns (uint256)
{
return totalPoints[_participant];
}
function getParticipantRanking(
address _participant
)
external
view
override
returns (uint256)
{
return winnerPosition[_participant];
}
function hasJudgeVoted(
address _judge
)
external
view
override
returns (bool)
{
return hasRevealed[_judge];
}
function getVotingStats()
external
view
override
returns (
uint256 _totalJudges,
uint256 _votedJudges,
uint256 _totalParticipants
)
{
_totalJudges = totalJudges;
uint256 votedCount = 0;
for (uint256 i = 0; i < judges.length; i++) {
if (hasRevealed[judges[i]]) {
votedCount++;
}
}
_votedJudges = votedCount;
_totalParticipants = winners.length;
}
function getWinnerCount()
external
view
override
returns (uint256)
{
return winners.length;
}
function getWinnerAtPosition(
uint256 _position
)
external
view
override
returns (address)
{
require(_position > 0 && _position <= winners.length, "Invalid position");
return winners[_position - 1];
}
function isWinner(
address _participant
)
external
view
override
returns (bool)
{
return winnerPosition[_participant] > 0;
}
// ============ ZK-SPECIFIC FUNCTIONS ============
/**
* @dev Generate commitment hash for batch votes
* @param _judge Judge address
* @param _participants Array of participant addresses
* @param _points Array of points
* @param _nonce Random nonce
* @return Commitment hash
*/
function generateBatchCommitment(
address _judge,
address[] calldata _participants,
uint256[] calldata _points,
uint256 _nonce
)
external
pure
returns (bytes32)
{
return keccak256(
abi.encodePacked(
_judge,
_participants,
_points,
_nonce
)
);
}
/**
* @dev Get judge's revealed votes
* @param _judge Judge address
* @return participants Array of participant addresses
* @return points Array of points
*/
function getJudgeRevealedVotes(
address _judge
)
external
view
returns (
address[] memory participants,
uint256[] memory points
) {
require(
hasRevealed[_judge],
"Judge has not revealed votes"
);
return (
revealedParticipants[_judge],
revealedPoints[_judge]
);
}
/**
* @dev Check if judge has committed votes
* @param _judge Judge address
* @return True if judge has committed
*/
function hasJudgeCommitted(
address _judge
)
external
view
returns (bool)
{
return hasCommitted[_judge];
}
/**
* @dev Get judge's commitment
* @param _judge Judge address
* @return Commitment hash
*/
function getJudgeCommitment(
address _judge
)
external
view
returns (bytes32)
{
return batchCommitments[_judge];
}
}{
"evmVersion": "cancun",
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": [
"project/:@openzeppelin/contracts/=npm/@openzeppelin/[email protected]/",
"project/:@openzeppelin/contracts/=npm/@openzeppelin/[email protected]/",
"project/:@openzeppelin/contracts/=npm/@openzeppelin/[email protected]/",
"project/:@openzeppelin/contracts/=npm/@openzeppelin/[email protected]/",
"project/:@openzeppelin/contracts/=npm/@openzeppelin/[email protected]/",
"project/:@openzeppelin/contracts/=npm/@openzeppelin/[email protected]/",
"project/:@openzeppelin/contracts/=npm/@openzeppelin/[email protected]/"
]
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_implementation","type":"address"},{"internalType":"address","name":"_curveRouter","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_pyusd","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FailedDeployment","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"judge","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pyusdAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minPyusdOut","type":"uint256"}],"name":"EthToPyusdConversion","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"judge","type":"address"}],"name":"GlobalJudgeAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"judge","type":"address"}],"name":"GlobalJudgeRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"hackathonAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"hackathonId","type":"uint256"},{"indexed":true,"internalType":"address","name":"organizer","type":"address"},{"indexed":false,"internalType":"uint256","name":"prizePool","type":"uint256"},{"indexed":false,"internalType":"enum VotingSystemType","name":"votingSystem","type":"uint8"},{"indexed":false,"internalType":"bool","name":"useQuadraticVoting","type":"bool"}],"name":"HackathonCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"judge","type":"address"},{"indexed":true,"internalType":"address","name":"delegate","type":"address"}],"name":"JudgeDelegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"judge","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"JudgeRewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"judge","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"JudgeRewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"proposalId","type":"bytes32"},{"indexed":false,"internalType":"string","name":"proposalType","type":"string"},{"indexed":true,"internalType":"address","name":"proposer","type":"address"}],"name":"ProposalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"proposalId","type":"bytes32"}],"name":"ProposalExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"proposalId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"bool","name":"support","type":"bool"}],"name":"VoteCast","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldVotesRequired","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newVotesRequired","type":"uint256"}],"name":"VotesRequiredChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"votingContract","type":"address"},{"indexed":false,"internalType":"enum VotingSystemType","name":"systemType","type":"uint8"},{"indexed":false,"internalType":"bool","name":"useQuadraticVoting","type":"bool"}],"name":"VotingSystemDeployed","type":"event"},{"inputs":[],"name":"MAX_PRIZE_CLAIM_COOLDOWN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RevealCommitVotingImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_judge","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"addJudgeReward","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"claimJudgeReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_hackathonId","type":"uint256"},{"internalType":"uint256","name":"_startTime","type":"uint256"},{"internalType":"uint256","name":"_endTime","type":"uint256"},{"internalType":"uint256","name":"_minimumSponsorContribution","type":"uint256"},{"internalType":"uint256","name":"_stakeAmount","type":"uint256"},{"internalType":"uint256[]","name":"_prizeDistribution","type":"uint256[]"},{"internalType":"address[]","name":"_selectedJudges","type":"address[]"},{"components":[{"internalType":"enum VotingSystemType","name":"systemType","type":"uint8"},{"internalType":"bool","name":"useQuadraticVoting","type":"bool"},{"internalType":"uint256","name":"votingPowerPerJudge","type":"uint256"},{"internalType":"uint256","name":"maxWinners","type":"uint256"}],"internalType":"struct VotingConfig","name":"_votingConfig","type":"tuple"}],"name":"createHackathon","outputs":[{"internalType":"address","name":"hackathonAddress","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"curveRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_delegate","type":"address"}],"name":"delegateToAgent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"delegateToJudge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"emergencyWithdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyWithdrawPyusd","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_ethAmount","type":"uint256"}],"name":"estimatePyusdOutput","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_proposalId","type":"bytes32"},{"internalType":"address","name":"_judge","type":"address"},{"internalType":"uint256","name":"_newVotesRequired","type":"uint256"}],"name":"executeProposal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurveRouterInfo","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_delegate","type":"address"}],"name":"getDelegateJudge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalJudges","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHackathonCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_judge","type":"address"}],"name":"getJudgeDelegate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_judge","type":"address"}],"name":"getJudgeRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_organizer","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getOrganizerHackathon","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_organizer","type":"address"}],"name":"getOrganizerHackathonCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_proposalId","type":"bytes32"}],"name":"getProposalVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPyusdBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWethBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"globalJudges","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"hasClaimedReward","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_proposalId","type":"bytes32"},{"internalType":"address","name":"_judge","type":"address"}],"name":"hasJudgeVoted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"","type":"address"}],"name":"hasVoted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isGlobalJudge","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"isJudgeOrDelegate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_proposalId","type":"bytes32"}],"name":"isProposalExecuted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"judgeDelegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"judgeRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openVotingImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"organizerHackathonCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"organizerHackathons","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"proposalExecuted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"proposalVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_judge","type":"address"}],"name":"proposeAddJudge","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newVotesRequired","type":"uint256"}],"name":"proposeChangeVotesRequired","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_judge","type":"address"}],"name":"proposeRemoveJudge","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pyusd","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"qvWrapperImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"revokeDelegation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalHackathons","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_proposalId","type":"bytes32"},{"internalType":"bool","name":"_support","type":"bool"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"votesRequired","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"zkVotingImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6101a06040526001600655348015610015575f5ffd5b5060405161bf7238038061bf7283398101604081905261003491610322565b306080526001600160a01b0384811660a0528381166101405282811661016052811661018052604051610066906102d3565b604051809103905ff08015801561007f573d5f5f3e3d5ffd5b506001600160a01b031660c052604051610098906102e0565b604051809103905ff0801580156100b1573d5f5f3e3d5ffd5b506001600160a01b031660e0526040516100ca906102ed565b604051809103905ff0801580156100e3573d5f5f3e3d5ffd5b506001600160a01b0316610100526040516100fd906102fa565b604051809103905ff080158015610116573d5f5f3e3d5ffd5b506001600160a01b03166101205261012d33610136565b50505050610373565b6001600160a01b0381166101915760405162461bcd60e51b815260206004820152601560248201527f496e76616c6964206a756467652061646472657373000000000000000000000060448201526064015b60405180910390fd5b6001600160a01b0381165f9081526020819052604090205460ff16156101f95760405162461bcd60e51b815260206004820152601460248201527f4a7564676520616c7265616479206578697374730000000000000000000000006044820152606401610188565b600154156102495760405162461bcd60e51b815260206004820152601960248201527f4669727374206a7564676520616c7265616479206164646564000000000000006044820152606401610188565b6001600160a01b0381165f81815260208190526040808220805460ff19166001908117909155805480820182559083527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b03191684179055517f0b221fa4a77e140717a07cc6edef3e54fbb0560c79d71e5fe77b8f7e618d664c9190a250565b6118468061379183390190565b61245780614fd783390190565b6137728061742e83390190565b6113d28061aba083390190565b80516001600160a01b038116811461031d575f5ffd5b919050565b5f5f5f5f60808587031215610335575f5ffd5b61033e85610307565b935061034c60208601610307565b925061035a60408601610307565b915061036860608601610307565b905092959194509250565b60805160a05160c05160e051610100516101205161014051610160516101805161333b6104565f395f818161043d0152818161056a01528181610eb001528181611bc5015281816122c70152818161234d015281816124bf015281816129710152612a5801525f81816104e1015281816105420152611c5301525f818161051d0152818161095201528181611034015261264501525f8181610789015261285c01525f8181610a3501526127c001525f8181610985015261277d01525f8181610678015261273301525f8181610626015261293e01525f6108bd015261333b5ff3fe6080604052600436106102bf575f3560e01c80638b52d56d1161016f578063cbf7719b116100d8578063da7bafce11610092578063e456766b1161006d578063e456766b14610a57578063e79b754714610a85578063ed440f5014610ab9578063fa71039114610ada575f5ffd5b8063da7bafce146109f1578063db5abbd014610a05578063e029983614610a24575f5ffd5b8063cbf7719b146108df578063cc60cf1f1461090a578063cda90b8814610941578063ce762f6414610974578063ce836b21146109a7578063d14b85b9146109c6575f5ffd5b8063a3c2001d11610129578063a3c2001d1461080c578063a4d3180514610821578063a717430c14610835578063aadc3b7214610848578063bff28ea814610881578063c45a0155146108ac575f5ffd5b80638b52d56d146107455780638bc6dae0146107645780638f02fc6f146107785780639dd58b58146107ab5780639f2ce678146107bf578063a074696a146107de575f5ffd5b80633fc8cef31161022b5780635c60da1b116101e55780636b792c4b116101c05780636b792c4b1461069a5780636f9948c7146106b957806373615775146106f85780637fae382714610726575f5ffd5b80635c60da1b146106155780635e2490211461064857806366cab36b14610667575f5ffd5b80633fc8cef3146104d057806340b1187d146105035780634dd218fd1461059957806351dc554b146105b8578063543f5d38146105cb57806355f8cf0d146105e1575f5ffd5b80632244d1cb1161027c5780632244d1cb146103f557806322e669d31461042c578063234c03551461045f5780632c7dc1931461047e57806332bb3ed81461049d5780633f3642dd146104bc575f5ffd5b80630368d346146102c35780630396ec101461030a5780630c2891b71461032b5780630cf79cde14610340578063138051281461037e5780632062b2a7146103a9575b5f5ffd5b3480156102ce575f5ffd5b506102f76102dd366004612bd5565b6001600160a01b03165f9081526002602052604090205490565b6040519081526020015b60405180910390f35b348015610315575f5ffd5b50610329610324366004612bee565b610aee565b005b348015610336575f5ffd5b506102f760065481565b34801561034b575f5ffd5b5061036e61035a366004612c21565b60096020525f908152604090205460ff1681565b6040519015158152602001610301565b348015610389575f5ffd5b506102f7610398366004612bd5565b600a6020525f908152604090205481565b3480156103b4575f5ffd5b506103dd6103c3366004612bd5565b60056020525f90815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610301565b348015610400575f5ffd5b506103dd61040f366004612bd5565b6001600160a01b039081165f908152600460205260409020541690565b348015610437575f5ffd5b506103dd7f000000000000000000000000000000000000000000000000000000000000000081565b34801561046a575f5ffd5b506103dd610479366004612c21565b610e3e565b348015610489575f5ffd5b506102f7610498366004612c21565b610e66565b3480156104a8575f5ffd5b506102f76104b7366004612bd5565b6110b7565b3480156104c7575f5ffd5b5061032961128f565b3480156104db575f5ffd5b506103dd7f000000000000000000000000000000000000000000000000000000000000000081565b34801561050e575f5ffd5b50604080516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000001691810191909152606001610301565b3480156105a4575f5ffd5b506103296105b3366004612bd5565b611429565b6103dd6105c6366004612d95565b6115d0565b3480156105d6575f5ffd5b506102f762093a8081565b3480156105ec575f5ffd5b506103dd6105fb366004612bd5565b60046020525f90815260409020546001600160a01b031681565b348015610620575f5ffd5b506103dd7f000000000000000000000000000000000000000000000000000000000000000081565b348015610653575f5ffd5b5061036e610662366004612e90565b6118bc565b348015610672575f5ffd5b506103dd7f000000000000000000000000000000000000000000000000000000000000000081565b3480156106a5575f5ffd5b506103296106b4366004612c21565b6118e8565b3480156106c4575f5ffd5b506103dd6106d3366004612eba565b600b60209081525f92835260408084209091529082529020546001600160a01b031681565b348015610703575f5ffd5b5061036e610712366004612bd5565b5f6020819052908152604090205460ff1681565b348015610731575f5ffd5b506103dd610740366004612eba565b61197b565b348015610750575f5ffd5b506102f761075f366004612bd5565b6119ff565b34801561076f575f5ffd5b506102f7611bae565b348015610783575f5ffd5b506103dd7f000000000000000000000000000000000000000000000000000000000000000081565b3480156107b6575f5ffd5b506102f7611c3c565b3480156107ca575f5ffd5b506103296107d9366004612ee2565b611c8a565b3480156107e9575f5ffd5b5061036e6107f8366004612bd5565b60036020525f908152604090205460ff1681565b348015610817575f5ffd5b506102f7600c5481565b34801561082c575f5ffd5b50610329611dd4565b610329610843366004612eba565b611ecf565b348015610853575f5ffd5b5061036e610862366004612e90565b600760209081525f928352604080842090915290825290205460ff1681565b34801561088c575f5ffd5b506102f761089b366004612c21565b5f9081526008602052604090205490565b3480156108b7575f5ffd5b506103dd7f000000000000000000000000000000000000000000000000000000000000000081565b3480156108ea575f5ffd5b506102f76108f9366004612bd5565b60026020525f908152604090205481565b348015610915575f5ffd5b506103dd610924366004612bd5565b6001600160a01b039081165f908152600560205260409020541690565b34801561094c575f5ffd5b506103dd7f000000000000000000000000000000000000000000000000000000000000000081565b34801561097f575f5ffd5b506103dd7f000000000000000000000000000000000000000000000000000000000000000081565b3480156109b2575f5ffd5b506102f76109c1366004612c21565b61201a565b3480156109d1575f5ffd5b506102f76109e0366004612c21565b60086020525f908152604090205481565b3480156109fc575f5ffd5b50600c546102f7565b348015610a10575f5ffd5b5061036e610a1f366004612bd5565b6121e1565b348015610a2f575f5ffd5b506103dd7f000000000000000000000000000000000000000000000000000000000000000081565b348015610a62575f5ffd5b5061036e610a71366004612c21565b5f9081526009602052604090205460ff1690565b348015610a90575f5ffd5b506102f7610a9f366004612bd5565b6001600160a01b03165f908152600a602052604090205490565b348015610ac4575f5ffd5b50610acd612222565b6040516103019190612f46565b348015610ae5575f5ffd5b50610329612282565b335f9081526020819052604090205460ff16610b255760405162461bcd60e51b8152600401610b1c90612f58565b60405180910390fd5b5f8381526009602052604090205460ff1615610b535760405162461bcd60e51b8152600401610b1c90612fa1565b6006545f848152600860205260409020541015610ba75760405162461bcd60e51b8152602060048201526012602482015271496e73756666696369656e7420766f74657360701b6044820152606401610b1c565b5f838152600960205260409020805460ff191660011790556001600160a01b03821615610dc6576001600160a01b0382165f9081526020819052604090205460ff1615610d3a576001600160a01b0382165f908152602081905260408120805460ff191690555b600154811015610d0157826001600160a01b031660018281548110610c3557610c35612fd8565b5f918252602090912001546001600160a01b031603610cf95760018054610c5d908290613000565b81548110610c6d57610c6d612fd8565b5f91825260209091200154600180546001600160a01b039092169183908110610c9857610c98612fd8565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055506001805480610cd457610cd4613013565b5f8281526020902081015f1990810180546001600160a01b0319169055019055610d01565b600101610c0e565b506040516001600160a01b038316907fc19950c209d8ad1fcb5d64f3c545c4ad0782202fcbfc1b564cc1ed120025e659905f90a2610e0f565b6001600160a01b0382165f81815260208190526040808220805460ff19166001908117909155805480820182559083527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b03191684179055517f0b221fa4a77e140717a07cc6edef3e54fbb0560c79d71e5fe77b8f7e618d664c9190a2610e0f565b8015610e0f57600680549082905560408051828152602081018490527f7f95e655f1cebcc68c938397e57f67e4dacc24fc905d34e073a3b65fbcfbd7fb910160405180910390a1505b60405183907f7b1bcf1ccf901a11589afff5504d59fd0a53780eed2a952adade0348985139e0905f90a2505050565b60018181548110610e4d575f80fd5b5f918252602090912001546001600160a01b0316905081565b604080516101608101825273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee815273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486020808301919091526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116838501525f6060808501829052608080860183905260a080870184905260c080880185905260e0808901869052610100808a01879052610120808b01889052610140808c018990528c519081018d52808601898152948101899052928301889052908201879052810186905290815288518083018a52858152808801869052808a018690528085018690528084018690528188015288518083018a52858152808801869052808a01869052808501869052808401869052818a015288518083018a52858152808801869052808a018690528085018690528084018690528185015288518083018a52858152808801869052808a018690528085018690528084018690528184015288519182018952737f86bf177dd4f3494b841a37e810a34dd56c829b825273383e6b4437b59fff47b619cba855ca29342a85599682019690965280880184905291820183905281018290529451637b1257b360e01b81529094917f00000000000000000000000000000000000000000000000000000000000000001690637b1257b39061106f90869086908a9087906004016130c6565b602060405180830381865afa15801561108a573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110ae91906130f8565b95945050505050565b335f9081526020819052604081205460ff166110e55760405162461bcd60e51b8152600401610b1c90612f58565b6001600160a01b0382165f9081526020819052604090205460ff166111435760405162461bcd60e51b8152602060048201526014602482015273129d5919d948191bd95cc81b9bdd08195e1a5cdd60621b6044820152606401610b1c565b336001600160a01b038316036111a55760405162461bcd60e51b815260206004820152602160248201527f43616e6e6f742070726f706f736520746f2072656d6f766520796f757273656c6044820152603360f91b6064820152608401610b1c565b6040516a72656d6f76654a7564676560a81b60208201526bffffffffffffffffffffffff19606084901b16602b82015242603f8201525f90605f0160408051601f1981840301815291815281516020928301205f818152600990935291205490915060ff16156112275760405162461bcd60e51b8152600401610b1c90612fa1565b336001600160a01b0316817f898a80665dd7411c488cdf4a8658fc065411886e2b29e8d29f159de5651f212460405161127f906020808252600b908201526a72656d6f76654a7564676560a81b604082015260600190565b60405180910390a390505b919050565b335f9081526020819052604081205460ff166112c257335f908152600560205260409020546001600160a01b03166112c4565b335b6001600160a01b0381165f908152600260205260409020549091506113215760405162461bcd60e51b81526020600482015260136024820152724e6f207265776172647320746f20636c61696d60681b6044820152606401610b1c565b6001600160a01b0381165f9081526003602052604090205460ff16156113895760405162461bcd60e51b815260206004820152601760248201527f416c726561647920636c61696d656420726577617264730000000000000000006044820152606401610b1c565b6001600160a01b0381165f908152600260209081526040808320546003909252808320805460ff19166001179055519091339183156108fc0291849190818181858888f193505050501580156113e1573d5f5f3e3d5ffd5b50816001600160a01b03167f2b8d51946813c957a7b1714546806778b063466901c289b82d967a22caed6e028260405161141d91815260200190565b60405180910390a25050565b335f9081526020819052604090205460ff166114575760405162461bcd60e51b8152600401610b1c90612f58565b6001600160a01b0381166114ad5760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642064656c6567617465206164647265737300000000000000006044820152606401610b1c565b336001600160a01b038216036115055760405162461bcd60e51b815260206004820152601b60248201527f43616e6e6f742064656c656761746520746f20796f757273656c6600000000006044820152606401610b1c565b335f908152600460205260409020546001600160a01b0316156115635760405162461bcd60e51b8152602060048201526016602482015275416c72656164792068617320612064656c656761746560501b6044820152606401610b1c565b335f81815260046020908152604080832080546001600160a01b0387166001600160a01b03199182168117909255818552600590935281842080549093168517909255519092917f0c195f050c9d1eae4d0a59bee2ef525b8a5d91975fa9342a54bfd6528ac8fb6f91a350565b5f4288116116205760405162461bcd60e51b815260206004820181905260248201527f53746172742074696d65206d75737420626520696e20746865206675747572656044820152606401610b1c565b8787116116795760405162461bcd60e51b815260206004820152602160248201527f456e642074696d65206d7573742062652061667465722073746172742074696d6044820152606560f81b6064820152608401610b1c565b5f805b85518110156116b45785818151811061169757611697612fd8565b6020026020010151826116aa919061310f565b915060010161167c565b505f6116bf82612377565b90505f6116cf826298968061241b565b90508281101561173a5760405162461bcd60e51b815260206004820152603060248201527f496e73756666696369656e7420505955534420616d6f756e7420666f7220707260448201526f34bd32903234b9ba3934b13aba34b7b760811b6064820152608401610b1c565b5f5b86518110156117cc5761176787828151811061175a5761175a612fd8565b60200260200101516121e1565b6117c45760405162461bcd60e51b815260206004820152602860248201527f53656c6563746564206a75646765206973206e6f7420696e20676c6f62616c20604482015267726567697374727960c01b6064820152608401610b1c565b60010161173c565b506117d78587612714565b506117e98c8c8c8c8c8c8c888a612938565b335f818152600a602081815260408084208054600b8452828620818752845291852080546001600160a01b0319166001600160a01b038916179055948452919052825493975092919061183b83613122565b9091555050600c8054905f61184f83613122565b9190505550336001600160a01b0316856001600160a01b03167f526b2cf7f5bb87f9a4c01a6eb8c01bf90405b9726286908ac1dfd93944da0e848f858a5f01518b602001516040516118a4949392919061316e565b60405180910390a35050505098975050505050505050565b5f8281526007602090815260408083206001600160a01b038516845290915290205460ff165b92915050565b335f9081526020819052604090205460ff166119165760405162461bcd60e51b8152600401610b1c90612f58565b604051339082156108fc029083905f818181858888f19350505050158015611940573d5f5f3e3d5ffd5b506040518181525f9033907f9495d03190a79a43e534c9e328ff322f6283261383f5f19c809564f6ad5a57b39060200160405180910390a350565b6001600160a01b0382165f908152600a602052604081205482106119d75760405162461bcd60e51b8152602060048201526013602482015272496e646578206f7574206f6620626f756e647360681b6044820152606401610b1c565b506001600160a01b039182165f908152600b6020908152604080832093835292905220541690565b335f9081526020819052604081205460ff16611a2d5760405162461bcd60e51b8152600401610b1c90612f58565b6001600160a01b038216611a7b5760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206a75646765206164647265737360581b6044820152606401610b1c565b6001600160a01b0382165f9081526020819052604090205460ff1615611ada5760405162461bcd60e51b81526020600482015260146024820152734a7564676520616c72656164792065786973747360601b6044820152606401610b1c565b604051676164644a7564676560c01b60208201526bffffffffffffffffffffffff19606084901b16602882015242603c8201525f90605c0160408051601f1981840301815291815281516020928301205f818152600990935291205490915060ff1615611b595760405162461bcd60e51b8152600401610b1c90612fa1565b336001600160a01b0316817f898a80665dd7411c488cdf4a8658fc065411886e2b29e8d29f159de5651f212460405161127f906020808252600890820152676164644a7564676560c01b604082015260600190565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a08231906024015b602060405180830381865afa158015611c13573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c3791906130f8565b905090565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401611bf8565b335f9081526020819052604090205460ff16611cb85760405162461bcd60e51b8152600401610b1c90612f58565b5f82815260076020908152604080832033845290915290205460ff1615611d215760405162461bcd60e51b815260206004820152601e60248201527f416c726561647920766f746564206f6e20746869732070726f706f73616c00006044820152606401610b1c565b5f8281526009602052604090205460ff1615611d4f5760405162461bcd60e51b8152600401610b1c90612fa1565b5f8281526007602090815260408083203384529091529020805460ff191660011790558015611d97575f828152600860205260408120805491611d9183613122565b91905055505b6040518115158152339083907f8b40665146691327ee30f5bf56e9b2d6f445d2830d3b09b56385cd30f630ecfb9060200160405180910390a35050565b335f9081526020819052604090205460ff16611e025760405162461bcd60e51b8152600401610b1c90612f58565b335f908152600460205260409020546001600160a01b0316611e665760405162461bcd60e51b815260206004820152601760248201527f4e6f2064656c65676174696f6e20746f207265766f6b650000000000000000006044820152606401610b1c565b335f81815260046020908152604080832080546001600160a01b03198082169092556001600160a01b0316808552600590935281842080549091169055519092907f0c195f050c9d1eae4d0a59bee2ef525b8a5d91975fa9342a54bfd6528ac8fb6f908390a350565b6001600160a01b038216611f1d5760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206a75646765206164647265737360581b6044820152606401610b1c565b5f8111611f6c5760405162461bcd60e51b815260206004820152601d60248201527f416d6f756e74206d7573742062652067726561746572207468616e20300000006044820152606401610b1c565b80341015611fb35760405162461bcd60e51b8152602060048201526014602482015273125b9cdd59999a58da595b9d081c185e5b595b9d60621b6044820152606401610b1c565b6001600160a01b0382165f9081526002602052604081208054839290611fda90849061310f565b90915550506040518181526001600160a01b038316907fd545e22a252a1e46a3ff5d635804684897b3b9831ac8163a9bc6c54ac88e53739060200161141d565b335f9081526020819052604081205460ff166120485760405162461bcd60e51b8152600401610b1c90612f58565b5f82116120a55760405162461bcd60e51b815260206004820152602560248201527f566f746573207265717569726564206d75737420626520677265617465722074604482015264068616e20360dc1b6064820152608401610b1c565b6001548211156121095760405162461bcd60e51b815260206004820152602960248201527f566f7465732072657175697265642063616e6e6f742065786365656420746f74604482015268616c206a756467657360b81b6064820152608401610b1c565b6040517218da185b99d9559bdd195cd4995c5d5a5c9959606a1b6020820152603381018390524260538201525f9060730160408051601f1981840301815291815281516020928301205f818152600990935291205490915060ff16156121815760405162461bcd60e51b8152600401610b1c90612fa1565b336001600160a01b0316817f898a80665dd7411c488cdf4a8658fc065411886e2b29e8d29f159de5651f212460405161127f9060208082526013908201527218da185b99d9559bdd195cd4995c5d5a5c9959606a1b604082015260600190565b6001600160a01b0381165f9081526020819052604081205460ff16806118e25750506001600160a01b039081165f9081526005602052604090205416151590565b6060600180548060200260200160405190810160405280929190818152602001828054801561227857602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161225a575b5050505050905090565b335f9081526020819052604090205460ff166122b05760405162461bcd60e51b8152600401610b1c90612f58565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612314573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061233891906130f8565b90508015612374576123746001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633836129bf565b50565b604051632c7dc19360e01b8152670de0b6b3a764000060048201525f9081903090632c7dc19390602401602060405180830381865afa1580156123bc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123e091906130f8565b90506123ed81606461319a565b6123ff84670de0b6b3a764000061319a565b61240a90606961319a565b61241491906131b1565b9392505050565b5f5f83116124755760405162461bcd60e51b815260206004820152602160248201527f45544820616d6f756e74206d7573742062652067726561746572207468616e206044820152600360fc1b6064820152608401610b1c565b604080516101608101825273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee815273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486020808301919091526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116838501525f6060808501829052608080860183905260a080870184905260c080880185905260e0808901869052610100808a01879052610120808b01889052610140808c018990528c519081018d52808601898152948101899052928301889052908201879052810186905290815288518083018a52858152808801869052808a018690528085018690528084018690528188015288518083018a52858152808801869052808a01869052808501869052808401869052818a015288518083018a52858152808801869052808a018690528085018690528084018690528185015288518083018a52858152808801869052808a018690528085018690528084018690528184015288519182018952737f86bf177dd4f3494b841a37e810a34dd56c829b825273383e6b4437b59fff47b619cba855ca29342a8559968201969096528088018490529182018390528101919091529351632e4e0c7160e11b8152929391927f000000000000000000000000000000000000000000000000000000000000000090911690635c9c18e2908890612686908790879084908c9089906004016131d0565b60206040518083038185885af11580156126a2573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906126c791906130f8565b60408051888152602081018390529081018790529094507f12aec4492341cce30775891eceb584f6fbbd825e56d94db93c1888839677903c9060600160405180910390a150505092915050565b5f80835160038111156127295761272961313a565b0361275e576127577f0000000000000000000000000000000000000000000000000000000000000000612a16565b90506127e7565b6001835160038111156127735761277361313a565b036127a1576127577f0000000000000000000000000000000000000000000000000000000000000000612a16565b6002835160038111156127b6576127b661313a565b036127e7576127e47f0000000000000000000000000000000000000000000000000000000000000000612a16565b90505b806001600160a01b0316637c33404f84604001518560600151856040518463ffffffff1660e01b815260040161281f93929190613213565b5f604051808303815f87803b158015612836575f5ffd5b505af1158015612848573d5f5f3e3d5ffd5b505050508260200151156128ea575f6128807f0000000000000000000000000000000000000000000000000000000000000000612a16565b604080860151905163cd6dc68760e01b81526001600160a01b038581166004830152602482019290925291925082169063cd6dc687906044015f604051808303815f87803b1580156128d0575f5ffd5b505af11580156128e2573d5f5f3e3d5ffd5b509293505050505b825160208401516040516001600160a01b038416927f5c968cbcc62446275183fea8d6eaa29a00505b77145a7fad4de724257508a33c9261292a92613231565b60405180910390a292915050565b5f6129627f0000000000000000000000000000000000000000000000000000000000000000612a16565b90506129986001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001682856129bf565b6129b2818b8b8b8b8b8b8b6129ad8b34613000565b612a21565b9998505050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052612a11908490612ab9565b505050565b5f6118e2825f612b2b565b60405162a8bae760e31b81526001600160a01b038a1690630545d738908390612a809033908d908d908d908d908d908d9030908e907f00000000000000000000000000000000000000000000000000000000000000009060040161324e565b5f604051808303818588803b158015612a97575f5ffd5b505af1158015612aa9573d5f5f3e3d5ffd5b5050505050505050505050505050565b5f5f60205f8451602086015f885af180612ad8576040513d5f823e3d81fd5b50505f513d91508115612aef578060011415612afc565b6001600160a01b0384163b155b15612b2557604051635274afe760e01b81526001600160a01b0385166004820152602401610b1c565b50505050565b5f81471015612b565760405163cf47918160e01b815247600482015260248101839052604401610b1c565b763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b176020526037600983f090506001600160a01b0381166118e25760405163b06ebf3d60e01b815260040160405180910390fd5b80356001600160a01b038116811461128a575f5ffd5b5f60208284031215612be5575f5ffd5b61241482612bbf565b5f5f5f60608486031215612c00575f5ffd5b83359250612c1060208501612bbf565b929592945050506040919091013590565b5f60208284031215612c31575f5ffd5b5035919050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612c7557612c75612c38565b604052919050565b5f67ffffffffffffffff821115612c9657612c96612c38565b5060051b60200190565b5f82601f830112612caf575f5ffd5b8135612cc2612cbd82612c7d565b612c4c565b8082825260208201915060208360051b860101925085831115612ce3575f5ffd5b602085015b83811015612d0757612cf981612bbf565b835260209283019201612ce8565b5095945050505050565b8035801515811461128a575f5ffd5b5f60808284031215612d30575f5ffd5b6040516080810167ffffffffffffffff81118282101715612d5357612d53612c38565b604052905080823560048110612d67575f5ffd5b8152612d7560208401612d11565b602082015260408381013590820152606092830135920191909152919050565b5f5f5f5f5f5f5f5f610160898b031215612dad575f5ffd5b883597506020890135965060408901359550606089013594506080890135935060a089013567ffffffffffffffff811115612de6575f5ffd5b8901601f81018b13612df6575f5ffd5b8035612e04612cbd82612c7d565b8082825260208201915060208360051b85010192508d831115612e25575f5ffd5b6020840193505b82841015612e47578335825260209384019390910190612e2c565b955050505060c089013567ffffffffffffffff811115612e65575f5ffd5b612e718b828c01612ca0565b925050612e818a60e08b01612d20565b90509295985092959890939650565b5f5f60408385031215612ea1575f5ffd5b82359150612eb160208401612bbf565b90509250929050565b5f5f60408385031215612ecb575f5ffd5b612ed483612bbf565b946020939093013593505050565b5f5f60408385031215612ef3575f5ffd5b82359150612eb160208401612d11565b5f8151808452602084019350602083015f5b82811015612f3c5781516001600160a01b0316865260209586019590910190600101612f15565b5093949350505050565b602081525f6124146020830184612f03565b60208082526029908201527f4f6e6c7920676c6f62616c206a75646765732063616e2063616c6c207468697360408201526810333ab731ba34b7b760b91b606082015260800190565b60208082526019908201527f50726f706f73616c20616c726561647920657865637574656400000000000000604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b818103818111156118e2576118e2612fec565b634e487b7160e01b5f52603160045260245ffd5b805f5b600b811015612b255781516001600160a01b031684526020938401939091019060010161302a565b805f5b6005811015612b25578151845f5b6005811015613082578251825260209283019290910190600101613063565b50505060a0939093019260209190910190600101613055565b805f5b6005811015612b255781516001600160a01b031684526020938401939091019060010161309e565b61054081016130d58287613027565b6130e3610160830186613052565b836104808301526110ae6104a083018461309b565b5f60208284031215613108575f5ffd5b5051919050565b808201808211156118e2576118e2612fec565b5f6001820161313357613133612fec565b5060010190565b634e487b7160e01b5f52602160045260245ffd5b6004811061316a57634e487b7160e01b5f52602160045260245ffd5b9052565b8481526020810184905260808101613189604083018561314e565b821515606083015295945050505050565b80820281158282048414176118e2576118e2612fec565b5f826131cb57634e487b7160e01b5f52601260045260245ffd5b500490565b61056081016131df8288613027565b6131ed610160830187613052565b84610480830152836104a08301526132096104c083018461309b565b9695505050505050565b838152826020820152606060408201525f6110ae6060830184612f03565b6040810161323f828561314e565b82151560208301529392505050565b5f610140820160018060a01b038d1683528b60208401528a60408401528960608401528860808401528760a084015261014060c0840152808751808352610160850191506020890192505f5b818110156132b857835183526020938401939092019160010161329a565b50506001600160a01b03871660e08501528381036101008501526132dc8187612f03565b925050506132f66101208301846001600160a01b03169052565b9b9a505050505050505050505056fea2646970667358221220f25571b24249f41926de9dbd3aa3581a313f63ac8b7eb8cd47de662ec25a43fd64736f6c634300081c00336080604052348015600e575f5ffd5b5061182a8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610153575f3560e01c8063842cb6de116100bf578063b06b036811610079578063b06b036814610367578063cde8e5d31461037a578063df15c37e14610383578063ed1224e514610398578063ed896d66146103ab578063edfbf7b6146103cd575f5ffd5b8063842cb6de146102d4578063927cab47146102fc5780639d9ca28d1461030f578063a2fb117514610339578063a46c36371461034c578063ae2ac4ae14610354575f5ffd5b8063522c90fa11610110578063522c90fa146102385780635ccbc7781461026357806376804a031461028b57806377e5c241146102ae5780637c33404f146102b75780637dc449c1146102cc575f5ffd5b8063036ee850146101575780630655fad61461017357806309eef43e146101925780630fac9100146101c45780633a7a53b2146101e35780634bd1c4921461020e575b5f5ffd5b61016060025481565b6040519081526020015b60405180910390f35b610160610181366004611547565b60096020525f908152604090205481565b6101b46101a0366004611547565b60046020525f908152604090205460ff1681565b604051901515815260200161016a565b6101606101d2366004611547565b60076020525f908152604090205481565b6101b46101f1366004611547565b6001600160a01b03165f9081526004602052604090205460ff1690565b61016061021c366004611567565b600860209081525f928352604080842090915290825290205481565b61024b610246366004611598565b6103e0565b6040516001600160a01b03909116815260200161016a565b610160610271366004611547565b6001600160a01b03165f9081526007602052604090205490565b61029361046d565b6040805193845260208401929092529082015260600161016a565b61016060065481565b6102ca6102c53660046115f7565b61059b565b005b6101605f5481565b6101606102e2366004611547565b6001600160a01b03165f9081526009602052604090205490565b6102ca61030a366004611547565b610630565b6101b461031d366004611547565b6001600160a01b03165f90815260096020526040902054151590565b61024b610347366004611598565b6106e9565b600a54610160565b6102ca610362366004611547565b610711565b61024b610375366004611598565b6107ea565b61016060015481565b61038b6107f9565b60405161016a9190611646565b6102ca6103a6366004611691565b610859565b6101b46103b9366004611547565b60036020525f908152604090205460ff1681565b6102ca6103db366004611598565b610d13565b5f5f821180156103f25750600a548211155b6104365760405162461bcd60e51b815260206004820152601060248201526f24b73b30b634b2103837b9b4ba34b7b760811b60448201526064015b60405180910390fd5b600a6104436001846116f9565b8154811061045357610453611712565b5f918252602090912001546001600160a01b031692915050565b6006545f808080805b6005548110156104d85760045f6005838154811061049657610496611712565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205460ff16156104d057826104cc81611726565b9350505b600101610476565b5090925082905f5b600554811015610590575f5b600554811015610587575f60085f6005858154811061050d5761050d611712565b5f9182526020808320909101546001600160a01b031683528201929092526040018120600580549192918590811061054757610547611712565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054111561057f578261057b81611726565b9350505b6001016104ec565b506001016104e0565b508092505050909192565b5f54156105e05760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604482015260640161042d565b5f84815560018490555b818110156106295761062183838381811061060757610607611712565b905060200201602081019061061c9190611547565b610d5f565b6001016105ea565b5050505050565b6001600160a01b03811661067e5760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206a75646765206164647265737360581b604482015260640161042d565b6001600160a01b0381165f9081526003602052604090205460ff16156106dd5760405162461bcd60e51b81526020600482015260146024820152734a7564676520616c72656164792065786973747360601b604482015260640161042d565b6106e681610d5f565b50565b600a81815481106106f8575f80fd5b5f918252602090912001546001600160a01b0316905081565b6001600160a01b0381165f9081526003602052604090205460ff1661076f5760405162461bcd60e51b8152602060048201526014602482015273129d5919d948191bd95cc81b9bdd08195e1a5cdd60621b604482015260640161042d565b6001600160a01b0381165f9081526004602052604090205460ff16156107e15760405162461bcd60e51b815260206004820152602160248201527f43616e6e6f742072656d6f7665206a756467652077686f2068617320766f74656044820152601960fa1b606482015260840161042d565b6106e681610e0c565b600581815481106106f8575f80fd5b6060600a80548060200260200160405190810160405280929190818152602001828054801561084f57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610831575b5050505050905090565b335f9081526003602052604090205460ff166108c35760405162461bcd60e51b815260206004820152602360248201527f4f6e6c79206a75646765732063616e20706572666f726d20746869732061637460448201526234b7b760e91b606482015260840161042d565b6002544211156109155760405162461bcd60e51b815260206004820152601a60248201527f566f74696e6720646561646c696e652068617320706173736564000000000000604482015260640161042d565b82811461095d5760405162461bcd60e51b8152602060048201526016602482015275082e4e4c2f2e640d8cadccee8d040dad2e6dac2e8c6d60531b604482015260640161042d565b826109b95760405162461bcd60e51b815260206004820152602660248201527f4d75737420766f746520666f72206174206c65617374206f6e6520706172746960448201526518da5c185b9d60d21b606482015260840161042d565b335f9081526004602052604090205460ff1615610a185760405162461bcd60e51b815260206004820152601760248201527f4a756467652068617320616c726561647920766f746564000000000000000000604482015260640161042d565b5f805b84811015610c63575f848483818110610a3657610a36611712565b9050602002013511610a8a5760405162461bcd60e51b815260206004820152601d60248201527f506f696e7473206d7573742062652067726561746572207468616e2030000000604482015260640161042d565b838382818110610a9c57610a9c611712565b9050602002013582610aae919061173e565b91505f60075f888885818110610ac657610ac6611712565b9050602002016020810190610adb9190611547565b6001600160a01b03166001600160a01b031681526020019081526020015f20549050848483818110610b0f57610b0f611712565b9050602002013560075f898986818110610b2b57610b2b611712565b9050602002016020810190610b409190611547565b6001600160a01b03166001600160a01b031681526020019081526020015f205f828254610b6d919061173e565b909155508590508483818110610b8557610b85611712565b335f90815260086020908152604082209202939093013592909150898986818110610bb257610bb2611712565b9050602002016020810190610bc79190611547565b6001600160a01b0316815260208101919091526040015f2055610c5a878784818110610bf557610bf5611712565b9050602002016020810190610c0a9190611547565b8260075f8b8b88818110610c2057610c20611712565b9050602002016020810190610c359190611547565b6001600160a01b03166001600160a01b031681526020019081526020015f2054610f6b565b50600101610a1b565b505f54811115610cb55760405162461bcd60e51b815260206004820152601e60248201527f4578636565647320706f696e747320706572206a75646765206c696d69740000604482015260640161042d565b335f8181526004602052604090819020805460ff19166001179055517f67d24c36e0629b147f8ecca5be58020627f9ca82bfacf3b9156fff8fe364c51d90610d04908890889088908890611751565b60405180910390a25050505050565b60025415610d5a5760405162461bcd60e51b8152602060048201526014602482015273111958591b1a5b9948185b1c9958591e481cd95d60621b604482015260640161042d565b600255565b6001600160a01b0381165f818152600360205260408120805460ff1916600190811790915560058054918201815582527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319169092179091556006805491610dd183611726565b90915550506040516001600160a01b038216907fe7f15f9ffff540ae14fdebcd9b6d6cc1fc143933338656c99caa595e8b809039905f90a250565b6001600160a01b0381165f908152600360205260408120805460ff191690555b600554811015610f2057816001600160a01b031660058281548110610e5357610e53611712565b5f918252602090912001546001600160a01b031603610f185760058054610e7c906001906116f9565b81548110610e8c57610e8c611712565b5f91825260209091200154600580546001600160a01b039092169183908110610eb757610eb7611712565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055506005805480610ef357610ef36117cb565b5f8281526020902081015f1990810180546001600160a01b0319169055019055610f20565b600101610e2c565b5060068054905f610f30836117df565b90915550506040516001600160a01b038216907f32a792a099c63670df7161917c5efb2fdc734cf9f943ebddccbecef87664f2b7905f90a250565b6001600160a01b0383165f908152600960205260409020548015610f9a57610f94848484610fed565b50505050565b5f82118015610fdf5750600154600a541080610fdf575060075f610fbc61102e565b6001600160a01b03166001600160a01b031681526020019081526020015f205482115b15610f9457610f9484611077565b6001600160a01b0383165f908152600960205260409020548282111561101c576110178482611125565b610f94565b82821015610f9457610f9484826111c7565b600a545f90810361103e57505f90565b600a805461104e906001906116f9565b8154811061105e5761105e611712565b5f918252602090912001546001600160a01b0316919050565b600154600a5410156110e957600a80546001810182557fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a80180546001600160a01b0319166001600160a01b03841690811790915590545f918252600960205260409091208190556106e6908290611125565b5f6110f261102e565b6001600160a01b0381165f90815260096020526040902054909150611117828261126a565b61112083611077565b505050565b805f6111326001836116f9565b90505b80156111b45760075f600a61114b6001856116f9565b8154811061115b5761115b611712565b5f9182526020808320909101546001600160a01b0390811684528382019490945260409283018220549388168252600790522054111561119d578091506111a2565b6111b4565b806111ac816117df565b915050611135565b5081811461112057611120838383611395565b80805b600a548110156112435760075f600a83815481106111ea576111ea611712565b5f9182526020808320909101546001600160a01b039081168452838201949094526040928301822054938816825260079052205410156112365761122f81600161173e565b915061123b565b611243565b6001016111ca565b5060015481111561125857611120838361126a565b81811461112057611120838383611395565b600a5481101561133657600a80545f9190611287906001906116f9565b8154811061129757611297611712565b5f918252602090912001546001600160a01b0316905080600a6112bb6001856116f9565b815481106112cb576112cb611712565b5f91825260208083209190910180546001600160a01b0319166001600160a01b039485161790559183168152600990915260409020829055600a805480611314576113146117cb565b5f8281526020902081015f1990810180546001600160a01b0319169055019055505b6001600160a01b0382165f81815260096020526040808220829055517f845de5d493589e5b262d7a53cad5ae62bd69b6cac33cd64b86eab5d1c90194a29161138991859190918252602082015260400190565b60405180910390a25050565b808214611120576001600160a01b0383165f90815260096020819052604082208390558391600a6113c76001866116f9565b815481106113d7576113d7611712565b5f9182526020808320909101546001600160a01b03168352820192909252604001812091909155600a61140b6001856116f9565b8154811061141b5761141b611712565b5f918252602090912001546001600160a01b03169050600a61143e6001846116f9565b8154811061144e5761144e611712565b5f918252602090912001546001600160a01b0316600a61146f6001866116f9565b8154811061147f5761147f611712565b5f91825260209091200180546001600160a01b0319166001600160a01b039290921691909117905580600a6114b56001856116f9565b815481106114c5576114c5611712565b5f9182526020918290200180546001600160a01b0319166001600160a01b0393841617905560408051868152918201859052918616917f845de5d493589e5b262d7a53cad5ae62bd69b6cac33cd64b86eab5d1c90194a2910160405180910390a250505050565b80356001600160a01b0381168114611542575f5ffd5b919050565b5f60208284031215611557575f5ffd5b6115608261152c565b9392505050565b5f5f60408385031215611578575f5ffd5b6115818361152c565b915061158f6020840161152c565b90509250929050565b5f602082840312156115a8575f5ffd5b5035919050565b5f5f83601f8401126115bf575f5ffd5b50813567ffffffffffffffff8111156115d6575f5ffd5b6020830191508360208260051b85010111156115f0575f5ffd5b9250929050565b5f5f5f5f6060858703121561160a575f5ffd5b8435935060208501359250604085013567ffffffffffffffff81111561162e575f5ffd5b61163a878288016115af565b95989497509550505050565b602080825282518282018190525f918401906040840190835b818110156116865783516001600160a01b031683526020938401939092019160010161165f565b509095945050505050565b5f5f5f5f604085870312156116a4575f5ffd5b843567ffffffffffffffff8111156116ba575f5ffd5b6116c6878288016115af565b909550935050602085013567ffffffffffffffff81111561162e575f5ffd5b634e487b7160e01b5f52601160045260245ffd5b8181038181111561170c5761170c6116e5565b92915050565b634e487b7160e01b5f52603260045260245ffd5b5f60018201611737576117376116e5565b5060010190565b8082018082111561170c5761170c6116e5565b604080825281018490525f8560608301825b87811015611791576001600160a01b0361177c8461152c565b16825260209283019290910190600101611763565b5083810360208501528481526001600160fb1b038511156117b0575f5ffd5b8460051b915081866020830137016020019695505050505050565b634e487b7160e01b5f52603160045260245ffd5b5f816117ed576117ed6116e5565b505f19019056fea26469706673582212208caa46ac82bbc19fa3a8d491d91e5b480fc081077e9765b424cc76bcb1e6debb64736f6c634300081c00336080604052348015600e575f5ffd5b5061243b8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610208575f3560e01c8063927cab471161011f578063c8b94787116100a9578063e8fcf72311610079578063e8fcf72314610534578063ed1224e514610553578063ed896d6614610566578063edfbf7b614610588578063f9ba1a641461059b575f5ffd5b8063c8b94787146104f0578063cde8e5d314610503578063dee9c8511461050c578063df15c37e1461051f575f5ffd5b8063accf1bd6116100ef578063accf1bd61461045e578063ae2ac4ae14610486578063b06b036814610499578063b9a53013146104ac578063c6dbe15a146104ce575f5ffd5b8063927cab47146104065780639d9ca28d14610419578063a2fb117514610443578063a46c363714610456575f5ffd5b80634bd1c492116101a057806376804a031161017057806376804a031461039757806377e5c241146103ba5780637c33404f146103c35780637dc449c1146103d6578063842cb6de146103de575f5ffd5b80634bd1c4921461031f578063522c90fa146103495780635ccbc7781461035c5780636b8e3ad614610384575f5ffd5b80630fac9100116101db5780630fac91001461029757806314a1e1c8146102b65780631a8fb550146102e15780633a7a53b2146102f4575f5ffd5b8063036ee8501461020c5780630655fad6146102285780630a744f57146102475780630a88effc14610282575b5f5ffd5b61021560025481565b6040519081526020015b60405180910390f35b610215610236366004611ed9565b600e6020525f908152604090205481565b610272610255366004611ed9565b6001600160a01b03165f9081526004602052604090205460ff1690565b604051901515815260200161021f565b610295610290366004611ef9565b6105ba565b005b6102156102a5366004611ed9565b600c6020525f908152604090205481565b6102c96102c4366004611f10565b610602565b6040516001600160a01b03909116815260200161021f565b6102156102ef366004611f80565b610636565b610272610302366004611ed9565b6001600160a01b03165f9081526005602052604090205460ff1690565b61021561032d366004612008565b600d60209081525f928352604080842090915290825290205481565b6102c9610357366004611ef9565b610674565b61021561036a366004611ed9565b6001600160a01b03165f908152600c602052604090205490565b610295610392366004612039565b6106fc565b61039f61097a565b6040805193845260208401929092529082015260600161021f565b61021560075481565b6102956103d13660046120ac565b6109ee565b6102155f5481565b6102156103ec366004611ed9565b6001600160a01b03165f908152600e602052604090205490565b610295610414366004611ed9565b610a83565b610272610427366004611ed9565b6001600160a01b03165f908152600e6020526040902054151590565b6102c9610451366004611ef9565b610b3c565b600f54610215565b61021561046c366004611ed9565b6001600160a01b03165f9081526008602052604090205490565b610295610494366004611ed9565b610b64565b6102c96104a7366004611ef9565b610c41565b6102726104ba366004611ed9565b60056020525f908152604090205460ff1681565b6102726104dc366004611ed9565b60046020525f908152604090205460ff1681565b6102156104fe366004611f10565b610c50565b61021560015481565b61029561051a366004612039565b610c7b565b61052761111d565b60405161021f91906120fb565b610215610542366004611ed9565b60086020525f908152604090205481565b610295610561366004612146565b61117d565b610272610574366004611ed9565b60036020525f908152604090205460ff1681565b610295610596366004611ef9565b6115f7565b6102156105a9366004611ed9565b600b6020525f908152604090205481565b335f9081526003602052604090205460ff166105f15760405162461bcd60e51b81526004016105e89061219a565b60405180910390fd5b335f908152600b6020526040902055565b6009602052815f5260405f20818154811061061b575f80fd5b5f918252602090912001546001600160a01b03169150829050565b5f868686868686604051602001610652969594939291906121dd565b6040516020818303038152906040528051906020012090509695505050505050565b5f5f821180156106865750600f548211155b6106c55760405162461bcd60e51b815260206004820152601060248201526f24b73b30b634b2103837b9b4ba34b7b760811b60448201526064016105e8565b600f6106d260018461226c565b815481106106e2576106e2612285565b5f918252602090912001546001600160a01b031692915050565b335f9081526003602052604090205460ff1661072a5760405162461bcd60e51b81526004016105e89061219a565b60025442111561077c5760405162461bcd60e51b815260206004820152601a60248201527f566f74696e6720646561646c696e65206861732070617373656400000000000060448201526064016105e8565b83821461079b5760405162461bcd60e51b81526004016105e890612299565b836107f75760405162461bcd60e51b815260206004820152602660248201527f4d75737420766f746520666f72206174206c65617374206f6e6520706172746960448201526518da5c185b9d60d21b60648201526084016105e8565b335f9081526004602052604090205460ff16156108565760405162461bcd60e51b815260206004820152601b60248201527f4a756467652068617320616c726561647920636f6d6d6974746564000000000060448201526064016105e8565b5f805b858110156108c6575f85858381811061087457610874612285565b90506020020135116108985760405162461bcd60e51b81526004016105e8906122c9565b8484828181106108aa576108aa612285565b90506020020135826108bc9190612300565b9150600101610859565b505f548111156108e85760405162461bcd60e51b81526004016105e890612313565b5f338787878787604051602001610904969594939291906121dd565b60408051601f198184030181528282528051602091820120335f818152600884528481208390556004845293909320805460ff19166001179055808452935090917fb7434d5379c2cc69394db30fab188026da2b3c7c3a087002c5c0ca51d4d1494191015b60405180910390a250505050505050565b6007545f8080805b6006548110156109e45760055f600683815481106109a2576109a2612285565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205460ff16156109dc57816109d88161234a565b9250505b600101610982565b5092935f92509050565b5f5415610a335760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016105e8565b5f84815560018490555b81811015610a7c57610a74838383818110610a5a57610a5a612285565b9050602002016020810190610a6f9190611ed9565b611643565b600101610a3d565b5050505050565b6001600160a01b038116610ad15760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206a75646765206164647265737360581b60448201526064016105e8565b6001600160a01b0381165f9081526003602052604090205460ff1615610b305760405162461bcd60e51b81526020600482015260146024820152734a7564676520616c72656164792065786973747360601b60448201526064016105e8565b610b3981611643565b50565b600f8181548110610b4b575f80fd5b5f918252602090912001546001600160a01b0316905081565b6001600160a01b0381165f9081526003602052604090205460ff16610bc25760405162461bcd60e51b8152602060048201526014602482015273129d5919d948191bd95cc81b9bdd08195e1a5cdd60621b60448201526064016105e8565b6001600160a01b0381165f9081526004602052604090205460ff1615610c385760405162461bcd60e51b815260206004820152602560248201527f43616e6e6f742072656d6f7665206a756467652077686f2068617320636f6d6d6044820152641a5d1d195960da1b60648201526084016105e8565b610b39816116f0565b60068181548110610b4b575f80fd5b600a602052815f5260405f208181548110610c69575f80fd5b905f5260205f20015f91509150505481565b335f9081526003602052604090205460ff16610ca95760405162461bcd60e51b81526004016105e89061219a565b6002544211610cf25760405162461bcd60e51b81526020600482015260156024820152745374696c6c20696e20636f6d6d697420706861736560581b60448201526064016105e8565b335f9081526004602052604090205460ff16610d4a5760405162461bcd60e51b8152602060048201526017602482015276135d5cdd0818dbdb5b5a5d081d9bdd195cc8199a5c9cdd604a1b60448201526064016105e8565b335f9081526005602052604090205460ff1615610da95760405162461bcd60e51b815260206004820152601a60248201527f4a756467652068617320616c72656164792072657665616c656400000000000060448201526064016105e8565b838214610dc85760405162461bcd60e51b81526004016105e890612299565b5f338686868686604051602001610de4969594939291906121dd565b60408051601f198184030181529181528151602092830120335f90815260089093529120549091508114610e505760405162461bcd60e51b8152602060048201526013602482015272086dedadad2e8dacadce840dad2e6dac2e8c6d606b1b60448201526064016105e8565b5f805b8681101561106b575f868683818110610e6e57610e6e612285565b9050602002013511610e925760405162461bcd60e51b81526004016105e8906122c9565b858582818110610ea457610ea4612285565b9050602002013582610eb69190612300565b91505f600c5f8a8a85818110610ece57610ece612285565b9050602002016020810190610ee39190611ed9565b6001600160a01b03166001600160a01b031681526020019081526020015f20549050868683818110610f1757610f17612285565b90506020020135600c5f8b8b86818110610f3357610f33612285565b9050602002016020810190610f489190611ed9565b6001600160a01b03166001600160a01b031681526020019081526020015f205f828254610f759190612300565b909155508790508683818110610f8d57610f8d612285565b335f908152600d60209081526040822092029390930135929091508b8b86818110610fba57610fba612285565b9050602002016020810190610fcf9190611ed9565b6001600160a01b0316815260208101919091526040015f2055611062898984818110610ffd57610ffd612285565b90506020020160208101906110129190611ed9565b82600c5f8d8d8881811061102857611028612285565b905060200201602081019061103d9190611ed9565b6001600160a01b03166001600160a01b031681526020019081526020015f205461184f565b50600101610e53565b505f5481111561108d5760405162461bcd60e51b81526004016105e890612313565b335f9081526009602052604090206110a6908888611e10565b50335f908152600a602052604090206110c0908686611e71565b50335f818152600b60209081526040808320879055600590915290819020805460ff19166001179055517fcf576a730e3c24bf60d013ce5190e6143f46ea37d574ea083a0abe84a7d134f590610969908a908a908a908a90612362565b6060600f80548060200260200160405190810160405280929190818152602001828054801561117357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611155575b5050505050905090565b335f9081526003602052604090205460ff166111ab5760405162461bcd60e51b81526004016105e89061219a565b60025442116111f45760405162461bcd60e51b81526020600482015260156024820152745374696c6c20696e20636f6d6d697420706861736560581b60448201526064016105e8565b335f9081526004602052604090205460ff1661124c5760405162461bcd60e51b8152602060048201526017602482015276135d5cdd0818dbdb5b5a5d081d9bdd195cc8199a5c9cdd604a1b60448201526064016105e8565b335f9081526005602052604090205460ff16156112ab5760405162461bcd60e51b815260206004820152601a60248201527f4a756467652068617320616c72656164792072657665616c656400000000000060448201526064016105e8565b8281146112ca5760405162461bcd60e51b81526004016105e890612299565b335f818152600b6020908152604080832054905192936112f5939092899289928992899291016121dd565b60408051601f198184030181529181528151602092830120335f908152600890935291205490915081146113615760405162461bcd60e51b8152602060048201526013602482015272086dedadad2e8dacadce840dad2e6dac2e8c6d606b1b60448201526064016105e8565b5f805b85811015611542575f85858381811061137f5761137f612285565b90506020020135116113a35760405162461bcd60e51b81526004016105e8906122c9565b8484828181106113b5576113b5612285565b90506020020135826113c79190612300565b91505f600c5f8989858181106113df576113df612285565b90506020020160208101906113f49190611ed9565b6001600160a01b03166001600160a01b031681526020019081526020015f2054905085858381811061142857611428612285565b90506020020135600c5f8a8a8681811061144457611444612285565b90506020020160208101906114599190611ed9565b6001600160a01b03166001600160a01b031681526020019081526020015f205f8282546114869190612300565b90915550869050858381811061149e5761149e612285565b335f908152600d60209081526040822092029390930135929091508a8a868181106114cb576114cb612285565b90506020020160208101906114e09190611ed9565b6001600160a01b0316815260208101919091526040015f205561153988888481811061150e5761150e612285565b90506020020160208101906115239190611ed9565b82600c5f8c8c8881811061102857611028612285565b50600101611364565b505f548111156115645760405162461bcd60e51b81526004016105e890612313565b335f90815260096020526040902061157d908787611e10565b50335f908152600a60205260409020611597908585611e71565b50335f8181526005602052604090819020805460ff19166001179055517fcf576a730e3c24bf60d013ce5190e6143f46ea37d574ea083a0abe84a7d134f5906115e7908990899089908990612362565b60405180910390a2505050505050565b6002541561163e5760405162461bcd60e51b8152602060048201526014602482015273111958591b1a5b9948185b1c9958591e481cd95d60621b60448201526064016105e8565b600255565b6001600160a01b0381165f818152600360205260408120805460ff1916600190811790915560068054918201815582527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b03191690921790915560078054916116b58361234a565b90915550506040516001600160a01b038216907fe7f15f9ffff540ae14fdebcd9b6d6cc1fc143933338656c99caa595e8b809039905f90a250565b6001600160a01b0381165f908152600360205260408120805460ff191690555b60065481101561180457816001600160a01b03166006828154811061173757611737612285565b5f918252602090912001546001600160a01b0316036117fc57600680546117609060019061226c565b8154811061177057611770612285565b5f91825260209091200154600680546001600160a01b03909216918390811061179b5761179b612285565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555060068054806117d7576117d76123dc565b5f8281526020902081015f1990810180546001600160a01b0319169055019055611804565b600101611710565b5060078054905f611814836123f0565b90915550506040516001600160a01b038216907f32a792a099c63670df7161917c5efb2fdc734cf9f943ebddccbecef87664f2b7905f90a250565b6001600160a01b0383165f908152600e6020526040902054801561187e576118788484846118d1565b50505050565b5f821180156118c35750600154600f5410806118c35750600c5f6118a0611912565b6001600160a01b03166001600160a01b031681526020019081526020015f205482115b15611878576118788461195b565b6001600160a01b0383165f908152600e602052604090205482821115611900576118fb8482611a09565b611878565b82821015611878576118788482611aab565b600f545f90810361192257505f90565b600f80546119329060019061226c565b8154811061194257611942612285565b5f918252602090912001546001600160a01b0316919050565b600154600f5410156119cd57600f80546001810182557f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac8020180546001600160a01b0319166001600160a01b03841690811790915590545f918252600e6020526040909120819055610b39908290611a09565b5f6119d6611912565b6001600160a01b0381165f908152600e60205260409020549091506119fb8282611b4e565b611a048361195b565b505050565b805f611a1660018361226c565b90505b8015611a9857600c5f600f611a2f60018561226c565b81548110611a3f57611a3f612285565b5f9182526020808320909101546001600160a01b0390811684528382019490945260409283018220549388168252600c905220541115611a8157809150611a86565b611a98565b80611a90816123f0565b915050611a19565b50818114611a0457611a04838383611c79565b80805b600f54811015611b2757600c5f600f8381548110611ace57611ace612285565b5f9182526020808320909101546001600160a01b0390811684528382019490945260409283018220549388168252600c905220541015611b1a57611b13816001612300565b9150611b1f565b611b27565b600101611aae565b50600154811115611b3c57611a048383611b4e565b818114611a0457611a04838383611c79565b600f54811015611c1a57600f80545f9190611b6b9060019061226c565b81548110611b7b57611b7b612285565b5f918252602090912001546001600160a01b0316905080600f611b9f60018561226c565b81548110611baf57611baf612285565b5f91825260208083209190910180546001600160a01b0319166001600160a01b039485161790559183168152600e90915260409020829055600f805480611bf857611bf86123dc565b5f8281526020902081015f1990810180546001600160a01b0319169055019055505b6001600160a01b0382165f818152600e6020526040808220829055517f845de5d493589e5b262d7a53cad5ae62bd69b6cac33cd64b86eab5d1c90194a291611c6d91859190918252602082015260400190565b60405180910390a25050565b808214611a04576001600160a01b0383165f908152600e6020819052604082208390558391600f611cab60018661226c565b81548110611cbb57611cbb612285565b5f9182526020808320909101546001600160a01b03168352820192909252604001812091909155600f611cef60018561226c565b81548110611cff57611cff612285565b5f918252602090912001546001600160a01b03169050600f611d2260018461226c565b81548110611d3257611d32612285565b5f918252602090912001546001600160a01b0316600f611d5360018661226c565b81548110611d6357611d63612285565b5f91825260209091200180546001600160a01b0319166001600160a01b039290921691909117905580600f611d9960018561226c565b81548110611da957611da9612285565b5f9182526020918290200180546001600160a01b0319166001600160a01b0393841617905560408051868152918201859052918616917f845de5d493589e5b262d7a53cad5ae62bd69b6cac33cd64b86eab5d1c90194a2910160405180910390a250505050565b828054828255905f5260205f20908101928215611e61579160200282015b82811115611e615781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190611e2e565b50611e6d929150611eaa565b5090565b828054828255905f5260205f20908101928215611e61579160200282015b82811115611e61578235825591602001919060010190611e8f565b5b80821115611e6d575f8155600101611eab565b80356001600160a01b0381168114611ed4575f5ffd5b919050565b5f60208284031215611ee9575f5ffd5b611ef282611ebe565b9392505050565b5f60208284031215611f09575f5ffd5b5035919050565b5f5f60408385031215611f21575f5ffd5b611f2a83611ebe565b946020939093013593505050565b5f5f83601f840112611f48575f5ffd5b50813567ffffffffffffffff811115611f5f575f5ffd5b6020830191508360208260051b8501011115611f79575f5ffd5b9250929050565b5f5f5f5f5f5f60808789031215611f95575f5ffd5b611f9e87611ebe565b9550602087013567ffffffffffffffff811115611fb9575f5ffd5b611fc589828a01611f38565b909650945050604087013567ffffffffffffffff811115611fe4575f5ffd5b611ff089828a01611f38565b979a9699509497949695606090950135949350505050565b5f5f60408385031215612019575f5ffd5b61202283611ebe565b915061203060208401611ebe565b90509250929050565b5f5f5f5f5f6060868803121561204d575f5ffd5b853567ffffffffffffffff811115612063575f5ffd5b61206f88828901611f38565b909650945050602086013567ffffffffffffffff81111561208e575f5ffd5b61209a88828901611f38565b96999598509660400135949350505050565b5f5f5f5f606085870312156120bf575f5ffd5b8435935060208501359250604085013567ffffffffffffffff8111156120e3575f5ffd5b6120ef87828801611f38565b95989497509550505050565b602080825282518282018190525f918401906040840190835b8181101561213b5783516001600160a01b0316835260209384019390920191600101612114565b509095945050505050565b5f5f5f5f60408587031215612159575f5ffd5b843567ffffffffffffffff81111561216f575f5ffd5b61217b87828801611f38565b909550935050602085013567ffffffffffffffff8111156120e3575f5ffd5b60208082526023908201527f4f6e6c79206a75646765732063616e20706572666f726d20746869732061637460408201526234b7b760e91b606082015260800190565b6bffffffffffffffffffffffff198760601b1681525f60148201875f5b88811015612229576001600160a01b0361221383611ebe565b16835260209283019291909101906001016121fa565b50506001600160fb1b0385111561223e575f5ffd5b8460051b8087833701928352505060200195945050505050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561227f5761227f612258565b92915050565b634e487b7160e01b5f52603260045260245ffd5b602080825260169082015275082e4e4c2f2e640d8cadccee8d040dad2e6dac2e8c6d60531b604082015260600190565b6020808252601d908201527f506f696e7473206d7573742062652067726561746572207468616e2030000000604082015260600190565b8082018082111561227f5761227f612258565b6020808252601e908201527f4578636565647320706f696e747320706572206a75646765206c696d69740000604082015260600190565b5f6001820161235b5761235b612258565b5060010190565b604080825281018490525f8560608301825b878110156123a2576001600160a01b0361238d84611ebe565b16825260209283019290910190600101612374565b5083810360208501528481526001600160fb1b038511156123c1575f5ffd5b8460051b915081866020830137016020019695505050505050565b634e487b7160e01b5f52603160045260245ffd5b5f816123fe576123fe612258565b505f19019056fea2646970667358221220cbe1ab33c0fb21f875b7a519672dc58b3aaac0c3af440eacea66fea67c6c8dac64736f6c634300081c00336080604052348015600e575f5ffd5b506137568061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061021e575f3560e01c80637fa417b31161012a578063b06b0368116100b4578063df15c37e11610079578063df15c37e14610597578063ed1224e5146105ac578063ed896d66146105bf578063edfbf7b6146105e1578063f9ba1a64146105f4575f5ffd5b8063b06b036814610524578063b9a5301314610537578063c6dbe15a14610559578063c8b947871461057b578063cde8e5d31461058e575f5ffd5b8063a2fb1175116100fa578063a2fb1175146104af578063a46c3637146104c2578063accf1bd6146104ca578063ae2ac4ae146104f2578063aedf1b4714610505575f5ffd5b80637fa417b314610437578063842cb6de1461044a578063927cab47146104725780639d9ca28d14610485575f5ffd5b80633a7a53b2116101ab5780636b8e3ad61161017b5780636b8e3ad6146103dd57806376804a03146103f057806377e5c241146104135780637c33404f1461041c5780637dc449c11461042f575f5ffd5b80633a7a53b21461034d5780634bd1c49214610378578063522c90fa146103a25780635ccbc778146103b5575f5ffd5b80630b4023cb116101f15780630b4023cb146102ba5780630fac9100146102cd57806314a1e1c8146102ec57806314bfa3da1461031757806338c043991461032c575f5ffd5b8063036ee850146102225780630655fad61461023e57806309eef43e1461025d5780630a744f571461028f575b5f5ffd5b61022b60025481565b6040519081526020015b60405180910390f35b61022b61024c366004611dd6565b60106020525f908152604090205481565b61027f61026b366004611dd6565b60046020525f908152604090205460ff1681565b6040519015158152602001610235565b61027f61029d366004611dd6565b6001600160a01b03165f9081526009602052604090205460ff1690565b61022b6102c8366004611e3e565b610613565b61022b6102db366004611dd6565b600e6020525f908152604090205481565b6102ff6102fa366004611ec6565b610652565b6040516001600160a01b039091168152602001610235565b61032a610325366004611eee565b610686565b005b61033f61033a366004611dd6565b610be8565b604051610235929190612008565b61027f61035b366004611dd6565b6001600160a01b03165f908152600d602052604090205460ff1690565b61022b610386366004612060565b600f60209081525f928352604080842090915290825290205481565b6102ff6103b0366004612091565b610d23565b61022b6103c3366004611dd6565b6001600160a01b03165f908152600e602052604090205490565b61032a6103eb3660046120a8565b610dab565b6103f86110b1565b60408051938452602084019290925290820152606001610235565b61022b60065481565b61032a61042a36600461211b565b611129565b61022b5f5481565b6007546102ff906001600160a01b031681565b61022b610458366004611dd6565b6001600160a01b03165f9081526010602052604090205490565b61032a610480366004611dd6565b611205565b61027f610493366004611dd6565b6001600160a01b03165f90815260106020526040902054151590565b6102ff6104bd366004612091565b6112be565b60115461022b565b61022b6104d8366004611dd6565b6001600160a01b03165f9081526008602052604090205490565b61032a610500366004611dd6565b6112e6565b61022b610513366004611dd6565b60086020525f908152604090205481565b6102ff610532366004612091565b6113c3565b61027f610545366004611dd6565b600d6020525f908152604090205460ff1681565b61027f610567366004611dd6565b60096020525f908152604090205460ff1681565b61022b610589366004611ec6565b6113d2565b61022b60015481565b61059f6113fd565b604051610235919061216a565b61032a6105ba36600461217c565b61145d565b61027f6105cd366004611dd6565b60036020525f908152604090205460ff1681565b61032a6105ef366004612091565b6114cb565b61022b610602366004611dd6565b600c6020525f908152604090205481565b5f86868686868660405160200161062f969594939291906121d0565b6040516020818303038152906040528051906020012090505b9695505050505050565b600a602052815f5260405f20818154811061066b575f80fd5b5f918252602090912001546001600160a01b03169150829050565b335f9081526003602052604090205460ff166106bd5760405162461bcd60e51b81526004016106b49061224b565b60405180910390fd5b335f9081526009602052604090205460ff1661071b5760405162461bcd60e51b815260206004820152601760248201527f4d75737420636f6d6d697420766f74657320666972737400000000000000000060448201526064016106b4565b335f908152600d602052604090205460ff161561077a5760405162461bcd60e51b815260206004820152601a60248201527f4a756467652068617320616c72656164792072657665616c656400000000000060448201526064016106b4565b8584146107c25760405162461bcd60e51b8152602060048201526016602482015275082e4e4c2f2e640d8cadccee8d040dad2e6dac2e8c6d60531b60448201526064016106b4565b5f3388888888886040516020016107de969594939291906121d0565b60408051601f198184030181529181528151602092830120335f9081526008909352912054909150811461084a5760405162461bcd60e51b8152602060048201526013602482015272086dedadad2e8dacadce840dad2e6dac2e8c6d606b1b60448201526064016106b4565b61085883838a8a8a8a611517565b6108a45760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964205a4b2070726f6f6620666f7220626174636820766f74650060448201526064016106b4565b5f805b88811015610aef575f8888838181106108c2576108c261228e565b90506020020135116109165760405162461bcd60e51b815260206004820152601d60248201527f506f696e7473206d7573742062652067726561746572207468616e203000000060448201526064016106b4565b8787828181106109285761092861228e565b905060200201358261093a91906122b6565b91505f600e5f8c8c858181106109525761095261228e565b90506020020160208101906109679190611dd6565b6001600160a01b03166001600160a01b031681526020019081526020015f2054905088888381811061099b5761099b61228e565b90506020020135600e5f8d8d868181106109b7576109b761228e565b90506020020160208101906109cc9190611dd6565b6001600160a01b03166001600160a01b031681526020019081526020015f205f8282546109f991906122b6565b909155508990508883818110610a1157610a1161228e565b335f908152600f60209081526040822092029390930135929091508d8d86818110610a3e57610a3e61228e565b9050602002016020810190610a539190611dd6565b6001600160a01b0316815260208101919091526040015f2055610ae68b8b84818110610a8157610a8161228e565b9050602002016020810190610a969190611dd6565b82600e5f8f8f88818110610aac57610aac61228e565b9050602002016020810190610ac19190611dd6565b6001600160a01b03166001600160a01b031681526020019081526020015f20546115ae565b506001016108a7565b505f54811115610b415760405162461bcd60e51b815260206004820152601e60248201527f4578636565647320706f696e747320706572206a75646765206c696d6974000060448201526064016106b4565b335f908152600a60205260409020610b5a908a8a611d00565b50335f908152600b60205260409020610b74908888611d61565b50335f818152600c60209081526040808320899055600d90915290819020805460ff19166001179055517f9238d665aa500bce2a9a2999db6f7e95e135c81a991b6c5f3ed08e35adee0dc490610bd5908c908c908c908c908b908b906122f7565b60405180910390a2505050505050505050565b6001600160a01b0381165f908152600d6020526040902054606090819060ff16610c545760405162461bcd60e51b815260206004820152601c60248201527f4a7564676520686173206e6f742072657665616c656420766f7465730000000060448201526064016106b4565b6001600160a01b0383165f908152600a60209081526040808320600b83529281902083548251818502810185019093528083529092849190830182828015610cc357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610ca5575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015610d1357602002820191905f5260205f20905b815481526020019060010190808311610cff575b5050505050905091509150915091565b5f5f82118015610d3557506011548211155b610d745760405162461bcd60e51b815260206004820152601060248201526f24b73b30b634b2103837b9b4ba34b7b760811b60448201526064016106b4565b6011610d8160018461238b565b81548110610d9157610d9161228e565b5f918252602090912001546001600160a01b031692915050565b335f9081526003602052604090205460ff16610dd95760405162461bcd60e51b81526004016106b49061224b565b600254421115610e2b5760405162461bcd60e51b815260206004820152601a60248201527f566f74696e6720646561646c696e65206861732070617373656400000000000060448201526064016106b4565b838214610e735760405162461bcd60e51b8152602060048201526016602482015275082e4e4c2f2e640d8cadccee8d040dad2e6dac2e8c6d60531b60448201526064016106b4565b83610ecf5760405162461bcd60e51b815260206004820152602660248201527f4d75737420766f746520666f72206174206c65617374206f6e6520706172746960448201526518da5c185b9d60d21b60648201526084016106b4565b335f9081526009602052604090205460ff1615610f2e5760405162461bcd60e51b815260206004820152601b60248201527f4a756467652068617320616c726561647920636f6d6d6974746564000000000060448201526064016106b4565b5f805b85811015610fce575f858583818110610f4c57610f4c61228e565b9050602002013511610fa05760405162461bcd60e51b815260206004820152601d60248201527f506f696e7473206d7573742062652067726561746572207468616e203000000060448201526064016106b4565b848482818110610fb257610fb261228e565b9050602002013582610fc491906122b6565b9150600101610f31565b505f548111156110205760405162461bcd60e51b815260206004820152601e60248201527f4578636565647320706f696e747320706572206a75646765206c696d6974000060448201526064016106b4565b5f33878787878760405160200161103c969594939291906121d0565b60408051601f198184030181528282528051602091820120335f818152600884528481208390556009845293909320805460ff19166001179055808452935090917f3caa01a3e66450d1ef355c5d8ca94ed78d63d3b40df4f0518633636aa650bd21910160405180910390a250505050505050565b6006545f8080805b60055481101561111b57600d5f600583815481106110d9576110d961228e565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205460ff1615611113578161110f8161239e565b9250505b6001016110b9565b506011549394909392509050565b5f541561116e5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016106b4565b5f849055600183905560405161118390611d9a565b604051809103905ff08015801561119c573d5f5f3e3d5ffd5b50600780546001600160a01b0319166001600160a01b03929092169190911790555f5b818110156111fe576111f68383838181106111dc576111dc61228e565b90506020020160208101906111f19190611dd6565b611630565b6001016111bf565b5050505050565b6001600160a01b0381166112535760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206a75646765206164647265737360581b60448201526064016106b4565b6001600160a01b0381165f9081526003602052604090205460ff16156112b25760405162461bcd60e51b81526020600482015260146024820152734a7564676520616c72656164792065786973747360601b60448201526064016106b4565b6112bb81611630565b50565b601181815481106112cd575f80fd5b5f918252602090912001546001600160a01b0316905081565b6001600160a01b0381165f9081526003602052604090205460ff166113445760405162461bcd60e51b8152602060048201526014602482015273129d5919d948191bd95cc81b9bdd08195e1a5cdd60621b60448201526064016106b4565b6001600160a01b0381165f9081526009602052604090205460ff16156113ba5760405162461bcd60e51b815260206004820152602560248201527f43616e6e6f742072656d6f7665206a756467652077686f2068617320636f6d6d6044820152641a5d1d195960da1b60648201526084016106b4565b6112bb816116dd565b600581815481106112cd575f80fd5b600b602052815f5260405f2081815481106113eb575f80fd5b905f5260205f20015f91509150505481565b6060601180548060200260200160405190810160405280929190818152602001828054801561145357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611435575b5050505050905090565b60405162461bcd60e51b815260206004820152603760248201527f55736520636f6d6d69744261746368566f74657320616e642072657665616c4260448201527f61746368566f74657320666f72205a4b20766f74696e6700000000000000000060648201526084016106b4565b600254156115125760405162461bcd60e51b8152602060048201526014602482015273111958591b1a5b9948185b1c9958591e481cd95d60621b60448201526064016106b4565b600255565b5f80805b83811015611551578484828181106115355761153561228e565b905060200201358261154791906122b6565b915060010161151b565b505f54811115611564575f915050610648565b5f5b8381101561159f578484828181106115805761158061228e565b905060200201355f03611597575f92505050610648565b600101611566565b50505093151595945050505050565b6001600160a01b0383165f9081526010602052604090205480156115dd576115d784848461183c565b50505050565b5f82118015611622575060015460115410806116225750600e5f6115ff61187d565b6001600160a01b03166001600160a01b031681526020019081526020015f205482115b156115d7576115d7846118c6565b6001600160a01b0381165f818152600360205260408120805460ff1916600190811790915560058054918201815582527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b03191690921790915560068054916116a28361239e565b90915550506040516001600160a01b038216907fe7f15f9ffff540ae14fdebcd9b6d6cc1fc143933338656c99caa595e8b809039905f90a250565b6001600160a01b0381165f908152600360205260408120805460ff191690555b6005548110156117f157816001600160a01b0316600582815481106117245761172461228e565b5f918252602090912001546001600160a01b0316036117e9576005805461174d9060019061238b565b8154811061175d5761175d61228e565b5f91825260209091200154600580546001600160a01b0390921691839081106117885761178861228e565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555060058054806117c4576117c46123b6565b5f8281526020902081015f1990810180546001600160a01b03191690550190556117f1565b6001016116fd565b5060068054905f611801836123ca565b90915550506040516001600160a01b038216907f32a792a099c63670df7161917c5efb2fdc734cf9f943ebddccbecef87664f2b7905f90a250565b6001600160a01b0383165f908152601060205260409020548282111561186b576118668482611974565b6115d7565b828210156115d7576115d78482611a16565b6011545f90810361188d57505f90565b6011805461189d9060019061238b565b815481106118ad576118ad61228e565b5f918252602090912001546001600160a01b0316919050565b600154601154101561193857601180546001810182557f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c680180546001600160a01b0319166001600160a01b03841690811790915590545f918252601060205260409091208190556112bb908290611974565b5f61194161187d565b6001600160a01b0381165f908152601060205260409020549091506119668282611ab9565b61196f836118c6565b505050565b805f61198160018361238b565b90505b8015611a0357600e5f601161199a60018561238b565b815481106119aa576119aa61228e565b5f9182526020808320909101546001600160a01b0390811684528382019490945260409283018220549388168252600e9052205411156119ec578091506119f1565b611a03565b806119fb816123ca565b915050611984565b5081811461196f5761196f838383611b9f565b80805b601154811015611a9257600e5f60118381548110611a3957611a3961228e565b5f9182526020808320909101546001600160a01b0390811684528382019490945260409283018220549388168252600e905220541015611a8557611a7e8160016122b6565b9150611a8a565b611a92565b600101611a19565b50600154811115611aa75761196f8383611ab9565b81811461196f5761196f838383611b9f565b601154811015611b8557601180545f9190611ad69060019061238b565b81548110611ae657611ae661228e565b5f918252602090912001546001600160a01b03169050806011611b0a60018561238b565b81548110611b1a57611b1a61228e565b5f91825260208083209190910180546001600160a01b0319166001600160a01b0394851617905591831681526010909152604090208290556011805480611b6357611b636123b6565b5f8281526020902081015f1990810180546001600160a01b0319169055019055505b506001600160a01b03165f90815260106020526040812055565b80821461196f576001600160a01b0383165f908152601060208190526040822083905583916011611bd160018661238b565b81548110611be157611be161228e565b5f9182526020808320909101546001600160a01b031683528201929092526040018120919091556011611c1560018561238b565b81548110611c2557611c2561228e565b5f918252602090912001546001600160a01b031690506011611c4860018461238b565b81548110611c5857611c5861228e565b5f918252602090912001546001600160a01b03166011611c7960018661238b565b81548110611c8957611c8961228e565b5f91825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055806011611cbf60018561238b565b81548110611ccf57611ccf61228e565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555050505050565b828054828255905f5260205f20908101928215611d51579160200282015b82811115611d515781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190611d1e565b50611d5d929150611da7565b5090565b828054828255905f5260205f20908101928215611d51579160200282015b82811115611d51578235825591602001919060010190611d7f565b611341806123e083390190565b5b80821115611d5d575f8155600101611da8565b80356001600160a01b0381168114611dd1575f5ffd5b919050565b5f60208284031215611de6575f5ffd5b611def82611dbb565b9392505050565b5f5f83601f840112611e06575f5ffd5b50813567ffffffffffffffff811115611e1d575f5ffd5b6020830191508360208260051b8501011115611e37575f5ffd5b9250929050565b5f5f5f5f5f5f60808789031215611e53575f5ffd5b611e5c87611dbb565b9550602087013567ffffffffffffffff811115611e77575f5ffd5b611e8389828a01611df6565b909650945050604087013567ffffffffffffffff811115611ea2575f5ffd5b611eae89828a01611df6565b979a9699509497949695606090950135949350505050565b5f5f60408385031215611ed7575f5ffd5b611ee083611dbb565b946020939093013593505050565b5f5f5f5f5f5f5f6080888a031215611f04575f5ffd5b873567ffffffffffffffff811115611f1a575f5ffd5b611f268a828b01611df6565b909850965050602088013567ffffffffffffffff811115611f45575f5ffd5b611f518a828b01611df6565b90965094505060408801359250606088013567ffffffffffffffff811115611f77575f5ffd5b8801601f81018a13611f87575f5ffd5b803567ffffffffffffffff811115611f9d575f5ffd5b8a6020828401011115611fae575f5ffd5b602082019350809250505092959891949750929550565b5f8151808452602084019350602083015f5b82811015611ffe5781516001600160a01b0316865260209586019590910190600101611fd7565b5093949350505050565b604081525f61201a6040830185611fc5565b82810360208401528084518083526020830191506020860192505f5b81811015612054578351835260209384019390920191600101612036565b50909695505050505050565b5f5f60408385031215612071575f5ffd5b61207a83611dbb565b915061208860208401611dbb565b90509250929050565b5f602082840312156120a1575f5ffd5b5035919050565b5f5f5f5f5f606086880312156120bc575f5ffd5b853567ffffffffffffffff8111156120d2575f5ffd5b6120de88828901611df6565b909650945050602086013567ffffffffffffffff8111156120fd575f5ffd5b61210988828901611df6565b96999598509660400135949350505050565b5f5f5f5f6060858703121561212e575f5ffd5b8435935060208501359250604085013567ffffffffffffffff811115612152575f5ffd5b61215e87828801611df6565b95989497509550505050565b602081525f611def6020830184611fc5565b5f5f5f5f6040858703121561218f575f5ffd5b843567ffffffffffffffff8111156121a5575f5ffd5b6121b187828801611df6565b909550935050602085013567ffffffffffffffff811115612152575f5ffd5b6bffffffffffffffffffffffff198760601b1681525f60148201875f5b8881101561221c576001600160a01b0361220683611dbb565b16835260209283019291909101906001016121ed565b50506001600160fb1b03851115612231575f5ffd5b8460051b8087833701928352505060200195945050505050565b60208082526023908201527f4f6e6c79206a75646765732063616e20706572666f726d20746869732061637460408201526234b7b760e91b606082015260800190565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b808201808211156122c9576122c96122a2565b92915050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b606080825281018690525f8760808301825b89811015612337576001600160a01b0361232284611dbb565b16825260209283019290910190600101612309565b5083810360208501528681526001600160fb1b03871115612356575f5ffd5b8660051b915081886020830137018281036020908101604085015261237e90820185876122cf565b9998505050505050505050565b818103818111156122c9576122c96122a2565b5f600182016123af576123af6122a2565b5060010190565b634e487b7160e01b5f52603160045260245ffd5b5f816123d8576123d86122a2565b505f19019056fe608060405234801561000f575f5ffd5b50338061003557604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b61003e8161004c565b5061004761009b565b6102c9565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b604080518082019091525f80825260208201526100bc90600190600261022b565b50604080518082019091525f80825260208201526100de90600390600261022b565b50604080518082019091525f808252602082015261010090600590600261022b565b50604080518082019091525f808252602082015261012290600790600261022b565b505f6009819055600a81905560408051808201909152818152602081019190915261015190600b90600261022b565b50604080518082019091525f808252602082015261017390600d90600261022b565b5060408051600480825260a082019092529060208201608080368337505081516101a492600f925060200190610269565b5060408051600480825260a082019092529060208201608080368337505081516101d5926010925060200190610269565b505f5b6004811015610228575f600f82815481106101f5576101f56102b5565b905f5260205f2001819055505f60108281548110610215576102156102b5565b5f918252602090912001556001016101d8565b50565b8260028101928215610259579160200282015b8281111561025957825182559160200191906001019061023e565b506102659291506102a1565b5090565b828054828255905f5260205f20908101928215610259579160200282018281111561025957825182559160200191906001019061023e565b5b80821115610265575f81556001016102a2565b634e487b7160e01b5f52603260045260245ffd5b61106b806102d65f395ff3fe608060405234801561000f575f5ffd5b5060043610610127575f3560e01c806376f1ddb0116100a95780639933986f1161006e5780639933986f1461023e578063a3a6ed2714610251578063dfc4cd4e14610264578063edeb764514610282578063f2fde38b14610295575f5ffd5b806376f1ddb0146101e4578063788723af146101eb578063788dfe91146101fe5780637d69eed9146102115780638da5cb5b14610224575f5ffd5b806325f87d23116100ef57806325f87d231461019357806331acc54d1461019b578063521790fa146101be578063588f357d146101d1578063715018a6146101da575f5ffd5b806301e92ceb1461012b578063055baf44146101475780630c1db8191461015a578063159bd5c41461016d57806318d1632b14610180575b5f5ffd5b61013460095481565b6040519081526020015b60405180910390f35b610134610155366004610b11565b6102a8565b610134610168366004610b11565b6102be565b61013461017b366004610b11565b6102dd565b61013461018e366004610b11565b6102ec565b610134606481565b6101ae6101a9366004610d50565b6102fb565b604051901515815260200161013e565b6101346101cc366004610b11565b610508565b610134600a5481565b6101e2610517565b005b6101345f81565b6101346101f9366004610b11565b61052a565b6101ae61020c366004610dc8565b610539565b6101ae61021f366004610dea565b6105d3565b5f546040516001600160a01b03909116815260200161013e565b6101e261024c366004610e2b565b6105f1565b61013461025f366004610b11565b6106b3565b61026c6106c2565b60405161013e9a99989796959493929190610f71565b610134610290366004610b11565b6108f7565b6101e26102a3366004611008565b610906565b600581600281106102b7575f80fd5b0154905081565b601081815481106102cd575f80fd5b5f91825260209091200154905081565b600181600281106102b7575f80fd5b600381600281106102b7575f80fd5b5f5f6103078787610943565b905080610317575f9150506104ff565b60038651101561036e5760405162461bcd60e51b815260206004820152601d60248201527f496e76616c6964207075626c6963207369676e616c73206c656e67746800000060448201526064015b60405180910390fd5b828660018151811061038257610382611021565b6020026020010151146103c95760405162461bcd60e51b815260206004820152600f60248201526e0a0ded2dce8e640dad2e6dac2e8c6d608b1b6044820152606401610365565b60648311156104115760405162461bcd60e51b8152602060048201526014602482015273496e76616c696420706f696e74732072616e676560601b6044820152606401610365565b6040516bffffffffffffffffffffffff19606087901b1660208201525f90603401604051602081830303815290604052805190602001205f1c9050808760028151811061046057610460611021565b6020026020010151146104ab5760405162461bcd60e51b8152602060048201526013602482015272094eac8ceca40d0c2e6d040dad2e6dac2e8c6d606b1b6044820152606401610365565b846001600160a01b0316866001600160a01b03167f164ad8fe51ccca8895bfc9ff121e38344eb66e2037b060dee3addb1f8ca87a2e866040516104f091815260200190565b60405180910390a36001925050505b95945050505050565b600b81600281106102b7575f80fd5b61051f6109ee565b6105285f610a1a565b565b600f81815481106102cd575f80fd5b8051515f90158061054d5750815160200151155b1561055957505f919050565b6020820151515115806105725750602082810151510151155b1561057e57505f919050565b602082810151015151158061059b57506020828101518101510151155b156105a757505f919050565b60408201515115806105bf5750604082015160200151155b156105cb57505f919050565b506001919050565b5f60648211156105e457505f6105e9565b508315155b949350505050565b6105f96109ee565b61060660018b6002610a69565b5061061460038a6002610a69565b506106226005896002610a69565b506106306007886002610a69565b506009869055600a859055610648600b856002610a69565b50610656600d846002610a69565b50815161066a90600f906020850190610aa7565b50805161067e906010906020840190610aa7565b506040517f700ad29a50ef4fb344099eee3ea1747a3082e61404dc86e753b7fde79277e806905f90a150505050505050505050565b600d81600281106102b7575f80fd5b6106ca610adf565b6106d2610adf565b6106da610adf565b6106e2610adf565b5f5f6106ec610adf565b6106f4610adf565b6060806001600360056007600954600a54600b600d600f601089600280602002604051908101604052809291908260028015610745576020028201915b815481526020019060010190808311610731575b50506040805180820191829052949e508d935060029250905082845b8154815260200190600101908083116107615750506040805180820191829052949d508c935060029250905082845b8154815260200190600101908083116107905750506040805180820191829052949c508b935060029250905082845b8154815260200190600101908083116107bf5750506040805180820191829052949b5088935060029250905082845b8154815260200190600101908083116107ee575050604080518082019182905294985087935060029250905082845b81548152602001906001019080831161081d57505050505092508180548060200260200160405190810160405280929190818152602001828054801561088057602002820191905f5260205f20905b81548152602001906001019080831161086c575b50505050509150808054806020026020016040519081016040528092919081815260200182805480156108d057602002820191905f5260205f20905b8154815260200190600101908083116108bc575b50505050509050995099509950995099509950995099509950995090919293949596979899565b600781600281106102b7575f80fd5b61090e6109ee565b6001600160a01b03811661093757604051631e4fbdf760e01b81525f6004820152602401610365565b61094081610a1a565b50565b8151515f9015806109575750825160200151155b1561096357505f6109e8565b60208301515151158061097c5750602083810151510151155b1561098857505f6109e8565b60208381015101515115806109a557506020838101518101510151155b156109b157505f6109e8565b60408301515115806109c95750604083015160200151155b156109d557505f6109e8565b81515f036109e457505f6109e8565b5060015b92915050565b5f546001600160a01b031633146105285760405163118cdaa760e01b8152336004820152602401610365565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b8260028101928215610a97579160200282015b82811115610a97578251825591602001919060010190610a7c565b50610aa3929150610afd565b5090565b828054828255905f5260205f20908101928215610a975791602002820182811115610a97578251825591602001919060010190610a7c565b60405180604001604052806002906020820280368337509192915050565b5b80821115610aa3575f8155600101610afe565b5f60208284031215610b21575f5ffd5b5035919050565b634e487b7160e01b5f52604160045260245ffd5b6040805190810167ffffffffffffffff81118282101715610b5f57610b5f610b28565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610b8e57610b8e610b28565b604052919050565b5f60408284031215610ba6575f5ffd5b610bae610b3c565b823581526020928301359281019290925250919050565b5f82601f830112610bd4575f5ffd5b610bdc610b3c565b806040840185811115610bed575f5ffd5b845b81811015610c07578035845260209384019301610bef565b509095945050505050565b5f818303610100811215610c24575f5ffd5b6040516060810167ffffffffffffffff81118282101715610c4757610c47610b28565b604052915081610c578585610b96565b81526080603f1983011215610c6a575f5ffd5b610c72610b3c565b9150610c818560408601610bc5565b8252610c908560808601610bc5565b6020830152816020820152610ca88560c08601610b96565b6040820152505092915050565b5f82601f830112610cc4575f5ffd5b813567ffffffffffffffff811115610cde57610cde610b28565b8060051b610cee60208201610b65565b91825260208185018101929081019086841115610d09575f5ffd5b6020860192505b83831015610d2b578235825260209283019290910190610d10565b9695505050505050565b80356001600160a01b0381168114610d4b575f5ffd5b919050565b5f5f5f5f5f6101808688031215610d65575f5ffd5b610d6f8787610c12565b945061010086013567ffffffffffffffff811115610d8b575f5ffd5b610d9788828901610cb5565b945050610da76101208701610d35565b9250610db66101408701610d35565b94979396509194610160013592915050565b5f6101008284031215610dd9575f5ffd5b610de38383610c12565b9392505050565b5f5f5f5f60808587031215610dfd575f5ffd5b84359350610e0d60208601610d35565b9250610e1b60408601610d35565b9396929550929360600135925050565b5f5f5f5f5f5f5f5f5f5f6102008b8d031215610e45575f5ffd5b610e4f8c8c610bc5565b9950610e5e8c60408d01610bc5565b9850610e6d8c60808d01610bc5565b9750610e7c8c60c08d01610bc5565b96506101008b013595506101208b01359450610e9c8c6101408d01610bc5565b9350610eac8c6101808d01610bc5565b92506101c08b013567ffffffffffffffff811115610ec8575f5ffd5b610ed48d828e01610cb5565b9250506101e08b013567ffffffffffffffff811115610ef1575f5ffd5b610efd8d828e01610cb5565b9150509295989b9194979a5092959850565b805f5b6002811015610f31578151845260209384019390910190600101610f12565b50505050565b5f8151808452602084019350602083015f5b82811015610f67578151865260209586019590910190600101610f49565b5093949350505050565b610f7b818c610f0f565b610f88604082018b610f0f565b610f95608082018a610f0f565b610fa260c0820189610f0f565b8661010082015285610120820152610fbe610140820186610f0f565b610fcc610180820185610f0f565b6102006101c08201525f610fe4610200830185610f37565b8281036101e0840152610ff78185610f37565b9d9c50505050505050505050505050565b5f60208284031215611018575f5ffd5b610de382610d35565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220f9bc302c8f5885bbcfccbb1f5fc4b0e3e617bd00c22580af95ab45c2febf228464736f6c634300081c0033a26469706673582212203482b60fbfecf3568725fa8037545610a8ab5da8b1487a62da7d499df061115764736f6c634300081c00336080604052348015600e575f5ffd5b506113b68061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061011c575f3560e01c8063927cab47116100a9578063cd6dc6871161006e578063cd6dc68714610273578063d663418514610286578063df15c37e14610299578063e61bce4a146102ae578063ed1224e5146102b7575f5ffd5b8063927cab471461021f5780639d9ca28d14610232578063a46c363714610245578063ad30909c1461024d578063ae2ac4ae14610260575f5ffd5b8063702d2dca116100ef578063702d2dca146101a157806376804a03146101c15780637c33404f146101e4578063842cb6de146101fa5780638d6859a31461020d575f5ffd5b8063158ef93e146101205780633a7a53b214610142578063522c90fa146101555780635ccbc77814610180575b5f5ffd5b60025461012d9060ff1681565b60405190151581526020015b60405180910390f35b61012d610150366004610e90565b6102ca565b610168610163366004610eab565b61033d565b6040516001600160a01b039091168152602001610139565b61019361018e366004610e90565b6103a8565b604051908152602001610139565b6101b46101af366004610f0a565b610415565b6040516101399190610fb0565b6101c96104af565b60408051938452602084019290925290820152606001610139565b6101f86101f2366004610fc2565b50505050565b005b610193610208366004610e90565b610530565b5f54610168906001600160a01b031681565b6101f861022d366004610e90565b610562565b61012d610240366004610e90565b610587565b6101936105b9565b61019361025b366004610ff9565b610632565b6101f861026e366004610e90565b610675565b6101f8610281366004611038565b6106f3565b61012d610294366004610ff9565b610826565b6102a1610866565b6040516101399190611062565b61019360015481565b6101f86102c5366004610f0a565b6108d3565b5f8054604051631d3d29d960e11b81526001600160a01b03848116600483015290911690633a7a53b2906024015b602060405180830381865afa158015610313573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061033791906110ad565b92915050565b5f8054604051632916487d60e11b8152600481018490526001600160a01b039091169063522c90fa90602401602060405180830381865afa158015610384573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061033791906110dc565b5f8054604051630b9978ef60e31b81526001600160a01b03848116600483015290911690635ccbc778906024015b602060405180830381865afa1580156103f1573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061033791906110f7565b60608382146104645760405162461bcd60e51b8152602060048201526016602482015275082e4e4c2f2e640d8cadccee8d040dad2e6dac2e8c6d60531b60448201526064015b60405180910390fd5b6104a6858590506001548585808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250610bac92505050565b95945050505050565b5f5f5f5f5f9054906101000a90046001600160a01b03166001600160a01b03166376804a036040518163ffffffff1660e01b8152600401606060405180830381865afa158015610501573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610525919061110e565b925092509250909192565b5f80546040516342165b6f60e11b81526001600160a01b0384811660048301529091169063842cb6de906024016103d6565b60025460ff166105845760405162461bcd60e51b815260040161045b90611139565b50565b5f8054604051639d9ca28d60e01b81526001600160a01b03848116600483015290911690639d9ca28d906024016102f8565b5f5f5f9054906101000a90046001600160a01b03166001600160a01b031663a46c36376040518163ffffffff1660e01b8152600401602060405180830381865afa158015610609573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061062d91906110f7565b905090565b5f61066e8383808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250610da392505050565b9392505050565b60025460ff166106975760405162461bcd60e51b815260040161045b90611139565b5f54604051635715625760e11b81526001600160a01b0383811660048301529091169063ae2ac4ae906024015f604051808303815f87803b1580156106da575f5ffd5b505af11580156106ec573d5f5f3e3d5ffd5b5050505050565b60025460ff161561073c5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604482015260640161045b565b6001600160a01b0382166107925760405162461bcd60e51b815260206004820152601a60248201527f496e76616c6964206261736520766f74696e672073797374656d000000000000604482015260640161045b565b5f81116107f25760405162461bcd60e51b815260206004820152602860248201527f4372656469747320706572206a75646765206d75737420626520677265617465604482015267072207468616e20360c41b606482015260840161045b565b5f80546001600160a01b0319166001600160a01b03939093169290921790915560019081556002805460ff19169091179055565b5f61066e8383808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506001549150610e099050565b5f805460408051636f8ae1bf60e11b815290516060936001600160a01b039093169263df15c37e92600480820193918290030181865afa1580156108ac573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261062d9190810190611184565b60025460ff166108f55760405162461bcd60e51b815260040161045b90611139565b82811461093d5760405162461bcd60e51b8152602060048201526016602482015275082e4e4c2f2e640d8cadccee8d040dad2e6dac2e8c6d60531b604482015260640161045b565b826109995760405162461bcd60e51b815260206004820152602660248201527f4d75737420766f746520666f72206174206c65617374206f6e6520706172746960448201526518da5c185b9d60d21b606482015260840161045b565b6109d88282808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506001549150610e099050565b610a2e5760405162461bcd60e51b815260206004820152602160248201527f496e76616c69642071756164726174696320766f746520616c6c6f636174696f6044820152603760f91b606482015260840161045b565b5f610a6a8383808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250610da392505050565b90505f8267ffffffffffffffff811115610a8657610a86611170565b604051908082528060200260200182016040528015610aaf578160200160208202803683370190505b5090505f5b83811015610afa57848482818110610ace57610ace61124f565b90506020020135828281518110610ae757610ae761124f565b6020908102919091010152600101610ab4565b505f5460405163ed1224e560e01b81526001600160a01b039091169063ed1224e590610b2e908990899086906004016112a1565b5f604051808303815f87803b158015610b45575f5ffd5b505af1158015610b57573d5f5f3e3d5ffd5b50505050336001600160a01b03167fddb4be8a0e1c30287cc29e02e106e0241158ba880ac46633c17714946c1e13528787878787604051610b9c9594939291906112d0565b60405180910390a2505050505050565b606081518414610bfe5760405162461bcd60e51b815260206004820152601b60248201527f507265666572656e636573206c656e677468206d69736d617463680000000000604482015260640161045b565b5f8411610c585760405162461bcd60e51b815260206004820152602260248201527f4d7573742068617665206174206c65617374206f6e65207061727469636970616044820152611b9d60f21b606482015260840161045b565b5f8467ffffffffffffffff811115610c7257610c72611170565b604051908082528060200260200182016040528015610c9b578160200160208202803683370190505b5090505f805b86811015610cd857848181518110610cbb57610cbb61124f565b602002602001015182610cce9190611337565b9150600101610ca1565b50805f03610d2c575f610cf3610cee888861134a565b610e1f565b90505f5b87811015610d255781848281518110610d1257610d1261124f565b6020908102919091010152600101610cf7565b5050610d9a565b5f5b86811015610d98575f82868381518110610d4a57610d4a61124f565b602002602001015188610d5d9190611369565b610d67919061134a565b9050610d7281610e1f565b848381518110610d8457610d8461124f565b602090810291909101015250600101610d2e565b505b50949350505050565b5f80805b8351811015610e0257838181518110610dc257610dc261124f565b6020026020010151848281518110610ddc57610ddc61124f565b6020026020010151610dee9190611369565b610df89083611337565b9150600101610da7565b5092915050565b5f5f610e1484610da3565b909210159392505050565b5f815f03610e2e57505f919050565b5f6002610e3c846001611337565b610e46919061134a565b9050825b8082101561066e575080600281610e61818761134a565b610e6b9190611337565b610e75919061134a565b9150610e4a565b6001600160a01b0381168114610584575f5ffd5b5f60208284031215610ea0575f5ffd5b813561066e81610e7c565b5f60208284031215610ebb575f5ffd5b5035919050565b5f5f83601f840112610ed2575f5ffd5b50813567ffffffffffffffff811115610ee9575f5ffd5b6020830191508360208260051b8501011115610f03575f5ffd5b9250929050565b5f5f5f5f60408587031215610f1d575f5ffd5b843567ffffffffffffffff811115610f33575f5ffd5b610f3f87828801610ec2565b909550935050602085013567ffffffffffffffff811115610f5e575f5ffd5b610f6a87828801610ec2565b95989497509550505050565b5f8151808452602084019350602083015f5b82811015610fa6578151865260209586019590910190600101610f88565b5093949350505050565b602081525f61066e6020830184610f76565b5f5f5f5f60608587031215610fd5575f5ffd5b8435935060208501359250604085013567ffffffffffffffff811115610f5e575f5ffd5b5f5f6020838503121561100a575f5ffd5b823567ffffffffffffffff811115611020575f5ffd5b61102c85828601610ec2565b90969095509350505050565b5f5f60408385031215611049575f5ffd5b823561105481610e7c565b946020939093013593505050565b602080825282518282018190525f918401906040840190835b818110156110a25783516001600160a01b031683526020938401939092019160010161107b565b509095945050505050565b5f602082840312156110bd575f5ffd5b8151801515811461066e575f5ffd5b80516110d781610e7c565b919050565b5f602082840312156110ec575f5ffd5b815161066e81610e7c565b5f60208284031215611107575f5ffd5b5051919050565b5f5f5f60608486031215611120575f5ffd5b5050815160208301516040909301519094929350919050565b60208082526018908201527f436f6e7472616374206e6f7420696e697469616c697a65640000000000000000604082015260600190565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215611194575f5ffd5b815167ffffffffffffffff8111156111aa575f5ffd5b8201601f810184136111ba575f5ffd5b805167ffffffffffffffff8111156111d4576111d4611170565b8060051b604051601f19603f830116810181811067ffffffffffffffff8211171561120157611201611170565b60405291825260208184018101929081018784111561121e575f5ffd5b6020850194505b8385101561124457611236856110cc565b815260209485019401611225565b509695505050505050565b634e487b7160e01b5f52603260045260245ffd5b8183526020830192505f815f5b84811015610fa657813561128381610e7c565b6001600160a01b031686526020958601959190910190600101611270565b604081525f6112b4604083018587611263565b82810360208401526112c68185610f76565b9695505050505050565b606081525f6112e3606083018789611263565b82810360208401528481526001600160fb1b03851115611301575f5ffd5b8460051b80876020840137604093909301939093525001602001949350505050565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561033757610337611323565b5f8261136457634e487b7160e01b5f52601260045260245ffd5b500490565b80820281158282048414176103375761033761132356fea2646970667358221220f265eef6c2bc7a7df1f0c9b6cb94dba12d095d89af30c745ee67e9504816bc3564736f6c634300081c0033000000000000000000000000c00779a913c9bb701a1e20f36381d860184a6b5600000000000000000000000045312ea0eff7e09c83cbe249fa1d7598c4c8cd4e000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e8
Deployed Bytecode
0x6080604052600436106102bf575f3560e01c80638b52d56d1161016f578063cbf7719b116100d8578063da7bafce11610092578063e456766b1161006d578063e456766b14610a57578063e79b754714610a85578063ed440f5014610ab9578063fa71039114610ada575f5ffd5b8063da7bafce146109f1578063db5abbd014610a05578063e029983614610a24575f5ffd5b8063cbf7719b146108df578063cc60cf1f1461090a578063cda90b8814610941578063ce762f6414610974578063ce836b21146109a7578063d14b85b9146109c6575f5ffd5b8063a3c2001d11610129578063a3c2001d1461080c578063a4d3180514610821578063a717430c14610835578063aadc3b7214610848578063bff28ea814610881578063c45a0155146108ac575f5ffd5b80638b52d56d146107455780638bc6dae0146107645780638f02fc6f146107785780639dd58b58146107ab5780639f2ce678146107bf578063a074696a146107de575f5ffd5b80633fc8cef31161022b5780635c60da1b116101e55780636b792c4b116101c05780636b792c4b1461069a5780636f9948c7146106b957806373615775146106f85780637fae382714610726575f5ffd5b80635c60da1b146106155780635e2490211461064857806366cab36b14610667575f5ffd5b80633fc8cef3146104d057806340b1187d146105035780634dd218fd1461059957806351dc554b146105b8578063543f5d38146105cb57806355f8cf0d146105e1575f5ffd5b80632244d1cb1161027c5780632244d1cb146103f557806322e669d31461042c578063234c03551461045f5780632c7dc1931461047e57806332bb3ed81461049d5780633f3642dd146104bc575f5ffd5b80630368d346146102c35780630396ec101461030a5780630c2891b71461032b5780630cf79cde14610340578063138051281461037e5780632062b2a7146103a9575b5f5ffd5b3480156102ce575f5ffd5b506102f76102dd366004612bd5565b6001600160a01b03165f9081526002602052604090205490565b6040519081526020015b60405180910390f35b348015610315575f5ffd5b50610329610324366004612bee565b610aee565b005b348015610336575f5ffd5b506102f760065481565b34801561034b575f5ffd5b5061036e61035a366004612c21565b60096020525f908152604090205460ff1681565b6040519015158152602001610301565b348015610389575f5ffd5b506102f7610398366004612bd5565b600a6020525f908152604090205481565b3480156103b4575f5ffd5b506103dd6103c3366004612bd5565b60056020525f90815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610301565b348015610400575f5ffd5b506103dd61040f366004612bd5565b6001600160a01b039081165f908152600460205260409020541690565b348015610437575f5ffd5b506103dd7f0000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e881565b34801561046a575f5ffd5b506103dd610479366004612c21565b610e3e565b348015610489575f5ffd5b506102f7610498366004612c21565b610e66565b3480156104a8575f5ffd5b506102f76104b7366004612bd5565b6110b7565b3480156104c7575f5ffd5b5061032961128f565b3480156104db575f5ffd5b506103dd7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b34801561050e575f5ffd5b50604080516001600160a01b037f00000000000000000000000045312ea0eff7e09c83cbe249fa1d7598c4c8cd4e811682527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2811660208301527f0000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e81691810191909152606001610301565b3480156105a4575f5ffd5b506103296105b3366004612bd5565b611429565b6103dd6105c6366004612d95565b6115d0565b3480156105d6575f5ffd5b506102f762093a8081565b3480156105ec575f5ffd5b506103dd6105fb366004612bd5565b60046020525f90815260409020546001600160a01b031681565b348015610620575f5ffd5b506103dd7f000000000000000000000000c00779a913c9bb701a1e20f36381d860184a6b5681565b348015610653575f5ffd5b5061036e610662366004612e90565b6118bc565b348015610672575f5ffd5b506103dd7f000000000000000000000000e930e161738a442e5e8787e0b9709e9c882e41e081565b3480156106a5575f5ffd5b506103296106b4366004612c21565b6118e8565b3480156106c4575f5ffd5b506103dd6106d3366004612eba565b600b60209081525f92835260408084209091529082529020546001600160a01b031681565b348015610703575f5ffd5b5061036e610712366004612bd5565b5f6020819052908152604090205460ff1681565b348015610731575f5ffd5b506103dd610740366004612eba565b61197b565b348015610750575f5ffd5b506102f761075f366004612bd5565b6119ff565b34801561076f575f5ffd5b506102f7611bae565b348015610783575f5ffd5b506103dd7f0000000000000000000000002bdc9cf04d8a5809000d1f56792c6e101d5bd9f481565b3480156107b6575f5ffd5b506102f7611c3c565b3480156107ca575f5ffd5b506103296107d9366004612ee2565b611c8a565b3480156107e9575f5ffd5b5061036e6107f8366004612bd5565b60036020525f908152604090205460ff1681565b348015610817575f5ffd5b506102f7600c5481565b34801561082c575f5ffd5b50610329611dd4565b610329610843366004612eba565b611ecf565b348015610853575f5ffd5b5061036e610862366004612e90565b600760209081525f928352604080842090915290825290205460ff1681565b34801561088c575f5ffd5b506102f761089b366004612c21565b5f9081526008602052604090205490565b3480156108b7575f5ffd5b506103dd7f000000000000000000000000bfb8d464962727d96afa07367a2150ec5e52341881565b3480156108ea575f5ffd5b506102f76108f9366004612bd5565b60026020525f908152604090205481565b348015610915575f5ffd5b506103dd610924366004612bd5565b6001600160a01b039081165f908152600560205260409020541690565b34801561094c575f5ffd5b506103dd7f00000000000000000000000045312ea0eff7e09c83cbe249fa1d7598c4c8cd4e81565b34801561097f575f5ffd5b506103dd7f000000000000000000000000df34dcc01b33f1f233f3a3ceeffd0ce0de8e67a581565b3480156109b2575f5ffd5b506102f76109c1366004612c21565b61201a565b3480156109d1575f5ffd5b506102f76109e0366004612c21565b60086020525f908152604090205481565b3480156109fc575f5ffd5b50600c546102f7565b348015610a10575f5ffd5b5061036e610a1f366004612bd5565b6121e1565b348015610a2f575f5ffd5b506103dd7f000000000000000000000000c743e2f72152fd99691b813a245019e5400b897a81565b348015610a62575f5ffd5b5061036e610a71366004612c21565b5f9081526009602052604090205460ff1690565b348015610a90575f5ffd5b506102f7610a9f366004612bd5565b6001600160a01b03165f908152600a602052604090205490565b348015610ac4575f5ffd5b50610acd612222565b6040516103019190612f46565b348015610ae5575f5ffd5b50610329612282565b335f9081526020819052604090205460ff16610b255760405162461bcd60e51b8152600401610b1c90612f58565b60405180910390fd5b5f8381526009602052604090205460ff1615610b535760405162461bcd60e51b8152600401610b1c90612fa1565b6006545f848152600860205260409020541015610ba75760405162461bcd60e51b8152602060048201526012602482015271496e73756666696369656e7420766f74657360701b6044820152606401610b1c565b5f838152600960205260409020805460ff191660011790556001600160a01b03821615610dc6576001600160a01b0382165f9081526020819052604090205460ff1615610d3a576001600160a01b0382165f908152602081905260408120805460ff191690555b600154811015610d0157826001600160a01b031660018281548110610c3557610c35612fd8565b5f918252602090912001546001600160a01b031603610cf95760018054610c5d908290613000565b81548110610c6d57610c6d612fd8565b5f91825260209091200154600180546001600160a01b039092169183908110610c9857610c98612fd8565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055506001805480610cd457610cd4613013565b5f8281526020902081015f1990810180546001600160a01b0319169055019055610d01565b600101610c0e565b506040516001600160a01b038316907fc19950c209d8ad1fcb5d64f3c545c4ad0782202fcbfc1b564cc1ed120025e659905f90a2610e0f565b6001600160a01b0382165f81815260208190526040808220805460ff19166001908117909155805480820182559083527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b03191684179055517f0b221fa4a77e140717a07cc6edef3e54fbb0560c79d71e5fe77b8f7e618d664c9190a2610e0f565b8015610e0f57600680549082905560408051828152602081018490527f7f95e655f1cebcc68c938397e57f67e4dacc24fc905d34e073a3b65fbcfbd7fb910160405180910390a1505b60405183907f7b1bcf1ccf901a11589afff5504d59fd0a53780eed2a952adade0348985139e0905f90a2505050565b60018181548110610e4d575f80fd5b5f918252602090912001546001600160a01b0316905081565b604080516101608101825273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee815273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486020808301919091526001600160a01b037f0000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e88116838501525f6060808501829052608080860183905260a080870184905260c080880185905260e0808901869052610100808a01879052610120808b01889052610140808c018990528c519081018d52808601898152948101899052928301889052908201879052810186905290815288518083018a52858152808801869052808a018690528085018690528084018690528188015288518083018a52858152808801869052808a01869052808501869052808401869052818a015288518083018a52858152808801869052808a018690528085018690528084018690528185015288518083018a52858152808801869052808a018690528085018690528084018690528184015288519182018952737f86bf177dd4f3494b841a37e810a34dd56c829b825273383e6b4437b59fff47b619cba855ca29342a85599682019690965280880184905291820183905281018290529451637b1257b360e01b81529094917f00000000000000000000000045312ea0eff7e09c83cbe249fa1d7598c4c8cd4e1690637b1257b39061106f90869086908a9087906004016130c6565b602060405180830381865afa15801561108a573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110ae91906130f8565b95945050505050565b335f9081526020819052604081205460ff166110e55760405162461bcd60e51b8152600401610b1c90612f58565b6001600160a01b0382165f9081526020819052604090205460ff166111435760405162461bcd60e51b8152602060048201526014602482015273129d5919d948191bd95cc81b9bdd08195e1a5cdd60621b6044820152606401610b1c565b336001600160a01b038316036111a55760405162461bcd60e51b815260206004820152602160248201527f43616e6e6f742070726f706f736520746f2072656d6f766520796f757273656c6044820152603360f91b6064820152608401610b1c565b6040516a72656d6f76654a7564676560a81b60208201526bffffffffffffffffffffffff19606084901b16602b82015242603f8201525f90605f0160408051601f1981840301815291815281516020928301205f818152600990935291205490915060ff16156112275760405162461bcd60e51b8152600401610b1c90612fa1565b336001600160a01b0316817f898a80665dd7411c488cdf4a8658fc065411886e2b29e8d29f159de5651f212460405161127f906020808252600b908201526a72656d6f76654a7564676560a81b604082015260600190565b60405180910390a390505b919050565b335f9081526020819052604081205460ff166112c257335f908152600560205260409020546001600160a01b03166112c4565b335b6001600160a01b0381165f908152600260205260409020549091506113215760405162461bcd60e51b81526020600482015260136024820152724e6f207265776172647320746f20636c61696d60681b6044820152606401610b1c565b6001600160a01b0381165f9081526003602052604090205460ff16156113895760405162461bcd60e51b815260206004820152601760248201527f416c726561647920636c61696d656420726577617264730000000000000000006044820152606401610b1c565b6001600160a01b0381165f908152600260209081526040808320546003909252808320805460ff19166001179055519091339183156108fc0291849190818181858888f193505050501580156113e1573d5f5f3e3d5ffd5b50816001600160a01b03167f2b8d51946813c957a7b1714546806778b063466901c289b82d967a22caed6e028260405161141d91815260200190565b60405180910390a25050565b335f9081526020819052604090205460ff166114575760405162461bcd60e51b8152600401610b1c90612f58565b6001600160a01b0381166114ad5760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642064656c6567617465206164647265737300000000000000006044820152606401610b1c565b336001600160a01b038216036115055760405162461bcd60e51b815260206004820152601b60248201527f43616e6e6f742064656c656761746520746f20796f757273656c6600000000006044820152606401610b1c565b335f908152600460205260409020546001600160a01b0316156115635760405162461bcd60e51b8152602060048201526016602482015275416c72656164792068617320612064656c656761746560501b6044820152606401610b1c565b335f81815260046020908152604080832080546001600160a01b0387166001600160a01b03199182168117909255818552600590935281842080549093168517909255519092917f0c195f050c9d1eae4d0a59bee2ef525b8a5d91975fa9342a54bfd6528ac8fb6f91a350565b5f4288116116205760405162461bcd60e51b815260206004820181905260248201527f53746172742074696d65206d75737420626520696e20746865206675747572656044820152606401610b1c565b8787116116795760405162461bcd60e51b815260206004820152602160248201527f456e642074696d65206d7573742062652061667465722073746172742074696d6044820152606560f81b6064820152608401610b1c565b5f805b85518110156116b45785818151811061169757611697612fd8565b6020026020010151826116aa919061310f565b915060010161167c565b505f6116bf82612377565b90505f6116cf826298968061241b565b90508281101561173a5760405162461bcd60e51b815260206004820152603060248201527f496e73756666696369656e7420505955534420616d6f756e7420666f7220707260448201526f34bd32903234b9ba3934b13aba34b7b760811b6064820152608401610b1c565b5f5b86518110156117cc5761176787828151811061175a5761175a612fd8565b60200260200101516121e1565b6117c45760405162461bcd60e51b815260206004820152602860248201527f53656c6563746564206a75646765206973206e6f7420696e20676c6f62616c20604482015267726567697374727960c01b6064820152608401610b1c565b60010161173c565b506117d78587612714565b506117e98c8c8c8c8c8c8c888a612938565b335f818152600a602081815260408084208054600b8452828620818752845291852080546001600160a01b0319166001600160a01b038916179055948452919052825493975092919061183b83613122565b9091555050600c8054905f61184f83613122565b9190505550336001600160a01b0316856001600160a01b03167f526b2cf7f5bb87f9a4c01a6eb8c01bf90405b9726286908ac1dfd93944da0e848f858a5f01518b602001516040516118a4949392919061316e565b60405180910390a35050505098975050505050505050565b5f8281526007602090815260408083206001600160a01b038516845290915290205460ff165b92915050565b335f9081526020819052604090205460ff166119165760405162461bcd60e51b8152600401610b1c90612f58565b604051339082156108fc029083905f818181858888f19350505050158015611940573d5f5f3e3d5ffd5b506040518181525f9033907f9495d03190a79a43e534c9e328ff322f6283261383f5f19c809564f6ad5a57b39060200160405180910390a350565b6001600160a01b0382165f908152600a602052604081205482106119d75760405162461bcd60e51b8152602060048201526013602482015272496e646578206f7574206f6620626f756e647360681b6044820152606401610b1c565b506001600160a01b039182165f908152600b6020908152604080832093835292905220541690565b335f9081526020819052604081205460ff16611a2d5760405162461bcd60e51b8152600401610b1c90612f58565b6001600160a01b038216611a7b5760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206a75646765206164647265737360581b6044820152606401610b1c565b6001600160a01b0382165f9081526020819052604090205460ff1615611ada5760405162461bcd60e51b81526020600482015260146024820152734a7564676520616c72656164792065786973747360601b6044820152606401610b1c565b604051676164644a7564676560c01b60208201526bffffffffffffffffffffffff19606084901b16602882015242603c8201525f90605c0160408051601f1981840301815291815281516020928301205f818152600990935291205490915060ff1615611b595760405162461bcd60e51b8152600401610b1c90612fa1565b336001600160a01b0316817f898a80665dd7411c488cdf4a8658fc065411886e2b29e8d29f159de5651f212460405161127f906020808252600890820152676164644a7564676560c01b604082015260600190565b6040516370a0823160e01b81523060048201525f907f0000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e86001600160a01b0316906370a08231906024015b602060405180830381865afa158015611c13573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c3791906130f8565b905090565b6040516370a0823160e01b81523060048201525f907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401611bf8565b335f9081526020819052604090205460ff16611cb85760405162461bcd60e51b8152600401610b1c90612f58565b5f82815260076020908152604080832033845290915290205460ff1615611d215760405162461bcd60e51b815260206004820152601e60248201527f416c726561647920766f746564206f6e20746869732070726f706f73616c00006044820152606401610b1c565b5f8281526009602052604090205460ff1615611d4f5760405162461bcd60e51b8152600401610b1c90612fa1565b5f8281526007602090815260408083203384529091529020805460ff191660011790558015611d97575f828152600860205260408120805491611d9183613122565b91905055505b6040518115158152339083907f8b40665146691327ee30f5bf56e9b2d6f445d2830d3b09b56385cd30f630ecfb9060200160405180910390a35050565b335f9081526020819052604090205460ff16611e025760405162461bcd60e51b8152600401610b1c90612f58565b335f908152600460205260409020546001600160a01b0316611e665760405162461bcd60e51b815260206004820152601760248201527f4e6f2064656c65676174696f6e20746f207265766f6b650000000000000000006044820152606401610b1c565b335f81815260046020908152604080832080546001600160a01b03198082169092556001600160a01b0316808552600590935281842080549091169055519092907f0c195f050c9d1eae4d0a59bee2ef525b8a5d91975fa9342a54bfd6528ac8fb6f908390a350565b6001600160a01b038216611f1d5760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206a75646765206164647265737360581b6044820152606401610b1c565b5f8111611f6c5760405162461bcd60e51b815260206004820152601d60248201527f416d6f756e74206d7573742062652067726561746572207468616e20300000006044820152606401610b1c565b80341015611fb35760405162461bcd60e51b8152602060048201526014602482015273125b9cdd59999a58da595b9d081c185e5b595b9d60621b6044820152606401610b1c565b6001600160a01b0382165f9081526002602052604081208054839290611fda90849061310f565b90915550506040518181526001600160a01b038316907fd545e22a252a1e46a3ff5d635804684897b3b9831ac8163a9bc6c54ac88e53739060200161141d565b335f9081526020819052604081205460ff166120485760405162461bcd60e51b8152600401610b1c90612f58565b5f82116120a55760405162461bcd60e51b815260206004820152602560248201527f566f746573207265717569726564206d75737420626520677265617465722074604482015264068616e20360dc1b6064820152608401610b1c565b6001548211156121095760405162461bcd60e51b815260206004820152602960248201527f566f7465732072657175697265642063616e6e6f742065786365656420746f74604482015268616c206a756467657360b81b6064820152608401610b1c565b6040517218da185b99d9559bdd195cd4995c5d5a5c9959606a1b6020820152603381018390524260538201525f9060730160408051601f1981840301815291815281516020928301205f818152600990935291205490915060ff16156121815760405162461bcd60e51b8152600401610b1c90612fa1565b336001600160a01b0316817f898a80665dd7411c488cdf4a8658fc065411886e2b29e8d29f159de5651f212460405161127f9060208082526013908201527218da185b99d9559bdd195cd4995c5d5a5c9959606a1b604082015260600190565b6001600160a01b0381165f9081526020819052604081205460ff16806118e25750506001600160a01b039081165f9081526005602052604090205416151590565b6060600180548060200260200160405190810160405280929190818152602001828054801561227857602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161225a575b5050505050905090565b335f9081526020819052604090205460ff166122b05760405162461bcd60e51b8152600401610b1c90612f58565b6040516370a0823160e01b81523060048201525f907f0000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e86001600160a01b0316906370a0823190602401602060405180830381865afa158015612314573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061233891906130f8565b90508015612374576123746001600160a01b037f0000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e81633836129bf565b50565b604051632c7dc19360e01b8152670de0b6b3a764000060048201525f9081903090632c7dc19390602401602060405180830381865afa1580156123bc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123e091906130f8565b90506123ed81606461319a565b6123ff84670de0b6b3a764000061319a565b61240a90606961319a565b61241491906131b1565b9392505050565b5f5f83116124755760405162461bcd60e51b815260206004820152602160248201527f45544820616d6f756e74206d7573742062652067726561746572207468616e206044820152600360fc1b6064820152608401610b1c565b604080516101608101825273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee815273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486020808301919091526001600160a01b037f0000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e88116838501525f6060808501829052608080860183905260a080870184905260c080880185905260e0808901869052610100808a01879052610120808b01889052610140808c018990528c519081018d52808601898152948101899052928301889052908201879052810186905290815288518083018a52858152808801869052808a018690528085018690528084018690528188015288518083018a52858152808801869052808a01869052808501869052808401869052818a015288518083018a52858152808801869052808a018690528085018690528084018690528185015288518083018a52858152808801869052808a018690528085018690528084018690528184015288519182018952737f86bf177dd4f3494b841a37e810a34dd56c829b825273383e6b4437b59fff47b619cba855ca29342a8559968201969096528088018490529182018390528101919091529351632e4e0c7160e11b8152929391927f00000000000000000000000045312ea0eff7e09c83cbe249fa1d7598c4c8cd4e90911690635c9c18e2908890612686908790879084908c9089906004016131d0565b60206040518083038185885af11580156126a2573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906126c791906130f8565b60408051888152602081018390529081018790529094507f12aec4492341cce30775891eceb584f6fbbd825e56d94db93c1888839677903c9060600160405180910390a150505092915050565b5f80835160038111156127295761272961313a565b0361275e576127577f000000000000000000000000e930e161738a442e5e8787e0b9709e9c882e41e0612a16565b90506127e7565b6001835160038111156127735761277361313a565b036127a1576127577f000000000000000000000000df34dcc01b33f1f233f3a3ceeffd0ce0de8e67a5612a16565b6002835160038111156127b6576127b661313a565b036127e7576127e47f000000000000000000000000c743e2f72152fd99691b813a245019e5400b897a612a16565b90505b806001600160a01b0316637c33404f84604001518560600151856040518463ffffffff1660e01b815260040161281f93929190613213565b5f604051808303815f87803b158015612836575f5ffd5b505af1158015612848573d5f5f3e3d5ffd5b505050508260200151156128ea575f6128807f0000000000000000000000002bdc9cf04d8a5809000d1f56792c6e101d5bd9f4612a16565b604080860151905163cd6dc68760e01b81526001600160a01b038581166004830152602482019290925291925082169063cd6dc687906044015f604051808303815f87803b1580156128d0575f5ffd5b505af11580156128e2573d5f5f3e3d5ffd5b509293505050505b825160208401516040516001600160a01b038416927f5c968cbcc62446275183fea8d6eaa29a00505b77145a7fad4de724257508a33c9261292a92613231565b60405180910390a292915050565b5f6129627f000000000000000000000000c00779a913c9bb701a1e20f36381d860184a6b56612a16565b90506129986001600160a01b037f0000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e81682856129bf565b6129b2818b8b8b8b8b8b8b6129ad8b34613000565b612a21565b9998505050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052612a11908490612ab9565b505050565b5f6118e2825f612b2b565b60405162a8bae760e31b81526001600160a01b038a1690630545d738908390612a809033908d908d908d908d908d908d9030908e907f0000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e89060040161324e565b5f604051808303818588803b158015612a97575f5ffd5b505af1158015612aa9573d5f5f3e3d5ffd5b5050505050505050505050505050565b5f5f60205f8451602086015f885af180612ad8576040513d5f823e3d81fd5b50505f513d91508115612aef578060011415612afc565b6001600160a01b0384163b155b15612b2557604051635274afe760e01b81526001600160a01b0385166004820152602401610b1c565b50505050565b5f81471015612b565760405163cf47918160e01b815247600482015260248101839052604401610b1c565b763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b176020526037600983f090506001600160a01b0381166118e25760405163b06ebf3d60e01b815260040160405180910390fd5b80356001600160a01b038116811461128a575f5ffd5b5f60208284031215612be5575f5ffd5b61241482612bbf565b5f5f5f60608486031215612c00575f5ffd5b83359250612c1060208501612bbf565b929592945050506040919091013590565b5f60208284031215612c31575f5ffd5b5035919050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612c7557612c75612c38565b604052919050565b5f67ffffffffffffffff821115612c9657612c96612c38565b5060051b60200190565b5f82601f830112612caf575f5ffd5b8135612cc2612cbd82612c7d565b612c4c565b8082825260208201915060208360051b860101925085831115612ce3575f5ffd5b602085015b83811015612d0757612cf981612bbf565b835260209283019201612ce8565b5095945050505050565b8035801515811461128a575f5ffd5b5f60808284031215612d30575f5ffd5b6040516080810167ffffffffffffffff81118282101715612d5357612d53612c38565b604052905080823560048110612d67575f5ffd5b8152612d7560208401612d11565b602082015260408381013590820152606092830135920191909152919050565b5f5f5f5f5f5f5f5f610160898b031215612dad575f5ffd5b883597506020890135965060408901359550606089013594506080890135935060a089013567ffffffffffffffff811115612de6575f5ffd5b8901601f81018b13612df6575f5ffd5b8035612e04612cbd82612c7d565b8082825260208201915060208360051b85010192508d831115612e25575f5ffd5b6020840193505b82841015612e47578335825260209384019390910190612e2c565b955050505060c089013567ffffffffffffffff811115612e65575f5ffd5b612e718b828c01612ca0565b925050612e818a60e08b01612d20565b90509295985092959890939650565b5f5f60408385031215612ea1575f5ffd5b82359150612eb160208401612bbf565b90509250929050565b5f5f60408385031215612ecb575f5ffd5b612ed483612bbf565b946020939093013593505050565b5f5f60408385031215612ef3575f5ffd5b82359150612eb160208401612d11565b5f8151808452602084019350602083015f5b82811015612f3c5781516001600160a01b0316865260209586019590910190600101612f15565b5093949350505050565b602081525f6124146020830184612f03565b60208082526029908201527f4f6e6c7920676c6f62616c206a75646765732063616e2063616c6c207468697360408201526810333ab731ba34b7b760b91b606082015260800190565b60208082526019908201527f50726f706f73616c20616c726561647920657865637574656400000000000000604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b818103818111156118e2576118e2612fec565b634e487b7160e01b5f52603160045260245ffd5b805f5b600b811015612b255781516001600160a01b031684526020938401939091019060010161302a565b805f5b6005811015612b25578151845f5b6005811015613082578251825260209283019290910190600101613063565b50505060a0939093019260209190910190600101613055565b805f5b6005811015612b255781516001600160a01b031684526020938401939091019060010161309e565b61054081016130d58287613027565b6130e3610160830186613052565b836104808301526110ae6104a083018461309b565b5f60208284031215613108575f5ffd5b5051919050565b808201808211156118e2576118e2612fec565b5f6001820161313357613133612fec565b5060010190565b634e487b7160e01b5f52602160045260245ffd5b6004811061316a57634e487b7160e01b5f52602160045260245ffd5b9052565b8481526020810184905260808101613189604083018561314e565b821515606083015295945050505050565b80820281158282048414176118e2576118e2612fec565b5f826131cb57634e487b7160e01b5f52601260045260245ffd5b500490565b61056081016131df8288613027565b6131ed610160830187613052565b84610480830152836104a08301526132096104c083018461309b565b9695505050505050565b838152826020820152606060408201525f6110ae6060830184612f03565b6040810161323f828561314e565b82151560208301529392505050565b5f610140820160018060a01b038d1683528b60208401528a60408401528960608401528860808401528760a084015261014060c0840152808751808352610160850191506020890192505f5b818110156132b857835183526020938401939092019160010161329a565b50506001600160a01b03871660e08501528381036101008501526132dc8187612f03565b925050506132f66101208301846001600160a01b03169052565b9b9a505050505050505050505056fea2646970667358221220f25571b24249f41926de9dbd3aa3581a313f63ac8b7eb8cd47de662ec25a43fd64736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c00779a913c9bb701a1e20f36381d860184a6b5600000000000000000000000045312ea0eff7e09c83cbe249fa1d7598c4c8cd4e000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e8
-----Decoded View---------------
Arg [0] : _implementation (address): 0xc00779a913c9BB701a1e20F36381D860184a6b56
Arg [1] : _curveRouter (address): 0x45312ea0eFf7E09C83CBE249fa1d7598c4C8cd4e
Arg [2] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [3] : _pyusd (address): 0x6c3ea9036406852006290770BEdFcAbA0e23A0e8
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000c00779a913c9bb701a1e20f36381d860184a6b56
Arg [1] : 00000000000000000000000045312ea0eff7e09c83cbe249fa1d7598c4c8cd4e
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [3] : 0000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e8
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.