Sepolia Testnet

Contract

0xB02ACeb337eD4920b6BFf73C6cf24aB257d28B19

Overview

ETH Balance

0 ETH

Token Holdings

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Post Process Dkg41544652023-08-25 0:28:36578 days ago1692923316IN
0xB02ACeb3...257d28B19
0 ETH0.00041973.0020313
Commit Dkg41544452023-08-25 0:24:00578 days ago1692923040IN
0xB02ACeb3...257d28B19
0 ETH0.001237773.00321602
Commit Dkg41544452023-08-25 0:24:00578 days ago1692923040IN
0xB02ACeb3...257d28B19
0 ETH0.000826953.00321602
Commit Dkg41544452023-08-25 0:24:00578 days ago1692923040IN
0xB02ACeb3...257d28B19
0 ETH0.001214353.00321602
Node Activate41544232023-08-25 0:18:48578 days ago1692922728IN
0xB02ACeb3...257d28B19
0 ETH0.005732893.00353362
Node Activate41544212023-08-25 0:18:12578 days ago1692922692IN
0xB02ACeb3...257d28B19
0 ETH0.000700313.0040757
Node Activate41544192023-08-25 0:17:48578 days ago1692922668IN
0xB02ACeb3...257d28B19
0 ETH0.000663983.0035227
Node Activate41544172023-08-25 0:17:24578 days ago1692922644IN
0xB02ACeb3...257d28B19
0 ETH0.000790363.00326583
Node Quit41544152023-08-25 0:16:48578 days ago1692922608IN
0xB02ACeb3...257d28B19
0 ETH0.00037253.00352764
Node Activate41519572023-08-24 15:28:36578 days ago1692890916IN
0xB02ACeb3...257d28B19
0 ETH0.000789513.00000375
Node Activate41519552023-08-24 15:28:12578 days ago1692890892IN
0xB02ACeb3...257d28B19
0 ETH0.000753363.00000331
Node Quit41519542023-08-24 15:28:00578 days ago1692890880IN
0xB02ACeb3...257d28B19
0 ETH0.000329933.00000299
Post Process Dkg41519522023-08-24 15:27:36578 days ago1692890856IN
0xB02ACeb3...257d28B19
0 ETH0.000550653.00000332
Node Quit41519512023-08-24 15:27:24578 days ago1692890844IN
0xB02ACeb3...257d28B19
0 ETH0.000363713.00000335
Post Process Dkg41519492023-08-24 15:27:00578 days ago1692890820IN
0xB02ACeb3...257d28B19
0 ETH0.000777173.00000344
Node Quit41519142023-08-24 15:19:48579 days ago1692890388IN
0xB02ACeb3...257d28B19
0 ETH0.000625563.00000339
Node Quit41519132023-08-24 15:19:36579 days ago1692890376IN
0xB02ACeb3...257d28B19
0 ETH0.000669443.0000033
Node Quit41519122023-08-24 15:19:24579 days ago1692890364IN
0xB02ACeb3...257d28B19
0 ETH0.000728273.00000327
Node Quit41519102023-08-24 15:19:00579 days ago1692890340IN
0xB02ACeb3...257d28B19
0 ETH0.005481683.0000032
Node Quit41519092023-08-24 15:18:48579 days ago1692890328IN
0xB02ACeb3...257d28B19
0 ETH0.005900743.00000291
Node Quit41519072023-08-24 15:18:12579 days ago1692890292IN
0xB02ACeb3...257d28B19
0 ETH0.005740163.00000248
Post Process Dkg36332262023-06-06 5:21:12658 days ago1686028872IN
0xB02ACeb3...257d28B19
0 ETH0.000394283.00000001
Post Process Dkg36332252023-06-06 5:21:00658 days ago1686028860IN
0xB02ACeb3...257d28B19
0 ETH0.000395023.00000001
Post Process Dkg36332252023-06-06 5:21:00658 days ago1686028860IN
0xB02ACeb3...257d28B19
0 ETH0.000433053.00000001
Post Process Dkg36332252023-06-06 5:21:00658 days ago1686028860IN
0xB02ACeb3...257d28B19
0 ETH0.000393533.00000001
View all transactions

Latest 23 internal transactions

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer41544652023-08-25 0:28:36578 days ago1692923316
0xB02ACeb3...257d28B19
0 ETH
0x60c0604041544232023-08-25 0:18:48578 days ago1692922728
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
Transfer41519522023-08-24 15:27:36578 days ago1692890856
0xB02ACeb3...257d28B19
0 ETH
Transfer41519492023-08-24 15:27:00578 days ago1692890820
0xB02ACeb3...257d28B19
0 ETH
0x60c0604041519102023-08-24 15:19:00579 days ago1692890340
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
0x60c0604041519092023-08-24 15:18:48579 days ago1692890328
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
0x60c0604041519072023-08-24 15:18:12579 days ago1692890292
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
Transfer36332252023-06-06 5:21:00658 days ago1686028860
0xB02ACeb3...257d28B19
0 ETH
Transfer36332252023-06-06 5:21:00658 days ago1686028860
0xB02ACeb3...257d28B19
0 ETH
0x60c0604036331832023-06-06 5:11:12658 days ago1686028272
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
0x60c0604036331832023-06-06 5:11:12658 days ago1686028272
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
0x60c0604036331832023-06-06 5:11:12658 days ago1686028272
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
0x60c0604036331832023-06-06 5:11:12658 days ago1686028272
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
Transfer36330792023-06-06 4:47:48658 days ago1686026868
0xB02ACeb3...257d28B19
0 ETH
0x60c0604036330372023-06-06 4:38:36658 days ago1686026316
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
0x60c0604036330362023-06-06 4:38:24658 days ago1686026304
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
0x60c0604036330362023-06-06 4:38:24658 days ago1686026304
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
0x60c0604036330352023-06-06 4:38:12658 days ago1686026292
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
0x60c0604036330352023-06-06 4:38:12658 days ago1686026292
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
0x60c0604036330352023-06-06 4:38:12658 days ago1686026292
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
0x60c0604036330352023-06-06 4:38:12658 days ago1686026292
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
Transfer36326652023-06-06 3:17:12658 days ago1686021432
0xB02ACeb3...257d28B19
0 ETH
0x60c0604036326232023-06-06 3:08:12658 days ago1686020892
0xB02ACeb3...257d28B19
 Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Controller

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 500 runs

Other Settings:
default evmVersion
File 1 of 22 : Controller.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {IERC20, SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol";
import {Initializable} from "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import {IController} from "./interfaces/IController.sol";
import {IControllerOwner} from "./interfaces/IControllerOwner.sol";
import {IAdapter} from "./interfaces/IAdapter.sol";
import {ICoordinator} from "./interfaces/ICoordinator.sol";
import {INodeStaking} from "Staking-v0.1/interfaces/INodeStaking.sol";
import {BLS} from "./libraries/BLS.sol";
import {GroupLib} from "./libraries/GroupLib.sol";
import {Coordinator} from "./Coordinator.sol";

contract Controller is Initializable, IController, IControllerOwner, OwnableUpgradeable {
    using SafeERC20 for IERC20;
    using GroupLib for GroupLib.GroupData;

    // *Constants*
    uint16 private constant _BALANCE_BASE = 1;

    // *Controller Config*
    ControllerConfig private _config;
    IERC20 private _arpa;

    // *Node State Variables*
    mapping(address => Node) private _nodes; // maps node address to Node Struct
    mapping(address => uint256) private _withdrawableEths; // maps node address to withdrawable eth amount
    mapping(address => uint256) private _arpaRewards; // maps node address to arpa rewards

    // *DKG Variables*
    mapping(uint256 => address) private _coordinators; // maps group index to coordinator address

    // *Group Variables*
    GroupLib.GroupData internal _groupData;

    // *Task Variables*
    uint256 private _lastOutput;

    // *Structs*
    struct ControllerConfig {
        address stakingContractAddress;
        address adapterContractAddress;
        uint256 nodeStakingAmount;
        uint256 disqualifiedNodePenaltyAmount;
        uint256 defaultDkgPhaseDuration;
        uint256 pendingBlockAfterQuit;
        uint256 dkgPostProcessReward;
    }

    // *Events*
    event NodeRegistered(address indexed nodeAddress, bytes dkgPublicKey, uint256 groupIndex);
    event NodeActivated(address indexed nodeAddress, uint256 groupIndex);
    event NodeQuit(address indexed nodeAddress);
    event DkgPublicKeyChanged(address indexed nodeAddress, bytes dkgPublicKey);
    event NodeSlashed(address indexed nodeIdAddress, uint256 stakingRewardPenalty, uint256 pendingBlock);
    event NodeRewarded(address indexed nodeAddress, uint256 ethAmount, uint256 arpaAmount);
    event ControllerConfigSet(
        address stakingContractAddress,
        address adapterContractAddress,
        uint256 nodeStakingAmount,
        uint256 disqualifiedNodePenaltyAmount,
        uint256 defaultNumberOfCommitters,
        uint256 defaultDkgPhaseDuration,
        uint256 groupMaxCapacity,
        uint256 idealNumberOfGroups,
        uint256 pendingBlockAfterQuit,
        uint256 dkgPostProcessReward
    );
    event DkgTask(
        uint256 indexed globalEpoch,
        uint256 indexed groupIndex,
        uint256 indexed groupEpoch,
        uint256 size,
        uint256 threshold,
        address[] members,
        uint256 assignmentBlockHeight,
        address coordinatorAddress
    );

    // *Errors*
    error NodeNotRegistered();
    error NodeAlreadyRegistered();
    error NodeAlreadyActive();
    error NodeStillPending(uint256 pendingUntilBlock);
    error GroupNotExist(uint256 groupIndex);
    error CoordinatorNotFound(uint256 groupIndex);
    error DkgNotInProgress(uint256 groupIndex);
    error DkgStillInProgress(uint256 groupIndex, int8 phase);
    error EpochMismatch(uint256 groupIndex, uint256 inputGroupEpoch, uint256 currentGroupEpoch);
    error NodeNotInGroup(uint256 groupIndex, address nodeIdAddress);
    error PartialKeyAlreadyRegistered(uint256 groupIndex, address nodeIdAddress);
    error SenderNotAdapter();
    error InvalidZeroAddress();

    function initialize(address arpa, uint256 lastOutput) public initializer {
        _arpa = IERC20(arpa);
        _lastOutput = lastOutput;

        __Ownable_init();
    }

    // =============
    // IControllerOwner
    // =============
    function setControllerConfig(
        address stakingContractAddress,
        address adapterContractAddress,
        uint256 nodeStakingAmount,
        uint256 disqualifiedNodePenaltyAmount,
        uint256 defaultNumberOfCommitters,
        uint256 defaultDkgPhaseDuration,
        uint256 groupMaxCapacity,
        uint256 idealNumberOfGroups,
        uint256 pendingBlockAfterQuit,
        uint256 dkgPostProcessReward
    ) external override(IControllerOwner) onlyOwner {
        _config = ControllerConfig({
            stakingContractAddress: stakingContractAddress,
            adapterContractAddress: adapterContractAddress,
            nodeStakingAmount: nodeStakingAmount,
            disqualifiedNodePenaltyAmount: disqualifiedNodePenaltyAmount,
            defaultDkgPhaseDuration: defaultDkgPhaseDuration,
            pendingBlockAfterQuit: pendingBlockAfterQuit,
            dkgPostProcessReward: dkgPostProcessReward
        });

        _groupData.setConfig(idealNumberOfGroups, groupMaxCapacity, defaultNumberOfCommitters);

        emit ControllerConfigSet(
            stakingContractAddress,
            adapterContractAddress,
            nodeStakingAmount,
            disqualifiedNodePenaltyAmount,
            defaultNumberOfCommitters,
            defaultDkgPhaseDuration,
            groupMaxCapacity,
            idealNumberOfGroups,
            pendingBlockAfterQuit,
            dkgPostProcessReward
        );
    }

    // =============
    // IController
    // =============
    function nodeRegister(bytes calldata dkgPublicKey) external override(IController) {
        if (_nodes[msg.sender].idAddress != address(0)) {
            revert NodeAlreadyRegistered();
        }

        uint256[4] memory publicKey = BLS.fromBytesPublicKey(dkgPublicKey);
        if (!BLS.isValidPublicKey(publicKey)) {
            revert BLS.InvalidPublicKey();
        }
        // Lock staking amount in Staking contract
        INodeStaking(_config.stakingContractAddress).lock(msg.sender, _config.nodeStakingAmount);

        // Populate Node struct and insert into nodes
        Node storage n = _nodes[msg.sender];
        n.idAddress = msg.sender;
        n.dkgPublicKey = dkgPublicKey;
        n.state = true;

        // Initialize withdrawable eths and arpa rewards to save gas for adapter call
        _withdrawableEths[msg.sender] = _BALANCE_BASE;
        _arpaRewards[msg.sender] = _BALANCE_BASE;

        (uint256 groupIndex, uint256[] memory groupIndicesToEmitEvent) = _groupData.nodeJoin(msg.sender, _lastOutput);

        for (uint256 i = 0; i < groupIndicesToEmitEvent.length; i++) {
            _emitGroupEvent(groupIndicesToEmitEvent[i]);
        }

        emit NodeRegistered(msg.sender, dkgPublicKey, groupIndex);
    }

    function nodeActivate() external override(IController) {
        Node storage node = _nodes[msg.sender];
        if (node.idAddress != msg.sender) {
            revert NodeNotRegistered();
        }

        if (node.state) {
            revert NodeAlreadyActive();
        }

        if (node.pendingUntilBlock > block.number) {
            revert NodeStillPending(node.pendingUntilBlock);
        }

        // lock up to staking amount in Staking contract
        uint256 lockedAmount = INodeStaking(_config.stakingContractAddress).getLockedAmount(msg.sender);
        if (lockedAmount < _config.nodeStakingAmount) {
            INodeStaking(_config.stakingContractAddress).lock(msg.sender, _config.nodeStakingAmount - lockedAmount);
        }

        node.state = true;

        (uint256 groupIndex, uint256[] memory groupIndicesToEmitEvent) = _groupData.nodeJoin(msg.sender, _lastOutput);

        for (uint256 i = 0; i < groupIndicesToEmitEvent.length; i++) {
            _emitGroupEvent(groupIndicesToEmitEvent[i]);
        }

        emit NodeActivated(msg.sender, groupIndex);
    }

    function nodeQuit() external override(IController) {
        Node storage node = _nodes[msg.sender];

        if (node.idAddress != msg.sender) {
            revert NodeNotRegistered();
        }
        uint256[] memory groupIndicesToEmitEvent = _groupData.nodeLeave(msg.sender, _lastOutput);

        for (uint256 i = 0; i < groupIndicesToEmitEvent.length; i++) {
            _emitGroupEvent(groupIndicesToEmitEvent[i]);
        }

        _freezeNode(msg.sender, _config.pendingBlockAfterQuit);

        // unlock staking amount in Staking contract
        INodeStaking(_config.stakingContractAddress).unlock(msg.sender, _config.nodeStakingAmount);

        emit NodeQuit(msg.sender);
    }

    function changeDkgPublicKey(bytes calldata dkgPublicKey) external override(IController) {
        Node storage node = _nodes[msg.sender];
        if (node.idAddress != msg.sender) {
            revert NodeNotRegistered();
        }

        if (node.state) {
            revert NodeAlreadyActive();
        }

        uint256[4] memory publicKey = BLS.fromBytesPublicKey(dkgPublicKey);
        if (!BLS.isValidPublicKey(publicKey)) {
            revert BLS.InvalidPublicKey();
        }

        node.dkgPublicKey = dkgPublicKey;

        emit DkgPublicKeyChanged(msg.sender, dkgPublicKey);
    }

    function commitDkg(CommitDkgParams memory params) external override(IController) {
        if (params.groupIndex >= _groupData.groupCount) revert GroupNotExist(params.groupIndex);

        // require coordinator exists
        if (_coordinators[params.groupIndex] == address(0)) {
            revert CoordinatorNotFound(params.groupIndex);
        }

        // Ensure DKG Proccess is in Phase
        ICoordinator coordinator = ICoordinator(_coordinators[params.groupIndex]);
        if (coordinator.inPhase() == -1) {
            revert DkgNotInProgress(params.groupIndex);
        }

        // Ensure epoch is correct, node is in group, and has not already submitted a partial key
        Group storage g = _groupData.groups[params.groupIndex];
        if (params.groupEpoch != g.epoch) {
            revert EpochMismatch(params.groupIndex, params.groupEpoch, g.epoch);
        }

        if (_groupData.getMemberIndexByAddress(params.groupIndex, msg.sender) == -1) {
            revert NodeNotInGroup(params.groupIndex, msg.sender);
        }

        // check to see if member has called commitdkg in the past.
        if (isPartialKeyRegistered(params.groupIndex, msg.sender)) {
            revert PartialKeyAlreadyRegistered(params.groupIndex, msg.sender);
        }

        // require publickey and partial public key are not empty  / are the right format
        uint256[4] memory partialPublicKey = BLS.fromBytesPublicKey(params.partialPublicKey);
        if (!BLS.isValidPublicKey(partialPublicKey)) {
            revert BLS.InvalidPartialPublicKey();
        }

        uint256[4] memory publicKey = BLS.fromBytesPublicKey(params.publicKey);
        if (!BLS.isValidPublicKey(publicKey)) {
            revert BLS.InvalidPublicKey();
        }

        // Populate CommitResult / CommitCache
        CommitResult memory commitResult = CommitResult({
            groupEpoch: params.groupEpoch,
            publicKey: publicKey,
            disqualifiedNodes: params.disqualifiedNodes
        });

        if (!_groupData.tryAddToExistingCommitCache(params.groupIndex, commitResult)) {
            CommitCache memory commitCache = CommitCache({commitResult: commitResult, nodeIdAddress: new address[](1)});

            commitCache.nodeIdAddress[0] = msg.sender;
            g.commitCacheList.push(commitCache);
        }

        // no matter consensus previously reached, update the partial public key of the given node's member entry in the group
        g.members[uint256(_groupData.getMemberIndexByAddress(params.groupIndex, msg.sender))].partialPublicKey =
            partialPublicKey;

        // if not.. call get StrictlyMajorityIdenticalCommitmentResult for the group and check if consensus has been reached.
        if (!g.isStrictlyMajorityConsensusReached) {
            (bool success, address[] memory disqualifiedNodes) =
                _groupData.tryEnableGroup(params.groupIndex, _lastOutput);

            if (success) {
                // Iterate over disqualified nodes and call slashNode on each.
                for (uint256 i = 0; i < disqualifiedNodes.length; i++) {
                    _slashNode(disqualifiedNodes[i], _config.disqualifiedNodePenaltyAmount, 0);
                }
            }
        }
    }

    function postProcessDkg(uint256 groupIndex, uint256 groupEpoch) external override(IController) {
        if (groupIndex >= _groupData.groupCount) revert GroupNotExist(groupIndex);

        // require calling node is in group
        if (_groupData.getMemberIndexByAddress(groupIndex, msg.sender) == -1) {
            revert NodeNotInGroup(groupIndex, msg.sender);
        }

        // require correct epoch
        Group storage g = _groupData.groups[groupIndex];
        if (groupEpoch != g.epoch) {
            revert EpochMismatch(groupIndex, groupEpoch, g.epoch);
        }

        // require coordinator exists
        if (_coordinators[groupIndex] == address(0)) {
            revert CoordinatorNotFound(groupIndex);
        }

        // Ensure DKG Proccess is out of phase
        ICoordinator coordinator = ICoordinator(_coordinators[groupIndex]);
        if (coordinator.inPhase() != -1) {
            revert DkgStillInProgress(groupIndex, coordinator.inPhase());
        }

        // delete coordinator
        coordinator.selfDestruct(); // coordinator self destructs
        _coordinators[groupIndex] = address(0); // remove coordinator from mapping

        if (!g.isStrictlyMajorityConsensusReached) {
            (address[] memory nodesToBeSlashed, uint256[] memory groupIndicesToEmitEvent) =
                _groupData.handleUnsuccessfulGroupDkg(groupIndex, _lastOutput);

            for (uint256 i = 0; i < nodesToBeSlashed.length; i++) {
                _slashNode(nodesToBeSlashed[i], _config.disqualifiedNodePenaltyAmount, 0);
            }
            for (uint256 i = 0; i < groupIndicesToEmitEvent.length; i++) {
                _emitGroupEvent(groupIndicesToEmitEvent[i]);
            }
        }

        // update rewards for calling node
        _arpaRewards[msg.sender] += _config.dkgPostProcessReward;

        emit NodeRewarded(msg.sender, 0, _config.dkgPostProcessReward);
    }

    function nodeWithdraw(address recipient) external override(IController) {
        if (recipient == address(0)) {
            revert InvalidZeroAddress();
        }
        uint256 ethAmount = _withdrawableEths[msg.sender];
        uint256 arpaAmount = _arpaRewards[msg.sender];
        if (arpaAmount > _BALANCE_BASE) {
            _arpaRewards[msg.sender] = _BALANCE_BASE;
            _arpa.safeTransfer(recipient, arpaAmount - _BALANCE_BASE);
        }
        if (ethAmount > _BALANCE_BASE) {
            _withdrawableEths[msg.sender] = _BALANCE_BASE;
            IAdapter(_config.adapterContractAddress).nodeWithdrawETH(recipient, ethAmount - _BALANCE_BASE);
        }
    }

    function addReward(address[] memory nodes, uint256 ethAmount, uint256 arpaAmount) public override(IController) {
        if (msg.sender != _config.adapterContractAddress) {
            revert SenderNotAdapter();
        }
        for (uint256 i = 0; i < nodes.length; i++) {
            _withdrawableEths[nodes[i]] += ethAmount;
            _arpaRewards[nodes[i]] += arpaAmount;
            emit NodeRewarded(nodes[i], ethAmount, arpaAmount);
        }
    }

    function setLastOutput(uint256 lastOutput) external override(IController) {
        if (msg.sender != _config.adapterContractAddress) {
            revert SenderNotAdapter();
        }
        _lastOutput = lastOutput;
    }

    function getControllerConfig()
        external
        view
        returns (
            address stakingContractAddress,
            address adapterContractAddress,
            uint256 nodeStakingAmount,
            uint256 disqualifiedNodePenaltyAmount,
            uint256 defaultNumberOfCommitters,
            uint256 defaultDkgPhaseDuration,
            uint256 groupMaxCapacity,
            uint256 idealNumberOfGroups,
            uint256 pendingBlockAfterQuit,
            uint256 dkgPostProcessReward
        )
    {
        return (
            _config.stakingContractAddress,
            _config.adapterContractAddress,
            _config.nodeStakingAmount,
            _config.disqualifiedNodePenaltyAmount,
            _groupData.defaultNumberOfCommitters,
            _config.defaultDkgPhaseDuration,
            _groupData.groupMaxCapacity,
            _groupData.idealNumberOfGroups,
            _config.pendingBlockAfterQuit,
            _config.dkgPostProcessReward
        );
    }

    function getValidGroupIndices() public view override(IController) returns (uint256[] memory) {
        return _groupData.getValidGroupIndices();
    }

    function getGroupEpoch() external view returns (uint256) {
        return _groupData.epoch;
    }

    function getGroupCount() external view override(IController) returns (uint256) {
        return _groupData.groupCount;
    }

    function getGroup(uint256 groupIndex) public view override(IController) returns (Group memory) {
        return _groupData.groups[groupIndex];
    }

    function getGroupThreshold(uint256 groupIndex) public view override(IController) returns (uint256, uint256) {
        return (_groupData.groups[groupIndex].threshold, _groupData.groups[groupIndex].size);
    }

    function getNode(address nodeAddress) public view override(IController) returns (Node memory) {
        return _nodes[nodeAddress];
    }

    function getMember(uint256 groupIndex, uint256 memberIndex)
        public
        view
        override(IController)
        returns (Member memory)
    {
        return _groupData.groups[groupIndex].members[memberIndex];
    }

    function getBelongingGroup(address nodeAddress) external view override(IController) returns (int256, int256) {
        return _groupData.getBelongingGroupByMemberAddress(nodeAddress);
    }

    function getCoordinator(uint256 groupIndex) public view override(IController) returns (address) {
        return _coordinators[groupIndex];
    }

    function getNodeWithdrawableTokens(address nodeAddress)
        public
        view
        override(IController)
        returns (uint256, uint256)
    {
        return (
            _withdrawableEths[nodeAddress] == 0 ? 0 : (_withdrawableEths[nodeAddress] - _BALANCE_BASE),
            _arpaRewards[nodeAddress] == 0 ? 0 : (_arpaRewards[nodeAddress] - _BALANCE_BASE)
        );
    }

    function getLastOutput() external view returns (uint256) {
        return _lastOutput;
    }

    /// Check to see if a group has a partial public key registered for a given node.
    function isPartialKeyRegistered(uint256 groupIndex, address nodeIdAddress)
        public
        view
        override(IController)
        returns (bool)
    {
        Group memory g = _groupData.groups[groupIndex];
        for (uint256 i = 0; i < g.members.length; i++) {
            if (g.members[i].nodeIdAddress == nodeIdAddress) {
                return g.members[i].partialPublicKey[0] != 0;
            }
        }
        return false;
    }

    // =============
    // Internal
    // =============

    function _emitGroupEvent(uint256 groupIndex) internal {
        _groupData.prepareGroupEvent(groupIndex);

        Group memory g = _groupData.groups[groupIndex];

        // Deploy coordinator, add to coordinators mapping
        Coordinator coordinator;
        coordinator = new Coordinator(g.threshold, _config.defaultDkgPhaseDuration);
        _coordinators[groupIndex] = address(coordinator);

        // Initialize Coordinator
        address[] memory groupNodes = new address[](g.size);
        bytes[] memory groupKeys = new bytes[](g.size);

        for (uint256 i = 0; i < g.size; i++) {
            groupNodes[i] = g.members[i].nodeIdAddress;
            groupKeys[i] = _nodes[g.members[i].nodeIdAddress].dkgPublicKey;
        }

        coordinator.initialize(groupNodes, groupKeys);

        emit DkgTask(
            _groupData.epoch, g.index, g.epoch, g.size, g.threshold, groupNodes, block.number, address(coordinator)
        );
    }

    // Give node staking reward penalty and freezeNode
    function _slashNode(address nodeIdAddress, uint256 stakingRewardPenalty, uint256 pendingBlock) internal {
        // slash staking reward in Staking contract
        INodeStaking(_config.stakingContractAddress).slashDelegationReward(nodeIdAddress, stakingRewardPenalty);

        // remove node from group if handleGroup is true and deactivate it
        _freezeNode(nodeIdAddress, pendingBlock);

        emit NodeSlashed(nodeIdAddress, stakingRewardPenalty, pendingBlock);
    }

    function _freezeNode(address nodeIdAddress, uint256 pendingBlock) internal {
        // set node state to false for frozen node
        _nodes[nodeIdAddress].state = false;

        uint256 currentBlock = block.number;
        // if the node is already pending, add the pending block to the current pending block
        if (_nodes[nodeIdAddress].pendingUntilBlock > currentBlock) {
            _nodes[nodeIdAddress].pendingUntilBlock += pendingBlock;
            // else set the pending block to the current block + pending block
        } else {
            _nodes[nodeIdAddress].pendingUntilBlock = currentBlock + pendingBlock;
        }
    }
}

File 2 of 22 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 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 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 3 of 22 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.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.
 *
 * By default, the owner account will be the one that deploys the contract. 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 OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @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 {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing 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 {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _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);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 4 of 22 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

File 5 of 22 : IController.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

interface IController {
    struct Group {
        uint256 index;
        uint256 epoch;
        uint256 size;
        uint256 threshold;
        Member[] members;
        address[] committers;
        CommitCache[] commitCacheList;
        bool isStrictlyMajorityConsensusReached;
        uint256[4] publicKey;
    }

    struct Member {
        address nodeIdAddress;
        uint256[4] partialPublicKey;
    }

    struct CommitResult {
        uint256 groupEpoch;
        uint256[4] publicKey;
        address[] disqualifiedNodes;
    }

    struct CommitCache {
        address[] nodeIdAddress;
        CommitResult commitResult;
    }

    struct Node {
        address idAddress;
        bytes dkgPublicKey;
        bool state;
        uint256 pendingUntilBlock;
    }

    struct CommitDkgParams {
        uint256 groupIndex;
        uint256 groupEpoch;
        bytes publicKey;
        bytes partialPublicKey;
        address[] disqualifiedNodes;
    }

    // node transaction
    function nodeRegister(bytes calldata dkgPublicKey) external;

    function nodeActivate() external;

    function nodeQuit() external;

    function changeDkgPublicKey(bytes calldata dkgPublicKey) external;

    function commitDkg(CommitDkgParams memory params) external;

    function postProcessDkg(uint256 groupIndex, uint256 groupEpoch) external;

    function nodeWithdraw(address recipient) external;

    // adapter transaction
    function addReward(address[] memory nodes, uint256 ethAmount, uint256 arpaAmount) external;

    function setLastOutput(uint256 lastOutput) external;

    // view
    function getControllerConfig()
        external
        view
        returns (
            address stakingContractAddress,
            address adapterContractAddress,
            uint256 nodeStakingAmount,
            uint256 disqualifiedNodePenaltyAmount,
            uint256 defaultNumberOfCommitters,
            uint256 defaultDkgPhaseDuration,
            uint256 groupMaxCapacity,
            uint256 idealNumberOfGroups,
            uint256 pendingBlockAfterQuit,
            uint256 dkgPostProcessReward
        );

    /// @notice Get list of all group indexes where group.isStrictlyMajorityConsensusReached == true
    /// @return uint256[] List of valid group indexes
    function getValidGroupIndices() external view returns (uint256[] memory);

    function getGroupEpoch() external view returns (uint256);

    function getGroupCount() external view returns (uint256);

    function getGroup(uint256 index) external view returns (Group memory);

    function getGroupThreshold(uint256 groupIndex) external view returns (uint256, uint256);

    function getNode(address nodeAddress) external view returns (Node memory);

    function getMember(uint256 groupIndex, uint256 memberIndex) external view returns (Member memory);

    /// @notice Get the group index and member index of a given node.
    function getBelongingGroup(address nodeAddress) external view returns (int256, int256);

    function getCoordinator(uint256 groupIndex) external view returns (address);

    function getNodeWithdrawableTokens(address nodeAddress) external view returns (uint256, uint256);

    function getLastOutput() external view returns (uint256);

    /// @notice Check to see if a group has a partial public key registered for a given node.
    /// @return bool True if the node has a partial public key registered for the group.
    function isPartialKeyRegistered(uint256 groupIndex, address nodeIdAddress) external view returns (bool);
}

File 6 of 22 : IControllerOwner.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

interface IControllerOwner {
    /**
     * @notice Sets the configuration of the controller
     * @param stakingContract The address of the staking contract
     * @param adapterContract The address of the adapter contract
     * @param nodeStakingAmount The amount of ARPA must staked by a node
     * @param disqualifiedNodePenaltyAmount The amount of ARPA will be slashed from a node if it is disqualified
     * @param defaultNumberOfCommitters The default number of committers for a DKG
     * @param defaultDkgPhaseDuration The default duration(block number) of a DKG phase
     * @param groupMaxCapacity The maximum number of nodes in a group
     * @param idealNumberOfGroups The ideal number of groups
     * @param pendingBlockAfterQuit The number of blocks a node must wait before joining a group after quitting
     * @param dkgPostProcessReward The amount of ARPA will be rewarded to the node after dkgPostProcess is completed
     */
    function setControllerConfig(
        address stakingContract,
        address adapterContract,
        uint256 nodeStakingAmount,
        uint256 disqualifiedNodePenaltyAmount,
        uint256 defaultNumberOfCommitters,
        uint256 defaultDkgPhaseDuration,
        uint256 groupMaxCapacity,
        uint256 idealNumberOfGroups,
        uint256 pendingBlockAfterQuit,
        uint256 dkgPostProcessReward
    ) external;
}

File 7 of 22 : IAdapter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {IRequestTypeBase} from "./IRequestTypeBase.sol";

interface IAdapter is IRequestTypeBase {
    struct PartialSignature {
        uint256 index;
        uint256 partialSignature;
    }

    struct RandomnessRequestParams {
        RequestType requestType;
        bytes params;
        uint64 subId;
        uint256 seed;
        uint16 requestConfirmations;
        uint32 callbackGasLimit;
        uint256 callbackMaxGasPrice;
    }

    struct RequestDetail {
        uint64 subId;
        uint32 groupIndex;
        RequestType requestType;
        bytes params;
        address callbackContract;
        uint256 seed;
        uint16 requestConfirmations;
        uint32 callbackGasLimit;
        uint256 callbackMaxGasPrice;
        uint256 blockNum;
    }

    // controller transaction
    function nodeWithdrawETH(address recipient, uint256 ethAmount) external;

    // consumer contract transaction
    function requestRandomness(RandomnessRequestParams calldata params) external returns (bytes32);

    function fulfillRandomness(
        uint32 groupIndex,
        bytes32 requestId,
        uint256 signature,
        RequestDetail calldata requestDetail,
        PartialSignature[] calldata partialSignatures
    ) external;

    // user transaction
    function createSubscription() external returns (uint64);

    function addConsumer(uint64 subId, address consumer) external;

    function fundSubscription(uint64 subId) external payable;

    function setReferral(uint64 subId, uint64 referralSubId) external;

    function cancelSubscription(uint64 subId, address to) external;

    function removeConsumer(uint64 subId, address consumer) external;

    // view
    function getLastSubscription(address consumer) external view returns (uint64);

    function getSubscription(uint64 subId)
        external
        view
        returns (
            address owner,
            address[] memory consumers,
            uint256 balance,
            uint256 inflightCost,
            uint64 reqCount,
            uint64 freeRequestCount,
            uint64 referralSubId,
            uint64 reqCountInCurrentPeriod,
            uint256 lastRequestTimestamp
        );

    function getPendingRequestCommitment(bytes32 requestId) external view returns (bytes32);

    function getLastAssignedGroupIndex() external view returns (uint256);

    function getLastRandomness() external view returns (uint256);

    function getRandomnessCount() external view returns (uint256);

    function getCurrentSubId() external view returns (uint64);

    function getCumulativeData() external view returns (uint256, uint256, uint256);

    function getController() external view returns (address);

    function getAdapterConfig()
        external
        view
        returns (
            uint16 minimumRequestConfirmations,
            uint32 maxGasLimit,
            uint32 gasAfterPaymentCalculation,
            uint32 gasExceptCallback,
            uint256 signatureTaskExclusiveWindow,
            uint256 rewardPerSignature,
            uint256 committerRewardPerSignature
        );

    function getFlatFeeConfig()
        external
        view
        returns (
            uint32 fulfillmentFlatFeeLinkPPMTier1,
            uint32 fulfillmentFlatFeeLinkPPMTier2,
            uint32 fulfillmentFlatFeeLinkPPMTier3,
            uint32 fulfillmentFlatFeeLinkPPMTier4,
            uint32 fulfillmentFlatFeeLinkPPMTier5,
            uint24 reqsForTier2,
            uint24 reqsForTier3,
            uint24 reqsForTier4,
            uint24 reqsForTier5,
            uint16 flatFeePromotionGlobalPercentage,
            bool isFlatFeePromotionEnabledPermanently,
            uint256 flatFeePromotionStartTimestamp,
            uint256 flatFeePromotionEndTimestamp
        );

    function getReferralConfig()
        external
        view
        returns (bool isReferralEnabled, uint16 freeRequestCountForReferrer, uint16 freeRequestCountForReferee);

    /*
     * @notice Compute fee based on the request count
     * @param reqCount number of requests
     * @return feePPM fee in ARPA PPM
     */
    function getFeeTier(uint64 reqCount) external view returns (uint32);

    // Estimate the amount of gas used for fulfillment
    function estimatePaymentAmountInETH(
        uint32 callbackGasLimit,
        uint32 gasExceptCallback,
        uint32 fulfillmentFlatFeeEthPPM,
        uint256 weiPerUnitGas
    ) external view returns (uint256);
}

File 8 of 22 : ICoordinator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

interface ICoordinator {
    function inPhase() external view returns (int8);

    function initialize(address[] memory nodes, bytes[] memory publicKeys) external;

    function startBlock() external view returns (uint256);

    function selfDestruct() external;
}

File 9 of 22 : INodeStaking.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

interface INodeStaking {
    /// @notice This event is emitted when a node locks stake in the pool.
    /// @param staker Staker address
    /// @param newLock New principal amount locked
    event Locked(address staker, uint256 newLock);

    /// @notice This event is emitted when a node unlocks stake in the pool.
    /// @param staker Staker address
    /// @param newUnlock New principal amount unlocked
    event Unlocked(address staker, uint256 newUnlock);

    /// @notice This event is emitted when a node gets delegation reward slashed.
    /// @param staker Staker address
    /// @param amount Amount slashed
    event DelegationRewardSlashed(address staker, uint256 amount);

    /// @notice This error is raised when attempting to unlock with more than the current locked staking amount
    /// @param currentLockedStakingAmount Current locked staking amount
    error InadequateOperatorLockedStakingAmount(uint256 currentLockedStakingAmount);

    /// @notice This function allows controller to lock staking amount for a node.
    /// @param staker Node address
    /// @param amount Amount to lock
    function lock(address staker, uint256 amount) external;

    /// @notice This function allows controller to unlock staking amount for a node.
    /// @param staker Node address
    /// @param amount Amount to unlock
    function unlock(address staker, uint256 amount) external;

    /// @notice This function allows controller to slash delegation reward of a node.
    /// @param staker Node address
    /// @param amount Amount to slash
    function slashDelegationReward(address staker, uint256 amount) external;

    /// @notice This function returns the locked amount of a node.
    /// @param staker Node address
    function getLockedAmount(address staker) external view returns (uint256);
}

File 10 of 22 : BLS.sol
// SPDX-License-Identifier: LGPL 3.0
pragma solidity ^0.8.18;

import {BN256G2} from "./BN256G2.sol";

/**
 * @title BLS operations on bn254 curve
 * @author ARPA-Network adapted from https://github.com/ChihChengLiang/bls_solidity_python
 * @dev Homepage: https://github.com/ARPA-Network/BLS-TSS-Network
 *      Signature and Point hashed to G1 are represented by affine coordinate in big-endian order, deserialized from compressed format.
 *      Public key is represented and serialized by affine coordinate Q-x-re(x0), Q-x-im(x1), Q-y-re(y0), Q-y-im(y1) in big-endian order.
 */
library BLS {
    // Field order
    uint256 public constant N = 21888242871839275222246405745257275088696311157297823662689037894645226208583;

    // Negated genarator of G2
    uint256 public constant N_G2_X1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
    uint256 public constant N_G2_X0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
    uint256 public constant N_G2_Y1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052;
    uint256 public constant N_G2_Y0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653;

    uint256 public constant FIELD_MASK = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

    error MustNotBeInfinity();
    error InvalidPublicKeyEncoding();
    error InvalidSignatureFormat();
    error InvalidSignature();
    error InvalidPartialSignatureFormat();
    error InvalidPartialSignatures();
    error EmptyPartialSignatures();
    error InvalidPublicKey();
    error InvalidPartialPublicKey();

    function verifySingle(uint256[2] memory signature, uint256[4] memory pubkey, uint256[2] memory message)
        public
        view
        returns (bool)
    {
        uint256[12] memory input = [
            signature[0],
            signature[1],
            N_G2_X1,
            N_G2_X0,
            N_G2_Y1,
            N_G2_Y0,
            message[0],
            message[1],
            pubkey[1],
            pubkey[0],
            pubkey[3],
            pubkey[2]
        ];
        uint256[1] memory out;
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            success := staticcall(sub(gas(), 2000), 8, input, 384, out, 0x20)
            switch success
            case 0 { invalid() }
        }
        require(success, "");
        return out[0] != 0;
    }

    function verifyPartials(uint256[2][] memory partials, uint256[4][] memory pubkeys, uint256[2] memory message)
        public
        view
        returns (bool)
    {
        uint256[2] memory aggregatedSignature;
        uint256[4] memory aggregatedPublicKey;
        for (uint256 i = 0; i < partials.length; i++) {
            aggregatedSignature = addPoints(aggregatedSignature, partials[i]);
            aggregatedPublicKey = BN256G2.ecTwistAdd(aggregatedPublicKey, pubkeys[i]);
        }

        uint256[12] memory input = [
            aggregatedSignature[0],
            aggregatedSignature[1],
            N_G2_X1,
            N_G2_X0,
            N_G2_Y1,
            N_G2_Y0,
            message[0],
            message[1],
            aggregatedPublicKey[1],
            aggregatedPublicKey[0],
            aggregatedPublicKey[3],
            aggregatedPublicKey[2]
        ];
        uint256[1] memory out;
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            success := staticcall(sub(gas(), 2000), 8, input, 384, out, 0x20)
            switch success
            case 0 { invalid() }
        }
        require(success, "");
        return out[0] != 0;
    }

    // TODO a simple hash and increment implementation, can be improved later
    function hashToPoint(bytes memory data) public view returns (uint256[2] memory p) {
        bool found;
        bytes32 candidateHash = keccak256(data);
        while (true) {
            (p, found) = mapToPoint(candidateHash);
            if (found) {
                break;
            }
            candidateHash = keccak256(bytes.concat(candidateHash));
        }
    }

    //  we take the y-coordinate as the lexicographically largest of the two associated with the encoded x-coordinate
    function mapToPoint(bytes32 _x) internal view returns (uint256[2] memory p, bool found) {
        uint256 y;
        uint256 x = uint256(_x) % N;
        (y, found) = deriveYOnG1(x);
        if (found) {
            p[0] = x;
            p[1] = y > N / 2 ? N - y : y;
        }
    }

    function deriveYOnG1(uint256 x) internal view returns (uint256, bool) {
        uint256 y;
        y = mulmod(x, x, N);
        y = mulmod(y, x, N);
        y = addmod(y, 3, N);
        return sqrt(y);
    }

    function isValidPublicKey(uint256[4] memory publicKey) public pure returns (bool) {
        if ((publicKey[0] >= N) || (publicKey[1] >= N) || (publicKey[2] >= N || (publicKey[3] >= N))) {
            return false;
        } else {
            return isOnCurveG2(publicKey);
        }
    }

    function fromBytesPublicKey(bytes memory point) public pure returns (uint256[4] memory pubkey) {
        if (point.length != 128) {
            revert InvalidPublicKeyEncoding();
        }
        uint256 x0;
        uint256 x1;
        uint256 y0;
        uint256 y1;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // look the first 32 bytes of a bytes struct is its length
            x0 := mload(add(point, 32))
            x1 := mload(add(point, 64))
            y0 := mload(add(point, 96))
            y1 := mload(add(point, 128))
        }
        pubkey = [x0, x1, y0, y1];
    }

    function decompress(uint256 compressedSignature) public view returns (uint256[2] memory uncompressed) {
        uint256 x = compressedSignature & FIELD_MASK;
        // The most significant bit, when set, indicates that the y-coordinate of the point
        // is the lexicographically largest of the two associated values.
        // The second-most significant bit indicates that the point is at infinity. If this bit is set,
        // the remaining bits of the group element's encoding should be set to zero.
        // We don't accept infinity as valid signature.
        uint256 decision = compressedSignature >> 254;
        if (decision & 1 == 1) {
            revert MustNotBeInfinity();
        }
        uint256 y;
        (y,) = deriveYOnG1(x);

        // If the following two conditions or their negative forms are not met at the same time, get the negative y.
        // 1. The most significant bit of compressed signature is set
        // 2. The y we recovered first is the lexicographically largest
        if (((decision >> 1) ^ (y > N / 2 ? 1 : 0)) == 1) {
            y = N - y;
        }
        return [x, y];
    }

    function isValid(uint256 compressedSignature) public view returns (bool) {
        uint256 x = compressedSignature & FIELD_MASK;
        if (x >= N) {
            return false;
        } else if (x == 0) {
            return false;
        }
        return isOnCurveG1(x);
    }

    function isOnCurveG1(uint256[2] memory point) internal pure returns (bool _isOnCurve) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let t0 := mload(point)
            let t1 := mload(add(point, 32))
            let t2 := mulmod(t0, t0, N)
            t2 := mulmod(t2, t0, N)
            t2 := addmod(t2, 3, N)
            t1 := mulmod(t1, t1, N)
            _isOnCurve := eq(t1, t2)
        }
    }

    function isOnCurveG1(uint256 x) internal view returns (bool _isOnCurve) {
        bool callSuccess;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let t0 := x
            let t1 := mulmod(t0, t0, N)
            t1 := mulmod(t1, t0, N)
            // x ^ 3 + b
            t1 := addmod(t1, 3, N)

            let freemem := mload(0x40)
            mstore(freemem, 0x20)
            mstore(add(freemem, 0x20), 0x20)
            mstore(add(freemem, 0x40), 0x20)
            mstore(add(freemem, 0x60), t1)
            // (N - 1) / 2 = 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3
            mstore(add(freemem, 0x80), 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3)
            // N = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
            mstore(add(freemem, 0xA0), 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47)
            callSuccess := staticcall(sub(gas(), 2000), 5, freemem, 0xC0, freemem, 0x20)
            _isOnCurve := eq(1, mload(freemem))
        }
    }

    function isOnCurveG2(uint256[4] memory point) internal pure returns (bool _isOnCurve) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // x0, x1
            let t0 := mload(point)
            let t1 := mload(add(point, 32))
            // x0 ^ 2
            let t2 := mulmod(t0, t0, N)
            // x1 ^ 2
            let t3 := mulmod(t1, t1, N)
            // 3 * x0 ^ 2
            let t4 := add(add(t2, t2), t2)
            // 3 * x1 ^ 2
            let t5 := addmod(add(t3, t3), t3, N)
            // x0 * (x0 ^ 2 - 3 * x1 ^ 2)
            t2 := mulmod(add(t2, sub(N, t5)), t0, N)
            // x1 * (3 * x0 ^ 2 - x1 ^ 2)
            t3 := mulmod(add(t4, sub(N, t3)), t1, N)

            // x ^ 3 + b
            t0 := addmod(t2, 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5, N)
            t1 := addmod(t3, 0x009713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2, N)

            // y0, y1
            t2 := mload(add(point, 64))
            t3 := mload(add(point, 96))
            // y ^ 2
            t4 := mulmod(addmod(t2, t3, N), addmod(t2, sub(N, t3), N), N)
            t3 := mulmod(shl(1, t2), t3, N)

            // y ^ 2 == x ^ 3 + b
            _isOnCurve := and(eq(t0, t4), eq(t1, t3))
        }
    }

    function sqrt(uint256 xx) internal view returns (uint256 x, bool hasRoot) {
        bool callSuccess;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let freemem := mload(0x40)
            mstore(freemem, 0x20)
            mstore(add(freemem, 0x20), 0x20)
            mstore(add(freemem, 0x40), 0x20)
            mstore(add(freemem, 0x60), xx)
            // this is enabled by N % 4 = 3 and Fermat's little theorem
            // (N + 1) / 4 = 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52
            mstore(add(freemem, 0x80), 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52)
            // N = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
            mstore(add(freemem, 0xA0), 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47)
            callSuccess := staticcall(sub(gas(), 2000), 5, freemem, 0xC0, freemem, 0x20)
            x := mload(freemem)
            hasRoot := eq(xx, mulmod(x, x, N))
        }
        require(callSuccess, "BLS: sqrt modexp call failed");
    }

    /// @notice Add two points in G1
    function addPoints(uint256[2] memory p1, uint256[2] memory p2) internal view returns (uint256[2] memory ret) {
        uint256[4] memory input;
        input[0] = p1[0];
        input[1] = p1[1];
        input[2] = p2[0];
        input[3] = p2[1];
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            success := staticcall(sub(gas(), 2000), 6, input, 0xc0, ret, 0x60)
        }
        // solhint-disable-next-line reason-string
        require(success);
    }
}

File 11 of 22 : GroupLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {IController} from "../interfaces/IController.sol";
// solhint-disable-next-line no-global-import
import "../utils/Utils.sol" as Utils;

library GroupLib {
    // *Constants*
    uint256 public constant DEFAULT_MINIMUM_THRESHOLD = 3;

    struct GroupData {
        uint256 epoch;
        uint256 groupCount;
        mapping(uint256 => IController.Group) groups; // group_index => Group struct
        uint256 idealNumberOfGroups;
        uint256 groupMaxCapacity;
        uint256 defaultNumberOfCommitters;
    }

    event GroupRebalanced(uint256 indexed groupIndex1, uint256 indexed groupIndex2);

    // =============
    // Transaction
    // =============

    function setConfig(
        GroupData storage groupData,
        uint256 idealNumberOfGroups,
        uint256 groupMaxCapacity,
        uint256 defaultNumberOfCommitters
    ) public {
        groupData.idealNumberOfGroups = idealNumberOfGroups;
        groupData.groupMaxCapacity = groupMaxCapacity;
        groupData.defaultNumberOfCommitters = defaultNumberOfCommitters;
    }

    function nodeJoin(GroupData storage groupData, address idAddress, uint256 lastOutput)
        public
        returns (uint256 groupIndex, uint256[] memory groupIndicesToEmitEvent)
    {
        groupIndicesToEmitEvent = new uint256[](0);

        bool needRebalance;
        (groupIndex, needRebalance) = findOrCreateTargetGroup(groupData);

        bool needEmitGroupEvent = addToGroup(groupData, idAddress, groupIndex);
        if (needEmitGroupEvent) {
            groupIndicesToEmitEvent = new uint256[](1);
            groupIndicesToEmitEvent[0] = groupIndex;
            return (groupIndex, groupIndicesToEmitEvent);
        }

        if (needRebalance) {
            (bool rebalanceSuccess, uint256 groupIndexToRebalance) =
                tryRebalanceGroup(groupData, groupIndex, lastOutput);
            if (rebalanceSuccess) {
                groupIndicesToEmitEvent = new uint256[](2);
                groupIndicesToEmitEvent[0] = groupIndex;
                groupIndicesToEmitEvent[1] = groupIndexToRebalance;
            }
        }
    }

    function nodeLeave(GroupData storage groupData, address idAddress, uint256 lastOutput)
        public
        returns (uint256[] memory groupIndicesToEmitEvent)
    {
        groupIndicesToEmitEvent = new uint256[](0);

        (int256 groupIndex, int256 memberIndex) = getBelongingGroupByMemberAddress(groupData, idAddress);

        if (groupIndex != -1) {
            (bool needRebalance, bool needEmitGroupEvent) =
                removeFromGroup(groupData, uint256(memberIndex), uint256(groupIndex));
            if (needEmitGroupEvent) {
                groupIndicesToEmitEvent = new uint256[](1);
                groupIndicesToEmitEvent[0] = uint256(groupIndex);
                return groupIndicesToEmitEvent;
            }
            if (needRebalance) {
                return arrangeMembersInGroup(groupData, uint256(groupIndex), lastOutput);
            }
        }
    }

    function tryEnableGroup(GroupData storage groupData, uint256 groupIndex, uint256 lastOutput)
        public
        returns (bool success, address[] memory disqualifiedNodes)
    {
        IController.Group storage g = groupData.groups[groupIndex];
        IController.CommitCache memory identicalCommits =
            getStrictlyMajorityIdenticalCommitmentResult(groupData, groupIndex);

        if (identicalCommits.nodeIdAddress.length != 0) {
            disqualifiedNodes = identicalCommits.commitResult.disqualifiedNodes;

            // Get list of majority members with disqualified nodes excluded
            address[] memory majorityMembers =
                Utils.getNonDisqualifiedMajorityMembers(identicalCommits.nodeIdAddress, disqualifiedNodes);

            if (majorityMembers.length >= g.threshold) {
                // Remove all members from group where member.nodeIdAddress is in the disqualified nodes.
                for (uint256 i = 0; i < disqualifiedNodes.length; i++) {
                    for (uint256 j = 0; j < g.members.length; j++) {
                        if (g.members[j].nodeIdAddress == disqualifiedNodes[i]) {
                            g.members[j] = g.members[g.members.length - 1];
                            g.members.pop();
                            break;
                        }
                    }
                }

                // Update group with new values
                g.isStrictlyMajorityConsensusReached = true;
                g.size -= identicalCommits.commitResult.disqualifiedNodes.length;
                g.publicKey = identicalCommits.commitResult.publicKey;

                // Create indexMemberMap: Iterate through group.members and create mapping: memberIndex -> nodeIdAddress
                // Create qualifiedIndices: Iterate through group, add all member indexes found in majorityMembers.
                uint256[] memory qualifiedIndices = new uint256[](
                        majorityMembers.length
                    );

                for (uint256 j = 0; j < majorityMembers.length; j++) {
                    for (uint256 i = 0; i < g.members.length; i++) {
                        if (g.members[i].nodeIdAddress == majorityMembers[j]) {
                            qualifiedIndices[j] = i;
                            break;
                        }
                    }
                }

                // Compute commiter_indices by calling pickRandomIndex with qualifiedIndices as input.
                uint256[] memory committerIndices =
                    Utils.pickRandomIndex(lastOutput, qualifiedIndices, groupData.defaultNumberOfCommitters);

                // For selected commiter_indices: add corresponding members into g.committers
                g.committers = new address[](committerIndices.length);
                for (uint256 i = 0; i < committerIndices.length; i++) {
                    g.committers[i] = g.members[committerIndices[i]].nodeIdAddress;
                }

                return (true, disqualifiedNodes);
            }
        }
    }

    function handleUnsuccessfulGroupDkg(GroupData storage groupData, uint256 groupIndex, uint256 lastOutput)
        public
        returns (address[] memory nodesToBeSlashed, uint256[] memory groupIndicesToEmitEvent)
    {
        IController.Group storage g = groupData.groups[groupIndex];

        // get strictly majority identical commitment result
        IController.CommitCache memory majorityMembers =
            getStrictlyMajorityIdenticalCommitmentResult(groupData, groupIndex);

        if (majorityMembers.nodeIdAddress.length == 0) {
            // if empty cache: zero out group
            g.size = 0;
            g.threshold = 0;

            nodesToBeSlashed = new address[](g.members.length);
            for (uint256 i = 0; i < g.members.length; i++) {
                nodesToBeSlashed[i] = g.members[i].nodeIdAddress;
            }

            // zero out group members
            delete g.members;

            return (nodesToBeSlashed, new uint256[](0));
        } else {
            address[] memory disqualifiedNodes = majorityMembers.commitResult.disqualifiedNodes;
            g.size -= disqualifiedNodes.length;
            uint256 minimum = Utils.minimumThreshold(g.size);

            // set g.threshold to max (default min threshold / minimum threshold)
            g.threshold = GroupLib.DEFAULT_MINIMUM_THRESHOLD > minimum ? GroupLib.DEFAULT_MINIMUM_THRESHOLD : minimum;

            // Delete disqualified members from group
            for (uint256 j = 0; j < disqualifiedNodes.length; j++) {
                for (uint256 i = 0; i < g.members.length; i++) {
                    if (g.members[i].nodeIdAddress == disqualifiedNodes[j]) {
                        g.members[i] = g.members[g.members.length - 1];
                        g.members.pop();
                        break;
                    }
                }
            }

            return (disqualifiedNodes, arrangeMembersInGroup(groupData, groupIndex, lastOutput));
        }
    }

    function tryAddToExistingCommitCache(
        GroupData storage groupData,
        uint256 groupIndex,
        IController.CommitResult memory commitResult
    ) public returns (bool isExist) {
        IController.Group storage g = groupData.groups[groupIndex];
        for (uint256 i = 0; i < g.commitCacheList.length; i++) {
            if (keccak256(abi.encode(g.commitCacheList[i].commitResult)) == keccak256(abi.encode(commitResult))) {
                g.commitCacheList[i].nodeIdAddress.push(msg.sender);
                return true;
            }
        }
    }

    function prepareGroupEvent(GroupData storage groupData, uint256 groupIndex) internal {
        groupData.epoch++;
        IController.Group storage g = groupData.groups[groupIndex];
        g.epoch++;
        g.isStrictlyMajorityConsensusReached = false;

        delete g.committers;
        delete g.commitCacheList;

        for (uint256 i = 0; i < g.members.length; i++) {
            delete g.members[i].partialPublicKey;
        }
    }

    // =============
    // View
    // =============
    // Find group with member address equals to nodeIdAddress, return -1 if not found.
    function getBelongingGroupByMemberAddress(GroupData storage groupData, address nodeIdAddress)
        public
        view
        returns (int256, int256)
    {
        for (uint256 i = 0; i < groupData.groupCount; i++) {
            int256 memberIndex = getMemberIndexByAddress(groupData, i, nodeIdAddress);
            if (memberIndex != -1) {
                return (int256(i), memberIndex);
            }
        }
        return (-1, -1);
    }

    function getMemberIndexByAddress(GroupData storage groupData, uint256 groupIndex, address nodeIdAddress)
        public
        view
        returns (int256)
    {
        IController.Group memory g = groupData.groups[groupIndex];
        for (uint256 i = 0; i < g.members.length; i++) {
            if (g.members[i].nodeIdAddress == nodeIdAddress) {
                return int256(i);
            }
        }
        return -1;
    }

    function getValidGroupIndices(GroupData storage groupData) public view returns (uint256[] memory) {
        uint256[] memory groupIndices = new uint256[](groupData.groupCount); //max length is group count
        uint256 index = 0;
        for (uint256 i = 0; i < groupData.groupCount; i++) {
            IController.Group memory g = groupData.groups[i];
            if (g.isStrictlyMajorityConsensusReached) {
                groupIndices[index] = i;
                index++;
            }
        }

        return Utils.trimTrailingElements(groupIndices, index);
    }

    // =============
    // Internal
    // =============
    // Tries to rebalance the groups, and if it fails, it collects the IDs of the members in the group and tries to add them to other groups.
    // If a member is added to another group, the group is checked to see if its size meets a threshold; if it does, a group event is emitted.
    function arrangeMembersInGroup(GroupData storage groupData, uint256 groupIndex, uint256 lastOutput)
        internal
        returns (uint256[] memory groupIndicesToEmitEvent)
    {
        groupIndicesToEmitEvent = new uint256[](0);
        IController.Group storage g = groupData.groups[groupIndex];
        if (g.size == 0) {
            return groupIndicesToEmitEvent;
        }

        (bool rebalanceSuccess, uint256 groupIndexToRebalance) = tryRebalanceGroup(groupData, groupIndex, lastOutput);
        if (rebalanceSuccess) {
            groupIndicesToEmitEvent = new uint256[](2);
            groupIndicesToEmitEvent[0] = groupIndex;
            groupIndicesToEmitEvent[1] = groupIndexToRebalance;
            return groupIndicesToEmitEvent;
        }

        // Get group and set isStrictlyMajorityConsensusReached to false
        g.isStrictlyMajorityConsensusReached = false;

        // collect idAddress of members in group
        address[] memory membersLeftInGroup = new address[](g.members.length);
        for (uint256 i = 0; i < g.members.length; i++) {
            membersLeftInGroup[i] = g.members[i].nodeIdAddress;
        }
        uint256[] memory involvedGroups = new uint256[](groupData.groupCount); // max number of groups involved is groupCount
        uint256 currentIndex;

        // for each membersLeftInGroup, call findOrCreateTargetGroup and then add that member to the new group.
        for (uint256 i = 0; i < membersLeftInGroup.length; i++) {
            // find a suitable group for the member
            (uint256 targetGroupIndex,) = findOrCreateTargetGroup(groupData);

            // if the current group index is selected, break
            if (groupIndex == targetGroupIndex) {
                break;
            }

            // add member to target group
            addToGroup(groupData, membersLeftInGroup[i], targetGroupIndex);

            if (groupData.groups[i].size >= DEFAULT_MINIMUM_THRESHOLD) {
                involvedGroups[currentIndex] = targetGroupIndex;
                currentIndex++;
            }
        }

        return Utils.trimTrailingElements(involvedGroups, currentIndex);
    }

    function tryRebalanceGroup(GroupData storage groupData, uint256 groupIndex, uint256 lastOutput)
        internal
        returns (bool rebalanceSuccess, uint256 groupIndexToRebalance)
    {
        // get all group indices excluding the current groupIndex
        uint256[] memory groupIndices = new uint256[](groupData.groupCount -1);
        uint256 index = 0;
        for (uint256 i = 0; i < groupData.groupCount; i++) {
            if (i != groupIndex) {
                groupIndices[index] = i;
                index++;
            }
        }

        // try to reblance each group, if succesful, return true
        for (uint256 i = 0; i < groupIndices.length; i++) {
            if (rebalanceGroup(groupData, groupIndices[i], groupIndex, lastOutput)) {
                return (true, groupIndices[i]);
            }
        }
    }

    function rebalanceGroup(GroupData storage groupData, uint256 groupAIndex, uint256 groupBIndex, uint256 lastOutput)
        internal
        returns (bool)
    {
        IController.Group memory groupA = groupData.groups[groupAIndex];
        IController.Group memory groupB = groupData.groups[groupBIndex];

        if (groupB.size > groupA.size) {
            (groupA, groupB) = (groupB, groupA);
            (groupAIndex, groupBIndex) = (groupBIndex, groupAIndex);
        }

        uint256 expectedSizeToMove = groupA.size - (groupA.size + groupB.size) / 2;
        if (expectedSizeToMove == 0 || groupA.size - expectedSizeToMove < DEFAULT_MINIMUM_THRESHOLD) {
            return false;
        }

        // Move members from group A to group B
        for (uint256 i = 0; i < expectedSizeToMove; i++) {
            uint256 memberIndex = Utils.pickRandomIndex(lastOutput, groupA.members.length - i);
            address memberAddress = getMemberAddressByIndex(groupData, groupAIndex, memberIndex);
            removeFromGroup(groupData, memberIndex, groupAIndex);
            addToGroup(groupData, memberAddress, groupBIndex);
        }

        emit GroupRebalanced(groupAIndex, groupBIndex);

        return true;
    }

    function findOrCreateTargetGroup(GroupData storage groupData)
        internal
        returns (uint256 groupIndex, bool needsRebalance)
    {
        // if group is empty, addgroup.
        if (groupData.groupCount == 0) {
            return (addGroup(groupData), false);
        }

        // get the group index of the group with the minimum size, as well as the min size
        uint256 indexOfMinSize;
        uint256 minSize = groupData.groupMaxCapacity;
        for (uint256 i = 0; i < groupData.groupCount; i++) {
            IController.Group memory g = groupData.groups[i];
            if (g.size < minSize) {
                minSize = g.size;
                indexOfMinSize = i;
            }
        }

        // compute the valid group count
        uint256 validGroupCount = getValidGroupIndices(groupData).length;

        // check if valid group count < ideal_number_of_groups || minSize == group_max_capacity
        // If either condition is met and the number of valid groups == group count, call add group and return (index of new group, true)
        if (
            (validGroupCount < groupData.idealNumberOfGroups && validGroupCount == groupData.groupCount)
                || (minSize == groupData.groupMaxCapacity)
        ) return (addGroup(groupData), true);

        // if none of the above conditions are met:
        return (indexOfMinSize, false);
    }

    function addGroup(GroupData storage groupData) internal returns (uint256) {
        uint256 groupIndex = groupData.groupCount; // groupIndex starts at 0. groupCount is index of next group to be added
        groupData.groupCount++;

        IController.Group storage g = groupData.groups[groupIndex];
        g.index = groupIndex;
        g.size = 0;
        g.threshold = DEFAULT_MINIMUM_THRESHOLD;

        return groupIndex;
    }

    function addToGroup(GroupData storage groupData, address idAddress, uint256 groupIndex)
        internal
        returns (bool needEmitGroupEvent)
    {
        // Get group from group index
        IController.Group storage g = groupData.groups[groupIndex];

        // Add Member Struct to group at group index
        IController.Member memory m;
        m.nodeIdAddress = idAddress;

        // insert (node id address - > member) into group.members
        g.members.push(m);
        g.size++;

        // assign group threshold
        uint256 minimum = Utils.minimumThreshold(g.size); // 51% of group size
        // max of 51% of group size and DEFAULT_MINIMUM_THRESHOLD
        g.threshold = minimum > DEFAULT_MINIMUM_THRESHOLD ? minimum : DEFAULT_MINIMUM_THRESHOLD;

        if (g.size >= 3) {
            return true;
        }
    }

    function removeFromGroup(GroupData storage groupData, uint256 memberIndex, uint256 groupIndex)
        public
        returns (bool needRebalance, bool needEmitGroupEvent)
    {
        IController.Group storage g = groupData.groups[groupIndex];
        g.size--;

        if (g.size == 0) {
            delete g.members;
            g.threshold = 0;
            return (false, false);
        }

        // Remove node from members
        g.members[memberIndex] = g.members[g.members.length - 1];
        g.members.pop();

        uint256 minimum = Utils.minimumThreshold(g.size);
        g.threshold = minimum > DEFAULT_MINIMUM_THRESHOLD ? minimum : DEFAULT_MINIMUM_THRESHOLD;

        if (g.size < 3) {
            return (true, false);
        }

        return (false, true);
    }

    // Get array of majority members with identical commit result. Return commit cache. if no majority, return empty commit cache.
    function getStrictlyMajorityIdenticalCommitmentResult(GroupData storage groupData, uint256 groupIndex)
        internal
        view
        returns (IController.CommitCache memory)
    {
        IController.CommitCache memory emptyCache;

        // If there are no commit caches, return empty commit cache.
        IController.Group memory g = groupData.groups[groupIndex];
        if (g.commitCacheList.length == 0) {
            return (emptyCache);
        }

        // If there is only one commit cache, return it.
        if (g.commitCacheList.length == 1) {
            return (g.commitCacheList[0]);
        }

        // If there are multiple commit caches, check if there is a majority.
        bool isStrictlyMajorityExist = true;
        IController.CommitCache memory majorityCommitCache = g.commitCacheList[0];
        for (uint256 i = 1; i < g.commitCacheList.length; i++) {
            IController.CommitCache memory commitCache = g.commitCacheList[i];
            if (commitCache.nodeIdAddress.length > majorityCommitCache.nodeIdAddress.length) {
                isStrictlyMajorityExist = true;
                majorityCommitCache = commitCache;
            } else if (commitCache.nodeIdAddress.length == majorityCommitCache.nodeIdAddress.length) {
                isStrictlyMajorityExist = false;
            }
        }

        // If no majority, return empty commit cache.
        if (!isStrictlyMajorityExist) {
            return (emptyCache);
        }
        // If majority, return majority commit cache
        return (majorityCommitCache);
    }

    function getMemberAddressByIndex(GroupData storage groupData, uint256 groupIndex, uint256 memberIndex)
        internal
        view
        returns (address nodeIdAddress)
    {
        IController.Group memory g = groupData.groups[groupIndex];
        return g.members[memberIndex].nodeIdAddress;
    }
}

File 12 of 22 : Coordinator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol";

contract Coordinator is Ownable {
    /// Mapping of Ethereum Address => DKG public keys
    mapping(address => bytes) public keys;

    /// Mapping of Ethereum Address => DKG Phase 1 Shares
    mapping(address => bytes) public shares;

    /// Mapping of Ethereum Address => DKG Phase 2 Responses
    mapping(address => bytes) public responses;

    /// Mapping of Ethereum Address => DKG Phase 3 Justifications
    mapping(address => bytes) public justifications;

    // List of registered Ethereum keys (used for conveniently fetching data)
    address[] public participants;

    /// The duration of each phase
    uint256 public immutable phaseDuration;

    /// The dkgThreshold of the DKG
    uint256 public immutable dkgThreshold;

    /// If it's 0 then the DKG is still pending start. If >0, it is the DKG's start block
    uint256 public startBlock = 0;

    /// A group member is one whose pubkey's length > 0
    modifier onlyGroupMember() {
        require(keys[msg.sender].length > 0, "you are not a group member!");
        _;
    }

    /// The DKG starts when startBlock > 0
    modifier onlyWhenNotStarted() {
        require(startBlock == 0, "DKG has already started");
        _;
    }

    constructor(uint256 threshold, uint256 duration) {
        dkgThreshold = threshold;
        phaseDuration = duration;
    }

    function initialize(address[] calldata nodes, bytes[] calldata publicKeys) external onlyWhenNotStarted onlyOwner {
        for (uint256 i = 0; i < nodes.length; i++) {
            participants.push(nodes[i]);
            keys[nodes[i]] = publicKeys[i];
        }

        startBlock = block.number;
    }

    /// Participant publishes their data and depending on the phase the data gets inserted
    /// in the shares, responses or justifications mapping. Reverts if the participant
    /// has already published their data for a phase or if the DKG has ended.
    function publish(bytes calldata value) external onlyGroupMember {
        uint256 blocksSinceStart = block.number - startBlock;

        if (blocksSinceStart <= phaseDuration) {
            require(shares[msg.sender].length == 0, "share existed");
            shares[msg.sender] = value;
        } else if (blocksSinceStart <= 2 * phaseDuration) {
            require(responses[msg.sender].length == 0, "response existed");
            responses[msg.sender] = value;
        } else if (blocksSinceStart <= 3 * phaseDuration) {
            require(justifications[msg.sender].length == 0, "justification existed");
            justifications[msg.sender] = value;
        } else {
            revert("DKG Publish has ended");
        }
    }

    // Helpers to fetch data in the mappings. If a participant has registered but not
    // published their data for a phase, the array element at their index is expected to be 0

    /// Gets the participants' shares
    function getShares() external view returns (bytes[] memory) {
        bytes[] memory _shares = new bytes[](participants.length);
        for (uint256 i = 0; i < participants.length; i++) {
            _shares[i] = shares[participants[i]];
        }

        return _shares;
    }

    /// Gets the participants' responses
    function getResponses() external view returns (bytes[] memory) {
        bytes[] memory _responses = new bytes[](participants.length);
        for (uint256 i = 0; i < participants.length; i++) {
            _responses[i] = responses[participants[i]];
        }

        return _responses;
    }

    /// Gets the participants' justifications
    function getJustifications() external view returns (bytes[] memory) {
        bytes[] memory _justifications = new bytes[](participants.length);
        for (uint256 i = 0; i < participants.length; i++) {
            _justifications[i] = justifications[participants[i]];
        }

        return _justifications;
    }

    /// Gets the participants' ethereum addresses
    function getParticipants() external view returns (address[] memory) {
        return participants;
    }

    /// Gets the participants' DKG keys along with the thershold of the DKG
    function getDkgKeys() external view returns (uint256, bytes[] memory) {
        bytes[] memory _keys = new bytes[](participants.length);
        for (uint256 i = 0; i < participants.length; i++) {
            _keys[i] = keys[participants[i]];
        }

        return (dkgThreshold, _keys);
    }

    /// Returns the current phase of the DKG.
    function inPhase() public view returns (int8) {
        // Phase 0 for after deployment before initialization.
        if (startBlock == 0) {
            return 0;
        }

        uint256 blocksSinceStart = block.number - startBlock;

        if (blocksSinceStart <= phaseDuration) {
            return 1; // share
        }

        if (blocksSinceStart <= 2 * phaseDuration) {
            return 2; // response
        }

        if (blocksSinceStart <= 3 * phaseDuration) {
            return 3; // justification
        }
        if (blocksSinceStart <= 4 * phaseDuration) {
            return 4; // Commit DKG: Handled in controller
        }

        // DKG Ended, commit_dkg should be called before this
        return -1;
    }

    function selfDestruct() external onlyOwner {
        selfdestruct(payable(owner()));
    }
}

File 13 of 22 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` 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 amount
    ) external returns (bool);
}

File 14 of 22 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 15 of 22 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 16 of 22 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 17 of 22 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 18 of 22 : IRequestTypeBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

interface IRequestTypeBase {
    enum RequestType {
        Randomness,
        RandomWords,
        Shuffling
    }
}

File 19 of 22 : BN256G2.sol
// SPDX-License-Identifier: LGPL 3.0
pragma solidity ^0.8.18;

/**
 * @title Elliptic curve operations on twist points for alt_bn128
 * @author ARPA-Network adapted from https://github.com/musalbas/solidity-BN256G2
 * @dev Homepage: https://github.com/ARPA-Network/BLS-TSS-Network
 */

library BN256G2 {
    uint256 public constant FIELD_MODULUS = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47;
    uint256 public constant TWISTBX = 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5;
    uint256 public constant TWISTBY = 0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2;
    uint256 public constant PTXX = 0;
    uint256 public constant PTXY = 1;
    uint256 public constant PTYX = 2;
    uint256 public constant PTYY = 3;
    uint256 public constant PTZX = 4;
    uint256 public constant PTZY = 5;

    function ecTwistAdd(uint256[4] memory pt1, uint256[4] memory pt2) internal view returns (uint256[4] memory pt) {
        (uint256 xx, uint256 xy, uint256 yx, uint256 yy) =
            ecTwistAdd(pt1[0], pt1[1], pt1[2], pt1[3], pt2[0], pt2[1], pt2[2], pt2[3]);
        pt = [xx, xy, yx, yy];
    }

    /**
     * @notice Add two twist points
     * @param pt1xx Coefficient 1 of x on point 1
     * @param pt1xy Coefficient 2 of x on point 1
     * @param pt1yx Coefficient 1 of y on point 1
     * @param pt1yy Coefficient 2 of y on point 1
     * @param pt2xx Coefficient 1 of x on point 2
     * @param pt2xy Coefficient 2 of x on point 2
     * @param pt2yx Coefficient 1 of y on point 2
     * @param pt2yy Coefficient 2 of y on point 2
     * @return (pt3xx, pt3xy, pt3yx, pt3yy)
     */
    function ecTwistAdd(
        uint256 pt1xx,
        uint256 pt1xy,
        uint256 pt1yx,
        uint256 pt1yy,
        uint256 pt2xx,
        uint256 pt2xy,
        uint256 pt2yx,
        uint256 pt2yy
    ) internal view returns (uint256, uint256, uint256, uint256) {
        if (pt1xx == 0 && pt1xy == 0 && pt1yx == 0 && pt1yy == 0) {
            if (!(pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0)) {
                assert(isOnCurve(pt2xx, pt2xy, pt2yx, pt2yy));
            }
            return (pt2xx, pt2xy, pt2yx, pt2yy);
        } else if (pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0) {
            assert(isOnCurve(pt1xx, pt1xy, pt1yx, pt1yy));
            return (pt1xx, pt1xy, pt1yx, pt1yy);
        }

        assert(isOnCurve(pt1xx, pt1xy, pt1yx, pt1yy));
        assert(isOnCurve(pt2xx, pt2xy, pt2yx, pt2yy));

        uint256[6] memory pt1 = [pt1xx, pt1xy, pt1yx, pt1yy, 1, 0];
        uint256[6] memory pt2 = [pt2xx, pt2xy, pt2yx, pt2yy, 1, 0];
        uint256[6] memory pt3 = ecTwistAddJacobian(pt1, pt2);

        return fromJacobian(pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]);
    }

    function submod(uint256 a, uint256 b, uint256 n) internal pure returns (uint256) {
        return addmod(a, n - b, n);
    }

    function fq2Mul(uint256 xx, uint256 xy, uint256 yx, uint256 yy) internal pure returns (uint256, uint256) {
        return (
            submod(mulmod(xx, yx, FIELD_MODULUS), mulmod(xy, yy, FIELD_MODULUS), FIELD_MODULUS),
            addmod(mulmod(xx, yy, FIELD_MODULUS), mulmod(xy, yx, FIELD_MODULUS), FIELD_MODULUS)
        );
    }

    function fq2Muc(uint256 xx, uint256 xy, uint256 c) internal pure returns (uint256, uint256) {
        return (mulmod(xx, c, FIELD_MODULUS), mulmod(xy, c, FIELD_MODULUS));
    }

    function fq2Sub(uint256 xx, uint256 xy, uint256 yx, uint256 yy) internal pure returns (uint256 rx, uint256 ry) {
        return (submod(xx, yx, FIELD_MODULUS), submod(xy, yy, FIELD_MODULUS));
    }

    function fq2Inv(uint256 x, uint256 y) internal view returns (uint256, uint256) {
        uint256 inv =
            modInv(addmod(mulmod(y, y, FIELD_MODULUS), mulmod(x, x, FIELD_MODULUS), FIELD_MODULUS), FIELD_MODULUS);
        return (mulmod(x, inv, FIELD_MODULUS), FIELD_MODULUS - mulmod(y, inv, FIELD_MODULUS));
    }

    function isOnCurve(uint256 xx, uint256 xy, uint256 yx, uint256 yy) internal pure returns (bool) {
        uint256 yyx;
        uint256 yyy;
        uint256 xxxx;
        uint256 xxxy;
        (yyx, yyy) = fq2Mul(yx, yy, yx, yy);
        (xxxx, xxxy) = fq2Mul(xx, xy, xx, xy);
        (xxxx, xxxy) = fq2Mul(xxxx, xxxy, xx, xy);
        (yyx, yyy) = fq2Sub(yyx, yyy, xxxx, xxxy);
        (yyx, yyy) = fq2Sub(yyx, yyy, TWISTBX, TWISTBY);
        return yyx == 0 && yyy == 0;
    }

    function modInv(uint256 a, uint256 n) internal view returns (uint256 result) {
        bool success;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            let freemem := mload(0x40)
            mstore(freemem, 0x20)
            mstore(add(freemem, 0x20), 0x20)
            mstore(add(freemem, 0x40), 0x20)
            mstore(add(freemem, 0x60), a)
            mstore(add(freemem, 0x80), sub(n, 2))
            mstore(add(freemem, 0xA0), n)
            success := staticcall(sub(gas(), 2000), 5, freemem, 0xC0, freemem, 0x20)
            result := mload(freemem)
        }
        // solhint-disable-next-line reason-string
        require(success);
    }

    function fromJacobian(uint256 pt1xx, uint256 pt1xy, uint256 pt1yx, uint256 pt1yy, uint256 pt1zx, uint256 pt1zy)
        internal
        view
        returns (uint256 pt2xx, uint256 pt2xy, uint256 pt2yx, uint256 pt2yy)
    {
        uint256 invzx;
        uint256 invzy;
        (invzx, invzy) = fq2Inv(pt1zx, pt1zy);
        (pt2xx, pt2xy) = fq2Mul(pt1xx, pt1xy, invzx, invzy);
        (pt2yx, pt2yy) = fq2Mul(pt1yx, pt1yy, invzx, invzy);
    }

    function ecTwistAddJacobian(uint256[6] memory pt1, uint256[6] memory pt2)
        public
        pure
        returns (uint256[6] memory pt3)
    {
        if (pt1[4] == 0 && pt1[5] == 0) {
            (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) =
                (pt2[0], pt2[1], pt2[2], pt2[3], pt2[4], pt2[5]);
            return pt3;
        } else if (pt2[4] == 0 && pt2[5] == 0) {
            (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) =
                (pt1[0], pt1[1], pt1[2], pt1[3], pt1[4], pt1[5]);
            return pt3;
        }

        (pt2[2], pt2[3]) = fq2Mul(pt2[2], pt2[3], pt1[4], pt1[5]); // U1 = y2 * z1
        (pt3[PTYX], pt3[PTYY]) = fq2Mul(pt1[2], pt1[3], pt2[4], pt2[5]); // U2 = y1 * z2
        (pt2[0], pt2[1]) = fq2Mul(pt2[0], pt2[1], pt1[4], pt1[5]); // V1 = x2 * z1
        (pt3[PTZX], pt3[PTZY]) = fq2Mul(pt1[0], pt1[1], pt2[4], pt2[5]); // V2 = x1 * z2

        if (pt2[0] == pt3[PTZX] && pt2[1] == pt3[PTZY]) {
            if (pt2[2] == pt3[PTYX] && pt2[3] == pt3[PTYY]) {
                (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) =
                    ecTwistDoubleJacobian(pt1[0], pt1[1], pt1[2], pt1[3], pt1[4], pt1[5]);
                return pt3;
            }
            (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = (1, 0, 1, 0, 0, 0);
            return pt3;
        }

        (pt2[4], pt2[5]) = fq2Mul(pt1[4], pt1[5], pt2[4], pt2[5]); // W = z1 * z2
        (pt1[0], pt1[1]) = fq2Sub(pt2[2], pt2[3], pt3[PTYX], pt3[PTYY]); // U = U1 - U2
        (pt1[2], pt1[3]) = fq2Sub(pt2[0], pt2[1], pt3[PTZX], pt3[PTZY]); // V = V1 - V2
        (pt1[4], pt1[5]) = fq2Mul(pt1[2], pt1[3], pt1[2], pt1[3]); // V_squared = V * V
        (pt2[2], pt2[3]) = fq2Mul(pt1[4], pt1[5], pt3[PTZX], pt3[PTZY]); // V_squared_times_V2 = V_squared * V2
        (pt1[4], pt1[5]) = fq2Mul(pt1[4], pt1[5], pt1[2], pt1[3]); // V_cubed = V * V_squared
        (pt3[PTZX], pt3[PTZY]) = fq2Mul(pt1[4], pt1[5], pt2[4], pt2[5]); // newz = V_cubed * W
        (pt2[0], pt2[1]) = fq2Mul(pt1[0], pt1[1], pt1[0], pt1[1]); // U * U
        (pt2[0], pt2[1]) = fq2Mul(pt2[0], pt2[1], pt2[4], pt2[5]); // U * U * W
        (pt2[0], pt2[1]) = fq2Sub(pt2[0], pt2[1], pt1[4], pt1[5]); // U * U * W - V_cubed
        (pt2[4], pt2[5]) = fq2Muc(pt2[2], pt2[3], 2); // 2 * V_squared_times_V2
        (pt2[0], pt2[1]) = fq2Sub(pt2[0], pt2[1], pt2[4], pt2[5]); // A = U * U * W - V_cubed - 2 * V_squared_times_V2
        (pt3[PTXX], pt3[PTXY]) = fq2Mul(pt1[2], pt1[3], pt2[0], pt2[1]); // newx = V * A
        (pt1[2], pt1[3]) = fq2Sub(pt2[2], pt2[3], pt2[0], pt2[1]); // V_squared_times_V2 - A
        (pt1[2], pt1[3]) = fq2Mul(pt1[0], pt1[1], pt1[2], pt1[3]); // U * (V_squared_times_V2 - A)
        (pt1[0], pt1[1]) = fq2Mul(pt1[4], pt1[5], pt3[PTYX], pt3[PTYY]); // V_cubed * U2
        (pt3[PTYX], pt3[PTYY]) = fq2Sub(pt1[2], pt1[3], pt1[0], pt1[1]); // newy = U * (V_squared_times_V2 - A) - V_cubed * U2
    }

    function ecTwistDoubleJacobian(
        uint256 pt1xx,
        uint256 pt1xy,
        uint256 pt1yx,
        uint256 pt1yy,
        uint256 pt1zx,
        uint256 pt1zy
    ) public pure returns (uint256 pt2xx, uint256 pt2xy, uint256 pt2yx, uint256 pt2yy, uint256 pt2zx, uint256 pt2zy) {
        (pt2xx, pt2xy) = fq2Muc(pt1xx, pt1xy, 3); // 3 * x
        (pt2xx, pt2xy) = fq2Mul(pt2xx, pt2xy, pt1xx, pt1xy); // W = 3 * x * x
        (pt1zx, pt1zy) = fq2Mul(pt1yx, pt1yy, pt1zx, pt1zy); // S = y * z
        (pt2yx, pt2yy) = fq2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // x * y
        (pt2yx, pt2yy) = fq2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // B = x * y * S
        (pt1xx, pt1xy) = fq2Mul(pt2xx, pt2xy, pt2xx, pt2xy); // W * W
        (pt2zx, pt2zy) = fq2Muc(pt2yx, pt2yy, 8); // 8 * B
        (pt1xx, pt1xy) = fq2Sub(pt1xx, pt1xy, pt2zx, pt2zy); // H = W * W - 8 * B
        (pt2zx, pt2zy) = fq2Mul(pt1zx, pt1zy, pt1zx, pt1zy); // S_squared = S * S
        (pt2yx, pt2yy) = fq2Muc(pt2yx, pt2yy, 4); // 4 * B
        (pt2yx, pt2yy) = fq2Sub(pt2yx, pt2yy, pt1xx, pt1xy); // 4 * B - H
        (pt2yx, pt2yy) = fq2Mul(pt2yx, pt2yy, pt2xx, pt2xy); // W * (4 * B - H)
        (pt2xx, pt2xy) = fq2Muc(pt1yx, pt1yy, 8); // 8 * y
        (pt2xx, pt2xy) = fq2Mul(pt2xx, pt2xy, pt1yx, pt1yy); // 8 * y * y
        (pt2xx, pt2xy) = fq2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // 8 * y * y * S_squared
        (pt2yx, pt2yy) = fq2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // newy = W * (4 * B - H) - 8 * y * y * S_squared
        (pt2xx, pt2xy) = fq2Muc(pt1xx, pt1xy, 2); // 2 * H
        (pt2xx, pt2xy) = fq2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // newx = 2 * H * S
        (pt2zx, pt2zy) = fq2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // S * S_squared
        (pt2zx, pt2zy) = fq2Muc(pt2zx, pt2zy, 8); // newz = 8 * S * S_squared
    }
}

File 20 of 22 : Utils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

// 5k is plenty for an EXTCODESIZE call (2600) + warm CALL (100)
// and some arithmetic operations.
uint256 constant GAS_FOR_CALL_EXACT_CHECK = 5_000;

function containElement(uint256[] memory arr, uint256 element) pure returns (bool) {
    for (uint256 i = 0; i < arr.length; i++) {
        if (arr[i] == element) {
            return true;
        }
    }
    return false;
}

function containElement(address[] memory arr, address element) pure returns (bool) {
    for (uint256 i = 0; i < arr.length; i++) {
        if (arr[i] == element) {
            return true;
        }
    }
    return false;
}

/**
 * @dev returns the minimum threshold for a group of size groupSize
 */
function minimumThreshold(uint256 groupSize) pure returns (uint256) {
    return groupSize / 2 + 1;
}

/**
 * @dev choose one random index from an array.
 */
function pickRandomIndex(uint256 seed, uint256 length) pure returns (uint256) {
    return uint256(keccak256(abi.encodePacked(seed))) % length;
}

/**
 * @dev choose "count" random indices from "indices" array.
 */
function pickRandomIndex(uint256 seed, uint256[] memory indices, uint256 count) pure returns (uint256[] memory) {
    uint256[] memory chosenIndices = new uint256[](count);

    // Create copy of indices to avoid modifying original array.
    uint256[] memory remainingIndices = new uint256[](indices.length);
    for (uint256 i = 0; i < indices.length; i++) {
        remainingIndices[i] = indices[i];
    }

    uint256 remainingCount = remainingIndices.length;
    for (uint256 i = 0; i < count; i++) {
        uint256 index = uint256(keccak256(abi.encodePacked(seed, i))) % remainingCount;
        chosenIndices[i] = remainingIndices[index];
        remainingIndices[index] = remainingIndices[remainingCount - 1];
        remainingCount--;
    }
    return chosenIndices;
}

/**
 * @dev iterates through list of members and remove disqualified nodes.
 */
function getNonDisqualifiedMajorityMembers(address[] memory nodeAddresses, address[] memory disqualifiedNodes)
    pure
    returns (address[] memory)
{
    address[] memory majorityMembers = new address[](nodeAddresses.length);
    uint256 majorityMembersLength = 0;
    for (uint256 i = 0; i < nodeAddresses.length; i++) {
        if (!containElement(disqualifiedNodes, nodeAddresses[i])) {
            majorityMembers[majorityMembersLength] = nodeAddresses[i];
            majorityMembersLength++;
        }
    }

    // remove trailing zero addresses
    return trimTrailingElements(majorityMembers, majorityMembersLength);
}

function trimTrailingElements(uint256[] memory arr, uint256 newLength) pure returns (uint256[] memory) {
    uint256[] memory output = new uint256[](newLength);
    for (uint256 i = 0; i < newLength; i++) {
        output[i] = arr[i];
    }
    return output;
}

function trimTrailingElements(address[] memory arr, uint256 newLength) pure returns (address[] memory) {
    address[] memory output = new address[](newLength);
    for (uint256 i = 0; i < newLength; i++) {
        output[i] = arr[i];
    }
    return output;
}

/**
 * @dev calls target address with exactly gasAmount gas and data as calldata
 * or reverts if at least gasAmount gas is not available.
 */
function callWithExactGas(uint256 gasAmount, address target, bytes memory data) returns (bool success) {
    // solhint-disable-next-line no-inline-assembly
    assembly {
        let g := gas()
        // Compute g -= GAS_FOR_CALL_EXACT_CHECK and check for underflow
        // The gas actually passed to the callee is min(gasAmount, 63//64*gas available).
        // We want to ensure that we revert if gasAmount >  63//64*gas available
        // as we do not want to provide them with less, however that check itself costs
        // gas.  GAS_FOR_CALL_EXACT_CHECK ensures we have at least enough gas to be able
        // to revert if gasAmount >  63//64*gas available.
        if lt(g, GAS_FOR_CALL_EXACT_CHECK) { revert(0, 0) }
        g := sub(g, GAS_FOR_CALL_EXACT_CHECK)
        // if g - g//64 <= gasAmount, revert
        // (we subtract g//64 because of EIP-150)
        if iszero(gt(sub(g, div(g, 64)), gasAmount)) { revert(0, 0) }
        // solidity calls check that a contract actually exists at the destination, so we do the same
        if iszero(extcodesize(target)) { revert(0, 0) }
        // call and return whether we succeeded. ignore return data
        // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength)
        success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0)
    }
    return success;
}

File 21 of 22 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../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.
 *
 * By default, the owner account will be the one that deploys the contract. 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;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @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 {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing 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 {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _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);
    }
}

File 22 of 22 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @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;
    }
}

Settings
{
  "remappings": [
    "Staking-v0.1/=lib/Staking-v0.1/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 500
  },
  "metadata": {
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {
    "src/libraries/BLS.sol": {
      "BLS": "0x34E6885e0e9BBEA0Bae36d00D4D9B4ED873f2cA8"
    },
    "src/libraries/GroupLib.sol": {
      "GroupLib": "0xCd97Ed85f6EDDC535A4f55A4c67C2e02800C2E67"
    }
  }
}

Contract ABI

API
[{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"}],"name":"CoordinatorNotFound","type":"error"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"}],"name":"DkgNotInProgress","type":"error"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"int8","name":"phase","type":"int8"}],"name":"DkgStillInProgress","type":"error"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"uint256","name":"inputGroupEpoch","type":"uint256"},{"internalType":"uint256","name":"currentGroupEpoch","type":"uint256"}],"name":"EpochMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"}],"name":"GroupNotExist","type":"error"},{"inputs":[],"name":"InvalidPartialPublicKey","type":"error"},{"inputs":[],"name":"InvalidPublicKey","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[],"name":"NodeAlreadyActive","type":"error"},{"inputs":[],"name":"NodeAlreadyRegistered","type":"error"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"address","name":"nodeIdAddress","type":"address"}],"name":"NodeNotInGroup","type":"error"},{"inputs":[],"name":"NodeNotRegistered","type":"error"},{"inputs":[{"internalType":"uint256","name":"pendingUntilBlock","type":"uint256"}],"name":"NodeStillPending","type":"error"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"address","name":"nodeIdAddress","type":"address"}],"name":"PartialKeyAlreadyRegistered","type":"error"},{"inputs":[],"name":"SenderNotAdapter","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"stakingContractAddress","type":"address"},{"indexed":false,"internalType":"address","name":"adapterContractAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"nodeStakingAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"disqualifiedNodePenaltyAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"defaultNumberOfCommitters","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"defaultDkgPhaseDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"groupMaxCapacity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"idealNumberOfGroups","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pendingBlockAfterQuit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dkgPostProcessReward","type":"uint256"}],"name":"ControllerConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nodeAddress","type":"address"},{"indexed":false,"internalType":"bytes","name":"dkgPublicKey","type":"bytes"}],"name":"DkgPublicKeyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"globalEpoch","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"groupIndex","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"groupEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"size","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"threshold","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"members","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"assignmentBlockHeight","type":"uint256"},{"indexed":false,"internalType":"address","name":"coordinatorAddress","type":"address"}],"name":"DkgTask","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nodeAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"groupIndex","type":"uint256"}],"name":"NodeActivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nodeAddress","type":"address"}],"name":"NodeQuit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nodeAddress","type":"address"},{"indexed":false,"internalType":"bytes","name":"dkgPublicKey","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"groupIndex","type":"uint256"}],"name":"NodeRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nodeAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"arpaAmount","type":"uint256"}],"name":"NodeRewarded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nodeIdAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakingRewardPenalty","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pendingBlock","type":"uint256"}],"name":"NodeSlashed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address[]","name":"nodes","type":"address[]"},{"internalType":"uint256","name":"ethAmount","type":"uint256"},{"internalType":"uint256","name":"arpaAmount","type":"uint256"}],"name":"addReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"dkgPublicKey","type":"bytes"}],"name":"changeDkgPublicKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"uint256","name":"groupEpoch","type":"uint256"},{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"bytes","name":"partialPublicKey","type":"bytes"},{"internalType":"address[]","name":"disqualifiedNodes","type":"address[]"}],"internalType":"struct IController.CommitDkgParams","name":"params","type":"tuple"}],"name":"commitDkg","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nodeAddress","type":"address"}],"name":"getBelongingGroup","outputs":[{"internalType":"int256","name":"","type":"int256"},{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getControllerConfig","outputs":[{"internalType":"address","name":"stakingContractAddress","type":"address"},{"internalType":"address","name":"adapterContractAddress","type":"address"},{"internalType":"uint256","name":"nodeStakingAmount","type":"uint256"},{"internalType":"uint256","name":"disqualifiedNodePenaltyAmount","type":"uint256"},{"internalType":"uint256","name":"defaultNumberOfCommitters","type":"uint256"},{"internalType":"uint256","name":"defaultDkgPhaseDuration","type":"uint256"},{"internalType":"uint256","name":"groupMaxCapacity","type":"uint256"},{"internalType":"uint256","name":"idealNumberOfGroups","type":"uint256"},{"internalType":"uint256","name":"pendingBlockAfterQuit","type":"uint256"},{"internalType":"uint256","name":"dkgPostProcessReward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"}],"name":"getCoordinator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"}],"name":"getGroup","outputs":[{"components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"components":[{"internalType":"address","name":"nodeIdAddress","type":"address"},{"internalType":"uint256[4]","name":"partialPublicKey","type":"uint256[4]"}],"internalType":"struct IController.Member[]","name":"members","type":"tuple[]"},{"internalType":"address[]","name":"committers","type":"address[]"},{"components":[{"internalType":"address[]","name":"nodeIdAddress","type":"address[]"},{"components":[{"internalType":"uint256","name":"groupEpoch","type":"uint256"},{"internalType":"uint256[4]","name":"publicKey","type":"uint256[4]"},{"internalType":"address[]","name":"disqualifiedNodes","type":"address[]"}],"internalType":"struct IController.CommitResult","name":"commitResult","type":"tuple"}],"internalType":"struct IController.CommitCache[]","name":"commitCacheList","type":"tuple[]"},{"internalType":"bool","name":"isStrictlyMajorityConsensusReached","type":"bool"},{"internalType":"uint256[4]","name":"publicKey","type":"uint256[4]"}],"internalType":"struct IController.Group","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGroupCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGroupEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"}],"name":"getGroupThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastOutput","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"uint256","name":"memberIndex","type":"uint256"}],"name":"getMember","outputs":[{"components":[{"internalType":"address","name":"nodeIdAddress","type":"address"},{"internalType":"uint256[4]","name":"partialPublicKey","type":"uint256[4]"}],"internalType":"struct IController.Member","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nodeAddress","type":"address"}],"name":"getNode","outputs":[{"components":[{"internalType":"address","name":"idAddress","type":"address"},{"internalType":"bytes","name":"dkgPublicKey","type":"bytes"},{"internalType":"bool","name":"state","type":"bool"},{"internalType":"uint256","name":"pendingUntilBlock","type":"uint256"}],"internalType":"struct IController.Node","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nodeAddress","type":"address"}],"name":"getNodeWithdrawableTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getValidGroupIndices","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"arpa","type":"address"},{"internalType":"uint256","name":"lastOutput","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"address","name":"nodeIdAddress","type":"address"}],"name":"isPartialKeyRegistered","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nodeActivate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nodeQuit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"dkgPublicKey","type":"bytes"}],"name":"nodeRegister","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"nodeWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"groupIndex","type":"uint256"},{"internalType":"uint256","name":"groupEpoch","type":"uint256"}],"name":"postProcessDkg","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"stakingContractAddress","type":"address"},{"internalType":"address","name":"adapterContractAddress","type":"address"},{"internalType":"uint256","name":"nodeStakingAmount","type":"uint256"},{"internalType":"uint256","name":"disqualifiedNodePenaltyAmount","type":"uint256"},{"internalType":"uint256","name":"defaultNumberOfCommitters","type":"uint256"},{"internalType":"uint256","name":"defaultDkgPhaseDuration","type":"uint256"},{"internalType":"uint256","name":"groupMaxCapacity","type":"uint256"},{"internalType":"uint256","name":"idealNumberOfGroups","type":"uint256"},{"internalType":"uint256","name":"pendingBlockAfterQuit","type":"uint256"},{"internalType":"uint256","name":"dkgPostProcessReward","type":"uint256"}],"name":"setControllerConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lastOutput","type":"uint256"}],"name":"setLastOutput","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50615ff6806100206000396000f3fe60806040523480156200001157600080fd5b5060043610620001dd5760003560e01c80638da5cb5b116200010d578063ceb6065411620000a3578063e37eb96c116200007a578063e37eb96c146200048e578063f2fde38b14620004a5578063f3df080214620004bc578063f49e0ba914620004d357600080fd5b8063ceb6065414620003dd578063d11b8e681462000403578063e275cde6146200047757600080fd5b80639d20904811620000e45780639d209048146200035f578063b330a0fd1462000385578063c2db900b146200039e578063cd6dc68714620003c657600080fd5b80638da5cb5b146200032c578063914eb34d146200033e5780639a075bc3146200035557600080fd5b80634d79a8931162000183578063715018a6116200015a578063715018a614620002f85780637a2af56e14620003025780637ee49cfd146200030c57806380110e4e146200031557600080fd5b80634d79a89314620002b25780634ecea80d14620002d857806351a2b9a014620002ef57600080fd5b80632ff9e52011620001b85780632ff9e520146200023f5780633b6c00b0146200025657806342424d6f146200026d57600080fd5b806306545a9314620001e25780630bf9c5c614620001f9578063227d0f461462000212575b600080fd5b6072545b6040519081526020015b60405180910390f35b620002106200020a36600462003aa3565b62000501565b005b620002296200022336600462003adc565b62000999565b60408051928352602083019190915201620001f0565b620002106200025036600462003b03565b62000a42565b620002296200026736600462003adc565b62000bcf565b620002996200027e36600462003b86565b6000908152607060205260409020546001600160a01b031690565b6040516001600160a01b039091168152602001620001f0565b620002c9620002c336600462003aa3565b62000c59565b604051620001f0919062003be6565b62000210620002e936600462003adc565b62000cf6565b607754620001e6565b6200021062000e26565b6200021062000e3e565b607154620001e6565b620002106200032636600462003bf6565b62000ff1565b6033546001600160a01b031662000299565b620002106200034f36600462003d88565b62001336565b62000210620014a7565b620003766200037036600462003adc565b62001751565b604051620001f0919062003e2e565b6200038f62001875565b604051620001f0919062003e81565b620003b5620003af36600462003ec7565b620018f8565b6040519015158152602001620001f0565b62000210620003d736600462003efa565b62001cc4565b620003f4620003ee36600462003b86565b62001e0e565b604051620001f0919062004056565b606554606654606754606854607654606954607554607454606a54606b54604080516001600160a01b039b8c1681529a90991660208b0152978901969096526060880194909452608087019290925260a086015260c085015260e084015261010083015261012082015261014001620001f0565b620002106200048836600462003bf6565b6200212c565b620002106200049f3660046200418b565b620022fe565b62000210620004b636600462003adc565b62002ada565b62000210620004cd36600462003b86565b62002b59565b62000229620004e436600462003b86565b600090815260736020526040902060038101546002909101549091565b60725482106200052c57604051637f6efcb560e11b8152600481018390526024015b60405180910390fd5b6040516358914d0360e01b8152607160048201526024810183905233604482015273cd97ed85f6eddc535a4f55a4c67c2e02800c2e67906358914d0390606401602060405180830381865af41580156200058a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005b0919062004262565b19620005d95760405163eb51f5f960e01b81526004810183905233602482015260440162000523565b6000828152607360205260409020600181015482146200062457600181015460405163048b63c360e11b81526004810185905260248101849052604481019190915260640162000523565b6000838152607060205260409020546001600160a01b03166200065e57604051635291bbcf60e01b81526004810184905260240162000523565b60008381526070602090815260409182902054825163221f951160e01b815292516001600160a01b0390911692839263221f9511926004808401938290030181865afa158015620006b3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620006d991906200427c565b60000b60001914620007715783816001600160a01b031663221f95116040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000725573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200074b91906200427c565b60405163f7c06f9160e01b8152600481019290925260000b602482015260440162000523565b806001600160a01b0316639cb8a26a6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015620007ad57600080fd5b505af1158015620007c2573d6000803e3d6000fd5b505050600085815260706020526040902080546001600160a01b031916905550600782015460ff166200092457607754604051630295316d60e51b815260716004820152602481018690526044810191909152600090819073cd97ed85f6eddc535a4f55a4c67c2e02800c2e67906352a62da090606401600060405180830381865af415801562000857573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000881919081019062004370565b9150915060005b8251811015620008d657620008c1838281518110620008ab57620008ab620043db565b6020026020010151606560030154600062002b8a565b80620008cd8162004407565b91505062000888565b5060005b815181101562000920576200090b828281518110620008fd57620008fd620043db565b602002602001015162002c47565b80620009178162004407565b915050620008da565b5050505b606b54336000908152606f6020526040812080549091906200094890849062004423565b9091555050606b5460405133917f8353a804115421789f3ab2eeb3f5215943906ce12100c91d40fc865caf742b6f916200098b9160008252602082015260400190565b60405180910390a250505050565b6001600160a01b0381166000908152606e6020526040812054819015620009e7576001600160a01b0383166000908152606e6020526040902054620009e19060019062004439565b620009ea565b60005b6001600160a01b0384166000908152606f60205260409020541562000a36576001600160a01b0384166000908152606f602052604090205462000a309060019062004439565b62000a39565b60005b91509150915091565b62000a4c620032ba565b6040805160e0810182526001600160a01b038c8116808352908c16602083018190528284018c9052606083018b90526080830189905260a0830186905260c0909201849052606580546001600160a01b03199081169092179055606680549091169091179055606789905560688890556069869055606a839055606b829055516301a79f6360e71b81526071600482015260248101849052604481018590526064810187905273cd97ed85f6eddc535a4f55a4c67c2e02800c2e679063d3cfb1809060840160006040518083038186803b15801562000b2a57600080fd5b505af415801562000b3f573d6000803e3d6000fd5b5050604080516001600160a01b03808f1682528d1660208201529081018b9052606081018a90526080810189905260a0810188905260c0810187905260e08101869052610100810185905261012081018490527f3202ca2a288846b65f27bc3e13c1e38aaf2e4356b80b963d67aea8a822645e1f925061014001905060405180910390a150505050505050505050565b60405163787c576760e01b8152607160048201526001600160a01b0382166024820152600090819073cd97ed85f6eddc535a4f55a4c67c2e02800c2e679063787c5767906044016040805180830381865af415801562000c33573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000a3991906200444f565b62000c63620038af565b600083815260736020526040902060040180548390811062000c895762000c89620043db565b6000918252602091829020604080518082018252600590930290910180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b81548152602001906001019080831162000cd05750505050508152505090505b92915050565b6001600160a01b03811662000d1e5760405163f6b2911f60e01b815260040160405180910390fd5b336000908152606e6020908152604080832054606f90925290912054600181111562000d8057336000908152606f6020526040902060019081905562000d8090849062000d6c908462004439565b606c546001600160a01b0316919062003316565b600182111562000e2157336000908152606e602052604090206001908190556066546001600160a01b031690630ad98f6a90859062000dc0908662004439565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b15801562000e0757600080fd5b505af115801562000e1c573d6000803e3d6000fd5b505050505b505050565b62000e30620032ba565b62000e3c60006200337f565b565b336000818152606d60205260409020805490916001600160a01b039091161462000e7a576040516229eaad60e31b815260040160405180910390fd5b60775460405163a6ae8a8160e01b815260716004820152336024820152604481019190915260009073cd97ed85f6eddc535a4f55a4c67c2e02800c2e679063a6ae8a8190606401600060405180830381865af415801562000edf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000f09919081019062004474565b905060005b815181101562000f465762000f31828281518110620008fd57620008fd620043db565b8062000f3d8162004407565b91505062000f0e565b5062000f5833606560050154620033d1565b606554606754604051637eee288d60e01b815233600482015260248101919091526001600160a01b0390911690637eee288d90604401600060405180830381600087803b15801562000fa957600080fd5b505af115801562000fbe573d6000803e3d6000fd5b50506040513392507f68577adbb6b0647e21353ff032be5797d9fa0879ce7e05fe617e40368441f97d9150600090a25050565b336000908152606d60205260409020546001600160a01b0316156200102957604051630eb0d31360e11b815260040160405180910390fd5b6040516316f6db8160e01b81526000907334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca8906316f6db8190620010679086908690600401620044d6565b608060405180830381865af415801562001085573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620010ab9190620044ec565b604051636fda2c7960e01b81529091507334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca890636fda2c7990620010e790849060040162004572565b602060405180830381865af415801562001105573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200112b919062004598565b620011495760405163145a1fdd60e31b815260040160405180910390fd5b60655460675460405163282d3fdf60e01b815233600482015260248101919091526001600160a01b039091169063282d3fdf90604401600060405180830381600087803b1580156200119a57600080fd5b505af1158015620011af573d6000803e3d6000fd5b5050336000818152606d6020526040902080546001600160a01b031916909117815591505060018101620011e58486836200463c565b5060028101805460ff19166001908117909155336000818152606e60209081526040808320859055606f909152808220939093556077549251629bf68360e11b81526071600482015260248101929092526044820192909252819073cd97ed85f6eddc535a4f55a4c67c2e02800c2e6790630137ed0690606401600060405180830381865af41580156200127d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620012a791908101906200470a565b9150915060005b8151811015620012e657620012d1828281518110620008fd57620008fd620043db565b80620012dd8162004407565b915050620012ae565b50336001600160a01b03167fd4ec586f4f9f417f99e20fe821fbaa10a10a4b95f8712a0c57c6d8ed970e98bd87878560405162001326939291906200474b565b60405180910390a2505050505050565b6066546001600160a01b03163314620013625760405163469666c560e01b815260040160405180910390fd5b60005b8351811015620014a15782606e6000868481518110620013895762001389620043db565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000828254620013c2919062004423565b9250508190555081606f6000868481518110620013e357620013e3620043db565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060008282546200141c919062004423565b92505081905550838181518110620014385762001438620043db565b60200260200101516001600160a01b03167f8353a804115421789f3ab2eeb3f5215943906ce12100c91d40fc865caf742b6f848460405162001484929190918252602082015260400190565b60405180910390a280620014988162004407565b91505062001365565b50505050565b336000818152606d60205260409020805490916001600160a01b0390911614620014e3576040516229eaad60e31b815260040160405180910390fd5b600281015460ff16156200150a576040516324a228bb60e21b815260040160405180910390fd5b4381600301541115620015395780600301546040516363525acb60e11b81526004016200052391815260200190565b60655460405163929ec53760e01b81523360048201526000916001600160a01b03169063929ec53790602401602060405180830381865afa15801562001583573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620015a9919062004262565b6067549091508110156200163f576065546067546001600160a01b039091169063282d3fdf903390620015de90859062004439565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b1580156200162557600080fd5b505af11580156200163a573d6000803e3d6000fd5b505050505b60028201805460ff19166001179055607754604051629bf68360e11b8152607160048201523360248201526044810191909152600090819073cd97ed85f6eddc535a4f55a4c67c2e02800c2e6790630137ed0690606401600060405180830381865af4158015620016b4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620016de91908101906200470a565b9150915060005b81518110156200171d5762001708828281518110620008fd57620008fd620043db565b80620017148162004407565b915050620016e5565b5060405182815233907ffc97cd9154b40031874ef09a9436a4b60052e4dcf40f21b1258be265fac4a397906020016200098b565b62001788604051806080016040528060006001600160a01b0316815260200160608152602001600015158152602001600081525090565b6001600160a01b038083166000908152606d60209081526040918290208251608081019093528054909316825260018301805492939291840191620017cd90620045b6565b80601f0160208091040260200160405190810160405280929190818152602001828054620017fb90620045b6565b80156200184c5780601f1062001820576101008083540402835291602001916200184c565b820191906000526020600020905b8154815290600101906020018083116200182e57829003601f168201915b5050509183525050600282015460ff161515602082015260039091015460409091015292915050565b604051631c2da66560e31b81526071600482015260609073cd97ed85f6eddc535a4f55a4c67c2e02800c2e679063e16d332890602401600060405180830381865af4158015620018c9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620018f3919081019062004474565b905090565b6000828152607360209081526040808320815161012081018352815481526001820154818501526002820154818401526003820154606082015260048201805484518187028101870190955280855286959294608086019390929190879084015b82821015620019d55760008481526020908190206040805180820182526005860290920180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b815481526020019060010190808311620019a8575050505050815250508152602001906001019062001959565b5050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801562001a3857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001a19575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b8282101562001bb657838290600052602060002090600702016040518060400160405290816000820180548060200260200160405190810160405280929190818152602001828054801562001ae557602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001ac6575b50505091835250506040805160608101825260018401805482528251608081019384905260209485019492939192840191600287019060049082845b81548152602001906001019080831162001b2157505050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801562001b9957602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001b7a575b505050505081525050815250508152602001906001019062001a66565b50505090825250600782015460ff1615156020820152604080516080810182529101906008830160048282826020028201915b81548152602001906001019080831162001be957505050505081525050905060005b81608001515181101562001cb957836001600160a01b03168260800151828151811062001c3c5762001c3c620043db565b6020026020010151600001516001600160a01b03160362001ca4578160800151818151811062001c705762001c70620043db565b60200260200101516020015160006004811062001c915762001c91620043db565b6020020151600014159250505062000cf0565b8062001cb08162004407565b91505062001c0b565b506000949350505050565b600054610100900460ff161580801562001ce55750600054600160ff909116105b8062001d015750303b15801562001d01575060005460ff166001145b62001d755760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840162000523565b6000805460ff19166001179055801562001d99576000805461ff0019166101001790555b606c80546001600160a01b0319166001600160a01b038516179055607782905562001dc362003469565b801562000e21576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b62001e18620038d9565b60008281526073602090815260408083208151610120810183528154815260018201548185015260028201548184015260038201546060820152600482018054845181870281018701909552808552919592946080870194939192919084015b8282101562001ef45760008481526020908190206040805180820182526005860290920180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b81548152602001906001019080831162001ec7575050505050815250508152602001906001019062001e78565b5050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801562001f5757602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001f38575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b82821015620020d55783829060005260206000209060070201604051806040016040529081600082018054806020026020016040519081016040528092919081815260200182805480156200200457602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001fe5575b50505091835250506040805160608101825260018401805482528251608081019384905260209485019492939192840191600287019060049082845b81548152602001906001019080831162002040575050505050815260200160058201805480602002602001604051908101604052809291908181526020018280548015620020b857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162002099575b505050505081525050815250508152602001906001019062001f85565b50505090825250600782015460ff1615156020820152604080516080810182529101906008830160048282826020028201915b81548152602001906001019080831162002108575050505050815250509050919050565b336000818152606d60205260409020805490916001600160a01b039091161462002168576040516229eaad60e31b815260040160405180910390fd5b600281015460ff16156200218f576040516324a228bb60e21b815260040160405180910390fd5b6040516316f6db8160e01b81526000907334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca8906316f6db8190620021cd9087908790600401620044d6565b608060405180830381865af4158015620021eb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620022119190620044ec565b604051636fda2c7960e01b81529091507334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca890636fda2c79906200224d90849060040162004572565b602060405180830381865af41580156200226b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002291919062004598565b620022af5760405163145a1fdd60e31b815260040160405180910390fd5b60018201620022c08486836200463c565b50336001600160a01b03167f4a327ac4843af7a9586b5a2a2c312bd17289ae1d70da32855ed539fe39f86a5085856040516200098b929190620044d6565b60725481511062002329578051604051637f6efcb560e11b8152600481019190915260240162000523565b80516000908152607060205260409020546001600160a01b031662002368578051604051635291bbcf60e01b8152600481019190915260240162000523565b805160009081526070602090815260409182902054825163221f951160e01b815292516001600160a01b0390911692839263221f9511926004808401938290030181865afa158015620023bf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620023e591906200427c565b60000b196200240e5781516040516343609fe160e01b8152600481019190915260240162000523565b81516000908152607360209081526040909120600181015491840151909114620024695782516020840151600183015460405163048b63c360e11b815260048101939093526024830191909152604482015260640162000523565b82516040516358914d0360e01b815260716004820152602481019190915233604482015273cd97ed85f6eddc535a4f55a4c67c2e02800c2e67906358914d0390606401602060405180830381865af4158015620024ca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620024f0919062004262565b196200251c57825160405163eb51f5f960e01b8152600481019190915233602482015260440162000523565b82516200252a9033620018f8565b156200255657825160405163173ca10960e31b8152600481019190915233602482015260440162000523565b60608301516040516316f6db8160e01b81526000917334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca8916316f6db8191620025959160040162004771565b608060405180830381865af4158015620025b3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620025d99190620044ec565b604051636fda2c7960e01b81529091507334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca890636fda2c79906200261590849060040162004572565b602060405180830381865af415801562002633573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002659919062004598565b6200267757604051631886185760e01b815260040160405180910390fd5b60408085015190516316f6db8160e01b81526000917334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca8916316f6db8191620026b69160040162004771565b608060405180830381865af4158015620026d4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620026fa9190620044ec565b604051636fda2c7960e01b81529091507334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca890636fda2c79906200273690849060040162004572565b602060405180830381865af415801562002754573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200277a919062004598565b620027985760405163145a1fdd60e31b815260040160405180910390fd5b604080516060810182526020808801518252810183905260808701518183015286519151636aba6eeb60e11b8152909173cd97ed85f6eddc535a4f55a4c67c2e02800c2e679163d574ddd691620027f89160719190869060040162004786565b602060405180830381865af415801562002816573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200283c919062004598565b6200292357604080516001818301818152608083019093526000928291606083016020803683375050508152602001839052805180519192503391600090620028895762002889620043db565b6001600160a01b03909216602092830291909101820152600686018054600181018255600091825290829020835180518594600790940290920192620028d59284929091019062003929565b5060208281015180516001840190815591810151909190620028fe906002850190600462003993565b50604082015180516200291c91600584019160209091019062003929565b5050505050505b85516040516358914d0360e01b815260716004808301919091526024820192909252336044820152849186019073cd97ed85f6eddc535a4f55a4c67c2e02800c2e67906358914d0390606401602060405180830381865af41580156200298d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620029b3919062004262565b81548110620029c657620029c6620043db565b9060005260206000209060050201600101906004620029e792919062003993565b50600784015460ff1662002ad257855160775460405163449cf2fd60e11b81526071600482015260248101929092526044820152600090819073cd97ed85f6eddc535a4f55a4c67c2e02800c2e6790638939e5fa90606401600060405180830381865af415801562002a5d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262002a87919081019062004811565b91509150811562002acf5760005b815181101562002acd5762002ab8828281518110620008ab57620008ab620043db565b8062002ac48162004407565b91505062002a95565b505b50505b505050505050565b62002ae4620032ba565b6001600160a01b03811662002b4b5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840162000523565b62002b56816200337f565b50565b6066546001600160a01b0316331462002b855760405163469666c560e01b815260040160405180910390fd5b607755565b606554604051638899fdeb60e01b81526001600160a01b0385811660048301526024820185905290911690638899fdeb90604401600060405180830381600087803b15801562002bd957600080fd5b505af115801562002bee573d6000803e3d6000fd5b5050505062002bfe8382620033d1565b60408051838152602081018390526001600160a01b038516917fa8d720d0a0a2e7c96bf9eb87433901ebb6331356c8f3283b2568de34478703cc910160405180910390a2505050565b62002c54607182620034e0565b60008181526073602090815260408083208151610120810183528154815260018201548185015260028201548184015260038201546060820152600482018054845181870281018701909552808552919492936080860193909290879084015b8282101562002d305760008481526020908190206040805180820182526005860290920180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b81548152602001906001019080831162002d03575050505050815250508152602001906001019062002cb4565b5050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801562002d9357602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162002d74575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b8282101562002f1157838290600052602060002090600702016040518060400160405290816000820180548060200260200160405190810160405280929190818152602001828054801562002e4057602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162002e21575b50505091835250506040805160608101825260018401805482528251608081019384905260209485019492939192840191600287019060049082845b81548152602001906001019080831162002e7c57505050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801562002ef457602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162002ed5575b505050505081525050815250508152602001906001019062002dc1565b50505090825250600782015460ff1615156020820152604080516080810182529101906008830160048282826020028201915b81548152602001906001019080831162002f445750505050508152505090506000816060015160656004015460405162002f7e90620039c4565b9182526020820152604001604051809103906000f08015801562002fa6573d6000803e3d6000fd5b5060008481526070602052604080822080546001600160a01b0319166001600160a01b0385161790558401519192509067ffffffffffffffff81111562002ff15762002ff162003c6d565b6040519080825280602002602001820160405280156200301b578160200160208202803683370190505b5090506000836040015167ffffffffffffffff81111562003040576200304062003c6d565b6040519080825280602002602001820160405280156200307557816020015b60608152602001906001900390816200305f5790505b50905060005b8460400151811015620031f55784608001518181518110620030a157620030a1620043db565b602002602001015160000151838281518110620030c257620030c2620043db565b60200260200101906001600160a01b031690816001600160a01b031681525050606d600086608001518381518110620030ff57620030ff620043db565b6020026020010151600001516001600160a01b03166001600160a01b0316815260200190815260200160002060010180546200313b90620045b6565b80601f01602080910402602001604051908101604052809291908181526020018280546200316990620045b6565b8015620031ba5780601f106200318e57610100808354040283529160200191620031ba565b820191906000526020600020905b8154815290600101906020018083116200319c57829003601f168201915b5050505050828281518110620031d457620031d4620043db565b60200260200101819052508080620031ec9062004407565b9150506200307b565b506040516337f8d5ff60e01b81526001600160a01b038416906337f8d5ff906200322690859085906004016200485b565b600060405180830381600087803b1580156200324157600080fd5b505af115801562003256573d6000803e3d6000fd5b50505050836020015184600001516071600001547fbbd25d64683f157b2e3544d3d6430e14102db1e49592cf4dcaf827e2ded517ee8760400151886060015187438a604051620032ab959493929190620048d3565b60405180910390a45050505050565b6033546001600160a01b0316331462000e3c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162000523565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663a9059cbb60e01b17905262000e21908490620035bb565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b0382166000908152606d6020526040902060028101805460ff191690556003015443908110156200343c576001600160a01b0383166000908152606d6020526040812060030180548492906200343090849062004423565b9091555062000e219050565b62003448828262004423565b6001600160a01b0384166000908152606d6020526040902060030155505050565b600054610100900460ff16620034d65760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840162000523565b62000e3c62003694565b8154826000620034f08362004407565b9091555050600081815260028301602052604081206001810180549192620035188362004407565b909155505060078101805460ff1916905562003539600582016000620039d2565b62003549600682016000620039f2565b60005b6004820154811015620014a157816004018181548110620035715762003571620043db565b90600052602060002090600502016001016000620035a691905060008155600101600081556001016000815560010160009055565b80620035b28162004407565b9150506200354c565b600062003612826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166200370c9092919063ffffffff16565b80519091501562000e21578080602001905181019062003633919062004598565b62000e215760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840162000523565b600054610100900460ff16620037015760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840162000523565b62000e3c336200337f565b60606200371d848460008562003725565b949350505050565b606082471015620037885760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000523565b600080866001600160a01b03168587604051620037a6919062004915565b60006040518083038185875af1925050503d8060008114620037e5576040519150601f19603f3d011682016040523d82523d6000602084013e620037ea565b606091505b5091509150620037fd8783838762003808565b979650505050505050565b606083156200387c57825160000362003874576001600160a01b0385163b620038745760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000523565b50816200371d565b6200371d8383815115620038935781518083602001fd5b8060405162461bcd60e51b815260040162000523919062004771565b604051806040016040528060006001600160a01b03168152602001620038d462003a15565b905290565b60405180610120016040528060008152602001600081526020016000815260200160008152602001606081526020016060815260200160608152602001600015158152602001620038d462003a15565b82805482825590600052602060002090810192821562003981579160200282015b828111156200398157825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906200394a565b506200398f92915062003a33565b5090565b826004810192821562003981579160200282015b8281111562003981578251825591602001919060010190620039a7565b61168d806200493483390190565b508054600082559060005260206000209081019062002b56919062003a33565b508054600082556007029060005260206000209081019062002b56919062003a4a565b60405180608001604052806004906020820280368337509192915050565b5b808211156200398f576000815560010162003a34565b808211156200398f57600062003a618282620039d2565b600060018301818155600284018290556003840182905560048401829055600584018290559062003a97600583016000620039d2565b50505060070162003a4a565b6000806040838503121562003ab757600080fd5b50508035926020909101359150565b6001600160a01b038116811462002b5657600080fd5b60006020828403121562003aef57600080fd5b813562003afc8162003ac6565b9392505050565b6000806000806000806000806000806101408b8d03121562003b2457600080fd5b8a3562003b318162003ac6565b995060208b013562003b438162003ac6565b999c999b505050506040880135976060810135976080820135975060a0820135965060c0820135955060e082013594506101008201359350610120909101359150565b60006020828403121562003b9957600080fd5b5035919050565b8060005b6004811015620014a157815184526020938401939091019060010162003ba4565b6001600160a01b038151168252602081015162000e21602084018262003ba0565b60a0810162000cf0828462003bc5565b6000806020838503121562003c0a57600080fd5b823567ffffffffffffffff8082111562003c2357600080fd5b818501915085601f83011262003c3857600080fd5b81358181111562003c4857600080fd5b86602082850101111562003c5b57600080fd5b60209290920196919550909350505050565b634e487b7160e01b600052604160045260246000fd5b60405160a0810167ffffffffffffffff8111828210171562003ca95762003ca962003c6d565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171562003cdb5762003cdb62003c6d565b604052919050565b600067ffffffffffffffff82111562003d005762003d0062003c6d565b5060051b60200190565b600082601f83011262003d1c57600080fd5b8135602062003d3562003d2f8362003ce3565b62003caf565b82815260059290921b8401810191818101908684111562003d5557600080fd5b8286015b8481101562003d7d57803562003d6f8162003ac6565b835291830191830162003d59565b509695505050505050565b60008060006060848603121562003d9e57600080fd5b833567ffffffffffffffff81111562003db657600080fd5b62003dc48682870162003d0a565b9660208601359650604090950135949350505050565b60005b8381101562003df757818101518382015260200162003ddd565b50506000910152565b6000815180845262003e1a81602086016020860162003dda565b601f01601f19169290920160200192915050565b602081526001600160a01b038251166020820152600060208301516080604084015262003e5f60a084018262003e00565b9050604084015115156060840152606084015160808401528091505092915050565b6020808252825182820181905260009190848201906040850190845b8181101562003ebb5783518352928401929184019160010162003e9d565b50909695505050505050565b6000806040838503121562003edb57600080fd5b82359150602083013562003eef8162003ac6565b809150509250929050565b6000806040838503121562003f0e57600080fd5b823562003f1b8162003ac6565b946020939093013593505050565b600081518084526020808501945080840160005b8381101562003f675762003f5387835162003bc5565b60a096909601959082019060010162003f3d565b509495945050505050565b600081518084526020808501945080840160005b8381101562003f675781516001600160a01b03168752958201959082019060010162003f86565b600081518084526020808501808196508360051b8101915082860160005b858110156200404957828403895281516040815181875262003ff08288018262003f72565b90508783015192508681038888015260c08351825288840151620040178a84018262003ba0565b508284015193508060a0830152620040328183018562003f72565b9c89019c9750505092860192505060010162003fcb565b5091979650505050505050565b60208152815160208201526020820151604082015260408201516060820152606082015160808201526000608083015161018060a08401526200409e6101a084018262003f29565b905060a0840151601f19808584030160c0860152620040be838362003f72565b925060c08601519150808584030160e086015250620040de828262003fad565b91505060e0840151610100620040f78186018315159052565b85015190506200410c61012085018262003ba0565b509392505050565b600082601f8301126200412657600080fd5b813567ffffffffffffffff81111562004143576200414362003c6d565b62004158601f8201601f191660200162003caf565b8181528460208386010111156200416e57600080fd5b816020850160208301376000918101602001919091529392505050565b6000602082840312156200419e57600080fd5b813567ffffffffffffffff80821115620041b757600080fd5b9083019060a08286031215620041cc57600080fd5b620041d662003c83565b8235815260208301356020820152604083013582811115620041f757600080fd5b620042058782860162004114565b6040830152506060830135828111156200421e57600080fd5b6200422c8782860162004114565b6060830152506080830135828111156200424557600080fd5b620042538782860162003d0a565b60808301525095945050505050565b6000602082840312156200427557600080fd5b5051919050565b6000602082840312156200428f57600080fd5b81518060000b811462003afc57600080fd5b600082601f830112620042b357600080fd5b81516020620042c662003d2f8362003ce3565b82815260059290921b84018101918181019086841115620042e657600080fd5b8286015b8481101562003d7d578051620043008162003ac6565b8352918301918301620042ea565b600082601f8301126200432057600080fd5b815160206200433362003d2f8362003ce3565b82815260059290921b840181019181810190868411156200435357600080fd5b8286015b8481101562003d7d578051835291830191830162004357565b600080604083850312156200438457600080fd5b825167ffffffffffffffff808211156200439d57600080fd5b620043ab86838701620042a1565b93506020850151915080821115620043c257600080fd5b50620043d1858286016200430e565b9150509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016200441c576200441c620043f1565b5060010190565b8082018082111562000cf05762000cf0620043f1565b8181038181111562000cf05762000cf0620043f1565b600080604083850312156200446357600080fd5b505080516020909101519092909150565b6000602082840312156200448757600080fd5b815167ffffffffffffffff8111156200449f57600080fd5b6200371d848285016200430e565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6020815260006200371d602083018486620044ad565b600060808284031215620044ff57600080fd5b82601f8301126200450f57600080fd5b6040516080810181811067ffffffffffffffff8211171562004535576200453562003c6d565b6040528060808401858111156200454b57600080fd5b845b81811015620045675780518352602092830192016200454d565b509195945050505050565b6080810162000cf0828462003ba0565b805180151581146200459357600080fd5b919050565b600060208284031215620045ab57600080fd5b62003afc8262004582565b600181811c90821680620045cb57607f821691505b602082108103620045ec57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000e2157600081815260208120601f850160051c810160208610156200461b5750805b601f850160051c820191505b8181101562002ad25782815560010162004627565b67ffffffffffffffff83111562004657576200465762003c6d565b6200466f83620046688354620045b6565b83620045f2565b6000601f841160018114620046a657600085156200468d5750838201355b600019600387901b1c1916600186901b17835562004703565b600083815260209020601f19861690835b82811015620046d95786850135825560209485019460019092019101620046b7565b5086821015620046f75760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600080604083850312156200471e57600080fd5b82519150602083015167ffffffffffffffff8111156200473d57600080fd5b620043d1858286016200430e565b60408152600062004761604083018587620044ad565b9050826020830152949350505050565b60208152600062003afc602083018462003e00565b8381526000602084818401526060604084015261012083018451606085015281850151620047b8608086018262003ba0565b50604085015160c06101008601528051918290528201906000906101408601905b80831015620048045783516001600160a01b03168252928401926001929092019190840190620047d9565b5098975050505050505050565b600080604083850312156200482557600080fd5b620048308362004582565b9150602083015167ffffffffffffffff8111156200484d57600080fd5b620043d185828601620042a1565b60408152600062004870604083018562003f72565b6020838203818501528185518084528284019150828160051b85010183880160005b83811015620048c457601f19878403018552620048b183835162003e00565b9486019492509085019060010162004892565b50909998505050505050505050565b85815284602082015260a060408201526000620048f460a083018662003f72565b90508360608301526001600160a01b03831660808301529695505050505050565b600082516200492981846020870162003dda565b919091019291505056fe60c0604052600060065534801561001557600080fd5b5060405161168d38038061168d8339810160408190526100349161009b565b61003d3361004b565b60a0919091526080526100bf565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080604083850312156100ae57600080fd5b505080516020909101519092909150565b60805160a05161157161011c6000396000818161027f0152610775015260008181610243015281816103aa015281816103dc015281816104150152818161044e015281816108a80152818161094b01526109ff01526115716000f3fe608060405234801561001057600080fd5b506004361061012c5760003560e01c80638da5cb5b116100ad578063cc5ef00911610071578063cc5ef009146102a1578063cd5e3837146102a9578063ce7c2ac2146102bc578063d73fe0aa146102cf578063f2fde38b146102d757600080fd5b80638da5cb5b146102255780639cb8a26a14610236578063ac5553ce1461023e578063b0ef817914610265578063c27040241461027a57600080fd5b80634e3874a0116100f45780634e3874a0146101cc5780635aa68ac0146101e2578063670d14b2146101f7578063715018a61461020a5780637fd283461461021257600080fd5b80630ea6564814610131578063221f95111461015a57806335c1d3491461017557806337f8d5ff146101a057806348cd4cb1146101b5575b600080fd5b61014461013f366004611081565b6102ea565b60405161015191906110f7565b60405180910390f35b610162610384565b60405160009190910b8152602001610151565b61018861018336600461110a565b61048b565b6040516001600160a01b039091168152602001610151565b6101b36101ae36600461116f565b6104b5565b005b6101be60065481565b604051908152602001610151565b6101d4610617565b604051610151929190611233565b6101ea61079c565b6040516101519190611254565b610144610205366004611081565b6107fe565b6101b3610817565b6101b36102203660046112a1565b61082b565b6000546001600160a01b0316610188565b6101b3610afb565b6101be7f000000000000000000000000000000000000000000000000000000000000000081565b61026d610b11565b6040516101519190611313565b6101be7f000000000000000000000000000000000000000000000000000000000000000081565b61026d610c6f565b6101446102b7366004611081565b610dc7565b6101446102ca366004611081565b610de0565b61026d610df9565b6101b36102e5366004611081565b610f51565b6003602052600090815260409020805461030390611326565b80601f016020809104026020016040519081016040528092919081815260200182805461032f90611326565b801561037c5780601f106103515761010080835404028352916020019161037c565b820191906000526020600020905b81548152906001019060200180831161035f57829003601f168201915b505050505081565b60006006546000036103965750600090565b6000600654436103a69190611370565b90507f000000000000000000000000000000000000000000000000000000000000000081116103d757600191505090565b6104027f00000000000000000000000000000000000000000000000000000000000000006002611389565b811161041057600291505090565b61043b7f00000000000000000000000000000000000000000000000000000000000000006003611389565b811161044957600391505090565b6104747f00000000000000000000000000000000000000000000000000000000000000006004611389565b811161048257600491505090565b60001991505090565b6005818154811061049b57600080fd5b6000918252602090912001546001600160a01b0316905081565b6006541561050a5760405162461bcd60e51b815260206004820152601760248201527f444b472068617320616c7265616479207374617274656400000000000000000060448201526064015b60405180910390fd5b610512610fca565b60005b8381101561060c576005858583818110610531576105316113a0565b90506020020160208101906105469190611081565b815460018101835560009283526020909220909101805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909216919091179055828282818110610597576105976113a0565b90506020028101906105a991906113b6565b600160008888868181106105bf576105bf6113a0565b90506020020160208101906105d49190611081565b6001600160a01b031681526020810191909152604001600020916105f9919083611461565b508061060481611522565b915050610515565b505043600655505050565b60006060600060058054905067ffffffffffffffff81111561063b5761063b6113fd565b60405190808252806020026020018201604052801561066e57816020015b60608152602001906001900390816106595790505b50905060005b600554811015610772576001600060058381548110610695576106956113a0565b60009182526020808320909101546001600160a01b03168352820192909252604001902080546106c490611326565b80601f01602080910402602001604051908101604052809291908181526020018280546106f090611326565b801561073d5780601f106107125761010080835404028352916020019161073d565b820191906000526020600020905b81548152906001019060200180831161072057829003601f168201915b5050505050828281518110610754576107546113a0565b6020026020010181905250808061076a90611522565b915050610674565b507f0000000000000000000000000000000000000000000000000000000000000000939092509050565b606060058054806020026020016040519081016040528092919081815260200182805480156107f457602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116107d6575b5050505050905090565b6001602052600090815260409020805461030390611326565b61081f610fca565b6108296000611024565b565b336000908152600160205260408120805461084590611326565b9050116108945760405162461bcd60e51b815260206004820152601b60248201527f796f7520617265206e6f7420612067726f7570206d656d6265722100000000006044820152606401610501565b6000600654436108a49190611370565b90507f000000000000000000000000000000000000000000000000000000000000000081116109465733600090815260026020526040902080546108e790611326565b1590506109265760405162461bcd60e51b815260206004820152600d60248201526c1cda185c9948195e1a5cdd1959609a1b6044820152606401610501565b336000908152600260205260409020610940838583611461565b50505050565b6109717f00000000000000000000000000000000000000000000000000000000000000006002611389565b81116109fa57336000908152600360205260409020805461099190611326565b1590506109e05760405162461bcd60e51b815260206004820152601060248201527f726573706f6e73652065786973746564000000000000000000000000000000006044820152606401610501565b336000908152600360205260409020610940838583611461565b610a257f00000000000000000000000000000000000000000000000000000000000000006003611389565b8111610aae573360009081526004602052604090208054610a4590611326565b159050610a945760405162461bcd60e51b815260206004820152601560248201527f6a757374696669636174696f6e206578697374656400000000000000000000006044820152606401610501565b336000908152600460205260409020610940838583611461565b60405162461bcd60e51b815260206004820152601560248201527f444b47205075626c6973682068617320656e64656400000000000000000000006044820152606401610501565b505050565b610b03610fca565b6000546001600160a01b0316ff5b60055460609060009067ffffffffffffffff811115610b3257610b326113fd565b604051908082528060200260200182016040528015610b6557816020015b6060815260200190600190039081610b505790505b50905060005b600554811015610c69576004600060058381548110610b8c57610b8c6113a0565b60009182526020808320909101546001600160a01b0316835282019290925260400190208054610bbb90611326565b80601f0160208091040260200160405190810160405280929190818152602001828054610be790611326565b8015610c345780601f10610c0957610100808354040283529160200191610c34565b820191906000526020600020905b815481529060010190602001808311610c1757829003601f168201915b5050505050828281518110610c4b57610c4b6113a0565b60200260200101819052508080610c6190611522565b915050610b6b565b50919050565b60055460609060009067ffffffffffffffff811115610c9057610c906113fd565b604051908082528060200260200182016040528015610cc357816020015b6060815260200190600190039081610cae5790505b50905060005b600554811015610c69576003600060058381548110610cea57610cea6113a0565b60009182526020808320909101546001600160a01b0316835282019290925260400190208054610d1990611326565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4590611326565b8015610d925780601f10610d6757610100808354040283529160200191610d92565b820191906000526020600020905b815481529060010190602001808311610d7557829003601f168201915b5050505050828281518110610da957610da96113a0565b60200260200101819052508080610dbf90611522565b915050610cc9565b6004602052600090815260409020805461030390611326565b6002602052600090815260409020805461030390611326565b60055460609060009067ffffffffffffffff811115610e1a57610e1a6113fd565b604051908082528060200260200182016040528015610e4d57816020015b6060815260200190600190039081610e385790505b50905060005b600554811015610c69576002600060058381548110610e7457610e746113a0565b60009182526020808320909101546001600160a01b0316835282019290925260400190208054610ea390611326565b80601f0160208091040260200160405190810160405280929190818152602001828054610ecf90611326565b8015610f1c5780601f10610ef157610100808354040283529160200191610f1c565b820191906000526020600020905b815481529060010190602001808311610eff57829003601f168201915b5050505050828281518110610f3357610f336113a0565b60200260200101819052508080610f4990611522565b915050610e53565b610f59610fca565b6001600160a01b038116610fbe5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610501565b610fc781611024565b50565b6000546001600160a01b031633146108295760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610501565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561109357600080fd5b81356001600160a01b03811681146110aa57600080fd5b9392505050565b6000815180845260005b818110156110d7576020818501810151868301820152016110bb565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006110aa60208301846110b1565b60006020828403121561111c57600080fd5b5035919050565b60008083601f84011261113557600080fd5b50813567ffffffffffffffff81111561114d57600080fd5b6020830191508360208260051b850101111561116857600080fd5b9250929050565b6000806000806040858703121561118557600080fd5b843567ffffffffffffffff8082111561119d57600080fd5b6111a988838901611123565b909650945060208701359150808211156111c257600080fd5b506111cf87828801611123565b95989497509550505050565b600082825180855260208086019550808260051b84010181860160005b8481101561122657601f198684030189526112148383516110b1565b988401989250908301906001016111f8565b5090979650505050505050565b82815260406020820152600061124c60408301846111db565b949350505050565b6020808252825182820181905260009190848201906040850190845b818110156112955783516001600160a01b031683529284019291840191600101611270565b50909695505050505050565b600080602083850312156112b457600080fd5b823567ffffffffffffffff808211156112cc57600080fd5b818501915085601f8301126112e057600080fd5b8135818111156112ef57600080fd5b86602082850101111561130157600080fd5b60209290920196919550909350505050565b6020815260006110aa60208301846111db565b600181811c9082168061133a57607f821691505b602082108103610c6957634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b818103818111156113835761138361135a565b92915050565b80820281158282048414176113835761138361135a565b634e487b7160e01b600052603260045260246000fd5b6000808335601e198436030181126113cd57600080fd5b83018035915067ffffffffffffffff8211156113e857600080fd5b60200191503681900382131561116857600080fd5b634e487b7160e01b600052604160045260246000fd5b601f821115610af657600081815260208120601f850160051c8101602086101561143a5750805b601f850160051c820191505b8181101561145957828155600101611446565b505050505050565b67ffffffffffffffff831115611479576114796113fd565b61148d836114878354611326565b83611413565b6000601f8411600181146114c157600085156114a95750838201355b600019600387901b1c1916600186901b17835561151b565b600083815260209020601f19861690835b828110156114f257868501358255602094850194600190920191016114d2565b508682101561150f5760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b6000600182016115345761153461135a565b506001019056fea2646970667358221220f3e6e3e93788ae67c1eaf31cb805370313ba096ed5aa792e2b15c335a3903e5a64736f6c63430008120033a2646970667358221220452028a72bf515ef364f752d379d36e460edada7991f7fdb8d560ed8e00d58f964736f6c63430008120033

Deployed Bytecode

0x60806040523480156200001157600080fd5b5060043610620001dd5760003560e01c80638da5cb5b116200010d578063ceb6065411620000a3578063e37eb96c116200007a578063e37eb96c146200048e578063f2fde38b14620004a5578063f3df080214620004bc578063f49e0ba914620004d357600080fd5b8063ceb6065414620003dd578063d11b8e681462000403578063e275cde6146200047757600080fd5b80639d20904811620000e45780639d209048146200035f578063b330a0fd1462000385578063c2db900b146200039e578063cd6dc68714620003c657600080fd5b80638da5cb5b146200032c578063914eb34d146200033e5780639a075bc3146200035557600080fd5b80634d79a8931162000183578063715018a6116200015a578063715018a614620002f85780637a2af56e14620003025780637ee49cfd146200030c57806380110e4e146200031557600080fd5b80634d79a89314620002b25780634ecea80d14620002d857806351a2b9a014620002ef57600080fd5b80632ff9e52011620001b85780632ff9e520146200023f5780633b6c00b0146200025657806342424d6f146200026d57600080fd5b806306545a9314620001e25780630bf9c5c614620001f9578063227d0f461462000212575b600080fd5b6072545b6040519081526020015b60405180910390f35b620002106200020a36600462003aa3565b62000501565b005b620002296200022336600462003adc565b62000999565b60408051928352602083019190915201620001f0565b620002106200025036600462003b03565b62000a42565b620002296200026736600462003adc565b62000bcf565b620002996200027e36600462003b86565b6000908152607060205260409020546001600160a01b031690565b6040516001600160a01b039091168152602001620001f0565b620002c9620002c336600462003aa3565b62000c59565b604051620001f0919062003be6565b62000210620002e936600462003adc565b62000cf6565b607754620001e6565b6200021062000e26565b6200021062000e3e565b607154620001e6565b620002106200032636600462003bf6565b62000ff1565b6033546001600160a01b031662000299565b620002106200034f36600462003d88565b62001336565b62000210620014a7565b620003766200037036600462003adc565b62001751565b604051620001f0919062003e2e565b6200038f62001875565b604051620001f0919062003e81565b620003b5620003af36600462003ec7565b620018f8565b6040519015158152602001620001f0565b62000210620003d736600462003efa565b62001cc4565b620003f4620003ee36600462003b86565b62001e0e565b604051620001f0919062004056565b606554606654606754606854607654606954607554607454606a54606b54604080516001600160a01b039b8c1681529a90991660208b0152978901969096526060880194909452608087019290925260a086015260c085015260e084015261010083015261012082015261014001620001f0565b620002106200048836600462003bf6565b6200212c565b620002106200049f3660046200418b565b620022fe565b62000210620004b636600462003adc565b62002ada565b62000210620004cd36600462003b86565b62002b59565b62000229620004e436600462003b86565b600090815260736020526040902060038101546002909101549091565b60725482106200052c57604051637f6efcb560e11b8152600481018390526024015b60405180910390fd5b6040516358914d0360e01b8152607160048201526024810183905233604482015273cd97ed85f6eddc535a4f55a4c67c2e02800c2e67906358914d0390606401602060405180830381865af41580156200058a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005b0919062004262565b19620005d95760405163eb51f5f960e01b81526004810183905233602482015260440162000523565b6000828152607360205260409020600181015482146200062457600181015460405163048b63c360e11b81526004810185905260248101849052604481019190915260640162000523565b6000838152607060205260409020546001600160a01b03166200065e57604051635291bbcf60e01b81526004810184905260240162000523565b60008381526070602090815260409182902054825163221f951160e01b815292516001600160a01b0390911692839263221f9511926004808401938290030181865afa158015620006b3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620006d991906200427c565b60000b60001914620007715783816001600160a01b031663221f95116040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000725573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200074b91906200427c565b60405163f7c06f9160e01b8152600481019290925260000b602482015260440162000523565b806001600160a01b0316639cb8a26a6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015620007ad57600080fd5b505af1158015620007c2573d6000803e3d6000fd5b505050600085815260706020526040902080546001600160a01b031916905550600782015460ff166200092457607754604051630295316d60e51b815260716004820152602481018690526044810191909152600090819073cd97ed85f6eddc535a4f55a4c67c2e02800c2e67906352a62da090606401600060405180830381865af415801562000857573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000881919081019062004370565b9150915060005b8251811015620008d657620008c1838281518110620008ab57620008ab620043db565b6020026020010151606560030154600062002b8a565b80620008cd8162004407565b91505062000888565b5060005b815181101562000920576200090b828281518110620008fd57620008fd620043db565b602002602001015162002c47565b80620009178162004407565b915050620008da565b5050505b606b54336000908152606f6020526040812080549091906200094890849062004423565b9091555050606b5460405133917f8353a804115421789f3ab2eeb3f5215943906ce12100c91d40fc865caf742b6f916200098b9160008252602082015260400190565b60405180910390a250505050565b6001600160a01b0381166000908152606e6020526040812054819015620009e7576001600160a01b0383166000908152606e6020526040902054620009e19060019062004439565b620009ea565b60005b6001600160a01b0384166000908152606f60205260409020541562000a36576001600160a01b0384166000908152606f602052604090205462000a309060019062004439565b62000a39565b60005b91509150915091565b62000a4c620032ba565b6040805160e0810182526001600160a01b038c8116808352908c16602083018190528284018c9052606083018b90526080830189905260a0830186905260c0909201849052606580546001600160a01b03199081169092179055606680549091169091179055606789905560688890556069869055606a839055606b829055516301a79f6360e71b81526071600482015260248101849052604481018590526064810187905273cd97ed85f6eddc535a4f55a4c67c2e02800c2e679063d3cfb1809060840160006040518083038186803b15801562000b2a57600080fd5b505af415801562000b3f573d6000803e3d6000fd5b5050604080516001600160a01b03808f1682528d1660208201529081018b9052606081018a90526080810189905260a0810188905260c0810187905260e08101869052610100810185905261012081018490527f3202ca2a288846b65f27bc3e13c1e38aaf2e4356b80b963d67aea8a822645e1f925061014001905060405180910390a150505050505050505050565b60405163787c576760e01b8152607160048201526001600160a01b0382166024820152600090819073cd97ed85f6eddc535a4f55a4c67c2e02800c2e679063787c5767906044016040805180830381865af415801562000c33573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000a3991906200444f565b62000c63620038af565b600083815260736020526040902060040180548390811062000c895762000c89620043db565b6000918252602091829020604080518082018252600590930290910180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b81548152602001906001019080831162000cd05750505050508152505090505b92915050565b6001600160a01b03811662000d1e5760405163f6b2911f60e01b815260040160405180910390fd5b336000908152606e6020908152604080832054606f90925290912054600181111562000d8057336000908152606f6020526040902060019081905562000d8090849062000d6c908462004439565b606c546001600160a01b0316919062003316565b600182111562000e2157336000908152606e602052604090206001908190556066546001600160a01b031690630ad98f6a90859062000dc0908662004439565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b15801562000e0757600080fd5b505af115801562000e1c573d6000803e3d6000fd5b505050505b505050565b62000e30620032ba565b62000e3c60006200337f565b565b336000818152606d60205260409020805490916001600160a01b039091161462000e7a576040516229eaad60e31b815260040160405180910390fd5b60775460405163a6ae8a8160e01b815260716004820152336024820152604481019190915260009073cd97ed85f6eddc535a4f55a4c67c2e02800c2e679063a6ae8a8190606401600060405180830381865af415801562000edf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000f09919081019062004474565b905060005b815181101562000f465762000f31828281518110620008fd57620008fd620043db565b8062000f3d8162004407565b91505062000f0e565b5062000f5833606560050154620033d1565b606554606754604051637eee288d60e01b815233600482015260248101919091526001600160a01b0390911690637eee288d90604401600060405180830381600087803b15801562000fa957600080fd5b505af115801562000fbe573d6000803e3d6000fd5b50506040513392507f68577adbb6b0647e21353ff032be5797d9fa0879ce7e05fe617e40368441f97d9150600090a25050565b336000908152606d60205260409020546001600160a01b0316156200102957604051630eb0d31360e11b815260040160405180910390fd5b6040516316f6db8160e01b81526000907334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca8906316f6db8190620010679086908690600401620044d6565b608060405180830381865af415801562001085573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620010ab9190620044ec565b604051636fda2c7960e01b81529091507334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca890636fda2c7990620010e790849060040162004572565b602060405180830381865af415801562001105573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200112b919062004598565b620011495760405163145a1fdd60e31b815260040160405180910390fd5b60655460675460405163282d3fdf60e01b815233600482015260248101919091526001600160a01b039091169063282d3fdf90604401600060405180830381600087803b1580156200119a57600080fd5b505af1158015620011af573d6000803e3d6000fd5b5050336000818152606d6020526040902080546001600160a01b031916909117815591505060018101620011e58486836200463c565b5060028101805460ff19166001908117909155336000818152606e60209081526040808320859055606f909152808220939093556077549251629bf68360e11b81526071600482015260248101929092526044820192909252819073cd97ed85f6eddc535a4f55a4c67c2e02800c2e6790630137ed0690606401600060405180830381865af41580156200127d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620012a791908101906200470a565b9150915060005b8151811015620012e657620012d1828281518110620008fd57620008fd620043db565b80620012dd8162004407565b915050620012ae565b50336001600160a01b03167fd4ec586f4f9f417f99e20fe821fbaa10a10a4b95f8712a0c57c6d8ed970e98bd87878560405162001326939291906200474b565b60405180910390a2505050505050565b6066546001600160a01b03163314620013625760405163469666c560e01b815260040160405180910390fd5b60005b8351811015620014a15782606e6000868481518110620013895762001389620043db565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000828254620013c2919062004423565b9250508190555081606f6000868481518110620013e357620013e3620043db565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060008282546200141c919062004423565b92505081905550838181518110620014385762001438620043db565b60200260200101516001600160a01b03167f8353a804115421789f3ab2eeb3f5215943906ce12100c91d40fc865caf742b6f848460405162001484929190918252602082015260400190565b60405180910390a280620014988162004407565b91505062001365565b50505050565b336000818152606d60205260409020805490916001600160a01b0390911614620014e3576040516229eaad60e31b815260040160405180910390fd5b600281015460ff16156200150a576040516324a228bb60e21b815260040160405180910390fd5b4381600301541115620015395780600301546040516363525acb60e11b81526004016200052391815260200190565b60655460405163929ec53760e01b81523360048201526000916001600160a01b03169063929ec53790602401602060405180830381865afa15801562001583573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620015a9919062004262565b6067549091508110156200163f576065546067546001600160a01b039091169063282d3fdf903390620015de90859062004439565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b1580156200162557600080fd5b505af11580156200163a573d6000803e3d6000fd5b505050505b60028201805460ff19166001179055607754604051629bf68360e11b8152607160048201523360248201526044810191909152600090819073cd97ed85f6eddc535a4f55a4c67c2e02800c2e6790630137ed0690606401600060405180830381865af4158015620016b4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620016de91908101906200470a565b9150915060005b81518110156200171d5762001708828281518110620008fd57620008fd620043db565b80620017148162004407565b915050620016e5565b5060405182815233907ffc97cd9154b40031874ef09a9436a4b60052e4dcf40f21b1258be265fac4a397906020016200098b565b62001788604051806080016040528060006001600160a01b0316815260200160608152602001600015158152602001600081525090565b6001600160a01b038083166000908152606d60209081526040918290208251608081019093528054909316825260018301805492939291840191620017cd90620045b6565b80601f0160208091040260200160405190810160405280929190818152602001828054620017fb90620045b6565b80156200184c5780601f1062001820576101008083540402835291602001916200184c565b820191906000526020600020905b8154815290600101906020018083116200182e57829003601f168201915b5050509183525050600282015460ff161515602082015260039091015460409091015292915050565b604051631c2da66560e31b81526071600482015260609073cd97ed85f6eddc535a4f55a4c67c2e02800c2e679063e16d332890602401600060405180830381865af4158015620018c9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620018f3919081019062004474565b905090565b6000828152607360209081526040808320815161012081018352815481526001820154818501526002820154818401526003820154606082015260048201805484518187028101870190955280855286959294608086019390929190879084015b82821015620019d55760008481526020908190206040805180820182526005860290920180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b815481526020019060010190808311620019a8575050505050815250508152602001906001019062001959565b5050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801562001a3857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001a19575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b8282101562001bb657838290600052602060002090600702016040518060400160405290816000820180548060200260200160405190810160405280929190818152602001828054801562001ae557602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001ac6575b50505091835250506040805160608101825260018401805482528251608081019384905260209485019492939192840191600287019060049082845b81548152602001906001019080831162001b2157505050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801562001b9957602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001b7a575b505050505081525050815250508152602001906001019062001a66565b50505090825250600782015460ff1615156020820152604080516080810182529101906008830160048282826020028201915b81548152602001906001019080831162001be957505050505081525050905060005b81608001515181101562001cb957836001600160a01b03168260800151828151811062001c3c5762001c3c620043db565b6020026020010151600001516001600160a01b03160362001ca4578160800151818151811062001c705762001c70620043db565b60200260200101516020015160006004811062001c915762001c91620043db565b6020020151600014159250505062000cf0565b8062001cb08162004407565b91505062001c0b565b506000949350505050565b600054610100900460ff161580801562001ce55750600054600160ff909116105b8062001d015750303b15801562001d01575060005460ff166001145b62001d755760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840162000523565b6000805460ff19166001179055801562001d99576000805461ff0019166101001790555b606c80546001600160a01b0319166001600160a01b038516179055607782905562001dc362003469565b801562000e21576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b62001e18620038d9565b60008281526073602090815260408083208151610120810183528154815260018201548185015260028201548184015260038201546060820152600482018054845181870281018701909552808552919592946080870194939192919084015b8282101562001ef45760008481526020908190206040805180820182526005860290920180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b81548152602001906001019080831162001ec7575050505050815250508152602001906001019062001e78565b5050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801562001f5757602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001f38575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b82821015620020d55783829060005260206000209060070201604051806040016040529081600082018054806020026020016040519081016040528092919081815260200182805480156200200457602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162001fe5575b50505091835250506040805160608101825260018401805482528251608081019384905260209485019492939192840191600287019060049082845b81548152602001906001019080831162002040575050505050815260200160058201805480602002602001604051908101604052809291908181526020018280548015620020b857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162002099575b505050505081525050815250508152602001906001019062001f85565b50505090825250600782015460ff1615156020820152604080516080810182529101906008830160048282826020028201915b81548152602001906001019080831162002108575050505050815250509050919050565b336000818152606d60205260409020805490916001600160a01b039091161462002168576040516229eaad60e31b815260040160405180910390fd5b600281015460ff16156200218f576040516324a228bb60e21b815260040160405180910390fd5b6040516316f6db8160e01b81526000907334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca8906316f6db8190620021cd9087908790600401620044d6565b608060405180830381865af4158015620021eb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620022119190620044ec565b604051636fda2c7960e01b81529091507334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca890636fda2c79906200224d90849060040162004572565b602060405180830381865af41580156200226b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002291919062004598565b620022af5760405163145a1fdd60e31b815260040160405180910390fd5b60018201620022c08486836200463c565b50336001600160a01b03167f4a327ac4843af7a9586b5a2a2c312bd17289ae1d70da32855ed539fe39f86a5085856040516200098b929190620044d6565b60725481511062002329578051604051637f6efcb560e11b8152600481019190915260240162000523565b80516000908152607060205260409020546001600160a01b031662002368578051604051635291bbcf60e01b8152600481019190915260240162000523565b805160009081526070602090815260409182902054825163221f951160e01b815292516001600160a01b0390911692839263221f9511926004808401938290030181865afa158015620023bf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620023e591906200427c565b60000b196200240e5781516040516343609fe160e01b8152600481019190915260240162000523565b81516000908152607360209081526040909120600181015491840151909114620024695782516020840151600183015460405163048b63c360e11b815260048101939093526024830191909152604482015260640162000523565b82516040516358914d0360e01b815260716004820152602481019190915233604482015273cd97ed85f6eddc535a4f55a4c67c2e02800c2e67906358914d0390606401602060405180830381865af4158015620024ca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620024f0919062004262565b196200251c57825160405163eb51f5f960e01b8152600481019190915233602482015260440162000523565b82516200252a9033620018f8565b156200255657825160405163173ca10960e31b8152600481019190915233602482015260440162000523565b60608301516040516316f6db8160e01b81526000917334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca8916316f6db8191620025959160040162004771565b608060405180830381865af4158015620025b3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620025d99190620044ec565b604051636fda2c7960e01b81529091507334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca890636fda2c79906200261590849060040162004572565b602060405180830381865af415801562002633573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062002659919062004598565b6200267757604051631886185760e01b815260040160405180910390fd5b60408085015190516316f6db8160e01b81526000917334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca8916316f6db8191620026b69160040162004771565b608060405180830381865af4158015620026d4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620026fa9190620044ec565b604051636fda2c7960e01b81529091507334e6885e0e9bbea0bae36d00d4d9b4ed873f2ca890636fda2c79906200273690849060040162004572565b602060405180830381865af415801562002754573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200277a919062004598565b620027985760405163145a1fdd60e31b815260040160405180910390fd5b604080516060810182526020808801518252810183905260808701518183015286519151636aba6eeb60e11b8152909173cd97ed85f6eddc535a4f55a4c67c2e02800c2e679163d574ddd691620027f89160719190869060040162004786565b602060405180830381865af415801562002816573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200283c919062004598565b6200292357604080516001818301818152608083019093526000928291606083016020803683375050508152602001839052805180519192503391600090620028895762002889620043db565b6001600160a01b03909216602092830291909101820152600686018054600181018255600091825290829020835180518594600790940290920192620028d59284929091019062003929565b5060208281015180516001840190815591810151909190620028fe906002850190600462003993565b50604082015180516200291c91600584019160209091019062003929565b5050505050505b85516040516358914d0360e01b815260716004808301919091526024820192909252336044820152849186019073cd97ed85f6eddc535a4f55a4c67c2e02800c2e67906358914d0390606401602060405180830381865af41580156200298d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620029b3919062004262565b81548110620029c657620029c6620043db565b9060005260206000209060050201600101906004620029e792919062003993565b50600784015460ff1662002ad257855160775460405163449cf2fd60e11b81526071600482015260248101929092526044820152600090819073cd97ed85f6eddc535a4f55a4c67c2e02800c2e6790638939e5fa90606401600060405180830381865af415801562002a5d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262002a87919081019062004811565b91509150811562002acf5760005b815181101562002acd5762002ab8828281518110620008ab57620008ab620043db565b8062002ac48162004407565b91505062002a95565b505b50505b505050505050565b62002ae4620032ba565b6001600160a01b03811662002b4b5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840162000523565b62002b56816200337f565b50565b6066546001600160a01b0316331462002b855760405163469666c560e01b815260040160405180910390fd5b607755565b606554604051638899fdeb60e01b81526001600160a01b0385811660048301526024820185905290911690638899fdeb90604401600060405180830381600087803b15801562002bd957600080fd5b505af115801562002bee573d6000803e3d6000fd5b5050505062002bfe8382620033d1565b60408051838152602081018390526001600160a01b038516917fa8d720d0a0a2e7c96bf9eb87433901ebb6331356c8f3283b2568de34478703cc910160405180910390a2505050565b62002c54607182620034e0565b60008181526073602090815260408083208151610120810183528154815260018201548185015260028201548184015260038201546060820152600482018054845181870281018701909552808552919492936080860193909290879084015b8282101562002d305760008481526020908190206040805180820182526005860290920180546001600160a01b0316835281516080810190925291928301906001830160048282826020028201915b81548152602001906001019080831162002d03575050505050815250508152602001906001019062002cb4565b5050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801562002d9357602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162002d74575b5050505050815260200160068201805480602002602001604051908101604052809291908181526020016000905b8282101562002f1157838290600052602060002090600702016040518060400160405290816000820180548060200260200160405190810160405280929190818152602001828054801562002e4057602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162002e21575b50505091835250506040805160608101825260018401805482528251608081019384905260209485019492939192840191600287019060049082845b81548152602001906001019080831162002e7c57505050505081526020016005820180548060200260200160405190810160405280929190818152602001828054801562002ef457602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831162002ed5575b505050505081525050815250508152602001906001019062002dc1565b50505090825250600782015460ff1615156020820152604080516080810182529101906008830160048282826020028201915b81548152602001906001019080831162002f445750505050508152505090506000816060015160656004015460405162002f7e90620039c4565b9182526020820152604001604051809103906000f08015801562002fa6573d6000803e3d6000fd5b5060008481526070602052604080822080546001600160a01b0319166001600160a01b0385161790558401519192509067ffffffffffffffff81111562002ff15762002ff162003c6d565b6040519080825280602002602001820160405280156200301b578160200160208202803683370190505b5090506000836040015167ffffffffffffffff81111562003040576200304062003c6d565b6040519080825280602002602001820160405280156200307557816020015b60608152602001906001900390816200305f5790505b50905060005b8460400151811015620031f55784608001518181518110620030a157620030a1620043db565b602002602001015160000151838281518110620030c257620030c2620043db565b60200260200101906001600160a01b031690816001600160a01b031681525050606d600086608001518381518110620030ff57620030ff620043db565b6020026020010151600001516001600160a01b03166001600160a01b0316815260200190815260200160002060010180546200313b90620045b6565b80601f01602080910402602001604051908101604052809291908181526020018280546200316990620045b6565b8015620031ba5780601f106200318e57610100808354040283529160200191620031ba565b820191906000526020600020905b8154815290600101906020018083116200319c57829003601f168201915b5050505050828281518110620031d457620031d4620043db565b60200260200101819052508080620031ec9062004407565b9150506200307b565b506040516337f8d5ff60e01b81526001600160a01b038416906337f8d5ff906200322690859085906004016200485b565b600060405180830381600087803b1580156200324157600080fd5b505af115801562003256573d6000803e3d6000fd5b50505050836020015184600001516071600001547fbbd25d64683f157b2e3544d3d6430e14102db1e49592cf4dcaf827e2ded517ee8760400151886060015187438a604051620032ab959493929190620048d3565b60405180910390a45050505050565b6033546001600160a01b0316331462000e3c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162000523565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663a9059cbb60e01b17905262000e21908490620035bb565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b0382166000908152606d6020526040902060028101805460ff191690556003015443908110156200343c576001600160a01b0383166000908152606d6020526040812060030180548492906200343090849062004423565b9091555062000e219050565b62003448828262004423565b6001600160a01b0384166000908152606d6020526040902060030155505050565b600054610100900460ff16620034d65760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840162000523565b62000e3c62003694565b8154826000620034f08362004407565b9091555050600081815260028301602052604081206001810180549192620035188362004407565b909155505060078101805460ff1916905562003539600582016000620039d2565b62003549600682016000620039f2565b60005b6004820154811015620014a157816004018181548110620035715762003571620043db565b90600052602060002090600502016001016000620035a691905060008155600101600081556001016000815560010160009055565b80620035b28162004407565b9150506200354c565b600062003612826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166200370c9092919063ffffffff16565b80519091501562000e21578080602001905181019062003633919062004598565b62000e215760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840162000523565b600054610100900460ff16620037015760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840162000523565b62000e3c336200337f565b60606200371d848460008562003725565b949350505050565b606082471015620037885760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000523565b600080866001600160a01b03168587604051620037a6919062004915565b60006040518083038185875af1925050503d8060008114620037e5576040519150601f19603f3d011682016040523d82523d6000602084013e620037ea565b606091505b5091509150620037fd8783838762003808565b979650505050505050565b606083156200387c57825160000362003874576001600160a01b0385163b620038745760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000523565b50816200371d565b6200371d8383815115620038935781518083602001fd5b8060405162461bcd60e51b815260040162000523919062004771565b604051806040016040528060006001600160a01b03168152602001620038d462003a15565b905290565b60405180610120016040528060008152602001600081526020016000815260200160008152602001606081526020016060815260200160608152602001600015158152602001620038d462003a15565b82805482825590600052602060002090810192821562003981579160200282015b828111156200398157825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906200394a565b506200398f92915062003a33565b5090565b826004810192821562003981579160200282015b8281111562003981578251825591602001919060010190620039a7565b61168d806200493483390190565b508054600082559060005260206000209081019062002b56919062003a33565b508054600082556007029060005260206000209081019062002b56919062003a4a565b60405180608001604052806004906020820280368337509192915050565b5b808211156200398f576000815560010162003a34565b808211156200398f57600062003a618282620039d2565b600060018301818155600284018290556003840182905560048401829055600584018290559062003a97600583016000620039d2565b50505060070162003a4a565b6000806040838503121562003ab757600080fd5b50508035926020909101359150565b6001600160a01b038116811462002b5657600080fd5b60006020828403121562003aef57600080fd5b813562003afc8162003ac6565b9392505050565b6000806000806000806000806000806101408b8d03121562003b2457600080fd5b8a3562003b318162003ac6565b995060208b013562003b438162003ac6565b999c999b505050506040880135976060810135976080820135975060a0820135965060c0820135955060e082013594506101008201359350610120909101359150565b60006020828403121562003b9957600080fd5b5035919050565b8060005b6004811015620014a157815184526020938401939091019060010162003ba4565b6001600160a01b038151168252602081015162000e21602084018262003ba0565b60a0810162000cf0828462003bc5565b6000806020838503121562003c0a57600080fd5b823567ffffffffffffffff8082111562003c2357600080fd5b818501915085601f83011262003c3857600080fd5b81358181111562003c4857600080fd5b86602082850101111562003c5b57600080fd5b60209290920196919550909350505050565b634e487b7160e01b600052604160045260246000fd5b60405160a0810167ffffffffffffffff8111828210171562003ca95762003ca962003c6d565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171562003cdb5762003cdb62003c6d565b604052919050565b600067ffffffffffffffff82111562003d005762003d0062003c6d565b5060051b60200190565b600082601f83011262003d1c57600080fd5b8135602062003d3562003d2f8362003ce3565b62003caf565b82815260059290921b8401810191818101908684111562003d5557600080fd5b8286015b8481101562003d7d57803562003d6f8162003ac6565b835291830191830162003d59565b509695505050505050565b60008060006060848603121562003d9e57600080fd5b833567ffffffffffffffff81111562003db657600080fd5b62003dc48682870162003d0a565b9660208601359650604090950135949350505050565b60005b8381101562003df757818101518382015260200162003ddd565b50506000910152565b6000815180845262003e1a81602086016020860162003dda565b601f01601f19169290920160200192915050565b602081526001600160a01b038251166020820152600060208301516080604084015262003e5f60a084018262003e00565b9050604084015115156060840152606084015160808401528091505092915050565b6020808252825182820181905260009190848201906040850190845b8181101562003ebb5783518352928401929184019160010162003e9d565b50909695505050505050565b6000806040838503121562003edb57600080fd5b82359150602083013562003eef8162003ac6565b809150509250929050565b6000806040838503121562003f0e57600080fd5b823562003f1b8162003ac6565b946020939093013593505050565b600081518084526020808501945080840160005b8381101562003f675762003f5387835162003bc5565b60a096909601959082019060010162003f3d565b509495945050505050565b600081518084526020808501945080840160005b8381101562003f675781516001600160a01b03168752958201959082019060010162003f86565b600081518084526020808501808196508360051b8101915082860160005b858110156200404957828403895281516040815181875262003ff08288018262003f72565b90508783015192508681038888015260c08351825288840151620040178a84018262003ba0565b508284015193508060a0830152620040328183018562003f72565b9c89019c9750505092860192505060010162003fcb565b5091979650505050505050565b60208152815160208201526020820151604082015260408201516060820152606082015160808201526000608083015161018060a08401526200409e6101a084018262003f29565b905060a0840151601f19808584030160c0860152620040be838362003f72565b925060c08601519150808584030160e086015250620040de828262003fad565b91505060e0840151610100620040f78186018315159052565b85015190506200410c61012085018262003ba0565b509392505050565b600082601f8301126200412657600080fd5b813567ffffffffffffffff81111562004143576200414362003c6d565b62004158601f8201601f191660200162003caf565b8181528460208386010111156200416e57600080fd5b816020850160208301376000918101602001919091529392505050565b6000602082840312156200419e57600080fd5b813567ffffffffffffffff80821115620041b757600080fd5b9083019060a08286031215620041cc57600080fd5b620041d662003c83565b8235815260208301356020820152604083013582811115620041f757600080fd5b620042058782860162004114565b6040830152506060830135828111156200421e57600080fd5b6200422c8782860162004114565b6060830152506080830135828111156200424557600080fd5b620042538782860162003d0a565b60808301525095945050505050565b6000602082840312156200427557600080fd5b5051919050565b6000602082840312156200428f57600080fd5b81518060000b811462003afc57600080fd5b600082601f830112620042b357600080fd5b81516020620042c662003d2f8362003ce3565b82815260059290921b84018101918181019086841115620042e657600080fd5b8286015b8481101562003d7d578051620043008162003ac6565b8352918301918301620042ea565b600082601f8301126200432057600080fd5b815160206200433362003d2f8362003ce3565b82815260059290921b840181019181810190868411156200435357600080fd5b8286015b8481101562003d7d578051835291830191830162004357565b600080604083850312156200438457600080fd5b825167ffffffffffffffff808211156200439d57600080fd5b620043ab86838701620042a1565b93506020850151915080821115620043c257600080fd5b50620043d1858286016200430e565b9150509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016200441c576200441c620043f1565b5060010190565b8082018082111562000cf05762000cf0620043f1565b8181038181111562000cf05762000cf0620043f1565b600080604083850312156200446357600080fd5b505080516020909101519092909150565b6000602082840312156200448757600080fd5b815167ffffffffffffffff8111156200449f57600080fd5b6200371d848285016200430e565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6020815260006200371d602083018486620044ad565b600060808284031215620044ff57600080fd5b82601f8301126200450f57600080fd5b6040516080810181811067ffffffffffffffff8211171562004535576200453562003c6d565b6040528060808401858111156200454b57600080fd5b845b81811015620045675780518352602092830192016200454d565b509195945050505050565b6080810162000cf0828462003ba0565b805180151581146200459357600080fd5b919050565b600060208284031215620045ab57600080fd5b62003afc8262004582565b600181811c90821680620045cb57607f821691505b602082108103620045ec57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000e2157600081815260208120601f850160051c810160208610156200461b5750805b601f850160051c820191505b8181101562002ad25782815560010162004627565b67ffffffffffffffff83111562004657576200465762003c6d565b6200466f83620046688354620045b6565b83620045f2565b6000601f841160018114620046a657600085156200468d5750838201355b600019600387901b1c1916600186901b17835562004703565b600083815260209020601f19861690835b82811015620046d95786850135825560209485019460019092019101620046b7565b5086821015620046f75760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600080604083850312156200471e57600080fd5b82519150602083015167ffffffffffffffff8111156200473d57600080fd5b620043d1858286016200430e565b60408152600062004761604083018587620044ad565b9050826020830152949350505050565b60208152600062003afc602083018462003e00565b8381526000602084818401526060604084015261012083018451606085015281850151620047b8608086018262003ba0565b50604085015160c06101008601528051918290528201906000906101408601905b80831015620048045783516001600160a01b03168252928401926001929092019190840190620047d9565b5098975050505050505050565b600080604083850312156200482557600080fd5b620048308362004582565b9150602083015167ffffffffffffffff8111156200484d57600080fd5b620043d185828601620042a1565b60408152600062004870604083018562003f72565b6020838203818501528185518084528284019150828160051b85010183880160005b83811015620048c457601f19878403018552620048b183835162003e00565b9486019492509085019060010162004892565b50909998505050505050505050565b85815284602082015260a060408201526000620048f460a083018662003f72565b90508360608301526001600160a01b03831660808301529695505050505050565b600082516200492981846020870162003dda565b919091019291505056fe60c0604052600060065534801561001557600080fd5b5060405161168d38038061168d8339810160408190526100349161009b565b61003d3361004b565b60a0919091526080526100bf565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080604083850312156100ae57600080fd5b505080516020909101519092909150565b60805160a05161157161011c6000396000818161027f0152610775015260008181610243015281816103aa015281816103dc015281816104150152818161044e015281816108a80152818161094b01526109ff01526115716000f3fe608060405234801561001057600080fd5b506004361061012c5760003560e01c80638da5cb5b116100ad578063cc5ef00911610071578063cc5ef009146102a1578063cd5e3837146102a9578063ce7c2ac2146102bc578063d73fe0aa146102cf578063f2fde38b146102d757600080fd5b80638da5cb5b146102255780639cb8a26a14610236578063ac5553ce1461023e578063b0ef817914610265578063c27040241461027a57600080fd5b80634e3874a0116100f45780634e3874a0146101cc5780635aa68ac0146101e2578063670d14b2146101f7578063715018a61461020a5780637fd283461461021257600080fd5b80630ea6564814610131578063221f95111461015a57806335c1d3491461017557806337f8d5ff146101a057806348cd4cb1146101b5575b600080fd5b61014461013f366004611081565b6102ea565b60405161015191906110f7565b60405180910390f35b610162610384565b60405160009190910b8152602001610151565b61018861018336600461110a565b61048b565b6040516001600160a01b039091168152602001610151565b6101b36101ae36600461116f565b6104b5565b005b6101be60065481565b604051908152602001610151565b6101d4610617565b604051610151929190611233565b6101ea61079c565b6040516101519190611254565b610144610205366004611081565b6107fe565b6101b3610817565b6101b36102203660046112a1565b61082b565b6000546001600160a01b0316610188565b6101b3610afb565b6101be7f000000000000000000000000000000000000000000000000000000000000000081565b61026d610b11565b6040516101519190611313565b6101be7f000000000000000000000000000000000000000000000000000000000000000081565b61026d610c6f565b6101446102b7366004611081565b610dc7565b6101446102ca366004611081565b610de0565b61026d610df9565b6101b36102e5366004611081565b610f51565b6003602052600090815260409020805461030390611326565b80601f016020809104026020016040519081016040528092919081815260200182805461032f90611326565b801561037c5780601f106103515761010080835404028352916020019161037c565b820191906000526020600020905b81548152906001019060200180831161035f57829003601f168201915b505050505081565b60006006546000036103965750600090565b6000600654436103a69190611370565b90507f000000000000000000000000000000000000000000000000000000000000000081116103d757600191505090565b6104027f00000000000000000000000000000000000000000000000000000000000000006002611389565b811161041057600291505090565b61043b7f00000000000000000000000000000000000000000000000000000000000000006003611389565b811161044957600391505090565b6104747f00000000000000000000000000000000000000000000000000000000000000006004611389565b811161048257600491505090565b60001991505090565b6005818154811061049b57600080fd5b6000918252602090912001546001600160a01b0316905081565b6006541561050a5760405162461bcd60e51b815260206004820152601760248201527f444b472068617320616c7265616479207374617274656400000000000000000060448201526064015b60405180910390fd5b610512610fca565b60005b8381101561060c576005858583818110610531576105316113a0565b90506020020160208101906105469190611081565b815460018101835560009283526020909220909101805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909216919091179055828282818110610597576105976113a0565b90506020028101906105a991906113b6565b600160008888868181106105bf576105bf6113a0565b90506020020160208101906105d49190611081565b6001600160a01b031681526020810191909152604001600020916105f9919083611461565b508061060481611522565b915050610515565b505043600655505050565b60006060600060058054905067ffffffffffffffff81111561063b5761063b6113fd565b60405190808252806020026020018201604052801561066e57816020015b60608152602001906001900390816106595790505b50905060005b600554811015610772576001600060058381548110610695576106956113a0565b60009182526020808320909101546001600160a01b03168352820192909252604001902080546106c490611326565b80601f01602080910402602001604051908101604052809291908181526020018280546106f090611326565b801561073d5780601f106107125761010080835404028352916020019161073d565b820191906000526020600020905b81548152906001019060200180831161072057829003601f168201915b5050505050828281518110610754576107546113a0565b6020026020010181905250808061076a90611522565b915050610674565b507f0000000000000000000000000000000000000000000000000000000000000000939092509050565b606060058054806020026020016040519081016040528092919081815260200182805480156107f457602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116107d6575b5050505050905090565b6001602052600090815260409020805461030390611326565b61081f610fca565b6108296000611024565b565b336000908152600160205260408120805461084590611326565b9050116108945760405162461bcd60e51b815260206004820152601b60248201527f796f7520617265206e6f7420612067726f7570206d656d6265722100000000006044820152606401610501565b6000600654436108a49190611370565b90507f000000000000000000000000000000000000000000000000000000000000000081116109465733600090815260026020526040902080546108e790611326565b1590506109265760405162461bcd60e51b815260206004820152600d60248201526c1cda185c9948195e1a5cdd1959609a1b6044820152606401610501565b336000908152600260205260409020610940838583611461565b50505050565b6109717f00000000000000000000000000000000000000000000000000000000000000006002611389565b81116109fa57336000908152600360205260409020805461099190611326565b1590506109e05760405162461bcd60e51b815260206004820152601060248201527f726573706f6e73652065786973746564000000000000000000000000000000006044820152606401610501565b336000908152600360205260409020610940838583611461565b610a257f00000000000000000000000000000000000000000000000000000000000000006003611389565b8111610aae573360009081526004602052604090208054610a4590611326565b159050610a945760405162461bcd60e51b815260206004820152601560248201527f6a757374696669636174696f6e206578697374656400000000000000000000006044820152606401610501565b336000908152600460205260409020610940838583611461565b60405162461bcd60e51b815260206004820152601560248201527f444b47205075626c6973682068617320656e64656400000000000000000000006044820152606401610501565b505050565b610b03610fca565b6000546001600160a01b0316ff5b60055460609060009067ffffffffffffffff811115610b3257610b326113fd565b604051908082528060200260200182016040528015610b6557816020015b6060815260200190600190039081610b505790505b50905060005b600554811015610c69576004600060058381548110610b8c57610b8c6113a0565b60009182526020808320909101546001600160a01b0316835282019290925260400190208054610bbb90611326565b80601f0160208091040260200160405190810160405280929190818152602001828054610be790611326565b8015610c345780601f10610c0957610100808354040283529160200191610c34565b820191906000526020600020905b815481529060010190602001808311610c1757829003601f168201915b5050505050828281518110610c4b57610c4b6113a0565b60200260200101819052508080610c6190611522565b915050610b6b565b50919050565b60055460609060009067ffffffffffffffff811115610c9057610c906113fd565b604051908082528060200260200182016040528015610cc357816020015b6060815260200190600190039081610cae5790505b50905060005b600554811015610c69576003600060058381548110610cea57610cea6113a0565b60009182526020808320909101546001600160a01b0316835282019290925260400190208054610d1990611326565b80601f0160208091040260200160405190810160405280929190818152602001828054610d4590611326565b8015610d925780601f10610d6757610100808354040283529160200191610d92565b820191906000526020600020905b815481529060010190602001808311610d7557829003601f168201915b5050505050828281518110610da957610da96113a0565b60200260200101819052508080610dbf90611522565b915050610cc9565b6004602052600090815260409020805461030390611326565b6002602052600090815260409020805461030390611326565b60055460609060009067ffffffffffffffff811115610e1a57610e1a6113fd565b604051908082528060200260200182016040528015610e4d57816020015b6060815260200190600190039081610e385790505b50905060005b600554811015610c69576002600060058381548110610e7457610e746113a0565b60009182526020808320909101546001600160a01b0316835282019290925260400190208054610ea390611326565b80601f0160208091040260200160405190810160405280929190818152602001828054610ecf90611326565b8015610f1c5780601f10610ef157610100808354040283529160200191610f1c565b820191906000526020600020905b815481529060010190602001808311610eff57829003601f168201915b5050505050828281518110610f3357610f336113a0565b60200260200101819052508080610f4990611522565b915050610e53565b610f59610fca565b6001600160a01b038116610fbe5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610501565b610fc781611024565b50565b6000546001600160a01b031633146108295760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610501565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561109357600080fd5b81356001600160a01b03811681146110aa57600080fd5b9392505050565b6000815180845260005b818110156110d7576020818501810151868301820152016110bb565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006110aa60208301846110b1565b60006020828403121561111c57600080fd5b5035919050565b60008083601f84011261113557600080fd5b50813567ffffffffffffffff81111561114d57600080fd5b6020830191508360208260051b850101111561116857600080fd5b9250929050565b6000806000806040858703121561118557600080fd5b843567ffffffffffffffff8082111561119d57600080fd5b6111a988838901611123565b909650945060208701359150808211156111c257600080fd5b506111cf87828801611123565b95989497509550505050565b600082825180855260208086019550808260051b84010181860160005b8481101561122657601f198684030189526112148383516110b1565b988401989250908301906001016111f8565b5090979650505050505050565b82815260406020820152600061124c60408301846111db565b949350505050565b6020808252825182820181905260009190848201906040850190845b818110156112955783516001600160a01b031683529284019291840191600101611270565b50909695505050505050565b600080602083850312156112b457600080fd5b823567ffffffffffffffff808211156112cc57600080fd5b818501915085601f8301126112e057600080fd5b8135818111156112ef57600080fd5b86602082850101111561130157600080fd5b60209290920196919550909350505050565b6020815260006110aa60208301846111db565b600181811c9082168061133a57607f821691505b602082108103610c6957634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b818103818111156113835761138361135a565b92915050565b80820281158282048414176113835761138361135a565b634e487b7160e01b600052603260045260246000fd5b6000808335601e198436030181126113cd57600080fd5b83018035915067ffffffffffffffff8211156113e857600080fd5b60200191503681900382131561116857600080fd5b634e487b7160e01b600052604160045260246000fd5b601f821115610af657600081815260208120601f850160051c8101602086101561143a5750805b601f850160051c820191505b8181101561145957828155600101611446565b505050505050565b67ffffffffffffffff831115611479576114796113fd565b61148d836114878354611326565b83611413565b6000601f8411600181146114c157600085156114a95750838201355b600019600387901b1c1916600186901b17835561151b565b600083815260209020601f19861690835b828110156114f257868501358255602094850194600190920191016114d2565b508682101561150f5760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b6000600182016115345761153461135a565b506001019056fea2646970667358221220f3e6e3e93788ae67c1eaf31cb805370313ba096ed5aa792e2b15c335a3903e5a64736f6c63430008120033a2646970667358221220452028a72bf515ef364f752d379d36e460edada7991f7fdb8d560ed8e00d58f964736f6c63430008120033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.