ETH Price: $2,157.56 (+0.78%)

Transaction Decoder

Block:
12697533 at Jun-24-2021 03:07:26 PM +UTC
Transaction Fee:
0.00041094 ETH $0.89
Gas Used:
27,396 Gas / 15 Gwei

Account State Difference:

  Address   Before After State Difference Code
(2Miners: PPLNS)
1,591.364362281746321022 Eth1,591.364773221746321022 Eth0.00041094
0x29079965...561b09e57
0.294970211575722179 Eth
Nonce: 125
0.294559271575722179 Eth
Nonce: 126
0.00041094

Execution Trace

0x6bfa7f73d9733cde9c7197f62bdc03384a0ff34d.b52c05fe( )
  • VotingEscrowLock.createLock( amount=40798216343308952795028, epochs=2 )
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    interface IERC20Mintable is IERC20 {
        function mint(address to, uint256 amount) external;
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    library Utils {
        function find(address[] memory arr, address item)
            internal
            pure
            returns (bool exist, uint256 index)
        {
            for (uint256 i = 0; i < arr.length; i += 1) {
                if (arr[i] == item) {
                    return (true, i);
                }
            }
        }
        function find(bytes4[] memory arr, bytes4 sig)
            internal
            pure
            returns (bool exist, uint256 index)
        {
            for (uint256 i = 0; i < arr.length; i += 1) {
                if (arr[i] == sig) {
                    return (true, i);
                }
            }
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    contract ERC20Recoverer is Initializable {
        using SafeERC20 for IERC20;
        mapping(address => bool) public permanentlyNonRecoverable;
        mapping(address => bool) public nonRecoverable;
        event Recovered(address token, uint256 amount);
        address public recoverer;
        constructor() {}
        modifier onlyRecoverer() {
            require(msg.sender == recoverer, "Only allowed to recoverer");
            _;
        }
        function initialize(address _recoverer, address[] memory disableList)
            public
            initializer
        {
            for (uint256 i = 0; i < disableList.length; i++) {
                permanentlyNonRecoverable[disableList[i]] = true;
            }
            recoverer = _recoverer;
        }
        function setRecoverer(address _recoverer) public onlyRecoverer {
            recoverer = _recoverer;
        }
        // Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders
        function recoverERC20(address tokenAddress, uint256 tokenAmount)
            external
            onlyRecoverer
        {
            require(nonRecoverable[tokenAddress] == false, "Non-recoverable ERC20");
            require(
                permanentlyNonRecoverable[tokenAddress] == false,
                "Non-recoverable ERC20"
            );
            IERC20(tokenAddress).safeTransfer(recoverer, tokenAmount);
            emit Recovered(tokenAddress, tokenAmount);
        }
        function disable(address _contract) public onlyRecoverer {
            nonRecoverable[_contract] = true;
        }
        function disablePermanently(address _contract) public onlyRecoverer {
            permanentlyNonRecoverable[_contract] = true;
        }
        function enable(address _contract) public onlyRecoverer {
            permanentlyNonRecoverable[_contract] = true;
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import {
        ERC20Burnable,
        ERC20 as _ERC20
    } from "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
    /**
     * @title Commit Token
     * @notice Commit Token is used for redeeming stable coins, buying crypto products
     *      from the village market and mining vision tokens. It is minted by the admin and
     *      given to the contributors. The amount of mintable token is limited to the balance
     *      of redeemable stable coins. Therefore, it's 1:1 pegged to the given stable coin
     *      or expected to have higher value than the redeemable coin values.
     */
    contract ERC20 is ERC20Burnable {
        address public minter;
        constructor() _ERC20("ERC20Mock", "MOCK") {
            minter = msg.sender;
        }
        modifier onlyMinter {
            require(msg.sender == minter, "Not a minter");
            _;
        }
        function mint(address to, uint256 amount) public onlyMinter {
            _mint(to, amount);
        }
        function setMinter(address _minter) public onlyMinter {
            minter = _minter;
        }
    }
    //SPDX-License-Identifier: CC0
    pragma solidity ^0.7.0;
    /**
     * @title ERC-1620 Money Streaming Standard
     * @author Sablier
     * @dev See https://eips.ethereum.org/EIPS/eip-1620
     */
    interface IERC1620 {
        /**
         * @notice Emits when a stream is successfully created.
         */
        event CreateStream(
            uint256 indexed streamId,
            address indexed sender,
            address indexed recipient,
            uint256 deposit,
            address tokenAddress,
            uint256 startTime,
            uint256 stopTime
        );
        /**
         * @notice Emits when the recipient of a stream withdraws a portion or all their pro rata share of the stream.
         */
        event WithdrawFromStream(
            uint256 indexed streamId,
            address indexed recipient,
            uint256 amount
        );
        /**
         * @notice Emits when a stream is successfully cancelled and tokens are transferred back on a pro rata basis.
         */
        event CancelStream(
            uint256 indexed streamId,
            address indexed sender,
            address indexed recipient,
            uint256 senderBalance,
            uint256 recipientBalance
        );
        function balanceOf(uint256 streamId, address who)
            external
            view
            returns (uint256 balance);
        function getStream(uint256 streamId)
            external
            view
            returns (
                address sender,
                address recipient,
                uint256 deposit,
                address token,
                uint256 startTime,
                uint256 stopTime,
                uint256 remainingBalance,
                uint256 ratePerSecond
            );
        function createStream(
            address recipient,
            uint256 deposit,
            address tokenAddress,
            uint256 startTime,
            uint256 stopTime
        ) external returns (uint256 streamId);
        function withdrawFromStream(uint256 streamId, uint256 funds)
            external
            returns (bool);
        function cancelStream(uint256 streamId) external returns (bool);
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import {
        ERC1155Burnable,
        ERC1155 as _ERC1155
    } from "@openzeppelin/contracts/token/ERC1155/ERC1155Burnable.sol";
    /**
     * @title Commit Token
     * @notice Commit Token is used for redeeming stable coins, buying crypto products
     *      from the village market and mining vision tokens. It is minted by the admin and
     *      given to the contributors. The amount of mintable token is limited to the balance
     *      of redeemable stable coins. Therefore, it's 1:1 pegged to the given stable coin
     *      or expected to have higher value than the redeemable coin values.
     */
    contract ERC1155 is ERC1155Burnable {
        address public minter;
        constructor() _ERC1155("ERC1155Mock") {
            minter = msg.sender;
        }
        modifier onlyMinter {
            require(msg.sender == minter, "Not a minter");
            _;
        }
        function mint(
            address to,
            uint256 id,
            uint256 amount
        ) public onlyMinter {
            _mint(to, id, amount, bytes(""));
        }
        function setMinter(address _minter) public onlyMinter {
            minter = _minter;
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import {
        IERC20 as _IERC20
    } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    interface IERC20 is _IERC20 {}
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    contract WETH9 {
        string public name = "Wrapped Ether";
        string public symbol = "WETH";
        uint8 public decimals = 18;
        event Approval(address indexed src, address indexed guy, uint256 wad);
        event Transfer(address indexed src, address indexed dst, uint256 wad);
        event Deposit(address indexed dst, uint256 wad);
        event Withdrawal(address indexed src, uint256 wad);
        mapping(address => uint256) public balanceOf;
        mapping(address => mapping(address => uint256)) public allowance;
        receive() external payable {
            deposit();
        }
        function deposit() public payable {
            balanceOf[msg.sender] += msg.value;
            emit Deposit(msg.sender, msg.value);
        }
        function withdraw(uint256 wad) public {
            require(balanceOf[msg.sender] >= wad);
            balanceOf[msg.sender] -= wad;
            msg.sender.transfer(wad);
            emit Withdrawal(msg.sender, wad);
        }
        function totalSupply() public view returns (uint256) {
            return address(this).balance;
        }
        function approve(address guy, uint256 wad) public returns (bool) {
            allowance[msg.sender][guy] = wad;
            Approval(msg.sender, guy, wad);
            return true;
        }
        function transfer(address dst, uint256 wad) public returns (bool) {
            return transferFrom(msg.sender, dst, wad);
        }
        function transferFrom(
            address src,
            address dst,
            uint256 wad
        ) public returns (bool) {
            require(balanceOf[src] >= wad);
            if (src != msg.sender && allowance[src][msg.sender] != uint256(-1)) {
                require(allowance[src][msg.sender] >= wad);
                allowance[src][msg.sender] -= wad;
            }
            balanceOf[src] -= wad;
            balanceOf[dst] += wad;
            Transfer(src, dst, wad);
            return true;
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    library Sqrt {
        /**
         * @dev This code is written by Noah Zinsmeister @ Uniswap
         * https://github.com/Uniswap/uniswap-v2-core/blob/v1.0.1/contracts/libraries/Math.sol
         */
        function sqrt(uint256 y) internal pure returns (uint256 z) {
            if (y > 3) {
                z = y;
                uint256 x = y / 2 + 1;
                while (x < z) {
                    z = x;
                    x = (y / x + x) / 2;
                }
            } else if (y != 0) {
                z = 1;
            }
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/utils/SafeCast.sol";
    library Int128 {
        using SafeCast for uint256;
        using SafeCast for int256;
        function toInt128(uint256 val) internal pure returns (int128) {
            return val.toInt256().toInt128();
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
    import "../../../core/emission/libraries/MiningPool.sol";
    contract ERC20BurnMiningV1 is MiningPool {
        using SafeMath for uint256;
        function initialize(address tokenEmitter_, address baseToken_)
            public
            override
        {
            super.initialize(tokenEmitter_, baseToken_);
            _registerInterface(ERC20BurnMiningV1(0).burn.selector);
            _registerInterface(ERC20BurnMiningV1(0).exit.selector);
            _registerInterface(ERC20BurnMiningV1(0).erc20BurnMiningV1.selector);
        }
        function burn(uint256 amount) public {
            _dispatchMiners(amount);
            ERC20Burnable(baseToken()).burnFrom(msg.sender, amount);
        }
        function exit() public {
            // transfer vision token
            _mine();
            // withdraw all miners
            uint256 numOfMiners = dispatchedMiners(msg.sender);
            _withdrawMiners(numOfMiners);
        }
        function erc20BurnMiningV1() external pure returns (bool) {
            return true;
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    // Refactored synthetix StakingRewards.sol for general purpose mining pool logic.
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/math/Math.sol";
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/utils/Pausable.sol";
    import "@openzeppelin/contracts/introspection/ERC165.sol";
    import "../../../core/tokens/COMMIT.sol";
    import "../../../core/emission/interfaces/ITokenEmitter.sol";
    import "../../../core/emission/interfaces/IMiningPool.sol";
    import "../../../utils/ERC20Recoverer.sol";
    abstract contract MiningPool is
        ReentrancyGuard,
        Pausable,
        ERC20Recoverer,
        ERC165,
        IMiningPool
    {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        address private _baseToken;
        address private _token;
        address private _tokenEmitter;
        uint256 private _miningEnds = 0;
        uint256 private _miningRate = 0;
        uint256 private _lastUpdateTime;
        uint256 private _tokenPerMiner;
        uint256 private _totalMiners;
        mapping(address => uint256) private _paidTokenPerMiner;
        mapping(address => uint256) private _mined;
        mapping(address => uint256) private _dispatchedMiners;
        modifier onlyTokenEmitter() {
            require(
                msg.sender == address(_tokenEmitter),
                "Only the token emitter can call this function"
            );
            _;
        }
        modifier recordMining(address account) {
            _tokenPerMiner = tokenPerMiner();
            _lastUpdateTime = lastTimeMiningApplicable();
            if (account != address(0)) {
                _mined[account] = mined(account);
                _paidTokenPerMiner[account] = _tokenPerMiner;
            }
            _;
        }
        function initialize(address tokenEmitter_, address baseToken_)
            public
            virtual
            override
        {
            address token_ = ITokenEmitter(tokenEmitter_).token();
            require(address(_token) == address(0), "Already initialized");
            require(token_ != address(0), "Token is zero address");
            require(tokenEmitter_ != address(0), "Token emitter is zero address");
            require(baseToken_ != address(0), "Base token is zero address");
            _token = token_;
            _tokenEmitter = tokenEmitter_;
            _baseToken = baseToken_;
            // ERC20Recoverer
            address[] memory disable = new address[](2);
            disable[0] = token_;
            disable[1] = baseToken_;
            ERC20Recoverer.initialize(msg.sender, disable);
            // ERC165
            bytes4 _INTERFACE_ID_ERC165 = 0x01ffc9a7;
            _registerInterface(_INTERFACE_ID_ERC165);
            _registerInterface(MiningPool(0).allocate.selector);
        }
        function allocate(uint256 amount)
            public
            override
            onlyTokenEmitter
            recordMining(address(0))
        {
            uint256 miningPeriod = ITokenEmitter(_tokenEmitter).EMISSION_PERIOD();
            if (block.timestamp >= _miningEnds) {
                _miningRate = amount.div(miningPeriod);
            } else {
                uint256 remaining = _miningEnds.sub(block.timestamp);
                uint256 leftover = remaining.mul(_miningRate);
                _miningRate = amount.add(leftover).div(miningPeriod);
            }
            // Ensure the provided mining amount is not more than the balance in the contract.
            // This keeps the mining rate in the right range, preventing overflows due to
            // very high values of miningRate in the mined and tokenPerMiner functions;
            // (allocated_amount + leftover) must be less than 2^256 / 10^18 to avoid overflow.
            uint256 balance = IERC20(_token).balanceOf(address(this));
            require(_miningRate <= balance.div(miningPeriod), "not enough balance");
            _lastUpdateTime = block.timestamp;
            _miningEnds = block.timestamp.add(miningPeriod);
            emit Allocated(amount);
        }
        function token() public view override returns (address) {
            return _token;
        }
        function tokenEmitter() public view override returns (address) {
            return _tokenEmitter;
        }
        function baseToken() public view override returns (address) {
            return _baseToken;
        }
        function miningEnds() public view override returns (uint256) {
            return _miningEnds;
        }
        function miningRate() public view override returns (uint256) {
            return _miningRate;
        }
        function lastUpdateTime() public view override returns (uint256) {
            return _lastUpdateTime;
        }
        function lastTimeMiningApplicable() public view override returns (uint256) {
            return Math.min(block.timestamp, _miningEnds);
        }
        function tokenPerMiner() public view override returns (uint256) {
            if (_totalMiners == 0) {
                return _tokenPerMiner;
            }
            return
                _tokenPerMiner.add(
                    lastTimeMiningApplicable()
                        .sub(_lastUpdateTime)
                        .mul(_miningRate)
                        .mul(1e18)
                        .div(_totalMiners)
                );
        }
        function mined(address account) public view override returns (uint256) {
            // prev mined + ((token/miner - paidToken/miner) 1e18 unit) * dispatchedMiner
            return
                _dispatchedMiners[account]
                    .mul(tokenPerMiner().sub(_paidTokenPerMiner[account]))
                    .div(1e18)
                    .add(_mined[account]);
        }
        function getMineableForPeriod() public view override returns (uint256) {
            uint256 miningPeriod = ITokenEmitter(_tokenEmitter).EMISSION_PERIOD();
            return _miningRate.mul(miningPeriod);
        }
        function paidTokenPerMiner(address account)
            public
            view
            override
            returns (uint256)
        {
            return _paidTokenPerMiner[account];
        }
        function dispatchedMiners(address account)
            public
            view
            override
            returns (uint256)
        {
            return _dispatchedMiners[account];
        }
        function totalMiners() public view override returns (uint256) {
            return _totalMiners;
        }
        function _dispatchMiners(uint256 miners) internal {
            _dispatchMiners(msg.sender, miners);
        }
        function _dispatchMiners(address account, uint256 miners)
            internal
            nonReentrant
            whenNotPaused
            recordMining(account)
        {
            require(miners > 0, "Cannot stake 0");
            _totalMiners = _totalMiners.add(miners);
            _dispatchedMiners[account] = _dispatchedMiners[account].add(miners);
            emit Dispatched(account, miners);
        }
        function _withdrawMiners(uint256 miners) internal {
            _withdrawMiners(msg.sender, miners);
        }
        function _withdrawMiners(address account, uint256 miners)
            internal
            nonReentrant
            recordMining(account)
        {
            require(miners > 0, "Cannot withdraw 0");
            _totalMiners = _totalMiners.sub(miners);
            _dispatchedMiners[account] = _dispatchedMiners[account].sub(miners);
            emit Withdrawn(account, miners);
        }
        function _mine() internal {
            _mine(msg.sender);
        }
        function _mine(address account)
            internal
            nonReentrant
            recordMining(account)
        {
            uint256 amount = _mined[account];
            if (amount > 0) {
                _mined[account] = 0;
                IERC20(_token).safeTransfer(account, amount);
                emit Mined(account, amount);
            }
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    /**
     * @title Commit Token
     * @notice Commit Token is used for redeeming stable coins, buying crypto products
     *      from the village market and mining vision tokens. It is minted by the admin and
     *      given to the contributors. The amount of mintable token is limited to the balance
     *      of redeemable stable coins. Therefore, it's 1:1 pegged to the given stable coin
     *      or expected to have higher value than the redeemable coin values.
     */
    contract COMMIT is ERC20Burnable, Initializable {
        using SafeMath for uint256;
        address private _minter;
        uint256 private _totalBurned;
        string private _name;
        string private _symbol;
        constructor() ERC20("", "") {
            // this constructor will not be called since it'll be cloned by proxy pattern.
            // initalize() will be called instead.
        }
        modifier onlyMinter {
            require(msg.sender == _minter, "Not a minter");
            _;
        }
        function initialize(
            string memory name_,
            string memory symbol_,
            address minter_
        ) public initializer {
            _name = name_;
            _symbol = symbol_;
            _minter = minter_;
        }
        function mint(address to, uint256 amount) public onlyMinter {
            _mint(to, amount);
        }
        function setMinter(address minter_) public onlyMinter {
            _setMinter(minter_);
        }
        function _setMinter(address minter_) internal {
            _minter = minter_;
        }
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        function decimals() public pure override returns (uint8) {
            return 18;
        }
        function minter() public view returns (address) {
            return _minter;
        }
        function totalBurned() public view returns (uint256) {
            return _totalBurned;
        }
        function _burn(address account, uint256 amount) internal override {
            super._burn(account, amount);
            _totalBurned = _totalBurned.add(amount);
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
    struct EmissionWeight {
        address[] pools;
        uint256[] weights;
        uint256 treasury;
        uint256 caller;
        uint256 protocol;
        uint256 dev;
        uint256 sum;
    }
    struct EmitterConfig {
        uint256 projId;
        uint256 initialEmission;
        uint256 minEmissionRatePerWeek;
        uint256 emissionCutRate;
        uint256 founderShareRate;
        uint256 startDelay;
        address treasury;
        address gov;
        address token;
        address protocolPool;
        address contributionBoard;
        address erc20BurnMiningFactory;
        address erc20StakeMiningFactory;
        address erc721StakeMiningFactory;
        address erc1155StakeMiningFactory;
        address erc1155BurnMiningFactory;
        address initialContributorShareFactory;
    }
    struct MiningPoolConfig {
        uint256 weight;
        bytes4 poolType;
        address baseToken;
    }
    struct MiningConfig {
        MiningPoolConfig[] pools;
        uint256 treasuryWeight;
        uint256 callerWeight;
    }
    interface ITokenEmitter {
        event Start();
        event TokenEmission(uint256 amount);
        event EmissionCutRateUpdated(uint256 rate);
        event EmissionRateUpdated(uint256 rate);
        event EmissionWeightUpdated(uint256 numberOfPools);
        event NewMiningPool(bytes4 poolTypes, address baseToken, address pool);
        function start() external;
        function distribute() external;
        function token() external view returns (address);
        function projId() external view returns (uint256);
        function poolTypes(address pool) external view returns (bytes4);
        function factories(bytes4 poolType) external view returns (address);
        function minEmissionRatePerWeek() external view returns (uint256);
        function emissionCutRate() external view returns (uint256);
        function emission() external view returns (uint256);
        function initialContributorPool() external view returns (address);
        function initialContributorShare() external view returns (address);
        function treasury() external view returns (address);
        function protocolPool() external view returns (address);
        function pools(uint256 index) external view returns (address);
        function emissionWeight() external view returns (EmissionWeight memory);
        function emissionStarted() external view returns (uint256);
        function emissionWeekNum() external view returns (uint256);
        function INITIAL_EMISSION() external view returns (uint256);
        function FOUNDER_SHARE_DENOMINATOR() external view returns (uint256);
        function EMISSION_PERIOD() external pure returns (uint256);
        function DENOMINATOR() external pure returns (uint256);
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    interface IMiningPool {
        event Allocated(uint256 amount);
        event Dispatched(address indexed user, uint256 numOfMiners);
        event Withdrawn(address indexed user, uint256 numOfMiners);
        event Mined(address indexed user, uint256 amount);
        function initialize(address _tokenEmitter, address _baseToken) external;
        function allocate(uint256 amount) external;
        function token() external view returns (address);
        function tokenEmitter() external view returns (address);
        function baseToken() external view returns (address);
        function miningEnds() external view returns (uint256);
        function miningRate() external view returns (uint256);
        function lastUpdateTime() external view returns (uint256);
        function lastTimeMiningApplicable() external view returns (uint256);
        function tokenPerMiner() external view returns (uint256);
        function mined(address account) external view returns (uint256);
        function getMineableForPeriod() external view returns (uint256);
        function paidTokenPerMiner(address account) external view returns (uint256);
        function dispatchedMiners(address account) external view returns (uint256);
        function totalMiners() external view returns (uint256);
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/token/ERC1155/ERC1155Holder.sol";
    import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
    import "../../../core/emission/libraries/MiningPool.sol";
    contract ERC1155StakeMiningV1 is MiningPool, ERC1155Holder {
        using SafeMath for uint256;
        mapping(address => mapping(uint256 => uint256)) private _staking;
        function initialize(address tokenEmitter_, address baseToken_)
            public
            override
        {
            super.initialize(tokenEmitter_, baseToken_);
            _registerInterface(ERC1155StakeMiningV1(0).stake.selector);
            _registerInterface(ERC1155StakeMiningV1(0).mine.selector);
            _registerInterface(ERC1155StakeMiningV1(0).withdraw.selector);
            _registerInterface(ERC1155StakeMiningV1(0).exit.selector);
            _registerInterface(ERC1155StakeMiningV1(0).dispatchableMiners.selector);
            _registerInterface(
                ERC1155StakeMiningV1(0).erc1155StakeMiningV1.selector
            );
        }
        function stake(uint256 id, uint256 amount) public {
            bytes memory zero;
            IERC1155(baseToken()).safeTransferFrom(
                msg.sender,
                address(this),
                id,
                amount,
                zero
            );
        }
        function withdraw(uint256 tokenId, uint256 amount) public {
            uint256 staked = _staking[msg.sender][tokenId];
            require(staked >= amount, "Withdrawing more than staked.");
            _staking[msg.sender][tokenId] = staked - amount;
            uint256 miners = dispatchableMiners(tokenId).mul(amount);
            _withdrawMiners(miners);
            bytes memory zero;
            IERC1155(baseToken()).safeTransferFrom(
                address(this),
                msg.sender,
                tokenId,
                amount,
                zero
            );
        }
        function mine() public {
            _mine();
        }
        function exit(uint256 tokenId) public {
            mine();
            withdraw(tokenId, _staking[msg.sender][tokenId]);
        }
        function _stake(
            address account,
            uint256 tokenId,
            uint256 amount
        ) internal {
            _staking[account][tokenId] = _staking[account][tokenId].add(amount);
            uint256 miners = dispatchableMiners(tokenId).mul(amount);
            _dispatchMiners(account, miners);
        }
        function onERC1155Received(
            address,
            address from,
            uint256 id,
            uint256 value,
            bytes calldata
        ) public virtual override returns (bytes4) {
            _stake(from, id, value);
            return this.onERC1155Received.selector;
        }
        function onERC1155BatchReceived(
            address,
            address from,
            uint256[] calldata ids,
            uint256[] calldata values,
            bytes calldata
        ) public virtual override returns (bytes4) {
            require(ids.length == values.length, "Not a valid input");
            for (uint256 i = 0; i < ids.length; i++) {
                _stake(from, ids[i], values[i]);
            }
            return this.onERC1155BatchReceived.selector;
        }
        /**
         * @dev override this function if you customize this mining pool
         */
        function dispatchableMiners(uint256)
            public
            view
            virtual
            returns (uint256 numOfMiner)
        {
            return 1;
        }
        function erc1155StakeMiningV1() external pure returns (bool) {
            return true;
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/token/ERC1155/ERC1155Holder.sol";
    import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
    import "@openzeppelin/contracts/token/ERC1155/ERC1155Burnable.sol";
    import "../../../core/emission/libraries/MiningPool.sol";
    import "../../../core/emission/pools/ERC1155BurnMiningV1.sol";
    import "../../../core/emission/interfaces/ITokenEmitter.sol";
    contract InitialContributorShare is ERC1155BurnMiningV1 {
        using SafeMath for uint256;
        uint256 private _projId;
        function initialize(address tokenEmitter_, address baseToken_)
            public
            override
        {
            super.initialize(tokenEmitter_, baseToken_);
            _registerInterface(ERC1155BurnMiningV1(0).burn.selector);
            _registerInterface(ERC1155BurnMiningV1(0).exit.selector);
            _registerInterface(ERC1155BurnMiningV1(0).dispatchableMiners.selector);
            _registerInterface(ERC1155BurnMiningV1(0).erc1155BurnMiningV1.selector);
            _registerInterface(
                InitialContributorShare(0).initialContributorShare.selector
            );
            _projId = ITokenEmitter(tokenEmitter_).projId();
        }
        function burn(uint256 amount) public {
            burn(_projId, amount);
        }
        function burn(uint256 projId_, uint256 amount) public override {
            require(_projId == projId_);
            super.burn(_projId, amount);
        }
        function exit() public {
            exit(_projId);
        }
        function exit(uint256 projId_) public override {
            require(_projId == projId_);
            super.exit(_projId);
        }
        /**
         * @dev override this function if you customize this mining pool
         */
        function dispatchableMiners(uint256 id)
            public
            view
            override
            returns (uint256 numOfMiner)
        {
            if (_projId == id) return 1;
            else return 0;
        }
        function projId() public view returns (uint256) {
            return _projId;
        }
        function initialContributorShare() external pure returns (bool) {
            return true;
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/token/ERC1155/ERC1155Holder.sol";
    import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
    import "@openzeppelin/contracts/token/ERC1155/ERC1155Burnable.sol";
    import "../../../core/emission/libraries/MiningPool.sol";
    import "../../../core/emission/interfaces/ITokenEmitter.sol";
    contract ERC1155BurnMiningV1 is MiningPool, ERC1155Holder {
        using SafeMath for uint256;
        mapping(address => mapping(uint256 => uint256)) private _burned;
        function initialize(address tokenEmitter_, address baseToken_)
            public
            virtual
            override
        {
            super.initialize(tokenEmitter_, baseToken_);
            _registerInterface(ERC1155BurnMiningV1(0).burn.selector);
            _registerInterface(ERC1155BurnMiningV1(0).exit.selector);
            _registerInterface(ERC1155BurnMiningV1(0).dispatchableMiners.selector);
            _registerInterface(ERC1155BurnMiningV1(0).erc1155BurnMiningV1.selector);
        }
        function burn(uint256 tokenId, uint256 amount) public virtual {
            _dispatch(msg.sender, tokenId, amount);
            ERC1155Burnable(baseToken()).burn(msg.sender, tokenId, amount);
        }
        function exit(uint256 tokenId) public virtual {
            // transfer vision token
            _mine();
            uint256 burnedAmount = _burned[msg.sender][tokenId];
            _burned[msg.sender][tokenId] = 0;
            // withdraw all miners for the given token id
            uint256 minersToWithdraw =
                dispatchableMiners(tokenId).mul(burnedAmount);
            _withdrawMiners(minersToWithdraw);
        }
        function onERC1155Received(
            address,
            address from,
            uint256 id,
            uint256 value,
            bytes calldata
        ) public virtual override returns (bytes4) {
            _dispatch(from, id, value);
            ERC1155Burnable(baseToken()).burn(address(this), id, value);
            return this.onERC1155Received.selector;
        }
        function onERC1155BatchReceived(
            address,
            address from,
            uint256[] calldata ids,
            uint256[] calldata values,
            bytes calldata
        ) public virtual override returns (bytes4) {
            require(ids.length == values.length, "Not a valid input");
            for (uint256 i = 0; i < ids.length; i++) {
                _dispatch(from, ids[i], values[i]);
                ERC1155Burnable(baseToken()).burn(address(this), ids[i], values[i]);
            }
            return this.onERC1155BatchReceived.selector;
        }
        /**
         * @dev override this function if you customize this mining pool
         */
        function dispatchableMiners(uint256)
            public
            view
            virtual
            returns (uint256 numOfMiner)
        {
            return 1;
        }
        function erc1155BurnMiningV1() external pure returns (bool) {
            return true;
        }
        function _dispatch(
            address account,
            uint256 tokenId,
            uint256 amount
        ) internal virtual {
            uint256 minersToDispatch = dispatchableMiners(tokenId).mul(amount);
            _dispatchMiners(account, minersToDispatch);
            _burned[account][tokenId] = _burned[account][tokenId].add(amount);
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "../../../core/emission/libraries/MiningPool.sol";
    contract ERC20StakeMiningV1 is MiningPool {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        function initialize(address tokenEmitter_, address baseToken_)
            public
            override
        {
            super.initialize(tokenEmitter_, baseToken_);
            _registerInterface(ERC20StakeMiningV1(0).stake.selector);
            _registerInterface(ERC20StakeMiningV1(0).mine.selector);
            _registerInterface(ERC20StakeMiningV1(0).withdraw.selector);
            _registerInterface(ERC20StakeMiningV1(0).exit.selector);
            _registerInterface(ERC20StakeMiningV1(0).erc20StakeMiningV1.selector);
        }
        function stake(uint256 amount) public {
            IERC20(baseToken()).safeTransferFrom(msg.sender, address(this), amount);
            _dispatchMiners(amount);
        }
        function withdraw(uint256 amount) public {
            _withdrawMiners(amount);
            IERC20(baseToken()).safeTransfer(msg.sender, amount);
        }
        function mine() public {
            _mine();
        }
        function exit() public {
            mine();
            withdraw(dispatchedMiners(msg.sender));
        }
        function erc20StakeMiningV1() external pure returns (bool) {
            return true;
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/token/ERC721/ERC721Holder.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol";
    import "@openzeppelin/contracts/utils/EnumerableSet.sol";
    import "@openzeppelin/contracts/utils/EnumerableMap.sol";
    import "../../../core/emission/libraries/MiningPool.sol";
    contract ERC721StakeMiningV1 is MiningPool, ERC721Holder {
        using SafeMath for uint256;
        using EnumerableSet for EnumerableSet.UintSet;
        using EnumerableMap for EnumerableMap.UintToAddressMap;
        mapping(address => EnumerableSet.UintSet) private _stakedTokensOf;
        EnumerableMap.UintToAddressMap private _stakers;
        function initialize(address tokenEmitter_, address baseToken_)
            public
            override
        {
            super.initialize(tokenEmitter_, baseToken_);
            _registerInterface(ERC721StakeMiningV1(0).stake.selector);
            _registerInterface(ERC721StakeMiningV1(0).mine.selector);
            _registerInterface(ERC721StakeMiningV1(0).withdraw.selector);
            _registerInterface(ERC721StakeMiningV1(0).exit.selector);
            _registerInterface(ERC721StakeMiningV1(0).dispatchableMiners.selector);
            _registerInterface(ERC721StakeMiningV1(0).erc721StakeMiningV1.selector);
        }
        function stake(uint256 id) public {
            IERC721(baseToken()).safeTransferFrom(msg.sender, address(this), id);
        }
        function withdraw(uint256 tokenId) public {
            require(
                _stakers.get(tokenId) == msg.sender,
                "Only staker can withdraw"
            );
            _stakedTokensOf[msg.sender].remove(tokenId);
            _stakers.remove(tokenId);
            uint256 miners = dispatchableMiners(tokenId);
            _withdrawMiners(miners);
            IERC721(baseToken()).safeTransferFrom(
                address(this),
                msg.sender,
                tokenId
            );
        }
        function mine() public {
            _mine();
        }
        function exit() public {
            mine();
            uint256 bal = _stakedTokensOf[msg.sender].length();
            for (uint256 i = 0; i < bal; i++) {
                uint256 tokenId = _stakedTokensOf[msg.sender].at(i);
                withdraw(tokenId);
            }
        }
        function onERC721Received(
            address,
            address from,
            uint256 tokenId,
            bytes calldata
        ) public override returns (bytes4) {
            _stake(from, tokenId);
            return this.onERC721Received.selector;
        }
        /**
         * @dev override this function if you customize this mining pool
         */
        function dispatchableMiners(uint256 tokenId)
            public
            view
            virtual
            returns (uint256 numOfMiner)
        {
            if (IERC721(baseToken()).ownerOf(tokenId) != address(0)) return 1;
            else return 0;
        }
        function erc721StakeMiningV1() external pure returns (bool) {
            return true;
        }
        function _stake(address from, uint256 tokenId) internal {
            uint256 miners = dispatchableMiners(tokenId);
            _stakedTokensOf[from].add(tokenId);
            _stakers.set(tokenId, from);
            _dispatchMiners(from, miners);
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import {
        ERC20BurnMiningV1 as _ERC20BurnMiningV1
    } from "../../../core/emission/pools/ERC20BurnMiningV1.sol";
    import {
        ERC20StakeMiningV1 as _ERC20StakeMiningV1
    } from "../../../core/emission/pools/ERC20StakeMiningV1.sol";
    import {
        ERC721StakeMiningV1 as _ERC721StakeMiningV1
    } from "../../../core/emission/pools/ERC721StakeMiningV1.sol";
    import {
        ERC1155StakeMiningV1 as _ERC1155StakeMiningV1
    } from "../../../core/emission/pools/ERC1155StakeMiningV1.sol";
    import {
        ERC1155BurnMiningV1 as _ERC1155BurnMiningV1
    } from "../../../core/emission/pools/ERC1155BurnMiningV1.sol";
    import {
        InitialContributorShare as _InitialContributorShare
    } from "../../../core/emission/pools/InitialContributorShare.sol";
    library PoolType {
        bytes4 public constant ERC20BurnMiningV1 =
            _ERC20BurnMiningV1(0).erc20BurnMiningV1.selector;
        bytes4 public constant ERC20StakeMiningV1 =
            _ERC20StakeMiningV1(0).erc20StakeMiningV1.selector;
        bytes4 public constant ERC721StakeMiningV1 =
            _ERC721StakeMiningV1(0).erc721StakeMiningV1.selector;
        bytes4 public constant ERC1155StakeMiningV1 =
            _ERC1155StakeMiningV1(0).erc1155StakeMiningV1.selector;
        bytes4 public constant ERC1155BurnMiningV1 =
            _ERC1155BurnMiningV1(0).erc1155BurnMiningV1.selector;
        bytes4 public constant InitialContributorShare =
            _InitialContributorShare(0).initialContributorShare.selector;
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/proxy/Clones.sol";
    import "@openzeppelin/contracts/introspection/ERC165.sol";
    import "@openzeppelin/contracts/utils/Address.sol";
    import "../../../core/emission/interfaces/IMiningPoolFactory.sol";
    import "../../../core/emission/interfaces/IMiningPool.sol";
    abstract contract MiningPoolFactory is IMiningPoolFactory, ERC165 {
        using Clones for address;
        address private _controller;
        constructor() ERC165() {
            _registerInterface(IMiningPoolFactory(0).newPool.selector);
            _registerInterface(IMiningPoolFactory(0).poolType.selector);
        }
        function _setController(address controller_) internal {
            _controller = controller_;
        }
        function newPool(address emitter, address baseToken)
            public
            virtual
            override
            returns (address pool)
        {
            address predicted = this.poolAddress(emitter, baseToken);
            if (_isDeployed(predicted)) {
                // already deployed;
                return predicted;
            } else {
                // not deployed;
                bytes32 salt = keccak256(abi.encodePacked(emitter, baseToken));
                pool = _controller.cloneDeterministic(salt);
                require(
                    predicted == pool,
                    "Different result. This factory has a serious problem."
                );
                IMiningPool(pool).initialize(emitter, baseToken);
                emit NewMiningPool(emitter, baseToken, pool);
                return pool;
            }
        }
        function controller() public view override returns (address) {
            return _controller;
        }
        function getPool(address emitter, address baseToken)
            public
            view
            override
            returns (address)
        {
            address predicted = this.poolAddress(emitter, baseToken);
            return _isDeployed(predicted) ? predicted : address(0);
        }
        function poolAddress(address emitter, address baseToken)
            external
            view
            virtual
            override
            returns (address pool)
        {
            bytes32 salt = keccak256(abi.encodePacked(emitter, baseToken));
            pool = _controller.predictDeterministicAddress(salt);
        }
        function _isDeployed(address pool) private view returns (bool) {
            return Address.isContract(pool);
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    interface IMiningPoolFactory {
        event NewMiningPool(
            address _emitter,
            address _stakingToken,
            address _poolAddress
        );
        function newPool(address _emitter, address _baseToken)
            external
            returns (address);
        function controller() external view returns (address);
        function getPool(address _emitter, address _baseToken)
            external
            view
            returns (address);
        function poolType() external view returns (bytes4);
        function poolAddress(address _emitter, address _baseToken)
            external
            view
            returns (address _pool);
    }
    //SPDX-License-Identifier: GPL-3.0
    // This contract referenced Sushi's MasterChef.sol logic
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    import "@openzeppelin/contracts/math/Math.sol";
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/introspection/ERC165Checker.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "../../../core/emission/interfaces/ITokenEmitter.sol";
    import "../../../core/emission/interfaces/IMiningPool.sol";
    import "../../../core/emission/interfaces/IMiningPoolFactory.sol";
    import "../../../core/emission/libraries/PoolType.sol";
    import "../../../core/governance/Governed.sol";
    import "../../../core/dividend/interfaces/IDividendPool.sol";
    import "../../../utils/IERC20Mintable.sol";
    import "../../../utils/Utils.sol";
    import "../../../utils/ERC20Recoverer.sol";
    contract TokenEmitter is
        Governed,
        ReentrancyGuard,
        ITokenEmitter,
        Initializable,
        ERC20Recoverer
    {
        using ERC165Checker for address;
        using SafeMath for uint256;
        using Utils for bytes4[];
        uint256 public constant override DENOMINATOR = 10000;
        uint256 public constant override EMISSION_PERIOD = 1 weeks;
        uint256 private _INITIAL_EMISSION;
        uint256 private _FOUNDER_SHARE_DENOMINATOR;
        address private _token;
        uint256 private _minEmissionRatePerWeek = 60; // 0.006 per week ~= 36% yearly inflation
        uint256 private _emissionCutRate = 3000; // 30%
        uint256 private _emission;
        address private _initialContributorPool;
        address private _initialContributorShare;
        address private _treasury;
        address private _protocolPool;
        uint256 private _startDelay;
        mapping(bytes4 => address) private _factories;
        mapping(address => bytes4) private _poolTypes;
        EmissionWeight private _emissionWeight;
        uint256 private _emissionStarted;
        uint256 private _emissionWeekNum;
        uint256 private _projId;
        function initialize(EmitterConfig memory params) public initializer {
            require(params.treasury != address(0), "Should not be zero");
            Governed.initialize(msg.sender);
            // set params
            _projId = params.projId;
            _INITIAL_EMISSION = params.initialEmission;
            _emission = params.initialEmission;
            _minEmissionRatePerWeek = params.minEmissionRatePerWeek;
            _emissionCutRate = params.emissionCutRate;
            _protocolPool = params.protocolPool;
            _startDelay = params.startDelay;
            // set contract addresses
            _token = params.token;
            setTreasury(params.treasury);
            require(params.founderShareRate < DENOMINATOR);
            _FOUNDER_SHARE_DENOMINATOR = params.founderShareRate != 0
                ? DENOMINATOR / params.founderShareRate
                : 0;
            ERC20Recoverer.initialize(params.gov, new address[](0));
            setFactory(params.erc20BurnMiningFactory);
            setFactory(params.erc20StakeMiningFactory);
            setFactory(params.erc721StakeMiningFactory);
            setFactory(params.erc1155StakeMiningFactory);
            setFactory(params.erc1155BurnMiningFactory);
            setFactory(params.initialContributorShareFactory);
            address initialContributorPool_ =
                newPool(PoolType.InitialContributorShare, params.contributionBoard);
            _initialContributorPool = initialContributorPool_;
            _initialContributorShare = params.contributionBoard;
            Governed.setGovernance(params.gov);
        }
        /**
         * StakeMiningV1:
         */
        function newPool(bytes4 poolType, address token_) public returns (address) {
            address factory = _factories[poolType];
            require(factory != address(0), "Factory not exists");
            address _pool =
                IMiningPoolFactory(factory).getPool(address(this), token_);
            if (_pool == address(0)) {
                _pool = IMiningPoolFactory(factory).newPool(address(this), token_);
            }
            require(
                _pool.supportsInterface(poolType),
                "Does not have the given pool type"
            );
            require(
                _pool.supportsInterface(IMiningPool(0).allocate.selector),
                "Cannot allocate reward"
            );
            require(_poolTypes[_pool] == bytes4(0), "Pool already exists");
            _poolTypes[_pool] = poolType;
            emit NewMiningPool(poolType, token_, _pool);
            return _pool;
        }
        function setEmission(MiningConfig memory config) public governed {
            require(config.treasuryWeight < 1e4, "prevent overflow");
            require(config.callerWeight < 1e4, "prevent overflow");
            // starting the summation with treasury and caller weights
            uint256 _sum = config.treasuryWeight + config.callerWeight;
            // prepare list to store
            address[] memory _pools = new address[](config.pools.length);
            uint256[] memory _weights = new uint256[](config.pools.length);
            // deploy pool if not the pool exists and do the weight summation
            // udpate the pool & weight arr on memory
            for (uint256 i = 0; i < config.pools.length; i++) {
                address _pool =
                    _getOrDeployPool(
                        config.pools[i].poolType,
                        config.pools[i].baseToken
                    );
                require(
                    _poolTypes[_pool] != bytes4(0),
                    "Not a deployed mining pool"
                );
                require(config.pools[i].weight < 1e4, "prevent overflow");
                _weights[i] = config.pools[i].weight;
                _pools[i] = _pool;
                _sum += config.pools[i].weight; // doesn't overflow
            }
            // compute the founder share
            uint256 _dev =
                _FOUNDER_SHARE_DENOMINATOR != 0
                    ? _sum / _FOUNDER_SHARE_DENOMINATOR
                    : 0; // doesn't overflow;
            _sum += _dev;
            // compute the protocol share
            uint256 _protocol = _protocolPool == address(0) ? 0 : _sum / 33;
            _sum += _protocol;
            // store the updated emission weight
            _emissionWeight = EmissionWeight(
                _pools,
                _weights,
                config.treasuryWeight,
                config.callerWeight,
                _protocol,
                _dev,
                _sum
            );
            emit EmissionWeightUpdated(_pools.length);
        }
        function setFactory(address factory) public governed {
            bytes4[] memory interfaces = new bytes4[](2);
            interfaces[0] = IMiningPoolFactory(0).newPool.selector;
            interfaces[1] = IMiningPoolFactory(0).poolType.selector;
            require(
                factory.supportsAllInterfaces(interfaces),
                "Not a valid factory"
            );
            bytes4 _sig = IMiningPoolFactory(factory).poolType();
            require(_factories[_sig] == address(0), "Factory already exists.");
            _factories[_sig] = factory;
        }
        function setTreasury(address treasury_) public governed {
            _treasury = treasury_;
        }
        function start() public override governed {
            require(_emissionStarted == 0, "Already started");
            _emissionStarted = block.timestamp.add(_startDelay).sub(1 weeks);
            emit Start();
        }
        function setEmissionCutRate(uint256 rate) public governed {
            require(
                1000 <= rate && rate <= 9000,
                "Emission cut should be greater than 10% and less than 90%"
            );
            _emissionCutRate = rate;
            emit EmissionCutRateUpdated(rate);
        }
        function setMinimumRate(uint256 rate) public governed {
            require(
                rate <= 134,
                "Protect from the superinflationary(99.8% per year) situation"
            );
            _minEmissionRatePerWeek = rate;
            emit EmissionRateUpdated(rate);
        }
        function distribute() public override nonReentrant {
            // current week from the mining start;
            uint256 weekNum =
                block.timestamp.sub(_emissionStarted).div(EMISSION_PERIOD);
            // The first token token drop will be started a week after the "start" func called.
            require(
                weekNum > _emissionWeekNum,
                "Already minted or not started yet."
            );
            // update emission week num
            _emissionWeekNum = weekNum;
            // allocate to mining pools
            uint256 weightSum = _emissionWeight.sum;
            uint256 prevSupply = IERC20(_token).totalSupply();
            for (uint256 i = 0; i < _emissionWeight.pools.length; i++) {
                require(i < _emissionWeight.pools.length, "out of index");
                uint256 weighted =
                    _emissionWeight.weights[i].mul(_emission).div(weightSum);
                _mintAndNotifyAllocation(_emissionWeight.pools[i], weighted);
            }
            // Caller
            IERC20Mintable(_token).mint(
                msg.sender,
                _emissionWeight.caller.mul(_emission).div(weightSum)
            );
            if (_treasury != address(0)) {
                // Protocol fund(protocol treasury)
                IERC20Mintable(_token).mint(
                    _treasury,
                    _emissionWeight.treasury.mul(_emission).div(weightSum)
                );
            }
            // Protocol
            if (_protocolPool != address(0)) {
                IERC20Mintable(_token).mint(
                    _protocolPool,
                    _emissionWeight.protocol.mul(_emission).div(weightSum)
                );
                // balance diff automatically distributed. no approval needed
                IDividendPool(_protocolPool).distribute(_token, 0);
            }
            if (_initialContributorPool != address(0)) {
                // Founder
                _mintAndNotifyAllocation(
                    _initialContributorPool,
                    _emission.sub(IERC20(_token).totalSupply().sub(prevSupply))
                );
            }
            emit TokenEmission(_emission);
            _updateEmission();
        }
        function getNumberOfPools() public view returns (uint256) {
            return _emissionWeight.pools.length;
        }
        function getPoolWeight(uint256 poolIndex) public view returns (uint256) {
            return _emissionWeight.weights[poolIndex];
        }
        function token() public view override returns (address) {
            return _token;
        }
        function minEmissionRatePerWeek() public view override returns (uint256) {
            return _minEmissionRatePerWeek;
        }
        function emissionCutRate() public view override returns (uint256) {
            return _emissionCutRate;
        }
        function emission() public view override returns (uint256) {
            return _emission;
        }
        function initialContributorPool() public view override returns (address) {
            return _initialContributorPool;
        }
        function initialContributorShare() public view override returns (address) {
            return _initialContributorShare;
        }
        function treasury() public view override returns (address) {
            return _treasury;
        }
        function protocolPool() public view override returns (address) {
            return _protocolPool;
        }
        function pools(uint256 index) public view override returns (address) {
            return _emissionWeight.pools[index];
        }
        function emissionWeight()
            public
            view
            override
            returns (EmissionWeight memory)
        {
            return _emissionWeight;
        }
        function emissionStarted() public view override returns (uint256) {
            return _emissionStarted;
        }
        function emissionWeekNum() public view override returns (uint256) {
            return _emissionWeekNum;
        }
        function projId() public view override returns (uint256) {
            return _projId;
        }
        function poolTypes(address pool) public view override returns (bytes4) {
            return _poolTypes[pool];
        }
        function factories(bytes4 poolType) public view override returns (address) {
            return _factories[poolType];
        }
        function INITIAL_EMISSION() public view override returns (uint256) {
            return _INITIAL_EMISSION;
        }
        function FOUNDER_SHARE_DENOMINATOR()
            public
            view
            override
            returns (uint256)
        {
            return _FOUNDER_SHARE_DENOMINATOR;
        }
        function _mintAndNotifyAllocation(address miningPool, uint256 amount)
            private
        {
            IERC20Mintable(_token).mint(address(miningPool), amount);
            try IMiningPool(miningPool).allocate(amount) {
                // success
            } catch {
                // pool does not handled the emission
            }
        }
        function _updateEmission() private returns (uint256) {
            // Minimum emission 0.05% per week will make 2.63% of inflation per year
            uint256 minEmission =
                IERC20(_token).totalSupply().mul(_minEmissionRatePerWeek).div(
                    DENOMINATOR
                );
            // Emission will be continuously halved until it reaches to its minimum emission. It will be about 10 weeks.
            uint256 cutEmission =
                _emission.mul(DENOMINATOR.sub(_emissionCutRate)).div(DENOMINATOR);
            _emission = Math.max(cutEmission, minEmission);
            return _emission;
        }
        function _getOrDeployPool(bytes4 poolType, address baseToken)
            internal
            returns (address _pool)
        {
            address _factory = _factories[poolType];
            require(_factory != address(0), "Factory not exists");
            // get predicted pool address
            _pool = IMiningPoolFactory(_factory).poolAddress(
                address(this),
                baseToken
            );
            if (_poolTypes[_pool] == poolType) {
                // pool is registered successfully
                return _pool;
            } else {
                // try to deploy new pool and register
                return newPool(poolType, baseToken);
            }
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "../../utils/Utils.sol";
    contract Governed {
        using Utils for address[];
        bool private _initialized;
        address internal _gov;
        uint256 private _anarchizedAt = 0;
        uint256 private _forceAnarchizeAt = 0;
        event NewGovernance(
            address indexed _prevGovernance,
            address indexed _newGovernance
        );
        event Anarchized();
        constructor() {}
        modifier governed {
            require(msg.sender == _gov, "Not authorized");
            _;
        }
        function initialize(address gov_) public {
            require(!_initialized, "Initialized");
            _initialized = true;
            _gov = gov_;
        }
        function setGovernance(address gov_) public governed {
            require(gov_ != address(0), "Use anarchize() instead.");
            _setGovernance(gov_);
        }
        function setAnarchyPoint(uint256 timestamp) public governed {
            require(_forceAnarchizeAt == 0, "Cannot update.");
            require(
                timestamp >= block.timestamp,
                "Timepoint should be in the future."
            );
            _forceAnarchizeAt = timestamp;
        }
        function anarchize() public governed {
            _anarchize();
        }
        function forceAnarchize() public {
            require(_forceAnarchizeAt != 0, "Cannot disband the gov");
            require(block.timestamp >= _forceAnarchizeAt, "Cannot disband the gov");
            _anarchize();
        }
        function gov() public view returns (address) {
            return _gov;
        }
        function anarchizedAt() public view returns (uint256) {
            return _anarchizedAt;
        }
        function forceAnarchizeAt() public view returns (uint256) {
            return _forceAnarchizeAt;
        }
        function _anarchize() internal {
            _setGovernance(address(0));
            _anarchizedAt = block.timestamp;
            emit Anarchized();
        }
        function _setGovernance(address gov_) internal {
            emit NewGovernance(_gov, gov_);
            _gov = gov_;
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    interface IDividendPool {
        function distribute(address token, uint256 amount) external;
        function veVISION() external view returns (address);
        function veLocker() external view returns (address);
        function genesis() external view returns (uint256);
        function getEpoch(uint256 timestamp) external view returns (uint256);
        function getCurrentEpoch() external view returns (uint256);
        function distributedTokens() external view returns (address[] memory);
        function totalDistributed(address token) external view returns (uint256);
        function distributionBalance(address token) external view returns (uint256);
        function distributionOfWeek(address token, uint256 epochNum)
            external
            view
            returns (uint256);
        function claimStartWeek(address token, uint256 veLockId)
            external
            view
            returns (uint256);
        function claimable(address token) external view returns (uint256);
        function featuredRewards() external view returns (address[] memory);
    }
    //SPDX-License-Identifier: GPL-3.0
    // This contract referenced Sushi's MasterChef.sol logic
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    import "../../core/emission/libraries/TokenEmitter.sol";
    import "../../core/tokens/VISION.sol";
    contract VisionEmitter is TokenEmitter {}
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "../../core/governance/Governed.sol";
    contract VISION is ERC20, Governed, Initializable {
        address private _minter;
        string private _name;
        string private _symbol;
        constructor() ERC20("", "") {
            // this constructor will not be called since it'll be cloned by proxy pattern.
            // initalize() will be called instead.
        }
        modifier onlyMinter {
            require(msg.sender == _minter, "Not a minter");
            _;
        }
        function initialize(
            string memory name_,
            string memory symbol_,
            address minter_,
            address gov_
        ) public initializer {
            _name = name_;
            _symbol = symbol_;
            _minter = minter_;
            Governed.initialize(gov_);
        }
        function mint(address to, uint256 amount) public onlyMinter {
            _mint(to, amount);
        }
        function setMinter(address minter_) public governed {
            _setMinter(minter_);
        }
        function _setMinter(address minter_) internal {
            _minter = minter_;
        }
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        function decimals() public pure override returns (uint8) {
            return 18;
        }
        function minter() public view returns (address) {
            return _minter;
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    interface IMiningPool {
        function allocate(uint256 amount) external;
        function setMiningPeriod(uint256 period) external;
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/introspection/ERC165Checker.sol";
    import "../../../core/emission/pools/ERC1155StakeMiningV1.sol";
    import "../../../core/emission/libraries/MiningPoolFactory.sol";
    contract ERC1155StakeMiningV1Factory is MiningPoolFactory {
        using ERC165Checker for address;
        /*
         *     // copied from openzeppelin ERC1155 spec impl
         *     bytes4(keccak256('balanceOf(address,uint256)')) == 0x00fdd58e
         *     bytes4(keccak256('balanceOfBatch(address[],uint256[])')) == 0x4e1273f4
         *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
         *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256,uint256,bytes)')) == 0xf242432a
         *     bytes4(keccak256('safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)')) == 0x2eb2c2d6
         *
         *     => 0x00fdd58e ^ 0x4e1273f4 ^ 0xa22cb465 ^
         *        0xe985e9c5 ^ 0xf242432a ^ 0x2eb2c2d6 == 0xd9b67a26
         */
        bytes4 private constant _INTERFACE_ID_ERC1155 = 0xd9b67a26;
        bytes4 public override poolType =
            ERC1155StakeMiningV1(0).erc1155StakeMiningV1.selector;
        constructor() MiningPoolFactory() {
            address _controller = address(new ERC1155StakeMiningV1());
            _setController(_controller);
        }
        function newPool(address _emitter, address _stakingToken)
            public
            override
            returns (address _pool)
        {
            require(
                _stakingToken.supportsInterface(_INTERFACE_ID_ERC1155),
                "Not an ERC1155"
            );
            return super.newPool(_emitter, _stakingToken);
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "../../../core/emission/pools/ERC20BurnMiningV1.sol";
    import "../../../core/emission/libraries/MiningPoolFactory.sol";
    contract ERC20BurnMiningV1Factory is MiningPoolFactory {
        bytes4 public override poolType =
            ERC20BurnMiningV1(0).erc20BurnMiningV1.selector;
        constructor() MiningPoolFactory() {
            address _controller = address(new ERC20BurnMiningV1());
            _setController(_controller);
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/introspection/ERC165Checker.sol";
    import "../../../core/emission/pools/ERC721StakeMiningV1.sol";
    import "../../../core/emission/libraries/MiningPoolFactory.sol";
    contract ERC721StakeMiningV1Factory is MiningPoolFactory {
        using ERC165Checker for address;
        /*
         *     // copied from openzeppelin ERC721 spec impl
         *
         *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
         *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
         *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
         *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
         *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
         *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
         *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
         *
         *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
         *        0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
         */
        bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
        bytes4 public override poolType =
            ERC721StakeMiningV1(0).erc721StakeMiningV1.selector;
        constructor() MiningPoolFactory() {
            address _controller = address(new ERC721StakeMiningV1());
            _setController(_controller);
        }
        function newPool(address _emitter, address _stakingToken)
            public
            override
            returns (address _pool)
        {
            require(
                _stakingToken.supportsInterface(_INTERFACE_ID_ERC721),
                "Not an ERC721"
            );
            return super.newPool(_emitter, _stakingToken);
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/introspection/ERC165Checker.sol";
    import "../../../core/emission/libraries/MiningPoolFactory.sol";
    import "../../../core/emission/pools/InitialContributorShare.sol";
    contract InitialContributorShareFactory is MiningPoolFactory {
        using ERC165Checker for address;
        /*
         *     // copied from openzeppelin ERC1155 spec impl
         *     bytes4(keccak256('balanceOf(address,uint256)')) == 0x00fdd58e
         *     bytes4(keccak256('balanceOfBatch(address[],uint256[])')) == 0x4e1273f4
         *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
         *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256,uint256,bytes)')) == 0xf242432a
         *     bytes4(keccak256('safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)')) == 0x2eb2c2d6
         *
         *     => 0x00fdd58e ^ 0x4e1273f4 ^ 0xa22cb465 ^
         *        0xe985e9c5 ^ 0xf242432a ^ 0x2eb2c2d6 == 0xd9b67a26
         */
        bytes4 private constant _INTERFACE_ID_ERC1155 = 0xd9b67a26;
        bytes4 public override poolType =
            InitialContributorShare(0).initialContributorShare.selector;
        constructor() MiningPoolFactory() {
            address _controller = address(new InitialContributorShare());
            _setController(_controller);
        }
        function newPool(address _emitter, address _contributionBoard)
            public
            override
            returns (address _pool)
        {
            require(
                _contributionBoard.supportsInterface(_INTERFACE_ID_ERC1155),
                "Not an ERC1155"
            );
            return super.newPool(_emitter, _contributionBoard);
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "../../../core/emission/pools/ERC20StakeMiningV1.sol";
    import "../../../core/emission/libraries/MiningPoolFactory.sol";
    contract ERC20StakeMiningV1Factory is MiningPoolFactory {
        bytes4 public override poolType =
            ERC20StakeMiningV1(0).erc20StakeMiningV1.selector;
        constructor() MiningPoolFactory() {
            address _controller = address(new ERC20StakeMiningV1());
            _setController(_controller);
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/introspection/ERC165Checker.sol";
    import "../../../core/emission/pools/ERC1155BurnMiningV1.sol";
    import "../../../core/emission/libraries/MiningPoolFactory.sol";
    contract ERC1155BurnMiningV1Factory is MiningPoolFactory {
        using ERC165Checker for address;
        /*
         *     // copied from openzeppelin ERC1155 spec impl
         *     bytes4(keccak256('balanceOf(address,uint256)')) == 0x00fdd58e
         *     bytes4(keccak256('balanceOfBatch(address[],uint256[])')) == 0x4e1273f4
         *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
         *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256,uint256,bytes)')) == 0xf242432a
         *     bytes4(keccak256('safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)')) == 0x2eb2c2d6
         *
         *     => 0x00fdd58e ^ 0x4e1273f4 ^ 0xa22cb465 ^
         *        0xe985e9c5 ^ 0xf242432a ^ 0x2eb2c2d6 == 0xd9b67a26
         */
        bytes4 private constant _INTERFACE_ID_ERC1155 = 0xd9b67a26;
        bytes4 public override poolType =
            ERC1155BurnMiningV1(0).erc1155BurnMiningV1.selector;
        constructor() MiningPoolFactory() {
            address _controller = address(new ERC1155BurnMiningV1());
            _setController(_controller);
        }
        function newPool(address _emitter, address _burningToken)
            public
            override
            returns (address _pool)
        {
            require(
                _burningToken.supportsInterface(_INTERFACE_ID_ERC1155),
                "Not an ERC1155"
            );
            return super.newPool(_emitter, _burningToken);
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    import "@openzeppelin/contracts/proxy/Clones.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
    import "@openzeppelin/contracts/utils/EnumerableSet.sol";
    import "@openzeppelin/contracts/utils/EnumerableMap.sol";
    import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
    import "../../core/tokens/VISION.sol";
    import "../../core/tokens/COMMIT.sol";
    import "../../core/tokens/RIGHT.sol";
    import "../../core/work/StableReserve.sol";
    import "../../core/work/ContributionBoard.sol";
    import "../../core/work/interfaces/IContributionBoard.sol";
    import "../../core/governance/TimelockedGovernance.sol";
    import "../../core/governance/WorkersUnion.sol";
    import "../../core/governance/libraries/VoteCounter.sol";
    import "../../core/governance/libraries/VotingEscrowLock.sol";
    import "../../core/dividend/DividendPool.sol";
    import "../../core/emission/VisionEmitter.sol";
    import "../../core/emission/factories/ERC20BurnMiningV1Factory.sol";
    import "../../core/emission/libraries/PoolType.sol";
    import "../../core/marketplace/Marketplace.sol";
    contract Project is ERC721, ERC20Recoverer {
        using Clones for address;
        using EnumerableSet for EnumerableSet.UintSet;
        using EnumerableMap for EnumerableMap.UintToAddressMap;
        struct DAO {
            address multisig;
            address baseCurrency;
            address timelock;
            address vision;
            address commit;
            address right;
            address stableReserve;
            address contributionBoard;
            address marketplace;
            address dividendPool;
            address voteCounter;
            address workersUnion;
            address visionEmitter;
            address votingEscrow;
        }
        struct CommonContracts {
            address pool2Factory;
            address weth;
            address sablier;
            address erc20StakeMiningV1Factory;
            address erc20BurnMiningV1Factory;
            address erc721StakeMiningV1Factory;
            address erc1155StakeMiningV1Factory;
            address erc1155BurnMiningV1Factory;
            address initialContributorShareFactory;
        }
        struct CloneParams {
            address multisig;
            address treasury;
            address baseCurrency;
            // Project
            string projectName;
            string projectSymbol;
            // tokens
            string visionName;
            string visionSymbol;
            string commitName;
            string commitSymbol;
            string rightName;
            string rightSymbol;
            uint256 emissionStartDelay;
            uint256 minDelay; // timelock
            uint256 voteLaunchDelay;
            uint256 initialEmission;
            uint256 minEmissionRatePerWeek;
            uint256 emissionCutRate;
            uint256 founderShare;
        }
        // Metadata for each project
        mapping(uint256 => uint256) private _growth;
        mapping(uint256 => string) private _nameOf;
        mapping(uint256 => string) private _symbolOf;
        mapping(uint256 => bool) private _immortalized;
        // Common contracts and controller(not upgradeable)
        CommonContracts private _commons;
        DAO private _controller;
        // Launched DAO's contracts
        mapping(uint256 => DAO) private _dao;
        uint256[] private _allDAOs;
        mapping(address => uint256) private _daoAddressBook;
        // Mapping from holder address to their (enumerable) set of owned tokens
        mapping(address => EnumerableSet.UintSet) private _daoProjects; // timelock will be the pointing contract
        EnumerableMap.UintToAddressMap private _belongsTo;
        uint256 private projNum;
        event DAOLaunched(uint256 id);
        event NewProject(uint256 indexed daoId, uint256 id);
        event ProjectMoved(uint256 indexed from, uint256 indexed to);
        constructor(DAO memory controller, CommonContracts memory commons)
            ERC721("WORKHARD DAO", "WORKHARD")
        {
            _setBaseURI("ipfs://");
            _controller = controller;
            _commons = commons;
            uint256 masterDAOId = 0;
            address masterTimelock =
                Clones.predictDeterministicAddress(
                    controller.timelock,
                    bytes32(masterDAOId),
                    address(this)
                );
            createProject(
                masterDAOId,
                "QmTFKqcLx9utcxSDLbfWicLnUDFACbrGjcQ6Yhz13qWDqS"
            );
            ERC20Recoverer.initialize(masterTimelock, new address[](0));
        }
        modifier onlyOwnerOf(uint256 id) {
            require(msg.sender == ownerOf(id), "Not the project owner");
            _;
        }
        /**
         * Creating a project for another forked DAO.
         */
        function createProject(uint256 daoId, string memory uri)
            public
            returns (uint256 id)
        {
            id = projNum;
            projNum++;
            require(_growth[id] < 1, "Already created.");
            require(
                daoId == 0 || _growth[daoId] == 4,
                "Parent project should be a DAO."
            );
            _growth[id] = 1;
            _mint(msg.sender, id);
            _setTokenURI(id, uri);
            address daoAddress = _getGovAddressOfDAO(daoId);
            _daoProjects[daoAddress].add(id);
            _belongsTo.set(id, daoAddress);
            emit NewProject(daoId, id);
            return id;
        }
        function upgradeToDAO(uint256 id, CloneParams memory params)
            public
            onlyOwnerOf(id)
        {
            require(_dao[id].vision == address(0), "Already upgraded.");
            _deploy(id);
            _initialize(id, params);
            _daoAddressBook[_getGovAddressOfDAO(id)] = id;
            // Now it does not belong to any dao. A new dao!
            _daoProjects[_belongsTo.get(id, "owner query for nonexistent token")]
                .remove(id);
            _belongsTo.remove(id);
            _nameOf[id] = params.projectName;
            _symbolOf[id] = params.projectSymbol;
            emit DAOLaunched(id);
            _allDAOs.push(id);
        }
        function launch(
            uint256 id,
            uint256 liquidityMiningRate,
            uint256 commitMiningRate,
            uint256 treasury,
            uint256 caller
        ) public onlyOwnerOf(id) {
            // 1. deploy sushi LP
            DAO storage fork = _dao[id];
            address lp =
                IUniswapV2Factory(_commons.pool2Factory).getPair(
                    fork.vision,
                    _commons.weth
                );
            if (lp == address(0)) {
                IUniswapV2Factory(_commons.pool2Factory).createPair(
                    fork.vision,
                    _commons.weth
                );
                lp = IUniswapV2Factory(_commons.pool2Factory).getPair(
                    fork.vision,
                    _commons.weth
                );
            }
            MiningConfig memory miningConfig;
            miningConfig.pools = new MiningPoolConfig[](2);
            miningConfig.pools[0] = MiningPoolConfig(
                liquidityMiningRate,
                PoolType.ERC20StakeMiningV1,
                lp
            );
            miningConfig.pools[1] = MiningPoolConfig(
                commitMiningRate,
                PoolType.ERC20BurnMiningV1,
                fork.commit
            );
            miningConfig.treasuryWeight = treasury;
            miningConfig.callerWeight = caller;
            _launch(id, miningConfig);
        }
        function immortalize(uint256 id) public onlyOwnerOf(id) {
            _immortalized[id] = true;
        }
        function updateURI(uint256 id, string memory uri) public onlyOwnerOf(id) {
            require(!_immortalized[id], "This project is immortalized.");
            _setTokenURI(id, uri);
        }
        function changeMultisig(uint256 id, address newMultisig) public {
            require(
                msg.sender == _dao[id].multisig,
                "Only the prev owner can change this value."
            );
            _dao[id].multisig = newMultisig;
        }
        function growth(uint256 id) public view returns (uint256) {
            return _growth[id];
        }
        function nameOf(uint256 id) public view returns (string memory) {
            return _nameOf[id];
        }
        function symbolOf(uint256 id) public view returns (string memory) {
            return _symbolOf[id];
        }
        function immortalized(uint256 id) public view returns (bool) {
            return _immortalized[id];
        }
        function daoOf(uint256 id) public view returns (uint256 daoId) {
            address daoAddress =
                _belongsTo.get(id, "owner query for nonexistent token");
            return _getDAOIdOfGov(daoAddress);
        }
        function projectsOf(uint256 daoId) public view returns (uint256 len) {
            return _daoProjects[_getGovAddressOfDAO(daoId)].length();
        }
        function projectsOfDAOByIndex(uint256 daoId, uint256 index)
            public
            view
            returns (uint256 id)
        {
            return _daoProjects[_getGovAddressOfDAO(daoId)].at(index);
        }
        function getMasterDAO() public view returns (DAO memory) {
            return _dao[0];
        }
        function getCommons() public view returns (CommonContracts memory) {
            return _commons;
        }
        function getDAO(uint256 id) public view returns (DAO memory) {
            return _dao[id];
        }
        function getAllDAOs() public view returns (uint256[] memory) {
            return _allDAOs;
        }
        function getController() public view returns (DAO memory) {
            return _controller;
        }
        function _deploy(uint256 id) internal {
            require(msg.sender == ownerOf(id));
            require(_growth[id] < 2, "Already deployed.");
            require(_growth[id] > 0, "Project does not exists.");
            _growth[id] = 2;
            DAO storage fork = _dao[id];
            bytes32 salt = bytes32(id);
            fork.timelock = _controller.timelock.cloneDeterministic(salt);
            fork.vision = _controller.vision.cloneDeterministic(salt);
            fork.commit = _controller.commit.cloneDeterministic(salt);
            fork.right = _controller.right.cloneDeterministic(salt);
            fork.stableReserve = _controller.stableReserve.cloneDeterministic(salt);
            fork.dividendPool = _controller.dividendPool.cloneDeterministic(salt);
            fork.voteCounter = _controller.voteCounter.cloneDeterministic(salt);
            fork.contributionBoard = _controller
                .contributionBoard
                .cloneDeterministic(salt);
            fork.marketplace = _controller.marketplace.cloneDeterministic(salt);
            fork.workersUnion = _controller.workersUnion.cloneDeterministic(salt);
            fork.visionEmitter = _controller.visionEmitter.cloneDeterministic(salt);
            fork.votingEscrow = _controller.votingEscrow.cloneDeterministic(salt);
        }
        function _initialize(uint256 id, CloneParams memory params) internal {
            require(msg.sender == ownerOf(id));
            require(_growth[id] < 3, "Already initialized.");
            require(_growth[id] > 1, "Contracts are not deployed.");
            _growth[id] = 3;
            DAO storage fork = _dao[id];
            fork.multisig = params.multisig;
            fork.baseCurrency = params.baseCurrency;
            DAO storage parentDAO =
                _dao[
                    _getDAOIdOfGov(
                        _belongsTo.get(id, "owner query for nonexistent token")
                    )
                ];
            require(
                params.founderShare >=
                    ContributionBoard(parentDAO.contributionBoard).minimumShare(id),
                "founder share should be greater than the committed minimum share"
            );
            TimelockedGovernance(payable(fork.timelock)).initialize(
                params.minDelay,
                fork.multisig,
                fork.workersUnion
            );
            VISION(fork.vision).initialize(
                params.visionName,
                params.visionSymbol,
                fork.visionEmitter,
                fork.timelock
            );
            COMMIT(fork.commit).initialize(
                params.commitName,
                params.commitSymbol,
                fork.stableReserve
            );
            RIGHT(fork.right).initialize(
                params.rightName,
                params.rightSymbol,
                fork.votingEscrow
            );
            address[] memory stableReserveMinters = new address[](1);
            stableReserveMinters[0] = fork.contributionBoard;
            StableReserve(fork.stableReserve).initialize(
                fork.timelock,
                fork.commit,
                fork.baseCurrency,
                stableReserveMinters
            );
            ContributionBoard(fork.contributionBoard).initialize(
                address(this),
                fork.timelock,
                fork.dividendPool,
                fork.stableReserve,
                fork.commit,
                _commons.sablier
            );
            Marketplace(fork.marketplace).initialize(
                fork.timelock,
                fork.commit,
                fork.dividendPool
            );
            address[] memory _rewardTokens = new address[](2);
            _rewardTokens[0] = fork.commit;
            _rewardTokens[1] = fork.baseCurrency;
            DividendPool(fork.dividendPool).initialize(
                fork.timelock,
                fork.right,
                _rewardTokens
            );
            VoteCounter(fork.voteCounter).initialize(fork.right);
            WorkersUnion(payable(fork.workersUnion)).initialize(
                fork.voteCounter,
                fork.timelock,
                params.voteLaunchDelay
            );
            VisionEmitter(fork.visionEmitter).initialize(
                EmitterConfig(
                    id,
                    params.initialEmission,
                    params.minEmissionRatePerWeek,
                    params.emissionCutRate,
                    params.founderShare,
                    params.emissionStartDelay,
                    params.treasury,
                    address(this), // gov => will be transfered to timelock
                    fork.vision,
                    id != 0 ? parentDAO.dividendPool : address(0),
                    parentDAO.contributionBoard,
                    _commons.erc20BurnMiningV1Factory,
                    _commons.erc20StakeMiningV1Factory,
                    _commons.erc721StakeMiningV1Factory,
                    _commons.erc1155StakeMiningV1Factory,
                    _commons.erc1155BurnMiningV1Factory,
                    _commons.initialContributorShareFactory
                )
            );
            VotingEscrowLock(fork.votingEscrow).initialize(
                string(abi.encodePacked(params.projectName, " Voting Escrow Lock")),
                string(abi.encodePacked(params.projectSymbol, "-VE-LOCK")),
                fork.vision,
                fork.right,
                fork.timelock
            );
        }
        function _launch(uint256 id, MiningConfig memory config) internal {
            require(_growth[id] < 4, "Already launched.");
            require(_growth[id] > 2, "Not initialized.");
            _growth[id] = 4;
            DAO storage fork = _dao[id];
            // 1. set emission
            VisionEmitter(fork.visionEmitter).setEmission(config);
            // 2. start emission
            VisionEmitter(fork.visionEmitter).start();
            // 3. transfer governance
            VisionEmitter(fork.visionEmitter).setGovernance(fork.timelock);
            // 4. transfer ownership to timelock
            _transfer(msg.sender, fork.timelock, id);
            // 5. No more initial contribution record
            address initialContributorPool =
                VisionEmitter(fork.visionEmitter).initialContributorPool();
            IContributionBoard(IMiningPool(initialContributorPool).baseToken())
                .finalize(id);
        }
        /**
         * @notice it returns timelock governance contract's address.
         */
        function _getGovAddressOfDAO(uint256 id) private view returns (address) {
            return
                Clones.predictDeterministicAddress(
                    _controller.timelock,
                    bytes32(id),
                    address(this)
                );
        }
        /**
         * @notice it can return only launched DAO's token id.
         */
        function _getDAOIdOfGov(address daoAddress)
            private
            view
            returns (uint256 daoId)
        {
            return _daoAddressBook[daoAddress];
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    import "../../core/governance/libraries/VotingEscrowToken.sol";
    contract RIGHT is VotingEscrowToken {
        function decimals() public pure override returns (uint8) {
            return 18;
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol";
    import "@openzeppelin/contracts/math/Math.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "../../../core/governance/interfaces/IVotingEscrowToken.sol";
    import "../../../utils/Int128.sol";
    /**
     * @dev Voting Escrow Token is the solidity implementation of veCRV
     *      Its original code https://github.com/curvefi/curve-dao-contracts/blob/master/contracts/VotingEscrow.vy
     */
    contract VotingEscrowToken is ERC20, IVotingEscrowToken, Initializable {
        using SafeMath for uint256;
        using Int128 for uint256;
        uint256 public constant MAXTIME = 4 * (365 days);
        uint256 public constant MULTIPLIER = 1e18;
        address private _veLocker;
        mapping(uint256 => int128) private _slopeChanges;
        Point[] private _pointHistory;
        mapping(uint256 => Point[]) private _lockPointHistory;
        string private _name;
        string private _symbol;
        modifier onlyVELock() {
            require(
                msg.sender == _veLocker,
                "Only ve lock contract can call this."
            );
            _;
        }
        constructor() ERC20("", "") {
            // this constructor will not be called since it'll be cloned by proxy pattern.
            // initalize() will be called instead.
        }
        function initialize(
            string memory name_,
            string memory symbol_,
            address veLocker_
        ) public initializer {
            _name = name_;
            _symbol = symbol_;
            _veLocker = veLocker_;
        }
        function checkpoint(uint256 maxRecord) external override {
            // Point memory lastPoint = _recordPointHistory();
            // pointHistory[epoch] = lastPoint;
            _recordPointHistory(maxRecord);
        }
        function checkpoint(
            uint256 veLockId,
            Lock calldata oldLock,
            Lock calldata newLock
        ) external onlyVELock {
            // Record history
            _recordPointHistory(0);
            // Compute points
            (Point memory oldLockPoint, Point memory newLockPoint) =
                _computePointsFromLocks(oldLock, newLock);
            _updateLastPoint(oldLockPoint, newLockPoint);
            _recordLockPointHistory(
                veLockId,
                oldLock,
                newLock,
                oldLockPoint,
                newLockPoint
            );
        }
        // View functions
        function veLocker() public view virtual override returns (address) {
            return _veLocker;
        }
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        function decimals() public pure virtual override returns (uint8) {
            return 18;
        }
        function balanceOf(address account)
            public
            view
            override(IERC20, ERC20)
            returns (uint256)
        {
            uint256 numOfLocks = IERC721Enumerable(_veLocker).balanceOf(account);
            uint256 balance = 0;
            for (uint256 i = 0; i < numOfLocks; i++) {
                uint256 veLockId =
                    IERC721Enumerable(_veLocker).tokenOfOwnerByIndex(account, i);
                balance = balance.add(balanceOfLock(veLockId));
            }
            return balance;
        }
        function balanceOfAt(address account, uint256 timestamp)
            public
            view
            override
            returns (uint256)
        {
            uint256 numOfLocks = IERC721Enumerable(_veLocker).balanceOf(account);
            uint256 balance = 0;
            for (uint256 i = 0; i < numOfLocks; i++) {
                uint256 veLockId =
                    IERC721Enumerable(_veLocker).tokenOfOwnerByIndex(account, i);
                balance = balance.add(balanceOfLockAt(veLockId, timestamp));
            }
            return balance;
        }
        function balanceOfLock(uint256 veLockId)
            public
            view
            override
            returns (uint256)
        {
            return balanceOfLockAt(veLockId, block.timestamp);
        }
        function balanceOfLockAt(uint256 veLockId, uint256 timestamp)
            public
            view
            override
            returns (uint256)
        {
            (bool success, Point memory point) =
                _searchClosestPoint(_lockPointHistory[veLockId], timestamp);
            if (success) {
                int128 bal =
                    point.bias -
                        point.slope *
                        (timestamp.toInt128() - point.timestamp.toInt128());
                return bal > 0 ? uint256(bal) : 0;
            } else {
                return 0;
            }
        }
        function totalSupply()
            public
            view
            override(IERC20, ERC20)
            returns (uint256)
        {
            return totalSupplyAt(block.timestamp);
        }
        function totalSupplyAt(uint256 timestamp)
            public
            view
            override
            returns (uint256)
        {
            (bool success, Point memory point) =
                _searchClosestPoint(_pointHistory, timestamp);
            if (success) {
                return _computeSupplyFrom(point, timestamp);
            } else {
                return 0;
            }
        }
        function slopeChanges(uint256 timestamp)
            public
            view
            override
            returns (int128)
        {
            return _slopeChanges[timestamp];
        }
        function pointHistory(uint256 index)
            public
            view
            override
            returns (Point memory)
        {
            return _pointHistory[index];
        }
        function lockPointHistory(uint256 index)
            public
            view
            override
            returns (Point[] memory)
        {
            return _lockPointHistory[index];
        }
        // checkpoint() should be called if it emits out of gas error.
        function _computeSupplyFrom(Point memory point, uint256 timestamp)
            internal
            view
            returns (uint256)
        {
            require(point.timestamp <= timestamp, "scan only to the rightward");
            Point memory _point = point;
            uint256 x = (point.timestamp / 1 weeks) * 1 weeks;
            // find the closest point
            do {
                x = Math.min(x + 1 weeks, timestamp);
                uint256 delta = x - point.timestamp; // always greater than 0
                _point.timestamp = x;
                _point.bias -= (_point.slope) * int128(delta);
                _point.slope += _slopeChanges[x];
                _point.bias = _point.bias > 0 ? _point.bias : 0;
                _point.slope = _point.slope > 0 ? _point.slope : 0;
            } while (timestamp != x);
            int128 y = _point.bias - _point.slope * (timestamp - x).toInt128();
            return y > 0 ? uint256(y) : 0;
        }
        function _computePointsFromLocks(Lock memory oldLock, Lock memory newLock)
            internal
            view
            returns (Point memory oldPoint, Point memory newPoint)
        {
            if (oldLock.end > block.timestamp && oldLock.amount > 0) {
                oldPoint.slope = (oldLock.amount / MAXTIME).toInt128();
                oldPoint.bias =
                    oldPoint.slope *
                    int128(oldLock.end - block.timestamp);
            }
            if (newLock.end > block.timestamp && newLock.amount > 0) {
                newPoint.slope = (newLock.amount / MAXTIME).toInt128();
                newPoint.bias =
                    newPoint.slope *
                    int128((newLock.end - block.timestamp));
            }
        }
        function _recordPointHistory(uint256 maxRecord) internal {
            // last_point: Point = Point({bias: 0, slope: 0, ts: block.timestamp})
            Point memory _point;
            // Get the latest right most point
            if (_pointHistory.length > 0) {
                _point = _pointHistory[_pointHistory.length - 1];
            } else {
                _point = Point({bias: 0, slope: 0, timestamp: block.timestamp});
            }
            // fill history
            uint256 timestamp = block.timestamp;
            uint256 x = (_point.timestamp / 1 weeks) * 1 weeks;
            // record intermediate histories
            uint256 i = 0;
            do {
                x = Math.min(x + 1 weeks, timestamp);
                uint256 delta = Math.min(timestamp - x, 1 weeks);
                _point.timestamp = x;
                _point.bias -= (_point.slope) * int128(delta);
                _point.slope += _slopeChanges[x];
                _point.bias = _point.bias > 0 ? _point.bias : 0;
                _point.slope = _point.slope > 0 ? _point.slope : 0;
                _pointHistory.push(_point);
                i++;
            } while (timestamp != x && i != maxRecord);
        }
        function _recordLockPointHistory(
            uint256 veLockId,
            Lock memory oldLock,
            Lock memory newLock,
            Point memory oldPoint,
            Point memory newPoint
        ) internal {
            require(
                (oldLock.end / 1 weeks) * 1 weeks == oldLock.end,
                "should be exact epoch timestamp"
            );
            require(
                (newLock.end / 1 weeks) * 1 weeks == newLock.end,
                "should be exact epoch timestamp"
            );
            int128 oldSlope = _slopeChanges[oldLock.end];
            int128 newSlope;
            if (newLock.end != 0) {
                if (newLock.end == oldLock.end) {
                    newSlope = oldSlope;
                } else {
                    newSlope = _slopeChanges[newLock.end];
                }
            }
            if (oldLock.end > block.timestamp) {
                oldSlope += oldPoint.slope;
                if (newLock.end == oldLock.end) {
                    oldSlope -= newPoint.slope;
                }
                _slopeChanges[oldLock.end] = oldSlope;
            }
            if (newLock.end > block.timestamp) {
                if (newLock.end > oldLock.end) {
                    newSlope -= newPoint.slope;
                    _slopeChanges[newLock.end] = newSlope;
                }
            }
            newPoint.timestamp = block.timestamp;
            _lockPointHistory[veLockId].push(newPoint);
        }
        function _updateLastPoint(
            Point memory oldLockPoint,
            Point memory newLockPoint
        ) internal {
            if (_pointHistory.length == 0) {
                _pointHistory.push(
                    Point({bias: 0, slope: 0, timestamp: block.timestamp})
                );
            }
            Point memory newLastPoint =
                _computeTheLatestSupplyGraphPoint(
                    oldLockPoint,
                    newLockPoint,
                    _pointHistory[_pointHistory.length - 1]
                );
            _pointHistory[_pointHistory.length - 1] = newLastPoint;
        }
        function _computeTheLatestSupplyGraphPoint(
            Point memory oldLockPoint,
            Point memory newLockPoint,
            Point memory lastPoint
        ) internal pure returns (Point memory newLastPoint) {
            newLastPoint = lastPoint;
            newLastPoint.slope += (newLockPoint.slope - oldLockPoint.slope);
            newLastPoint.bias += (newLockPoint.bias - oldLockPoint.bias);
            if (newLastPoint.slope < 0) {
                newLastPoint.slope = 0;
            }
            if (newLastPoint.bias < 0) {
                newLastPoint.bias = 0;
            }
        }
        function _searchClosestPoint(Point[] storage history, uint256 timestamp)
            internal
            view
            returns (bool success, Point memory point)
        {
            require(timestamp <= block.timestamp, "Only past blocks");
            if (history.length == 0) {
                return (false, point);
            } else if (timestamp < history[0].timestamp) {
                // block num is before the first lock
                return (false, point);
            } else if (timestamp == block.timestamp) {
                return (true, history[history.length - 1]);
            }
            // binary search
            uint256 min = 0;
            uint256 max = history.length - 1;
            uint256 mid;
            for (uint256 i = 0; i < 128; i++) {
                if (min >= max) {
                    break;
                }
                mid = (min + max + 1) / 2;
                if (history[mid].timestamp <= timestamp) {
                    min = mid;
                } else {
                    max = mid - 1;
                }
            }
            return (true, history[min]);
        }
        function _beforeTokenTransfer(
            address,
            address,
            uint256
        ) internal pure override {
            revert("Non-transferrable. You can only transfer locks.");
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    struct Point {
        int128 bias;
        int128 slope;
        uint256 timestamp;
    }
    struct Lock {
        uint256 amount;
        uint256 start;
        uint256 end;
    }
    interface IVotingEscrowToken is IERC20 {
        function veLocker() external view returns (address);
        function checkpoint(uint256 maxRecord) external;
        function totalSupplyAt(uint256 timestamp) external view returns (uint256);
        function balanceOfAt(address account, uint256 timestamp)
            external
            view
            returns (uint256);
        function balanceOfLock(uint256 veLockId) external view returns (uint256);
        function balanceOfLockAt(uint256 veLockId, uint256 timestamp)
            external
            view
            returns (uint256);
        function slopeChanges(uint256 timestamp) external view returns (int128);
        function pointHistory(uint256 index) external view returns (Point memory);
        function lockPointHistory(uint256 index)
            external
            view
            returns (Point[] memory);
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    import "@openzeppelin/contracts/utils/Address.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "../../core/work/interfaces/IStableReserve.sol";
    import "../../core/work/interfaces/IGrantReceiver.sol";
    import "../../core/tokens/COMMIT.sol";
    import "../../core/governance/Governed.sol";
    import "../../utils/ERC20Recoverer.sol";
    /**
     * @notice StableReserve is the $COMMIT minter. It allows ContributionBoard to mint $COMMIT token.
     */
    contract StableReserve is ERC20Recoverer, Governed, IStableReserve {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        using Address for address;
        address private _commitToken;
        address private _baseCurrency;
        uint256 private _priceOfCommit;
        mapping(address => bool) private _allowed; // allowed crypto job board contracts
        address private _deployer;
        function initialize(
            address gov_,
            address commitToken_,
            address baseCurrency_,
            address[] memory admins
        ) public initializer {
            _priceOfCommit = 20000; // denominator = 10000, ~= $2
            _commitToken = commitToken_;
            _baseCurrency = baseCurrency_;
            address[] memory disable = new address[](2);
            disable[0] = commitToken_;
            disable[1] = baseCurrency_;
            ERC20Recoverer.initialize(gov_, disable);
            Governed.initialize(gov_);
            _deployer = msg.sender;
            _allow(gov_, true);
            for (uint256 i = 0; i < admins.length; i++) {
                _allow(admins[i], true);
            }
        }
        modifier onlyAllowed() {
            require(_allowed[msg.sender], "Not authorized");
            _;
        }
        function redeem(uint256 amount) public override {
            require(
                COMMIT(_commitToken).balanceOf(msg.sender) >= amount,
                "Not enough balance"
            );
            COMMIT(_commitToken).burnFrom(msg.sender, amount);
            IERC20(_baseCurrency).transfer(msg.sender, amount);
            emit Redeemed(msg.sender, amount);
        }
        function payInsteadOfWorking(uint256 amount) public override {
            uint256 amountToPay = amount.mul(_priceOfCommit).div(10000);
            IERC20(_baseCurrency).safeTransferFrom(
                msg.sender,
                address(this),
                amountToPay
            );
            _mintCOMMIT(msg.sender, amount);
        }
        function reserveAndMint(uint256 amount) public override onlyAllowed {
            IERC20(_baseCurrency).safeTransferFrom(
                msg.sender,
                address(this),
                amount
            );
            _mintCOMMIT(msg.sender, amount);
        }
        function grant(
            address recipient,
            uint256 amount,
            bytes memory data
        ) public override governed {
            _mintCOMMIT(recipient, amount);
            bytes memory returndata =
                address(recipient).functionCall(
                    abi.encodeWithSelector(
                        IGrantReceiver(recipient).receiveGrant.selector,
                        _commitToken,
                        amount,
                        data
                    ),
                    "GrantReceiver: low-level call failed"
                );
            if (returndata.length > 0) {
                // Return data is optional
                // solhint-disable-next-line max-line-length
                require(
                    abi.decode(returndata, (bool)),
                    "GrantReceiver: low-level call failed"
                );
            }
        }
        function allow(address account, bool active) public override governed {
            _allow(account, active);
        }
        function baseCurrency() public view override returns (address) {
            return _baseCurrency;
        }
        function commitToken() public view override returns (address) {
            return _commitToken;
        }
        function priceOfCommit() public view override returns (uint256) {
            return _priceOfCommit;
        }
        function mintable() public view override returns (uint256) {
            uint256 currentSupply = COMMIT(_commitToken).totalSupply();
            uint256 currentRedeemable =
                IERC20(_baseCurrency).balanceOf(address(this));
            return currentRedeemable.sub(currentSupply);
        }
        function allowed(address account) public view override returns (bool) {
            return _allowed[account];
        }
        function _mintCOMMIT(address to, uint256 amount) internal {
            require(amount <= mintable(), "Not enough reserve");
            COMMIT(_commitToken).mint(to, amount);
        }
        function _allow(address account, bool active) internal {
            if (_allowed[account] != active) {
                emit AdminUpdated(account);
            }
            _allowed[account] = active;
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    interface IStableReserve {
        event AdminUpdated(address indexed minter);
        event Redeemed(address to, uint256 amount);
        function redeem(uint256 amount) external;
        function payInsteadOfWorking(uint256 amount) external;
        function reserveAndMint(uint256 amount) external;
        function grant(
            address recipient,
            uint256 amount,
            bytes memory data
        ) external;
        function allow(address account, bool active) external;
        function baseCurrency() external view returns (address);
        function commitToken() external view returns (address);
        function priceOfCommit() external view returns (uint256);
        function allowed(address account) external view returns (bool);
        function mintable() external view returns (uint256);
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    interface IGrantReceiver {
        function receiveGrant(
            address currency,
            uint256 amount,
            bytes calldata data
        ) external returns (bool result);
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/token/ERC1155/ERC1155Burnable.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol";
    import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/cryptography/ECDSA.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "@openzeppelin/contracts/math/Math.sol";
    import "../../core/governance/Governed.sol";
    import "../../core/work/libraries/CommitMinter.sol";
    import "../../core/work/libraries/GrantReceiver.sol";
    import "../../core/work/interfaces/IStableReserve.sol";
    import "../../core/work/interfaces/IContributionBoard.sol";
    import "../../core/dividend/libraries/Distributor.sol";
    import "../../core/dividend/interfaces/IDividendPool.sol";
    import "../../utils/IERC1620.sol";
    import "../../utils/Utils.sol";
    contract ContributionBoard is
        CommitMinter,
        GrantReceiver,
        Distributor,
        Governed,
        ReentrancyGuard,
        Initializable,
        ERC1155Burnable,
        IContributionBoard
    {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        using ECDSA for bytes32;
        using Utils for address[];
        address private _sablier;
        IERC721 private _project;
        mapping(uint256 => uint256) private _projectFund;
        mapping(uint256 => uint256) private _totalSupplyOf;
        mapping(uint256 => uint256) private _maxSupplyOf;
        mapping(uint256 => uint256) private _minimumShare;
        mapping(uint256 => bool) private _fundingPaused;
        mapping(uint256 => bool) private _finalized;
        mapping(uint256 => uint256) private _projectOf;
        mapping(uint256 => uint256[]) private _streams;
        mapping(uint256 => address[]) private _contributors;
        constructor() ERC1155("") {
            // this will not be called
        }
        function initialize(
            address project_,
            address gov_,
            address dividendPool_,
            address stableReserve_,
            address commit_,
            address sablier_
        ) public initializer {
            CommitMinter._setup(stableReserve_, commit_);
            Distributor._setup(dividendPool_);
            _project = IERC721(project_);
            _sablier = sablier_;
            Governed.initialize(gov_);
            _setURI("");
            // register the supported interfaces to conform to ERC1155 via ERC165
            bytes4 _INTERFACE_ID_ERC165 = 0x01ffc9a7;
            bytes4 _INTERFACE_ID_ERC1155 = 0xd9b67a26;
            bytes4 _INTERFACE_ID_ERC1155_METADATA_URI = 0x0e89341c;
            _registerInterface(_INTERFACE_ID_ERC165);
            _registerInterface(_INTERFACE_ID_ERC1155);
            _registerInterface(_INTERFACE_ID_ERC1155_METADATA_URI);
        }
        modifier onlyStableReserve() {
            require(
                address(stableReserve) == msg.sender,
                "Only the stable reserves can call this function"
            );
            _;
        }
        modifier onlyProjectOwner(uint256 projId) {
            require(_project.ownerOf(projId) == msg.sender, "Not authorized");
            _;
        }
        function addProjectFund(uint256 projId, uint256 amount) public override {
            require(!_fundingPaused[projId], "Should resume funding");
            IERC20(commitToken).safeTransferFrom(msg.sender, address(this), amount);
            uint256 updated = _projectFund[projId].add(amount);
            _projectFund[projId] = updated;
            if (_initialContributorShareProgram(projId)) {
                // record funding
                _recordContribution(msg.sender, projId, amount);
            }
        }
        function startInitialContributorShareProgram(
            uint256 projectId,
            uint256 minimumShare_,
            uint256 maxContribution
        ) public override onlyProjectOwner(projectId) {
            require(0 < minimumShare_, "Should be greater than 0");
            require(minimumShare_ < 10000, "Cannot be greater than denominator");
            require(_minimumShare[projectId] == 0, "Funding is already enabled.");
            _minimumShare[projectId] = minimumShare_;
            _setMaxContribution(projectId, maxContribution);
        }
        /**
         * @notice Usually the total supply = funded + paid. If you want to raise
         *         10000 COMMITs you should set the max contribution at least 20000.
         */
        function setMaxContribution(uint256 projectId, uint256 maxContribution)
            public
            override
            onlyProjectOwner(projectId)
        {
            _setMaxContribution(projectId, maxContribution);
        }
        function pauseFunding(uint256 projectId)
            public
            override
            onlyProjectOwner(projectId)
        {
            require(!_fundingPaused[projectId], "Already paused");
            _fundingPaused[projectId] = true;
        }
        function resumeFunding(uint256 projectId)
            public
            override
            onlyProjectOwner(projectId)
        {
            require(_fundingPaused[projectId], "Already unpaused");
            _fundingPaused[projectId] = false;
        }
        function compensate(
            uint256 projectId,
            address to,
            uint256 amount
        ) public override onlyProjectOwner(projectId) {
            require(_projectFund[projectId] >= amount, "Not enough fund.");
            _projectFund[projectId] = _projectFund[projectId] - amount; // "require" protects underflow
            IERC20(commitToken).safeTransfer(to, amount);
            _recordContribution(to, projectId, amount);
            emit Payed(projectId, to, amount);
        }
        function compensateInStream(
            uint256 projectId,
            address to,
            uint256 amount,
            uint256 period
        ) public override onlyProjectOwner(projectId) {
            require(_projectFund[projectId] >= amount);
            _projectFund[projectId] = _projectFund[projectId] - amount; // "require" protects underflow
            _recordContribution(to, projectId, amount);
            IERC20(commitToken).approve(_sablier, amount); // approve the transfer
            uint256 streamId =
                IERC1620(_sablier).createStream(
                    to,
                    amount,
                    commitToken,
                    block.timestamp,
                    block.timestamp + period
                );
            _projectOf[streamId] = projectId;
            _streams[projectId].push(streamId);
            emit PayedInStream(projectId, to, amount, streamId);
        }
        function cancelStream(uint256 projectId, uint256 streamId)
            public
            override
            onlyProjectOwner(projectId)
        {
            require(projectOf(streamId) == projectId, "Invalid project id");
            (
                ,
                address recipient,
                uint256 deposit,
                ,
                uint256 startTime,
                uint256 stopTime,
                ,
                uint256 ratePerSecond
            ) = IERC1620(_sablier).getStream(streamId);
            uint256 earned = Math.min(block.timestamp, stopTime).sub(startTime);
            uint256 remaining = deposit.sub(ratePerSecond.mul(earned));
            require(IERC1620(_sablier).cancelStream(streamId), "Failed to cancel");
            _projectFund[projectId] = _projectFund[projectId].add(remaining);
            uint256 cancelContribution =
                Math.min(balanceOf(recipient, projectId), remaining);
            _burn(recipient, projectId, cancelContribution);
        }
        function recordContribution(
            address to,
            uint256 id,
            uint256 amount
        ) external override onlyProjectOwner(id) {
            require(
                !_initialContributorShareProgram(id),
                "Once it starts to get funding, you cannot record additional contribution"
            );
            require(
                _recordContribution(to, id, amount),
                "Cannot record after it's launched."
            );
        }
        function finalize(uint256 id) external override {
            require(
                msg.sender == address(_project),
                "this should be called only for upgrade"
            );
            require(!_finalized[id], "Already _finalized");
            _finalized[id] = true;
        }
        function receiveGrant(
            address currency,
            uint256 amount,
            bytes calldata data
        ) external override onlyStableReserve returns (bool result) {
            require(
                currency == commitToken,
                "Only can get $COMMIT token for its grant"
            );
            uint256 projId = abi.decode(data, (uint256));
            require(_project.ownerOf(projId) != address(0), "No budget owner");
            _projectFund[projId] = _projectFund[projId].add(amount);
            emit Grant(projId, amount);
            return true;
        }
        function sablier() public view override returns (address) {
            return _sablier;
        }
        function project() public view override returns (address) {
            return address(_project);
        }
        function projectFund(uint256 projId)
            public
            view
            override
            returns (uint256)
        {
            return _projectFund[projId];
        }
        function totalSupplyOf(uint256 projId)
            public
            view
            override
            returns (uint256)
        {
            return _totalSupplyOf[projId];
        }
        function maxSupplyOf(uint256 projId)
            public
            view
            override
            returns (uint256)
        {
            return _maxSupplyOf[projId];
        }
        function initialContributorShareProgram(uint256 projId)
            public
            view
            override
            returns (bool)
        {
            return _initialContributorShareProgram(projId);
        }
        function minimumShare(uint256 projId)
            public
            view
            override
            returns (uint256)
        {
            return _minimumShare[projId];
        }
        function fundingPaused(uint256 projId) public view override returns (bool) {
            return _fundingPaused[projId];
        }
        function finalized(uint256 projId) public view override returns (bool) {
            return _finalized[projId];
        }
        function projectOf(uint256 streamId)
            public
            view
            override
            returns (uint256 id)
        {
            return _projectOf[streamId];
        }
        function getStreams(uint256 projId)
            public
            view
            override
            returns (uint256[] memory)
        {
            return _streams[projId];
        }
        function getContributors(uint256 projId)
            public
            view
            override
            returns (address[] memory)
        {
            return _contributors[projId];
        }
        function uri(uint256 id)
            external
            view
            override(ERC1155, IContributionBoard)
            returns (string memory)
        {
            return IERC721Metadata(address(_project)).tokenURI(id);
        }
        function _setMaxContribution(uint256 id, uint256 maxContribution) internal {
            require(!_finalized[id], "DAO is launched. You cannot update it.");
            _maxSupplyOf[id] = maxContribution;
            emit NewMaxContribution(id, maxContribution);
        }
        function _recordContribution(
            address to,
            uint256 id,
            uint256 amount
        ) internal returns (bool) {
            if (_finalized[id]) return false;
            (bool exist, ) = _contributors[id].find(to);
            if (!exist) {
                _contributors[id].push(to);
            }
            bytes memory zero;
            _mint(to, id, amount, zero);
            return true;
        }
        function _mint(
            address account,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal override {
            super._mint(account, id, amount, data);
            _totalSupplyOf[id] = _totalSupplyOf[id].add(amount);
            require(
                _maxSupplyOf[id] == 0 || _totalSupplyOf[id] <= _maxSupplyOf[id],
                "Exceeds the max supply. Set a new max supply value."
            );
        }
        function _burn(
            address account,
            uint256 id,
            uint256 amount
        ) internal override {
            super._burn(account, id, amount);
            _totalSupplyOf[id] = _totalSupplyOf[id].sub(amount);
        }
        function _beforeTokenTransfer(
            address,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory,
            bytes memory
        ) internal override {
            if (from == address(0) || to == address(0)) {
                // contribution can be minted or burned before the dao launch
            } else {
                // transfer is only allowed after the finalization
                for (uint256 i = 0; i < ids.length; i++) {
                    require(_finalized[ids[i]], "Not finalized");
                }
            }
        }
        function _initialContributorShareProgram(uint256 projId)
            internal
            view
            returns (bool)
        {
            return _minimumShare[projId] != 0;
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "../../../core/work/interfaces/IStableReserve.sol";
    contract CommitMinter {
        using SafeERC20 for IERC20;
        address public stableReserve;
        address public commitToken;
        function _setup(address _stableReserve, address _commit) internal {
            stableReserve = _stableReserve;
            commitToken = _commit;
        }
        function _mintCommit(uint256 amount) internal virtual {
            address _baseCurrency = IStableReserve(stableReserve).baseCurrency();
            IERC20(_baseCurrency).safeApprove(address(stableReserve), amount);
            IStableReserve(stableReserve).reserveAndMint(amount);
        }
    }
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "../../../core/work/interfaces/IGrantReceiver.sol";
    abstract contract GrantReceiver is IGrantReceiver {}
    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/token/ERC1155/IERC1155MetadataURI.sol";
    interface IContributionBoard is IERC1155MetadataURI {
        event ManagerUpdated(address indexed manager, bool active);
        event ProjectPosted(uint256 projId);
        event ProjectClosed(uint256 projId);
        event Grant(uint256 projId, uint256 amount);
        event Payed(uint256 projId, address to, uint256 amount);
        event PayedInStream(
            uint256 projId,
            address to,
            uint256 amount,
            uint256 streamId
        );
        event ProjectFunded(uint256 indexed projId, uint256 amount);
        event NewMaxContribution(uint256 _id, uint256 _maxContribution);
        function finalize(uint256 id) external;
        function addProjectFund(uint256 projId, uint256 amount) external;
        function startInitialContributorShareProgram(
            uint256 projectId,
            uint256 _minimumShare,
            uint256 _maxContribution
        ) external;
        function setMaxContribution(uint256 projectId, uint256 maxContribution)
            external;
        function pauseFunding(uint256 projectId) external;
        function resumeFunding(uint256 projectId) external;
        function compensate(
            uint256 projectId,
            address to,
            uint256 amount
        ) external;
        function compensateInStream(
            uint256 projectId,
            address to,
            uint256 amount,
            uint256 period
        ) external;
        function cancelStream(uint256 projectId, uint256 streamId) external;
        function recordContribution(
            address to,
            uint256 id,
            uint256 amount
        ) external;
        function sablier() external view returns (address);
        function project() external view returns (address);
        function projectFund(uint256 projId) external view returns (uint256);
        function totalSupplyOf(uint256 projId) external view returns (uint256);
        function maxSupplyOf(uint256 projId) external view returns (uint256);
        function initialContributorShareProgram(uint256 projId)
            external
            view
            returns (bool);
        function minimumShare(uint256 projId) external view returns (uint256);
        function fundingPaused(uint256 projId) external view returns (bool);
        function finalized(uint256 projId) external view returns (bool);
        function projectOf(uint256 streamId) external view returns (uint256 id);
        function getStreams(uint256 projId)
            external
            view
            returns (uint256[] memory);
        function getContributors(uint256 projId)
            external
            view
            returns (address[] memory);
        function uri(uint256 id) external view override returns (string memory);
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "../../../core/dividend/interfaces/IDividendPool.sol";
    contract Distributor {
        using SafeERC20 for IERC20;
        IDividendPool public dividendPool;
        function _setup(address _dividendPool) internal {
            dividendPool = IDividendPool(_dividendPool);
        }
        function _distribute(address currency, uint256 amount) internal virtual {
            IERC20(currency).safeApprove(address(dividendPool), amount);
            dividendPool.distribute(currency, amount);
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    import "@openzeppelin/contracts/access/TimelockController.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    /**
     * @notice Gnosis Safe Multisig wallet has the Ownership of this contract.
     *      In the future, We can transfer the ownership to a well-formed governance contract.
     *      **Ownership grpah**
     *      TimelockedGovernance -controls-> COMMIT, ContributionBoard, Market, DividendPool, and VisionEmitter
     *      VisionEmitter -controls-> VISION
     */
    contract TimelockedGovernance is TimelockController, Initializable {
        mapping(bytes32 => bool) public nonCancelable;
        constructor()
            TimelockController(1 days, new address[](0), new address[](0))
        {
            // this constructor will not be called since it'll be cloned by proxy pattern.
            // initalize() will be called instead.
        }
        function initialize(
            uint256 delay,
            address multisig,
            address workersUnion
        ) public initializer {
            _setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE);
            _setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE);
            _setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE);
            // deployer + self administration
            _setupRole(TIMELOCK_ADMIN_ROLE, _msgSender());
            _setupRole(TIMELOCK_ADMIN_ROLE, address(this));
            _setupRole(TIMELOCK_ADMIN_ROLE, workersUnion);
            _setupRole(PROPOSER_ROLE, workersUnion);
            _setupRole(PROPOSER_ROLE, multisig);
            _setupRole(EXECUTOR_ROLE, workersUnion);
            _setupRole(EXECUTOR_ROLE, multisig);
            TimelockController(this).updateDelay(delay);
        }
        function cancel(bytes32 id) public override {
            require(!nonCancelable[id], "non-cancelable");
            super.cancel(id);
        }
        function forceSchedule(
            address target,
            uint256 value,
            bytes calldata data,
            bytes32 predecessor,
            bytes32 salt,
            uint256 delay
        ) public {
            bytes32 id = hashOperation(target, value, data, predecessor, salt);
            nonCancelable[id] = true;
            super.schedule(target, value, data, predecessor, salt, delay);
        }
        function forceScheduleBatch(
            address[] calldata target,
            uint256[] calldata value,
            bytes[] calldata data,
            bytes32 predecessor,
            bytes32 salt,
            uint256 delay
        ) public {
            bytes32 id = hashOperationBatch(target, value, data, predecessor, salt);
            nonCancelable[id] = true;
            super.scheduleBatch(target, value, data, predecessor, salt, delay);
        }
        function scheduleBatch(
            address[] calldata target,
            uint256[] calldata value,
            bytes[] calldata data,
            bytes32 predecessor,
            bytes32 salt,
            uint256 delay
        ) public override {
            super.scheduleBatch(target, value, data, predecessor, salt, delay);
        }
        function executeBatch(
            address[] calldata target,
            uint256[] calldata value,
            bytes[] calldata data,
            bytes32 predecessor,
            bytes32 salt
        ) public payable override {
            super.executeBatch(target, value, data, predecessor, salt);
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    pragma experimental ABIEncoderV2;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/utils/Pausable.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "../../core/dividend/interfaces/IDividendPool.sol";
    import "../../core/governance/Governed.sol";
    import "../../core/governance/TimelockedGovernance.sol";
    import "../../core/governance/interfaces/IVoteCounter.sol";
    import "../../core/governance/interfaces/IWorkersUnion.sol";
    import "../../utils/Sqrt.sol";
    /**
     * @notice referenced openzeppelin's TimelockController.sol
     */
    contract WorkersUnion is Pausable, Governed, Initializable, IWorkersUnion {
        using SafeMath for uint256;
        using Sqrt for uint256;
        bytes32 public constant NO_DEPENDENCY = bytes32(0);
        uint256 private _launch;
        VotingRule private _votingRule;
        mapping(bytes32 => Proposal) private _proposals;
        event TxProposed(
            bytes32 indexed txHash,
            address target,
            uint256 value,
            bytes data,
            bytes32 predecessor,
            bytes32 salt,
            uint256 start,
            uint256 end
        );
        event BatchTxProposed(
            bytes32 indexed txHash,
            address[] target,
            uint256[] value,
            bytes[] data,
            bytes32 predecessor,
            bytes32 salt,
            uint256 start,
            uint256 end
        );
        event Vote(bytes32 txHash, address voter, bool forVote);
        event VoteUpdated(bytes32 txHash, uint256 forVotes, uint256 againsVotes);
        function initialize(
            address voteCounter,
            address timelockGov,
            uint256 launchDelay
        ) public initializer {
            _votingRule = VotingRule(
                1 days, // minimum pending for vote
                1 weeks, // maximum pending for vote
                1 weeks, // minimum voting period
                4 weeks, // maximum voting period
                0 gwei, // minimum votes for proposing
                0 gwei, // minimum votes
                voteCounter
            );
            Governed.initialize(timelockGov);
            _pause();
            _launch = block.timestamp.add(launchDelay);
        }
        /**
         * @dev Contract might receive/hold ETH as part of the maintenance process.
         */
        receive() external payable {}
        function launch() public override {
            require(block.timestamp >= _launch, "Wait a bit please.");
            _unpause();
        }
        function changeVotingRule(
            uint256 minimumPendingPeriod,
            uint256 maximumPendingPeriod,
            uint256 minimumVotingPeriod,
            uint256 maximumVotingPeriod,
            uint256 minimumVotesForProposing,
            uint256 minimumVotes,
            address voteCounter
        ) public override governed {
            uint256 totalVotes = IVoteCounter(voteCounter).getTotalVotes();
            require(minimumPendingPeriod <= maximumPendingPeriod, "invalid arg");
            require(minimumVotingPeriod <= maximumVotingPeriod, "invalid arg");
            require(minimumVotingPeriod >= 1 days, "too short");
            require(minimumPendingPeriod >= 1 days, "too short");
            require(maximumVotingPeriod <= 30 days, "too long");
            require(maximumPendingPeriod <= 30 days, "too long");
            require(
                minimumVotesForProposing <= totalVotes.div(10),
                "too large number"
            );
            require(minimumVotes <= totalVotes.div(2), "too large number");
            require(address(voteCounter) != address(0), "null address");
            _votingRule = VotingRule(
                minimumPendingPeriod,
                maximumPendingPeriod,
                minimumVotingPeriod,
                maximumVotingPeriod,
                minimumVotesForProposing,
                minimumVotes,
                voteCounter
            );
        }
        function proposeTx(
            address target,
            uint256 value,
            bytes calldata data,
            bytes32 predecessor,
            bytes32 salt,
            uint256 startsIn,
            uint256 votingPeriod
        ) public override {
            _beforePropose(startsIn, votingPeriod);
            bytes32 txHash =
                _timelock().hashOperation(target, value, data, predecessor, salt);
            _propose(txHash, startsIn, votingPeriod);
            emit TxProposed(
                txHash,
                target,
                value,
                data,
                predecessor,
                salt,
                block.timestamp + startsIn,
                block.timestamp + startsIn + votingPeriod
            );
        }
        function proposeBatchTx(
            address[] calldata target,
            uint256[] calldata value,
            bytes[] calldata data,
            bytes32 predecessor,
            bytes32 salt,
            uint256 startsIn,
            uint256 votingPeriod
        ) public override whenNotPaused {
            _beforePropose(startsIn, votingPeriod);
            bytes32 txHash =
                _timelock().hashOperationBatch(
                    target,
                    value,
                    data,
                    predecessor,
                    salt
                );
            _propose(txHash, startsIn, votingPeriod);
            emit BatchTxProposed(
                txHash,
                target,
                value,
                data,
                predecessor,
                salt,
                block.timestamp + startsIn,
                block.timestamp + startsIn + votingPeriod
            );
        }
        /**
         * @notice Should use vote(bytes32, uint256[], bool) when too many voting rights are delegated to avoid out of gas.
         */
        function vote(bytes32 txHash, bool agree) public override {
            uint256[] memory votingRights =
                IVoteCounter(_votingRule.voteCounter).votingRights(msg.sender);
            manualVote(txHash, votingRights, agree);
        }
        /**
         * @notice The voting will be updated if the voter already voted. Please
         *      note that the voting power may change by the locking period or others.
         *      To have more detail information about how voting power is computed,
         *      Please go to the QVCounter.sol.
         */
        function manualVote(
            bytes32 txHash,
            uint256[] memory rightIds,
            bool agree
        ) public override {
            Proposal storage proposal = _proposals[txHash];
            uint256 timestamp = proposal.start;
            require(
                getVotingStatus(txHash) == VotingState.Voting,
                "Not in the voting period"
            );
            uint256 totalForVotes = proposal.totalForVotes;
            uint256 totalAgainstVotes = proposal.totalAgainstVotes;
            for (uint256 i = 0; i < rightIds.length; i++) {
                uint256 id = rightIds[i];
                require(
                    IVoteCounter(_votingRule.voteCounter).voterOf(id) == msg.sender,
                    "not the voting right owner"
                );
                uint256 prevForVotes = proposal.forVotes[id];
                uint256 prevAgainstVotes = proposal.againstVotes[id];
                uint256 votes =
                    IVoteCounter(_votingRule.voteCounter).getVotes(id, timestamp);
                proposal.forVotes[id] = agree ? votes : 0;
                proposal.againstVotes[id] = agree ? 0 : votes;
                totalForVotes = totalForVotes.add(agree ? votes : 0).sub(
                    prevForVotes
                );
                totalAgainstVotes = totalAgainstVotes.add(agree ? 0 : votes).sub(
                    prevAgainstVotes
                );
            }
            proposal.totalForVotes = totalForVotes;
            proposal.totalAgainstVotes = totalAgainstVotes;
            emit Vote(txHash, msg.sender, agree);
            emit VoteUpdated(txHash, totalForVotes, totalAgainstVotes);
        }
        function schedule(
            address target,
            uint256 value,
            bytes calldata data,
            bytes32 predecessor,
            bytes32 salt
        ) public override {
            bytes32 txHash =
                _timelock().hashOperation(target, value, data, predecessor, salt);
            require(
                getVotingStatus(txHash) == VotingState.Passed,
                "vote is not passed"
            );
            _timelock().forceSchedule(
                target,
                value,
                data,
                predecessor,
                salt,
                _timelock().getMinDelay()
            );
        }
        function scheduleBatch(
            address[] calldata target,
            uint256[] calldata value,
            bytes[] calldata data,
            bytes32 predecessor,
            bytes32 salt
        ) public override {
            bytes32 txHash =
                _timelock().hashOperationBatch(
                    target,
                    value,
                    data,
                    predecessor,
                    salt
                );
            require(
                getVotingStatus(txHash) == VotingState.Passed,
                "vote is not passed"
            );
            _timelock().forceScheduleBatch(
                target,
                value,
                data,
                predecessor,
                salt,
                _timelock().getMinDelay()
            );
        }
        function execute(
            address target,
            uint256 value,
            bytes calldata data,
            bytes32 predecessor,
            bytes32 salt
        ) public payable override {
            bytes32 txHash =
                _timelock().hashOperation(target, value, data, predecessor, salt);
            require(
                getVotingStatus(txHash) == VotingState.Passed,
                "vote is not passed"
            );
            _timelock().execute{value: value}(
                target,
                value,
                data,
                predecessor,
                salt
            );
        }
        function executeBatch(
            address[] calldata target,
            uint256[] calldata value,
            bytes[] calldata data,
            bytes32 predecessor,
            bytes32 salt
        ) public payable override {
            require(target.length == value.length, "length mismatch");
            require(target.length == data.length, "length mismatch");
            bytes32 txHash =
                _timelock().hashOperationBatch(
                    target,
                    value,
                    data,
                    predecessor,
                    salt
                );
            require(
                getVotingStatus(txHash) == VotingState.Passed,
                "vote is not passed"
            );
            uint256 valueSum = 0;
            for (uint256 i = 0; i < value.length; i++) {
                valueSum = valueSum.add(value[i]);
            }
            _timelock().executeBatch{value: valueSum}(
                target,
                value,
                data,
                predecessor,
                salt
            );
        }
        function votingRule() public view override returns (VotingRule memory) {
            return _votingRule;
        }
        function getVotingStatus(bytes32 txHash)
            public
            view
            override
            returns (VotingState)
        {
            Proposal storage proposal = _proposals[txHash];
            require(proposal.start != 0, "Not an existing proposal");
            if (block.timestamp < proposal.start) return VotingState.Pending;
            else if (block.timestamp <= proposal.end) return VotingState.Voting;
            else if (_timelock().isOperationDone(txHash))
                return VotingState.Executed;
            else if (proposal.totalForVotes < _votingRule.minimumVotes)
                return VotingState.Rejected;
            else if (proposal.totalForVotes > proposal.totalAgainstVotes)
                return VotingState.Passed;
            else return VotingState.Rejected;
        }
        function getVotesFor(address account, bytes32 txHash)
            public
            view
            override
            returns (uint256)
        {
            uint256 timestamp = _proposals[txHash].start;
            return getVotesAt(account, timestamp);
        }
        function getVotesAt(address account, uint256 timestamp)
            public
            view
            override
            returns (uint256)
        {
            uint256[] memory votingRights =
                IVoteCounter(_votingRule.voteCounter).votingRights(account);
            uint256 votes;
            for (uint256 i = 0; i < votingRights.length; i++) {
                votes = votes.add(
                    IVoteCounter(_votingRule.voteCounter).getVotes(
                        votingRights[i],
                        timestamp
                    )
                );
            }
            return votes;
        }
        function proposals(bytes32 proposalHash)
            public
            view
            override
            returns (
                address proposer,
                uint256 start,
                uint256 end,
                uint256 totalForVotes,
                uint256 totalAgainstVotes
            )
        {
            Proposal storage proposal = _proposals[proposalHash];
            return (
                proposal.proposer,
                proposal.start,
                proposal.end,
                proposal.totalForVotes,
                proposal.totalAgainstVotes
            );
        }
        function _propose(
            bytes32 txHash,
            uint256 startsIn,
            uint256 votingPeriod
        ) private whenNotPaused {
            Proposal storage proposal = _proposals[txHash];
            require(proposal.proposer == address(0));
            proposal.proposer = msg.sender;
            proposal.start = block.timestamp + startsIn;
            proposal.end = proposal.start + votingPeriod;
        }
        function _beforePropose(uint256 startsIn, uint256 votingPeriod)
            private
            view
        {
            uint256 votes = getVotesAt(msg.sender, block.timestamp);
            require(
                _votingRule.minimumVotesForProposing <= votes,
                "Not enough votes for proposing."
            );
            require(
                _votingRule.minimumPending <= startsIn,
                "Pending period is too short."
            );
            require(
                startsIn <= _votingRule.maximumPending,
                "Pending period is too long."
            );
            require(
                _votingRule.minimumVotingPeriod <= votingPeriod,
                "Voting period is too short."
            );
            require(
                votingPeriod <= _votingRule.maximumVotingPeriod,
                "Voting period is too long."
            );
        }
        function _timelock() internal view returns (TimelockedGovernance) {
            return TimelockedGovernance(payable(_gov));
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    interface IVoteCounter {
        function getVotes(uint256 votingRightId, uint256 timestamp)
            external
            view
            returns (uint256);
        function voterOf(uint256 votingRightId) external view returns (address);
        function votingRights(address voter)
            external
            view
            returns (uint256[] memory rights);
        function getTotalVotes() external view returns (uint256);
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    struct Proposal {
        address proposer;
        uint256 start;
        uint256 end;
        uint256 totalForVotes;
        uint256 totalAgainstVotes;
        mapping(uint256 => uint256) forVotes; // votingRightId => for vote amount
        mapping(uint256 => uint256) againstVotes; // votingRightId => against vote amount
    }
    struct VotingRule {
        uint256 minimumPending;
        uint256 maximumPending;
        uint256 minimumVotingPeriod;
        uint256 maximumVotingPeriod;
        uint256 minimumVotesForProposing;
        uint256 minimumVotes;
        address voteCounter;
    }
    interface IWorkersUnion {
        enum VotingState {Pending, Voting, Passed, Rejected, Executed} // Enum
        function launch() external;
        function changeVotingRule(
            uint256 minimumPendingPeriod,
            uint256 maximumPendingPeriod,
            uint256 minimumVotingPeriod,
            uint256 maximumVotingPeriod,
            uint256 minimumVotesForProposing,
            uint256 minimumVotes,
            address voteCounter
        ) external;
        function proposeTx(
            address target,
            uint256 value,
            bytes calldata data,
            bytes32 predecessor,
            bytes32 salt,
            uint256 startsIn,
            uint256 votingPeriod
        ) external;
        function proposeBatchTx(
            address[] calldata target,
            uint256[] calldata value,
            bytes[] calldata data,
            bytes32 predecessor,
            bytes32 salt,
            uint256 startsIn,
            uint256 votingPeriod
        ) external;
        function vote(bytes32 txHash, bool agree) external;
        function manualVote(
            bytes32 txHash,
            uint256[] memory rightIds,
            bool agree
        ) external;
        function schedule(
            address target,
            uint256 value,
            bytes calldata data,
            bytes32 predecessor,
            bytes32 salt
        ) external;
        function scheduleBatch(
            address[] calldata target,
            uint256[] calldata value,
            bytes[] calldata data,
            bytes32 predecessor,
            bytes32 salt
        ) external;
        function execute(
            address target,
            uint256 value,
            bytes calldata data,
            bytes32 predecessor,
            bytes32 salt
        ) external payable;
        function executeBatch(
            address[] calldata target,
            uint256[] calldata value,
            bytes[] calldata data,
            bytes32 predecessor,
            bytes32 salt
        ) external payable;
        function votingRule() external view returns (VotingRule memory);
        function getVotingStatus(bytes32 txHash)
            external
            view
            returns (VotingState);
        function getVotesFor(address account, bytes32 txHash)
            external
            view
            returns (uint256);
        function getVotesAt(address account, uint256 timestamp)
            external
            view
            returns (uint256);
        function proposals(bytes32 proposalHash)
            external
            view
            returns (
                address proposer,
                uint256 start,
                uint256 end,
                uint256 totalForVotes,
                uint256 totalAgainstVotes
            );
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/utils/EnumerableSet.sol";
    import "@openzeppelin/contracts/utils/EnumerableMap.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "../../../core/governance/interfaces/IVoteCounter.sol";
    import "../../../core/governance/interfaces/IVotingEscrowToken.sol";
    import "../../../core/governance/interfaces/IVotingEscrowLock.sol";
    import "../../../utils/Sqrt.sol";
    contract VoteCounter is IVoteCounter, Initializable {
        IVotingEscrowLock private _veLock;
        IVotingEscrowToken private _veToken;
        function initialize(address veToken_) public initializer {
            _veToken = IVotingEscrowToken(veToken_);
            _veLock = IVotingEscrowLock(_veToken.veLocker());
        }
        function getTotalVotes() public view virtual override returns (uint256) {
            return _veToken.totalSupply();
        }
        function getVotes(uint256 veLockId, uint256 timestamp)
            public
            view
            virtual
            override
            returns (uint256)
        {
            return _veToken.balanceOfLockAt(veLockId, timestamp);
        }
        function voterOf(uint256 veLockId)
            public
            view
            virtual
            override
            returns (address)
        {
            return _veLock.delegateeOf(veLockId);
        }
        function votingRights(address voter)
            public
            view
            virtual
            override
            returns (uint256[] memory rights)
        {
            uint256 totalLocks = _veLock.delegatedRights(voter);
            rights = new uint256[](totalLocks);
            for (uint256 i = 0; i < rights.length; i++) {
                rights[i] = _veLock.delegatedRightByIndex(voter, i);
            }
        }
        /**
         * @dev This should be used only for the snapshot voting feature.
         * Do not use this interface for other purposes.
         */
        function balanceOf(address account)
            external
            view
            virtual
            returns (uint256)
        {
            uint256[] memory rights = votingRights(account);
            uint256 sum;
            for (uint256 i = 0; i < rights.length; i++) {
                sum += getVotes(rights[i], block.timestamp);
            }
            return sum;
        }
        function veLock() public view returns (address) {
            return address(_veLock);
        }
        function veToken() public view returns (address) {
            return address(_veToken);
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    interface IVotingEscrowLock is IERC721 {
        event LockCreated(uint256 veLockId);
        event LockUpdate(uint256 veLockId, uint256 amount, uint256 end);
        event Withdraw(uint256 veLockId, uint256 amount);
        event VoteDelegated(uint256 veLockId, address to);
        function locks(uint256 veLockId)
            external
            view
            returns (
                uint256 amount,
                uint256 start,
                uint256 end
            );
        function createLock(uint256 amount, uint256 epochs) external;
        function createLockUntil(uint256 amount, uint256 lockEnd) external;
        function increaseAmount(uint256 veLockId, uint256 amount) external;
        function extendLock(uint256 veLockId, uint256 epochs) external;
        function extendLockUntil(uint256 veLockId, uint256 end) external;
        function withdraw(uint256 veLockId) external;
        function delegate(uint256 veLockId, address to) external;
        function totalLockedSupply() external view returns (uint256);
        function MAXTIME() external view returns (uint256);
        function baseToken() external view returns (address);
        function veToken() external view returns (address);
        function delegateeOf(uint256 veLockId) external view returns (address);
        function delegatedRights(address delegatee) external view returns (uint256);
        function delegatedRightByIndex(address delegatee, uint256 idx)
            external
            view
            returns (uint256 veLockId);
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/utils/EnumerableSet.sol";
    import "@openzeppelin/contracts/utils/EnumerableMap.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "../../../core/governance/Governed.sol";
    import "../../../core/governance/libraries/VotingEscrowToken.sol";
    import "../../../core/governance/interfaces/IVotingEscrowLock.sol";
    /**
     * @dev Voting Escrow Lock is the refactored solidity implementation of veCRV.
     *      The token lock is ERC721 and transferrable.
     *      Its original code https://github.com/curvefi/curve-dao-contracts/blob/master/contracts/VotingEscrow.vy
     */
    contract VotingEscrowLock is
        IVotingEscrowLock,
        ERC721,
        ReentrancyGuard,
        Initializable,
        Governed
    {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        using EnumerableSet for EnumerableSet.UintSet;
        using EnumerableMap for EnumerableMap.UintToAddressMap;
        uint256 public constant override MAXTIME = 4 * (365 days);
        address private _baseToken;
        address private _veToken;
        uint256 private _totalLockedSupply;
        mapping(uint256 => Lock) private _locks;
        mapping(address => EnumerableSet.UintSet) private _delegated;
        EnumerableMap.UintToAddressMap private _rightOwners;
        string private _name;
        string private _symbol;
        modifier onlyOwner(uint256 veLockId) {
            require(
                ownerOf(veLockId) == msg.sender,
                "Only the owner can call this function"
            );
            _;
        }
        constructor() ERC721("", "") {
            // this constructor will not be called since it'll be cloned by proxy pattern.
            // initalize() will be called instead.
        }
        function initialize(
            string memory name_,
            string memory symbol_,
            address baseToken_,
            address veToken_,
            address gov_
        ) public initializer {
            _baseToken = baseToken_;
            _veToken = veToken_;
            _name = name_;
            _symbol = symbol_;
            Governed.initialize(gov_);
        }
        function updateBaseUri(string memory baseURI_) public governed {
            _setBaseURI(baseURI_);
        }
        function createLock(uint256 amount, uint256 epochs) public override {
            uint256 until = block.timestamp.add(epochs.mul(1 weeks));
            createLockUntil(amount, until);
        }
        function createLockUntil(uint256 amount, uint256 lockEnd) public override {
            require(amount > 0, "should be greater than zero");
            uint256 veLockId =
                uint256(keccak256(abi.encodePacked(block.number, msg.sender)));
            require(!_exists(veLockId), "Already exists");
            _locks[veLockId].start = block.timestamp;
            _safeMint(msg.sender, veLockId);
            _updateLock(veLockId, amount, lockEnd);
            emit LockCreated(veLockId);
        }
        function increaseAmount(uint256 veLockId, uint256 amount)
            public
            override
            onlyOwner(veLockId)
        {
            require(amount > 0, "should be greater than zero");
            uint256 newAmount = _locks[veLockId].amount.add(amount);
            _updateLock(veLockId, newAmount, _locks[veLockId].end);
        }
        function extendLock(uint256 veLockId, uint256 epochs)
            public
            override
            onlyOwner(veLockId)
        {
            uint256 until = block.timestamp.add(epochs.mul(1 weeks));
            extendLockUntil(veLockId, until);
        }
        function extendLockUntil(uint256 veLockId, uint256 end)
            public
            override
            onlyOwner(veLockId)
        {
            _updateLock(veLockId, _locks[veLockId].amount, end);
        }
        function withdraw(uint256 veLockId) public override onlyOwner(veLockId) {
            Lock memory lock = _locks[veLockId];
            require(block.timestamp >= lock.end, "Locked.");
            // transfer
            IERC20(_baseToken).safeTransfer(msg.sender, lock.amount);
            _totalLockedSupply = _totalLockedSupply.sub(lock.amount);
            VotingEscrowToken(_veToken).checkpoint(veLockId, lock, Lock(0, 0, 0));
            _locks[veLockId].amount = 0;
            emit Withdraw(veLockId, lock.amount);
        }
        function delegate(uint256 veLockId, address to)
            external
            override
            onlyOwner(veLockId)
        {
            _delegate(veLockId, to);
        }
        function baseToken() public view override returns (address) {
            return _baseToken;
        }
        function veToken() public view override returns (address) {
            return _veToken;
        }
        function totalLockedSupply() public view override returns (uint256) {
            return _totalLockedSupply;
        }
        function delegateeOf(uint256 veLockId)
            public
            view
            override
            returns (address)
        {
            if (!_exists(veLockId)) {
                return address(0);
            }
            (bool delegated, address delegatee) = _rightOwners.tryGet(veLockId);
            return delegated ? delegatee : ownerOf(veLockId);
        }
        function delegatedRights(address voter)
            public
            view
            override
            returns (uint256)
        {
            require(
                voter != address(0),
                "VotingEscrowLock: delegate query for the zero address"
            );
            return _delegated[voter].length();
        }
        function delegatedRightByIndex(address voter, uint256 idx)
            public
            view
            override
            returns (uint256 veLockId)
        {
            require(
                voter != address(0),
                "VotingEscrowLock: delegate query for the zero address"
            );
            return _delegated[voter].at(idx);
        }
        function locks(uint256 veLockId)
            public
            view
            override
            returns (
                uint256 amount,
                uint256 start,
                uint256 end
            )
        {
            Lock memory lock = _locks[veLockId];
            return (lock.amount, lock.start, lock.end);
        }
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        function _updateLock(
            uint256 veLockId,
            uint256 amount,
            uint256 end
        ) internal nonReentrant {
            Lock memory prevLock = _locks[veLockId];
            Lock memory newLock =
                Lock(amount, prevLock.start, (end / 1 weeks).mul(1 weeks));
            require(_exists(veLockId), "Lock does not exist.");
            require(
                prevLock.end == 0 || prevLock.end > block.timestamp,
                "Cannot update expired. Create a new lock."
            );
            require(
                newLock.end > block.timestamp,
                "Unlock time should be in the future"
            );
            require(
                newLock.end <= block.timestamp + MAXTIME,
                "Max lock is 4 years"
            );
            require(
                !(prevLock.amount == newLock.amount && prevLock.end == newLock.end),
                "No update"
            );
            require(
                prevLock.amount <= newLock.amount,
                "new amount should be greater than before"
            );
            require(
                prevLock.end <= newLock.end,
                "new end timestamp should be greater than before"
            );
            uint256 increment = (newLock.amount - prevLock.amount); // require prevents underflow
            // 2. transfer
            if (increment > 0) {
                IERC20(_baseToken).safeTransferFrom(
                    msg.sender,
                    address(this),
                    increment
                );
                // 3. update lock amount
                _totalLockedSupply = _totalLockedSupply.add(increment);
            }
            _locks[veLockId] = newLock;
            // 4. updateCheckpoint
            VotingEscrowToken(_veToken).checkpoint(veLockId, prevLock, newLock);
            emit LockUpdate(veLockId, amount, newLock.end);
        }
        function _delegate(uint256 veLockId, address to) internal {
            address _voter = delegateeOf(veLockId);
            _delegated[_voter].remove(veLockId);
            _delegated[to].add(veLockId);
            _rightOwners.set(veLockId, to);
            emit VoteDelegated(veLockId, to);
        }
        function _beforeTokenTransfer(
            address,
            address to,
            uint256 veLockId
        ) internal override {
            _delegate(veLockId, to);
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol";
    import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "../../core/governance/interfaces/IVotingEscrowToken.sol";
    import "../../core/governance/interfaces/IVotingEscrowLock.sol";
    import "../../core/dividend/interfaces/IDividendPool.sol";
    import "../../core/governance/Governed.sol";
    import "../../utils/Utils.sol";
    struct Distribution {
        uint256 totalDistribution;
        uint256 balance;
        mapping(uint256 => uint256) tokenPerWeek; // key is week num
        mapping(uint256 => uint256) claimStartWeekNum; // key is lock id
    }
    /** @title Dividend Pool */
    contract DividendPool is
        IDividendPool,
        Governed,
        Initializable,
        ReentrancyGuard
    {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        using Utils for address[];
        // public constants
        uint256 public constant epochUnit = 1 weeks; // default 1 epoch is 1 week
        // state variables
        address private _veVISION; // a.k.a RIGHT
        address private _veLocker;
        mapping(address => Distribution) private _distributions;
        mapping(address => bool) private _distributed;
        uint256 private _genesis;
        address[] private _distributedTokens;
        address[] private _featuredRewards;
        // events
        event NewReward(address token);
        event NewDistribution(address indexed token, uint256 amount);
        function initialize(
            address gov,
            address RIGHT,
            address[] memory _rewardTokens
        ) public initializer {
            _veVISION = RIGHT;
            _veLocker = IVotingEscrowToken(RIGHT).veLocker();
            Governed.initialize(gov);
            _genesis = (block.timestamp / epochUnit) * epochUnit;
            _featuredRewards = _rewardTokens;
        }
        // distribution
        function distribute(address _token, uint256 _amount)
            public
            override
            nonReentrant
        {
            if (!_distributed[_token]) {
                _distributed[_token] = true;
                _distributedTokens.push(_token);
                emit NewReward(_token);
            }
            IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
            uint256 newBalance = IERC20(_token).balanceOf(address(this));
            Distribution storage distribution = _distributions[_token];
            uint256 increment = newBalance.sub(distribution.balance);
            distribution.balance = newBalance;
            distribution.totalDistribution = distribution.totalDistribution.add(
                increment
            );
            uint256 weekNum = getCurrentEpoch();
            distribution.tokenPerWeek[weekNum] = distribution.tokenPerWeek[weekNum]
                .add(increment);
            emit NewDistribution(_token, _amount);
        }
        /**
         * @notice If there's no ve token holder for that given epoch, anyone can call
         *          this function to redistribute the rewards to the closest epoch.
         */
        function redistribute(address token, uint256 epoch) public {
            require(
                epoch < getCurrentEpoch(),
                "Given epoch is still accepting rights."
            );
            uint256 timestamp = _genesis + epoch * epochUnit + 1 weeks;
            require(
                IVotingEscrowToken(_veVISION).totalSupplyAt(timestamp) == 0,
                "Locked Token exists for that epoch"
            );
            uint256 newEpoch;
            uint256 increment = 1;
            while (timestamp + (increment * 1 weeks) <= block.timestamp) {
                if (
                    IVotingEscrowToken(_veVISION).totalSupplyAt(
                        timestamp + (increment * 1 weeks)
                    ) > 0
                ) {
                    newEpoch = epoch + increment;
                    break;
                }
                increment += 1;
            }
            require(newEpoch > epoch, "Failed to find new epoch to redistribute");
            Distribution storage distribution = _distributions[token];
            distribution.tokenPerWeek[newEpoch] = distribution.tokenPerWeek[
                newEpoch
            ]
                .add(distribution.tokenPerWeek[epoch]);
            distribution.tokenPerWeek[epoch] = 0;
        }
        // claim
        function claim(address token) public nonReentrant {
            uint256 prevEpochTimestamp = block.timestamp - epochUnit; // safe from underflow
            _claimUpTo(token, prevEpochTimestamp);
        }
        function claimUpTo(address token, uint256 timestamp) public nonReentrant {
            _claimUpTo(token, timestamp);
        }
        function claimBatch(address[] memory tokens) public nonReentrant {
            uint256 prevEpochTimestamp = block.timestamp - epochUnit; // safe from underflow
            for (uint256 i = 0; i < tokens.length; i++) {
                _claimUpTo(tokens[i], prevEpochTimestamp);
            }
        }
        // governance
        function setFeaturedRewards(address[] memory featured) public governed {
            _featuredRewards = featured;
        }
        function genesis() public view override returns (uint256) {
            return _genesis;
        }
        function veVISION() public view override returns (address) {
            return _veVISION;
        }
        function veLocker() public view override returns (address) {
            return _veLocker;
        }
        function getEpoch(uint256 timestamp)
            public
            view
            override
            returns (uint256)
        {
            return (timestamp - _genesis) / epochUnit; // safe from underflow
        }
        /** @notice 1 epoch is 1 week */
        function getCurrentEpoch() public view override returns (uint256) {
            return getEpoch(block.timestamp);
        }
        function distributedTokens()
            public
            view
            override
            returns (address[] memory)
        {
            return _distributedTokens;
        }
        function totalDistributed(address token)
            public
            view
            override
            returns (uint256)
        {
            return _distributions[token].totalDistribution;
        }
        function distributionBalance(address token)
            public
            view
            override
            returns (uint256)
        {
            return _distributions[token].balance;
        }
        function distributionOfWeek(address token, uint256 epochNum)
            public
            view
            override
            returns (uint256)
        {
            return _distributions[token].tokenPerWeek[epochNum];
        }
        function claimStartWeek(address token, uint256 veLockId)
            public
            view
            override
            returns (uint256)
        {
            return _distributions[token].claimStartWeekNum[veLockId];
        }
        function claimable(address token) public view override returns (uint256) {
            Distribution storage distribution = _distributions[token];
            uint256 currentEpoch = getCurrentEpoch();
            if (currentEpoch == 0) return 0;
            uint256 myLocks = IVotingEscrowLock(_veLocker).balanceOf(msg.sender);
            uint256 acc;
            for (uint256 i = 0; i < myLocks; i++) {
                uint256 lockId =
                    IERC721Enumerable(_veLocker).tokenOfOwnerByIndex(msg.sender, i);
                acc = acc.add(_claimable(distribution, lockId, currentEpoch - 1));
            }
            return acc;
        }
        function featuredRewards() public view override returns (address[] memory) {
            return _featuredRewards;
        }
        function _claimUpTo(address token, uint256 timestamp) internal {
            uint256 epoch = getEpoch(timestamp);
            uint256 myLocks = IVotingEscrowLock(_veLocker).balanceOf(msg.sender);
            uint256 amountToClaim = 0;
            for (uint256 i = 0; i < myLocks; i++) {
                uint256 lockId =
                    IERC721Enumerable(_veLocker).tokenOfOwnerByIndex(msg.sender, i);
                uint256 amount = _recordClaim(token, lockId, epoch);
                amountToClaim = amountToClaim.add(amount);
            }
            if (amountToClaim != 0) {
                IERC20(token).safeTransfer(msg.sender, amountToClaim);
            }
        }
        function _recordClaim(
            address token,
            uint256 tokenId,
            uint256 epoch
        ) internal returns (uint256 amountToClaim) {
            Distribution storage distribution = _distributions[token];
            amountToClaim = _claimable(distribution, tokenId, epoch);
            distribution.claimStartWeekNum[tokenId] = epoch + 1;
            distribution.balance = distribution.balance.sub(amountToClaim);
            return amountToClaim;
        }
        function _claimable(
            Distribution storage distribution,
            uint256 tokenId,
            uint256 epoch
        ) internal view returns (uint256) {
            require(epoch < getCurrentEpoch(), "Current epoch is being updated.");
            uint256 epochCursor = distribution.claimStartWeekNum[tokenId];
            uint256 endEpoch;
            {
                (, uint256 start, uint256 end) =
                    IVotingEscrowLock(_veLocker).locks(tokenId);
                epochCursor = epochCursor != 0 ? epochCursor : getEpoch(start);
                endEpoch = getEpoch(end);
            }
            uint256 accumulated;
            while (epochCursor <= epoch && epochCursor <= endEpoch) {
                // check the balance when the epoch ends
                uint256 timestamp = _genesis + epochCursor * epochUnit + 1 weeks;
                // calculate amount;
                uint256 bal =
                    IVotingEscrowToken(_veVISION).balanceOfLockAt(
                        tokenId,
                        timestamp
                    );
                uint256 supply =
                    IVotingEscrowToken(_veVISION).totalSupplyAt(timestamp);
                if (supply != 0) {
                    accumulated = accumulated.add(
                        distribution.tokenPerWeek[epochCursor].mul(bal).div(supply)
                    );
                }
                // update cursor
                epochCursor += 1;
            }
            return accumulated;
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/token/ERC1155/ERC1155Burnable.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "../../utils/ERC20Recoverer.sol";
    import "../../core/dividend/libraries/Distributor.sol";
    import "../../core/dividend/interfaces/IDividendPool.sol";
    import "../../core/governance/Governed.sol";
    import "../../core/marketplace/interfaces/IMarketplace.sol";
    contract Marketplace is
        Distributor,
        ERC20Recoverer,
        Governed,
        ReentrancyGuard,
        ERC1155Burnable,
        IMarketplace
    {
        using SafeERC20 for IERC20;
        using SafeERC20 for ERC20Burnable;
        using SafeMath for uint256;
        uint256 public constant RATE_DENOMINATOR = 10000;
        ERC20Burnable private _commitToken;
        uint256 private _taxRate = 2000; // denominator is 10,000
        mapping(uint256 => Product) private _products;
        uint256[] private _featured;
        modifier onlyManufacturer(uint256 id) {
            require(
                msg.sender == _products[id].manufacturer,
                "allowed only for manufacturer"
            );
            _;
        }
        constructor() ERC1155("") {
            // this constructor will not be called since it'll be cloned by proxy pattern.
            // initalize() will be called instead.
        }
        function initialize(
            address _gov,
            address commitToken_,
            address _dividendPool
        ) public initializer {
            _taxRate = 2000; // denominator is 10,000
            _commitToken = ERC20Burnable(commitToken_);
            ERC20Recoverer.initialize(_gov, new address[](0));
            Governed.initialize(_gov);
            Distributor._setup(_dividendPool);
        }
        function buy(
            uint256 id,
            address to,
            uint256 amount
        ) public override nonReentrant {
            require(amount > 0, "cannot buy 0");
            // check the product is for sale
            Product storage product = _products[id];
            require(product.manufacturer != address(0), "Product not exists");
            if (product.maxSupply != 0) {
                uint256 stock = product.maxSupply.sub(product.totalSupply);
                require(amount <= stock, "Not enough stock");
                require(stock > 0, "Not for sale.");
            }
            uint256 totalPayment = product.price.mul(amount); // SafeMath prevents overflow
            // Vision Tax
            uint256 visionTax = totalPayment.mul(_taxRate).div(RATE_DENOMINATOR);
            // Burn tokens
            uint256 postTax = totalPayment.sub(visionTax);
            uint256 forManufacturer =
                postTax.mul(product.profitRate).div(RATE_DENOMINATOR);
            uint256 amountToBurn = postTax.sub(forManufacturer);
            _commitToken.safeTransferFrom(msg.sender, address(this), visionTax);
            _commitToken.safeTransferFrom(
                msg.sender,
                product.manufacturer,
                forManufacturer
            );
            _commitToken.burnFrom(msg.sender, amountToBurn);
            _distribute(address(_commitToken), visionTax);
            // mint & give
            _mint(to, id, amount, "");
        }
        function manufacture(
            string memory cid,
            uint256 profitRate,
            uint256 price
        ) external override {
            uint256 id = uint256(keccak256(abi.encodePacked(cid, msg.sender)));
            _products[id] = Product(msg.sender, 0, 0, price, profitRate, cid);
            emit NewProduct(id, msg.sender, cid);
        }
        function manufactureLimitedEdition(
            string memory cid,
            uint256 profitRate,
            uint256 price,
            uint256 maxSupply
        ) external override {
            uint256 id = uint256(keccak256(abi.encodePacked(cid, msg.sender)));
            _products[id] = Product(
                msg.sender,
                0,
                maxSupply,
                price,
                profitRate,
                cid
            );
            emit NewProduct(id, msg.sender, cid);
        }
        /**
         * @notice Set max supply and make it a limited edition.
         */
        function setMaxSupply(uint256 id, uint256 _maxSupply)
            external
            override
            onlyManufacturer(id)
        {
            require(_products[id].maxSupply == 0, "Max supply is already set");
            require(
                _products[id].totalSupply <= _maxSupply,
                "Max supply is less than current supply"
            );
            _products[id].maxSupply = _maxSupply;
        }
        function setPrice(uint256 id, uint256 price)
            public
            override
            onlyManufacturer(id)
        {
            // to prevent overflow
            require(price * 1000000000 > price, "Cannot be expensive too much");
            _products[id].price = price;
            emit PriceUpdated(id, price);
        }
        /**
         * @notice The profit rate is based on the post-tax amount of the payment.
         *      For example, when the price is 10000 DCT, tax rate is 2000, and profit rate is 5000,
         *      2000 DCT will go to the vision farm, 4000 DCT will be burnt, and 4000 will be given
         *      to the manufacturer.
         */
        function setProfitRate(uint256 id, uint256 profitRate)
            public
            override
            onlyManufacturer(id)
        {
            require(profitRate <= RATE_DENOMINATOR, "Profit rate is too high");
            _products[id].profitRate = profitRate;
            emit ProfitRateUpdated(id, profitRate);
        }
        function setFeatured(uint256[] calldata featured_)
            external
            override
            governed
        {
            _featured = featured_;
        }
        function setTaxRate(uint256 rate) public override governed {
            require(rate <= RATE_DENOMINATOR);
            _taxRate = rate;
        }
        function commitToken() public view override returns (address) {
            return address(_commitToken);
        }
        function taxRate() public view override returns (uint256) {
            return _taxRate;
        }
        function products(uint256 id)
            public
            view
            override
            returns (Product memory)
        {
            return _products[id];
        }
        function featured() public view override returns (uint256[] memory) {
            return _featured;
        }
        function uri(uint256 id)
            external
            view
            override(IERC1155MetadataURI, ERC1155)
            returns (string memory)
        {
            return string(abi.encodePacked("ipfs://", _products[id].uri));
        }
        function _mint(
            address account,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal override {
            uint256 newSupply = _products[id].totalSupply.add(amount);
            require(
                _products[id].maxSupply == 0 ||
                    newSupply <= _products[id].maxSupply,
                "Sold out"
            );
            _products[id].totalSupply = newSupply;
            super._mint(account, id, amount, data);
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    pragma abicoder v2;
    import "@openzeppelin/contracts/token/ERC1155/IERC1155MetadataURI.sol";
    struct Product {
        address manufacturer;
        uint256 totalSupply;
        uint256 maxSupply;
        uint256 price;
        uint256 profitRate;
        string uri;
    }
    interface IMarketplace is IERC1155MetadataURI {
        function buy(
            uint256 id,
            address to,
            uint256 amount
        ) external;
        function manufacture(
            string memory cid,
            uint256 profitRate,
            uint256 price
        ) external;
        function manufactureLimitedEdition(
            string memory cid,
            uint256 profitRate,
            uint256 price,
            uint256 maxSupply
        ) external;
        function setMaxSupply(uint256 id, uint256 _maxSupply) external;
        function setPrice(uint256 id, uint256 price) external;
        function setProfitRate(uint256 id, uint256 profitRate) external;
        function setTaxRate(uint256 rate) external;
        function setFeatured(uint256[] calldata _featured) external;
        function commitToken() external view returns (address);
        function taxRate() external view returns (uint256);
        function products(uint256 id) external view returns (Product memory);
        function featured() external view returns (uint256[] memory);
        event NewProduct(uint256 id, address manufacturer, string uri);
        event TaxRateUpdated(uint256 taxRate);
        event PriceUpdated(uint256 indexed productId, uint256 price);
        event ProfitRateUpdated(uint256 indexed productId, uint256 profitRate);
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "@openzeppelin/contracts/token/ERC1155/ERC1155Burnable.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
    import "@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol";
    import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    import "@openzeppelin/contracts/cryptography/ECDSA.sol";
    import "@openzeppelin/contracts/proxy/Initializable.sol";
    import "@openzeppelin/contracts/math/Math.sol";
    import "../../../core/governance/Governed.sol";
    import "../../../core/work/libraries/CommitMinter.sol";
    import "../../../core/work/libraries/GrantReceiver.sol";
    import "../../../core/work/interfaces/IStableReserve.sol";
    import "../../../core/work/interfaces/IContributionBoard.sol";
    import "../../../core/dividend/libraries/Distributor.sol";
    import "../../../core/dividend/interfaces/IDividendPool.sol";
    import "../../../core/project/Project.sol";
    import "../../../utils/IERC1620.sol";
    import "../../../utils/Utils.sol";
    struct Budget {
        uint256 amount;
        bool transferred;
    }
    contract JobBoard is
        CommitMinter,
        GrantReceiver,
        Distributor,
        Governed,
        ReentrancyGuard,
        Initializable
    {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        using ECDSA for bytes32;
        using Utils for address[];
        bool thirdPartyAccess;
        address public sablier;
        address public baseCurrency;
        Project public project;
        uint256 public normalTaxRate = 2000; // 20% goes to the vision sharing farm, 80% is swapped to stable coin and goes to the labor market
        uint256 public taxRateForUndeclared = 5000; // 50% goes to the vision farm when the budget is undeclared.
        mapping(address => bool) public acceptableTokens;
        mapping(uint256 => uint256) public projectFund;
        mapping(bytes32 => bool) public claimed;
        mapping(uint256 => Budget[]) public projectBudgets;
        mapping(uint256 => bool) public approvedProjects;
        mapping(uint256 => bool) public finalized;
        mapping(uint256 => uint256) private _projectOf;
        mapping(uint256 => uint256[]) private _streams;
        mapping(uint256 => address[]) private _contributors;
        event ManagerUpdated(address indexed manager, bool active);
        event ProjectPosted(uint256 projId);
        event ProjectClosed(uint256 projId);
        event Grant(uint256 projId, uint256 amount);
        event Payed(uint256 projId, address to, uint256 amount);
        event PayedInStream(
            uint256 projId,
            address to,
            uint256 amount,
            uint256 streamId
        );
        event BudgetAdded(
            uint256 indexed projId,
            uint256 index,
            address token,
            uint256 amount
        );
        event BudgetExecuted(uint256 projId, uint256 index);
        event BudgetWithdrawn(uint256 projId, uint256 index);
        constructor() {
            // this will not be called
        }
        function initialize(
            address _project,
            address _gov,
            address _dividendPool,
            address _stableReserve,
            address _baseCurrency,
            address _commit,
            address _sablier
        ) public initializer {
            normalTaxRate = 2000; // 20% goes to the vision sharing farm, 80% is swapped to stable coin and goes to the labor market
            taxRateForUndeclared = 5000; // 50% goes to the vision farm when the budget is undeclared.
            CommitMinter._setup(_stableReserve, _commit);
            Distributor._setup(_dividendPool);
            baseCurrency = _baseCurrency;
            project = Project(_project);
            acceptableTokens[_baseCurrency] = true;
            thirdPartyAccess = true;
            sablier = _sablier;
            Governed.initialize(_gov);
        }
        modifier onlyStableReserve() {
            require(
                address(stableReserve) == msg.sender,
                "Only the stable reserves can call this function"
            );
            _;
        }
        modifier onlyProjectOwner(uint256 projId) {
            require(project.ownerOf(projId) == msg.sender, "Not authorized");
            _;
        }
        modifier onlyApprovedProject(uint256 projId) {
            require(thirdPartyAccess, "Third party access is not allowed.");
            require(approvedProjects[projId], "Not an approved project.");
            _;
        }
        function addBudget(
            uint256 projId,
            address token,
            uint256 amount
        ) public onlyProjectOwner(projId) {
            _addBudget(projId, token, amount);
        }
        function addAndExecuteBudget(
            uint256 projId,
            address token,
            uint256 amount
        ) public onlyProjectOwner(projId) {
            uint256 budgetIdx = _addBudget(projId, token, amount);
            executeBudget(projId, budgetIdx);
        }
        function closeProject(uint256 projId) public onlyProjectOwner(projId) {
            _withdrawAllBudgets(projId);
            approvedProjects[projId] = false;
            emit ProjectClosed(projId);
        }
        function forceExecuteBudget(uint256 projId, uint256 index)
            public
            onlyProjectOwner(projId)
        {
            // force approve does not allow swap and approve func to prevent
            // exploitation using flash loan attack
            _convertStableToCommit(projId, index, taxRateForUndeclared);
        }
        // Operator functions
        function executeBudget(uint256 projId, uint256 index)
            public
            onlyApprovedProject(projId)
        {
            _convertStableToCommit(projId, index, normalTaxRate);
        }
        function addProjectFund(uint256 projId, uint256 amount) public {
            IERC20(commitToken).safeTransferFrom(msg.sender, address(this), amount);
            projectFund[projId] = projectFund[projId].add(amount);
        }
        function receiveGrant(
            address currency,
            uint256 amount,
            bytes calldata data
        ) external override onlyStableReserve returns (bool result) {
            require(
                currency == commitToken,
                "Only can get $COMMIT token for its grant"
            );
            uint256 projId = abi.decode(data, (uint256));
            require(project.ownerOf(projId) != address(0), "No budget owner");
            projectFund[projId] = projectFund[projId].add(amount);
            emit Grant(projId, amount);
            return true;
        }
        function compensate(
            uint256 projectId,
            address to,
            uint256 amount
        ) public onlyProjectOwner(projectId) {
            _compensate(projectId, to, amount);
        }
        function compensateInStream(
            uint256 projectId,
            address to,
            uint256 amount,
            uint256 period
        ) public onlyProjectOwner(projectId) {
            require(projectFund[projectId] >= amount);
            projectFund[projectId] = projectFund[projectId] - amount; // "require" protects underflow
            IERC20(commitToken).approve(sablier, amount); // approve the transfer
            uint256 streamId =
                IERC1620(sablier).createStream(
                    to,
                    amount,
                    commitToken,
                    block.timestamp,
                    block.timestamp + period
                );
            _projectOf[streamId] = projectId;
            _streams[projectId].push(streamId);
            emit PayedInStream(projectId, to, amount, streamId);
        }
        function cancelStream(uint256 projectId, uint256 streamId)
            public
            onlyProjectOwner(projectId)
        {
            require(projectOf(streamId) == projectId, "Invalid project id");
            (, , , , , , uint256 remainingBalance, ) =
                IERC1620(sablier).getStream(streamId);
            require(IERC1620(sablier).cancelStream(streamId), "Failed to cancel");
            projectFund[projectId] = projectFund[projectId].add(remainingBalance);
        }
        function claim(
            uint256 projectId,
            address to,
            uint256 amount,
            bytes32 salt,
            bytes memory sig
        ) public {
            bytes32 claimHash =
                keccak256(abi.encodePacked(projectId, to, amount, salt));
            require(!claimed[claimHash], "Already claimed");
            claimed[claimHash] = true;
            address signer = claimHash.recover(sig);
            require(project.ownerOf(projectId) == signer, "Invalid signer");
            _compensate(projectId, to, amount);
        }
        function projectOf(uint256 streamId) public view returns (uint256 id) {
            return _projectOf[streamId];
        }
        // Governed functions
        function addCurrency(address currency) public governed {
            acceptableTokens[currency] = true;
        }
        function removeCurrency(address currency) public governed {
            acceptableTokens[currency] = false;
        }
        function approveProject(uint256 projId) public governed {
            _approveProject(projId);
        }
        function disapproveProject(uint256 projId) public governed {
            _withdrawAllBudgets(projId);
            approvedProjects[projId] = false;
            emit ProjectClosed(projId);
        }
        function setTaxRate(uint256 rate) public governed {
            require(rate <= 10000);
            normalTaxRate = rate;
        }
        function setTaxRateForUndeclared(uint256 rate) public governed {
            require(rate <= 10000);
            taxRateForUndeclared = rate;
        }
        function allowThirdPartyAccess(bool allow) public governed {
            thirdPartyAccess = allow;
        }
        function getTotalBudgets(uint256 projId) public view returns (uint256) {
            return projectBudgets[projId].length;
        }
        function getStreams(uint256 projId) public view returns (uint256[] memory) {
            return _streams[projId];
        }
        function getContributors(uint256 projId)
            public
            view
            returns (address[] memory)
        {
            return _contributors[projId];
        }
        // Internal functions
        function _addBudget(
            uint256 projId,
            address token,
            uint256 amount
        ) internal returns (uint256) {
            require(acceptableTokens[token], "Not a supported currency");
            Budget memory budget = Budget(amount, false);
            projectBudgets[projId].push(budget);
            emit BudgetAdded(
                projId,
                projectBudgets[projId].length - 1,
                token,
                amount
            );
            IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
            return projectBudgets[projId].length - 1;
        }
        function _approveProject(uint256 projId) internal {
            require(!approvedProjects[projId], "Already approved");
            approvedProjects[projId] = true;
        }
        function _withdrawAllBudgets(uint256 projId) internal nonReentrant {
            Budget[] storage budgets = projectBudgets[projId];
            address projOwner = project.ownerOf(projId);
            for (uint256 i = 0; i < budgets.length; i += 1) {
                Budget storage budget = budgets[i];
                if (!budget.transferred) {
                    budget.transferred = true;
                    IERC20(baseCurrency).transfer(projOwner, budget.amount);
                    emit BudgetWithdrawn(projId, i);
                }
            }
            delete projectBudgets[projId];
        }
        /**
         * @param projId The project NFT id for this budget.
         * @param taxRate The tax rate to approve the budget.
         */
        function _convertStableToCommit(
            uint256 projId,
            uint256 index,
            uint256 taxRate
        ) internal {
            Budget storage budget = projectBudgets[projId][index];
            require(budget.transferred == false, "Budget is already transferred.");
            // Mark the budget as transferred
            budget.transferred = true;
            // take vision tax from the budget
            uint256 visionTax = budget.amount.mul(taxRate).div(10000);
            uint256 fund = budget.amount.sub(visionTax);
            _distribute(baseCurrency, visionTax);
            // Mint commit fund
            _mintCommit(fund);
            projectFund[projId] = projectFund[projId].add(fund);
            emit BudgetExecuted(projId, index);
        }
        function _compensate(
            uint256 projectId,
            address to,
            uint256 amount
        ) internal {
            require(projectFund[projectId] >= amount);
            projectFund[projectId] = projectFund[projectId] - amount; // "require" protects underflow
            IERC20(commitToken).safeTransfer(to, amount);
            emit Payed(projectId, to, amount);
        }
    }
    //SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.7.0;
    import "../../../core/governance/libraries/VoteCounter.sol";
    import "../../../utils/Sqrt.sol";
    contract SquareRootVoteCounter is VoteCounter {
        using Sqrt for uint256;
        function getVotes(uint256 veLockId, uint256 timestamp)
            public
            view
            override
            returns (uint256)
        {
            uint256 votes = super.getVotes(veLockId, timestamp);
            return votes.sqrt();
        }
        function getTotalVotes() public view virtual override returns (uint256) {
            return IVotingEscrowToken(veToken()).totalSupply().sqrt();
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @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 `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);
        /**
         * @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);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "./IERC20.sol";
    import "../../math/SafeMath.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 SafeMath for uint256;
        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'
            // solhint-disable-next-line max-line-length
            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).add(value);
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        /**
         * @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
                // solhint-disable-next-line max-line-length
                require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @dev Wrappers over Solidity's arithmetic operations with added overflow
     * checks.
     *
     * Arithmetic operations in Solidity wrap on overflow. This can easily result
     * in bugs, because programmers usually assume that an overflow raises an
     * error, which is the standard behavior in high level programming languages.
     * `SafeMath` restores this intuition by reverting the transaction when an
     * operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
        /**
         * @dev Returns the substraction of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         *
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
            return c;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b <= a, "SafeMath: subtraction overflow");
            return a - b;
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         *
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            if (a == 0) return 0;
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
            return c;
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b > 0, "SafeMath: division by zero");
            return a / b;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b > 0, "SafeMath: modulo by zero");
            return a % b;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {trySub}.
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b <= a, errorMessage);
            return a - b;
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
         * division by zero. The result is rounded towards zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryDiv}.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            return a / b;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting with custom message when dividing by zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryMod}.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @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
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize, which returns 0 for contracts in
            // construction, since the code is only stored at the end of the
            // constructor execution.
            uint256 size;
            // solhint-disable-next-line no-inline-assembly
            assembly { size := extcodesize(account) }
            return size > 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");
            // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
            (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 functionCall(target, data, "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");
            require(isContract(target), "Address: call to non-contract");
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory returndata) = target.call{ value: value }(data);
            return _verifyCallResult(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) {
            require(isContract(target), "Address: static call to non-contract");
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory returndata) = target.staticcall(data);
            return _verifyCallResult(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) {
            require(isContract(target), "Address: delegate call to non-contract");
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
            if (success) {
                return returndata;
            } else {
                // 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
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // solhint-disable-next-line compiler-version
    pragma solidity >=0.4.24 <0.8.0;
    import "../utils/Address.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 a proxied contract can't have 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.
     *
     * 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 {UpgradeableProxy-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.
     */
    abstract contract Initializable {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        bool private _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool private _initializing;
        /**
         * @dev Modifier to protect an initializer function from being invoked twice.
         */
        modifier initializer() {
            require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
            bool isTopLevelCall = !_initializing;
            if (isTopLevelCall) {
                _initializing = true;
                _initialized = true;
            }
            _;
            if (isTopLevelCall) {
                _initializing = false;
            }
        }
        /// @dev Returns true if and only if the function is running in the constructor
        function _isConstructor() private view returns (bool) {
            return !Address.isContract(address(this));
        }
    }
    pragma solidity >=0.5.0;
    interface IUniswapV2Factory {
        event PairCreated(address indexed token0, address indexed token1, address pair, uint);
        function feeTo() external view returns (address);
        function feeToSetter() external view returns (address);
        function getPair(address tokenA, address tokenB) external view returns (address pair);
        function allPairs(uint) external view returns (address pair);
        function allPairsLength() external view returns (uint);
        function createPair(address tokenA, address tokenB) external returns (address pair);
        function setFeeTo(address) external;
        function setFeeToSetter(address) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
     * checks.
     *
     * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
     * easily result in undesired exploitation or bugs, since developers usually
     * assume that overflows raise errors. `SafeCast` restores this intuition by
     * reverting the transaction when such an operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     *
     * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
     * all math on `uint256` and `int256` and then downcasting.
     */
    library SafeCast {
        /**
         * @dev Returns the downcasted uint128 from uint256, reverting on
         * overflow (when the input is greater than largest uint128).
         *
         * Counterpart to Solidity's `uint128` operator.
         *
         * Requirements:
         *
         * - input must fit into 128 bits
         */
        function toUint128(uint256 value) internal pure returns (uint128) {
            require(value < 2**128, "SafeCast: value doesn\\'t fit in 128 bits");
            return uint128(value);
        }
        /**
         * @dev Returns the downcasted uint64 from uint256, reverting on
         * overflow (when the input is greater than largest uint64).
         *
         * Counterpart to Solidity's `uint64` operator.
         *
         * Requirements:
         *
         * - input must fit into 64 bits
         */
        function toUint64(uint256 value) internal pure returns (uint64) {
            require(value < 2**64, "SafeCast: value doesn\\'t fit in 64 bits");
            return uint64(value);
        }
        /**
         * @dev Returns the downcasted uint32 from uint256, reverting on
         * overflow (when the input is greater than largest uint32).
         *
         * Counterpart to Solidity's `uint32` operator.
         *
         * Requirements:
         *
         * - input must fit into 32 bits
         */
        function toUint32(uint256 value) internal pure returns (uint32) {
            require(value < 2**32, "SafeCast: value doesn\\'t fit in 32 bits");
            return uint32(value);
        }
        /**
         * @dev Returns the downcasted uint16 from uint256, reverting on
         * overflow (when the input is greater than largest uint16).
         *
         * Counterpart to Solidity's `uint16` operator.
         *
         * Requirements:
         *
         * - input must fit into 16 bits
         */
        function toUint16(uint256 value) internal pure returns (uint16) {
            require(value < 2**16, "SafeCast: value doesn\\'t fit in 16 bits");
            return uint16(value);
        }
        /**
         * @dev Returns the downcasted uint8 from uint256, reverting on
         * overflow (when the input is greater than largest uint8).
         *
         * Counterpart to Solidity's `uint8` operator.
         *
         * Requirements:
         *
         * - input must fit into 8 bits.
         */
        function toUint8(uint256 value) internal pure returns (uint8) {
            require(value < 2**8, "SafeCast: value doesn\\'t fit in 8 bits");
            return uint8(value);
        }
        /**
         * @dev Converts a signed int256 into an unsigned uint256.
         *
         * Requirements:
         *
         * - input must be greater than or equal to 0.
         */
        function toUint256(int256 value) internal pure returns (uint256) {
            require(value >= 0, "SafeCast: value must be positive");
            return uint256(value);
        }
        /**
         * @dev Returns the downcasted int128 from int256, reverting on
         * overflow (when the input is less than smallest int128 or
         * greater than largest int128).
         *
         * Counterpart to Solidity's `int128` operator.
         *
         * Requirements:
         *
         * - input must fit into 128 bits
         *
         * _Available since v3.1._
         */
        function toInt128(int256 value) internal pure returns (int128) {
            require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\\'t fit in 128 bits");
            return int128(value);
        }
        /**
         * @dev Returns the downcasted int64 from int256, reverting on
         * overflow (when the input is less than smallest int64 or
         * greater than largest int64).
         *
         * Counterpart to Solidity's `int64` operator.
         *
         * Requirements:
         *
         * - input must fit into 64 bits
         *
         * _Available since v3.1._
         */
        function toInt64(int256 value) internal pure returns (int64) {
            require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\\'t fit in 64 bits");
            return int64(value);
        }
        /**
         * @dev Returns the downcasted int32 from int256, reverting on
         * overflow (when the input is less than smallest int32 or
         * greater than largest int32).
         *
         * Counterpart to Solidity's `int32` operator.
         *
         * Requirements:
         *
         * - input must fit into 32 bits
         *
         * _Available since v3.1._
         */
        function toInt32(int256 value) internal pure returns (int32) {
            require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\\'t fit in 32 bits");
            return int32(value);
        }
        /**
         * @dev Returns the downcasted int16 from int256, reverting on
         * overflow (when the input is less than smallest int16 or
         * greater than largest int16).
         *
         * Counterpart to Solidity's `int16` operator.
         *
         * Requirements:
         *
         * - input must fit into 16 bits
         *
         * _Available since v3.1._
         */
        function toInt16(int256 value) internal pure returns (int16) {
            require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\\'t fit in 16 bits");
            return int16(value);
        }
        /**
         * @dev Returns the downcasted int8 from int256, reverting on
         * overflow (when the input is less than smallest int8 or
         * greater than largest int8).
         *
         * Counterpart to Solidity's `int8` operator.
         *
         * Requirements:
         *
         * - input must fit into 8 bits.
         *
         * _Available since v3.1._
         */
        function toInt8(int256 value) internal pure returns (int8) {
            require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\\'t fit in 8 bits");
            return int8(value);
        }
        /**
         * @dev Converts an unsigned uint256 into a signed int256.
         *
         * Requirements:
         *
         * - input must be less than or equal to maxInt256.
         */
        function toInt256(uint256 value) internal pure returns (int256) {
            require(value < 2**255, "SafeCast: value doesn't fit in an int256");
            return int256(value);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "../../utils/Context.sol";
    import "./ERC20.sol";
    /**
     * @dev Extension of {ERC20} that allows token holders to destroy both their own
     * tokens and those that they have an allowance for, in a way that can be
     * recognized off-chain (via event analysis).
     */
    abstract contract ERC20Burnable is Context, ERC20 {
        using SafeMath for uint256;
        /**
         * @dev Destroys `amount` tokens from the caller.
         *
         * See {ERC20-_burn}.
         */
        function burn(uint256 amount) public virtual {
            _burn(_msgSender(), amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, deducting from the caller's
         * allowance.
         *
         * See {ERC20-_burn} and {ERC20-allowance}.
         *
         * Requirements:
         *
         * - the caller must have allowance for ``accounts``'s tokens of at least
         * `amount`.
         */
        function burnFrom(address account, uint256 amount) public virtual {
            uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
            _approve(account, _msgSender(), decreasedAllowance);
            _burn(account, amount);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <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 GSN 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 payable) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes memory) {
            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "../../utils/Context.sol";
    import "./IERC20.sol";
    import "../../math/SafeMath.sol";
    /**
     * @dev Implementation of the {IERC20} interface.
     *
     * This implementation is agnostic to the way tokens are created. This means
     * that a supply mechanism has to be added in a derived contract using {_mint}.
     * For a generic mechanism see {ERC20PresetMinterPauser}.
     *
     * TIP: For a detailed writeup see our guide
     * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
     * to implement supply mechanisms].
     *
     * We have followed general OpenZeppelin guidelines: functions revert instead
     * of returning `false` on failure. This behavior is nonetheless conventional
     * and does not conflict with the expectations of ERC20 applications.
     *
     * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
     * This allows applications to reconstruct the allowance for all accounts just
     * by listening to said events. Other implementations of the EIP may not emit
     * these events, as it isn't required by the specification.
     *
     * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
     * functions have been added to mitigate the well-known issues around setting
     * allowances. See {IERC20-approve}.
     */
    contract ERC20 is Context, IERC20 {
        using SafeMath for uint256;
        mapping (address => uint256) private _balances;
        mapping (address => mapping (address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        uint8 private _decimals;
        /**
         * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
         * a default value of 18.
         *
         * To select a different value for {decimals}, use {_setupDecimals}.
         *
         * All three of these values are immutable: they can only be set once during
         * construction.
         */
        constructor (string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
            _decimals = 18;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5,05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
         * called.
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view virtual returns (uint8) {
            return _decimals;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `recipient` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
            _transfer(_msgSender(), recipient, amount);
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(address owner, address spender) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function approve(address spender, uint256 amount) public virtual override returns (bool) {
            _approve(_msgSender(), spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * Requirements:
         *
         * - `sender` and `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         * - the caller must have allowance for ``sender``'s tokens of at least
         * `amount`.
         */
        function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
            _transfer(sender, recipient, amount);
            _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
            return true;
        }
        /**
         * @dev Moves tokens `amount` from `sender` to `recipient`.
         *
         * This is internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `sender` cannot be the zero address.
         * - `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         */
        function _transfer(address sender, address recipient, uint256 amount) internal virtual {
            require(sender != address(0), "ERC20: transfer from the zero address");
            require(recipient != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(sender, recipient, amount);
            _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
            _balances[recipient] = _balances[recipient].add(amount);
            emit Transfer(sender, recipient, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply = _totalSupply.add(amount);
            _balances[account] = _balances[account].add(amount);
            emit Transfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
            _totalSupply = _totalSupply.sub(amount);
            emit Transfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(address owner, address spender, uint256 amount) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Sets {decimals} to a value other than the default one of 18.
         *
         * WARNING: This function should only be called from the constructor. Most
         * applications that interact with token contracts will not expect
         * {decimals} to ever change, and may work incorrectly if it does.
         */
        function _setupDecimals(uint8 decimals_) internal virtual {
            _decimals = decimals_;
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be to transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a >= b ? a : b;
        }
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow, so we distribute
            return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        uint256 private _status;
        constructor () {
            _status = _NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and make it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            // On the first call to nonReentrant, _notEntered will be true
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
            _;
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = _NOT_ENTERED;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "./Context.sol";
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
        bool private _paused;
        /**
         * @dev Initializes the contract in unpaused state.
         */
        constructor () {
            _paused = false;
        }
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            require(!paused(), "Pausable: paused");
            _;
        }
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            require(paused(), "Pausable: not paused");
            _;
        }
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts may inherit from this and call {_registerInterface} to declare
     * their support of an interface.
     */
    abstract contract ERC165 is IERC165 {
        /*
         * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
         */
        bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
        /**
         * @dev Mapping of interface ids to whether or not it's supported.
         */
        mapping(bytes4 => bool) private _supportedInterfaces;
        constructor () {
            // Derived contracts need only register support for their own interfaces,
            // we register support for ERC165 itself here
            _registerInterface(_INTERFACE_ID_ERC165);
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         *
         * Time complexity O(1), guaranteed to always use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return _supportedInterfaces[interfaceId];
        }
        /**
         * @dev Registers the contract as an implementer of the interface defined by
         * `interfaceId`. Support of the actual ERC165 interface is automatic and
         * registering its interface id is not required.
         *
         * See {IERC165-supportsInterface}.
         *
         * Requirements:
         *
         * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
         */
        function _registerInterface(bytes4 interfaceId) internal virtual {
            require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
            _supportedInterfaces[interfaceId] = true;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "../../introspection/IERC165.sol";
    /**
     * @dev Required interface of an ERC1155 compliant contract, as defined in the
     * https://eips.ethereum.org/EIPS/eip-1155[EIP].
     *
     * _Available since v3.1._
     */
    interface IERC1155 is IERC165 {
        /**
         * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
         */
        event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
        /**
         * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
         * transfers.
         */
        event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
        /**
         * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
         * `approved`.
         */
        event ApprovalForAll(address indexed account, address indexed operator, bool approved);
        /**
         * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
         *
         * If an {URI} event was emitted for `id`, the standard
         * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
         * returned by {IERC1155MetadataURI-uri}.
         */
        event URI(string value, uint256 indexed id);
        /**
         * @dev Returns the amount of tokens of token type `id` owned by `account`.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function balanceOf(address account, uint256 id) external view returns (uint256);
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
        /**
         * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
         *
         * Emits an {ApprovalForAll} event.
         *
         * Requirements:
         *
         * - `operator` cannot be the caller.
         */
        function setApprovalForAll(address operator, bool approved) external;
        /**
         * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address account, address operator) external view returns (bool);
        /**
         * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
         * - `from` must have a balance of tokens of type `id` of at least `amount`.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "./ERC1155Receiver.sol";
    /**
     * @dev _Available since v3.1._
     */
    contract ERC1155Holder is ERC1155Receiver {
        function onERC1155Received(address, address, uint256, uint256, bytes memory) public virtual override returns (bytes4) {
            return this.onERC1155Received.selector;
        }
        function onERC1155BatchReceived(address, address, uint256[] memory, uint256[] memory, bytes memory) public virtual override returns (bytes4) {
            return this.onERC1155BatchReceived.selector;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "./IERC1155Receiver.sol";
    import "../../introspection/ERC165.sol";
    /**
     * @dev _Available since v3.1._
     */
    abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
        constructor() {
            _registerInterface(
                ERC1155Receiver(address(0)).onERC1155Received.selector ^
                ERC1155Receiver(address(0)).onERC1155BatchReceived.selector
            );
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "../../introspection/IERC165.sol";
    /**
     * _Available since v3.1._
     */
    interface IERC1155Receiver is IERC165 {
        /**
            @dev Handles the receipt of a single ERC1155 token type. This function is
            called at the end of a `safeTransferFrom` after the balance has been updated.
            To accept the transfer, this must return
            `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
            (i.e. 0xf23a6e61, or its own function selector).
            @param operator The address which initiated the transfer (i.e. msg.sender)
            @param from The address which previously owned the token
            @param id The ID of the token being transferred
            @param value The amount of tokens being transferred
            @param data Additional data with no specified format
            @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
        */
        function onERC1155Received(
            address operator,
            address from,
            uint256 id,
            uint256 value,
            bytes calldata data
        )
            external
            returns(bytes4);
        /**
            @dev Handles the receipt of a multiple ERC1155 token types. This function
            is called at the end of a `safeBatchTransferFrom` after the balances have
            been updated. To accept the transfer(s), this must return
            `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
            (i.e. 0xbc197c81, or its own function selector).
            @param operator The address which initiated the batch transfer (i.e. msg.sender)
            @param from The address which previously owned the token
            @param ids An array containing ids of each token being transferred (order and length must match values array)
            @param values An array containing amounts of each token being transferred (order and length must match ids array)
            @param data Additional data with no specified format
            @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
        */
        function onERC1155BatchReceived(
            address operator,
            address from,
            uint256[] calldata ids,
            uint256[] calldata values,
            bytes calldata data
        )
            external
            returns(bytes4);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "./ERC1155.sol";
    /**
     * @dev Extension of {ERC1155} that allows token holders to destroy both their
     * own tokens and those that they have been approved to use.
     *
     * _Available since v3.1._
     */
    abstract contract ERC1155Burnable is ERC1155 {
        function burn(address account, uint256 id, uint256 value) public virtual {
            require(
                account == _msgSender() || isApprovedForAll(account, _msgSender()),
                "ERC1155: caller is not owner nor approved"
            );
            _burn(account, id, value);
        }
        function burnBatch(address account, uint256[] memory ids, uint256[] memory values) public virtual {
            require(
                account == _msgSender() || isApprovedForAll(account, _msgSender()),
                "ERC1155: caller is not owner nor approved"
            );
            _burnBatch(account, ids, values);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "./IERC1155.sol";
    import "./IERC1155MetadataURI.sol";
    import "./IERC1155Receiver.sol";
    import "../../utils/Context.sol";
    import "../../introspection/ERC165.sol";
    import "../../math/SafeMath.sol";
    import "../../utils/Address.sol";
    /**
     *
     * @dev Implementation of the basic standard multi-token.
     * See https://eips.ethereum.org/EIPS/eip-1155
     * Originally based on code by Enjin: https://github.com/enjin/erc-1155
     *
     * _Available since v3.1._
     */
    contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
        using SafeMath for uint256;
        using Address for address;
        // Mapping from token ID to account balances
        mapping (uint256 => mapping(address => uint256)) private _balances;
        // Mapping from account to operator approvals
        mapping (address => mapping(address => bool)) private _operatorApprovals;
        // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
        string private _uri;
        /*
         *     bytes4(keccak256('balanceOf(address,uint256)')) == 0x00fdd58e
         *     bytes4(keccak256('balanceOfBatch(address[],uint256[])')) == 0x4e1273f4
         *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
         *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256,uint256,bytes)')) == 0xf242432a
         *     bytes4(keccak256('safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)')) == 0x2eb2c2d6
         *
         *     => 0x00fdd58e ^ 0x4e1273f4 ^ 0xa22cb465 ^
         *        0xe985e9c5 ^ 0xf242432a ^ 0x2eb2c2d6 == 0xd9b67a26
         */
        bytes4 private constant _INTERFACE_ID_ERC1155 = 0xd9b67a26;
        /*
         *     bytes4(keccak256('uri(uint256)')) == 0x0e89341c
         */
        bytes4 private constant _INTERFACE_ID_ERC1155_METADATA_URI = 0x0e89341c;
        /**
         * @dev See {_setURI}.
         */
        constructor (string memory uri_) {
            _setURI(uri_);
            // register the supported interfaces to conform to ERC1155 via ERC165
            _registerInterface(_INTERFACE_ID_ERC1155);
            // register the supported interfaces to conform to ERC1155MetadataURI via ERC165
            _registerInterface(_INTERFACE_ID_ERC1155_METADATA_URI);
        }
        /**
         * @dev See {IERC1155MetadataURI-uri}.
         *
         * This implementation returns the same URI for *all* token types. It relies
         * on the token type ID substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * Clients calling this function must replace the `\\{id\\}` substring with the
         * actual token type ID.
         */
        function uri(uint256) external view virtual override returns (string memory) {
            return _uri;
        }
        /**
         * @dev See {IERC1155-balanceOf}.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
            require(account != address(0), "ERC1155: balance query for the zero address");
            return _balances[id][account];
        }
        /**
         * @dev See {IERC1155-balanceOfBatch}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(
            address[] memory accounts,
            uint256[] memory ids
        )
            public
            view
            virtual
            override
            returns (uint256[] memory)
        {
            require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
            uint256[] memory batchBalances = new uint256[](accounts.length);
            for (uint256 i = 0; i < accounts.length; ++i) {
                batchBalances[i] = balanceOf(accounts[i], ids[i]);
            }
            return batchBalances;
        }
        /**
         * @dev See {IERC1155-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual override {
            require(_msgSender() != operator, "ERC1155: setting approval status for self");
            _operatorApprovals[_msgSender()][operator] = approved;
            emit ApprovalForAll(_msgSender(), operator, approved);
        }
        /**
         * @dev See {IERC1155-isApprovedForAll}.
         */
        function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[account][operator];
        }
        /**
         * @dev See {IERC1155-safeTransferFrom}.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        )
            public
            virtual
            override
        {
            require(to != address(0), "ERC1155: transfer to the zero address");
            require(
                from == _msgSender() || isApprovedForAll(from, _msgSender()),
                "ERC1155: caller is not owner nor approved"
            );
            address operator = _msgSender();
            _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);
            _balances[id][from] = _balances[id][from].sub(amount, "ERC1155: insufficient balance for transfer");
            _balances[id][to] = _balances[id][to].add(amount);
            emit TransferSingle(operator, from, to, id, amount);
            _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
        }
        /**
         * @dev See {IERC1155-safeBatchTransferFrom}.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        )
            public
            virtual
            override
        {
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
            require(to != address(0), "ERC1155: transfer to the zero address");
            require(
                from == _msgSender() || isApprovedForAll(from, _msgSender()),
                "ERC1155: transfer caller is not owner nor approved"
            );
            address operator = _msgSender();
            _beforeTokenTransfer(operator, from, to, ids, amounts, data);
            for (uint256 i = 0; i < ids.length; ++i) {
                uint256 id = ids[i];
                uint256 amount = amounts[i];
                _balances[id][from] = _balances[id][from].sub(
                    amount,
                    "ERC1155: insufficient balance for transfer"
                );
                _balances[id][to] = _balances[id][to].add(amount);
            }
            emit TransferBatch(operator, from, to, ids, amounts);
            _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
        }
        /**
         * @dev Sets a new URI for all token types, by relying on the token type ID
         * substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * By this mechanism, any occurrence of the `\\{id\\}` substring in either the
         * URI or any of the amounts in the JSON file at said URI will be replaced by
         * clients with the token type ID.
         *
         * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be
         * interpreted by clients as
         * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
         * for token type ID 0x4cce0.
         *
         * See {uri}.
         *
         * Because these URIs cannot be meaningfully represented by the {URI} event,
         * this function emits no events.
         */
        function _setURI(string memory newuri) internal virtual {
            _uri = newuri;
        }
        /**
         * @dev Creates `amount` tokens of token type `id`, and assigns them to `account`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - If `account` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function _mint(address account, uint256 id, uint256 amount, bytes memory data) internal virtual {
            require(account != address(0), "ERC1155: mint to the zero address");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data);
            _balances[id][account] = _balances[id][account].add(amount);
            emit TransferSingle(operator, address(0), account, id, amount);
            _doSafeTransferAcceptanceCheck(operator, address(0), account, id, amount, data);
        }
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function _mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) internal virtual {
            require(to != address(0), "ERC1155: mint to the zero address");
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
            for (uint i = 0; i < ids.length; i++) {
                _balances[ids[i]][to] = amounts[i].add(_balances[ids[i]][to]);
            }
            emit TransferBatch(operator, address(0), to, ids, amounts);
            _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
        }
        /**
         * @dev Destroys `amount` tokens of token type `id` from `account`
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens of token type `id`.
         */
        function _burn(address account, uint256 id, uint256 amount) internal virtual {
            require(account != address(0), "ERC1155: burn from the zero address");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, account, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");
            _balances[id][account] = _balances[id][account].sub(
                amount,
                "ERC1155: burn amount exceeds balance"
            );
            emit TransferSingle(operator, account, address(0), id, amount);
        }
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         */
        function _burnBatch(address account, uint256[] memory ids, uint256[] memory amounts) internal virtual {
            require(account != address(0), "ERC1155: burn from the zero address");
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
            address operator = _msgSender();
            _beforeTokenTransfer(operator, account, address(0), ids, amounts, "");
            for (uint i = 0; i < ids.length; i++) {
                _balances[ids[i]][account] = _balances[ids[i]][account].sub(
                    amounts[i],
                    "ERC1155: burn amount exceeds balance"
                );
            }
            emit TransferBatch(operator, account, address(0), ids, amounts);
        }
        /**
         * @dev Hook that is called before any token transfer. This includes minting
         * and burning, as well as batched variants.
         *
         * The same hook is called on both single and batched variants. For single
         * transfers, the length of the `id` and `amount` arrays will be 1.
         *
         * Calling conditions (for each `id` and `amount` pair):
         *
         * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * of token type `id` will be  transferred to `to`.
         * - When `from` is zero, `amount` tokens of token type `id` will be minted
         * for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
         * will be burned.
         * - `from` and `to` are never both zero.
         * - `ids` and `amounts` have the same, non-zero length.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        )
            internal
            virtual
        { }
        function _doSafeTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        )
            private
        {
            if (to.isContract()) {
                try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                    if (response != IERC1155Receiver(to).onERC1155Received.selector) {
                        revert("ERC1155: ERC1155Receiver rejected tokens");
                    }
                } catch Error(string memory reason) {
                    revert(reason);
                } catch {
                    revert("ERC1155: transfer to non ERC1155Receiver implementer");
                }
            }
        }
        function _doSafeBatchTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        )
            private
        {
            if (to.isContract()) {
                try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (bytes4 response) {
                    if (response != IERC1155Receiver(to).onERC1155BatchReceived.selector) {
                        revert("ERC1155: ERC1155Receiver rejected tokens");
                    }
                } catch Error(string memory reason) {
                    revert(reason);
                } catch {
                    revert("ERC1155: transfer to non ERC1155Receiver implementer");
                }
            }
        }
        function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
            uint256[] memory array = new uint256[](1);
            array[0] = element;
            return array;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "./IERC1155.sol";
    /**
     * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
     * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
     *
     * _Available since v3.1._
     */
    interface IERC1155MetadataURI is IERC1155 {
        /**
         * @dev Returns the URI for token type `id`.
         *
         * If the `\\{id\\}` substring is present in the URI, it must be replaced by
         * clients with the actual token type ID.
         */
        function uri(uint256 id) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "./IERC721Receiver.sol";
      /**
       * @dev Implementation of the {IERC721Receiver} interface.
       *
       * Accepts all token transfers. 
       * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
       */
    contract ERC721Holder is IERC721Receiver {
        /**
         * @dev See {IERC721Receiver-onERC721Received}.
         *
         * Always returns `IERC721Receiver.onERC721Received.selector`.
         */
        function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
            return this.onERC721Received.selector;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @title ERC721 token receiver interface
     * @dev Interface for any contract that wants to support safeTransfers
     * from ERC721 asset contracts.
     */
    interface IERC721Receiver {
        /**
         * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
         * by `operator` from `from`, this function is called.
         *
         * It must return its Solidity selector to confirm the token transfer.
         * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
         *
         * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
         */
        function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "../../introspection/IERC165.sol";
    /**
     * @dev Required interface of an ERC721 compliant contract.
     */
    interface IERC721 is IERC165 {
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        /**
         * @dev Returns the number of tokens in ``owner``'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(address from, address to, uint256 tokenId) external;
        /**
         * @dev Transfers `tokenId` token from `from` to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 tokenId) external;
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external;
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) external view returns (address operator);
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool _approved) external;
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}
         */
        function isApprovedForAll(address owner, address operator) external view returns (bool);
        /**
          * @dev Safely transfers `tokenId` token from `from` to `to`.
          *
          * Requirements:
          *
          * - `from` cannot be the zero address.
          * - `to` cannot be the zero address.
          * - `tokenId` token must exist and be owned by `from`.
          * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
          * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
          *
          * Emits a {Transfer} event.
          */
        function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "./IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    interface IERC721Enumerable is IERC721 {
        /**
         * @dev Returns the total amount of tokens stored by the contract.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
         * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
         */
        function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
        /**
         * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
         * Use along with {totalSupply} to enumerate all tokens.
         */
        function tokenByIndex(uint256 index) external view returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @dev Library for managing
     * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
     * types.
     *
     * Sets have the following properties:
     *
     * - Elements are added, removed, and checked for existence in constant time
     * (O(1)).
     * - Elements are enumerated in O(n). No guarantees are made on the ordering.
     *
     * ```
     * contract Example {
     *     // Add the library methods
     *     using EnumerableSet for EnumerableSet.AddressSet;
     *
     *     // Declare a set state variable
     *     EnumerableSet.AddressSet private mySet;
     * }
     * ```
     *
     * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
     * and `uint256` (`UintSet`) are supported.
     */
    library EnumerableSet {
        // To implement this library for multiple types with as little code
        // repetition as possible, we write it in terms of a generic Set type with
        // bytes32 values.
        // The Set implementation uses private functions, and user-facing
        // implementations (such as AddressSet) are just wrappers around the
        // underlying Set.
        // This means that we can only create new EnumerableSets for types that fit
        // in bytes32.
        struct Set {
            // Storage of set values
            bytes32[] _values;
            // Position of the value in the `values` array, plus 1 because index 0
            // means a value is not in the set.
            mapping (bytes32 => uint256) _indexes;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function _add(Set storage set, bytes32 value) private returns (bool) {
            if (!_contains(set, value)) {
                set._values.push(value);
                // The value is stored at length-1, but we add 1 to all indexes
                // and use 0 as a sentinel value
                set._indexes[value] = set._values.length;
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function _remove(Set storage set, bytes32 value) private returns (bool) {
            // We read and store the value's index to prevent multiple reads from the same storage slot
            uint256 valueIndex = set._indexes[value];
            if (valueIndex != 0) { // Equivalent to contains(set, value)
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                // the array, and then remove the last element (sometimes called as 'swap and pop').
                // This modifies the order of the array, as noted in {at}.
                uint256 toDeleteIndex = valueIndex - 1;
                uint256 lastIndex = set._values.length - 1;
                // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
                // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
                bytes32 lastvalue = set._values[lastIndex];
                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastvalue;
                // Update the index for the moved value
                set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
                // Delete the slot where the moved value was stored
                set._values.pop();
                // Delete the index for the deleted slot
                delete set._indexes[value];
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
            return set._indexes[value] != 0;
        }
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function _length(Set storage set) private view returns (uint256) {
            return set._values.length;
        }
       /**
        * @dev Returns the value stored at position `index` in the set. O(1).
        *
        * Note that there are no guarantees on the ordering of values inside the
        * array, and it may change when more values are added or removed.
        *
        * Requirements:
        *
        * - `index` must be strictly less than {length}.
        */
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
            require(set._values.length > index, "EnumerableSet: index out of bounds");
            return set._values[index];
        }
        // Bytes32Set
        struct Bytes32Set {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _add(set._inner, value);
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _remove(set._inner, value);
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
            return _contains(set._inner, value);
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(Bytes32Set storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
       /**
        * @dev Returns the value stored at position `index` in the set. O(1).
        *
        * Note that there are no guarantees on the ordering of values inside the
        * array, and it may change when more values are added or removed.
        *
        * Requirements:
        *
        * - `index` must be strictly less than {length}.
        */
        function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
            return _at(set._inner, index);
        }
        // AddressSet
        struct AddressSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(AddressSet storage set, address value) internal returns (bool) {
            return _add(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(AddressSet storage set, address value) internal returns (bool) {
            return _remove(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(AddressSet storage set, address value) internal view returns (bool) {
            return _contains(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(AddressSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
       /**
        * @dev Returns the value stored at position `index` in the set. O(1).
        *
        * Note that there are no guarantees on the ordering of values inside the
        * array, and it may change when more values are added or removed.
        *
        * Requirements:
        *
        * - `index` must be strictly less than {length}.
        */
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
            return address(uint160(uint256(_at(set._inner, index))));
        }
        // UintSet
        struct UintSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(UintSet storage set, uint256 value) internal returns (bool) {
            return _add(set._inner, bytes32(value));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(UintSet storage set, uint256 value) internal returns (bool) {
            return _remove(set._inner, bytes32(value));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(UintSet storage set, uint256 value) internal view returns (bool) {
            return _contains(set._inner, bytes32(value));
        }
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function length(UintSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
       /**
        * @dev Returns the value stored at position `index` in the set. O(1).
        *
        * Note that there are no guarantees on the ordering of values inside the
        * array, and it may change when more values are added or removed.
        *
        * Requirements:
        *
        * - `index` must be strictly less than {length}.
        */
        function at(UintSet storage set, uint256 index) internal view returns (uint256) {
            return uint256(_at(set._inner, index));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @dev Library for managing an enumerable variant of Solidity's
     * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
     * type.
     *
     * Maps have the following properties:
     *
     * - Entries are added, removed, and checked for existence in constant time
     * (O(1)).
     * - Entries are enumerated in O(n). No guarantees are made on the ordering.
     *
     * ```
     * contract Example {
     *     // Add the library methods
     *     using EnumerableMap for EnumerableMap.UintToAddressMap;
     *
     *     // Declare a set state variable
     *     EnumerableMap.UintToAddressMap private myMap;
     * }
     * ```
     *
     * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are
     * supported.
     */
    library EnumerableMap {
        // To implement this library for multiple types with as little code
        // repetition as possible, we write it in terms of a generic Map type with
        // bytes32 keys and values.
        // The Map implementation uses private functions, and user-facing
        // implementations (such as Uint256ToAddressMap) are just wrappers around
        // the underlying Map.
        // This means that we can only create new EnumerableMaps for types that fit
        // in bytes32.
        struct MapEntry {
            bytes32 _key;
            bytes32 _value;
        }
        struct Map {
            // Storage of map keys and values
            MapEntry[] _entries;
            // Position of the entry defined by a key in the `entries` array, plus 1
            // because index 0 means a key is not in the map.
            mapping (bytes32 => uint256) _indexes;
        }
        /**
         * @dev Adds a key-value pair to a map, or updates the value for an existing
         * key. O(1).
         *
         * Returns true if the key was added to the map, that is if it was not
         * already present.
         */
        function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
            // We read and store the key's index to prevent multiple reads from the same storage slot
            uint256 keyIndex = map._indexes[key];
            if (keyIndex == 0) { // Equivalent to !contains(map, key)
                map._entries.push(MapEntry({ _key: key, _value: value }));
                // The entry is stored at length-1, but we add 1 to all indexes
                // and use 0 as a sentinel value
                map._indexes[key] = map._entries.length;
                return true;
            } else {
                map._entries[keyIndex - 1]._value = value;
                return false;
            }
        }
        /**
         * @dev Removes a key-value pair from a map. O(1).
         *
         * Returns true if the key was removed from the map, that is if it was present.
         */
        function _remove(Map storage map, bytes32 key) private returns (bool) {
            // We read and store the key's index to prevent multiple reads from the same storage slot
            uint256 keyIndex = map._indexes[key];
            if (keyIndex != 0) { // Equivalent to contains(map, key)
                // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one
                // in the array, and then remove the last entry (sometimes called as 'swap and pop').
                // This modifies the order of the array, as noted in {at}.
                uint256 toDeleteIndex = keyIndex - 1;
                uint256 lastIndex = map._entries.length - 1;
                // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs
                // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
                MapEntry storage lastEntry = map._entries[lastIndex];
                // Move the last entry to the index where the entry to delete is
                map._entries[toDeleteIndex] = lastEntry;
                // Update the index for the moved entry
                map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based
                // Delete the slot where the moved entry was stored
                map._entries.pop();
                // Delete the index for the deleted slot
                delete map._indexes[key];
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Returns true if the key is in the map. O(1).
         */
        function _contains(Map storage map, bytes32 key) private view returns (bool) {
            return map._indexes[key] != 0;
        }
        /**
         * @dev Returns the number of key-value pairs in the map. O(1).
         */
        function _length(Map storage map) private view returns (uint256) {
            return map._entries.length;
        }
       /**
        * @dev Returns the key-value pair stored at position `index` in the map. O(1).
        *
        * Note that there are no guarantees on the ordering of entries inside the
        * array, and it may change when more entries are added or removed.
        *
        * Requirements:
        *
        * - `index` must be strictly less than {length}.
        */
        function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
            require(map._entries.length > index, "EnumerableMap: index out of bounds");
            MapEntry storage entry = map._entries[index];
            return (entry._key, entry._value);
        }
        /**
         * @dev Tries to returns the value associated with `key`.  O(1).
         * Does not revert if `key` is not in the map.
         */
        function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) {
            uint256 keyIndex = map._indexes[key];
            if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key)
            return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based
        }
        /**
         * @dev Returns the value associated with `key`.  O(1).
         *
         * Requirements:
         *
         * - `key` must be in the map.
         */
        function _get(Map storage map, bytes32 key) private view returns (bytes32) {
            uint256 keyIndex = map._indexes[key];
            require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key)
            return map._entries[keyIndex - 1]._value; // All indexes are 1-based
        }
        /**
         * @dev Same as {_get}, with a custom error message when `key` is not in the map.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {_tryGet}.
         */
        function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
            uint256 keyIndex = map._indexes[key];
            require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
            return map._entries[keyIndex - 1]._value; // All indexes are 1-based
        }
        // UintToAddressMap
        struct UintToAddressMap {
            Map _inner;
        }
        /**
         * @dev Adds a key-value pair to a map, or updates the value for an existing
         * key. O(1).
         *
         * Returns true if the key was added to the map, that is if it was not
         * already present.
         */
        function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
            return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the key was removed from the map, that is if it was present.
         */
        function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
            return _remove(map._inner, bytes32(key));
        }
        /**
         * @dev Returns true if the key is in the map. O(1).
         */
        function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
            return _contains(map._inner, bytes32(key));
        }
        /**
         * @dev Returns the number of elements in the map. O(1).
         */
        function length(UintToAddressMap storage map) internal view returns (uint256) {
            return _length(map._inner);
        }
       /**
        * @dev Returns the element stored at position `index` in the set. O(1).
        * Note that there are no guarantees on the ordering of values inside the
        * array, and it may change when more values are added or removed.
        *
        * Requirements:
        *
        * - `index` must be strictly less than {length}.
        */
        function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
            (bytes32 key, bytes32 value) = _at(map._inner, index);
            return (uint256(key), address(uint160(uint256(value))));
        }
        /**
         * @dev Tries to returns the value associated with `key`.  O(1).
         * Does not revert if `key` is not in the map.
         *
         * _Available since v3.4._
         */
        function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
            (bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));
            return (success, address(uint160(uint256(value))));
        }
        /**
         * @dev Returns the value associated with `key`.  O(1).
         *
         * Requirements:
         *
         * - `key` must be in the map.
         */
        function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
            return address(uint160(uint256(_get(map._inner, bytes32(key)))));
        }
        /**
         * @dev Same as {get}, with a custom error message when `key` is not in the map.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryGet}.
         */
        function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
            return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    /**
     * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
     * deploying minimal proxy contracts, also known as "clones".
     *
     * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
     * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
     *
     * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
     * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
     * deterministic method.
     *
     * _Available since v3.4._
     */
    library Clones {
        /**
         * @dev Deploys and returns the address of a clone that mimics the behaviour of `master`.
         *
         * This function uses the create opcode, which should never revert.
         */
        function clone(address master) internal returns (address instance) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                let ptr := mload(0x40)
                mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                mstore(add(ptr, 0x14), shl(0x60, master))
                mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
                instance := create(0, ptr, 0x37)
            }
            require(instance != address(0), "ERC1167: create failed");
        }
        /**
         * @dev Deploys and returns the address of a clone that mimics the behaviour of `master`.
         *
         * This function uses the create2 opcode and a `salt` to deterministically deploy
         * the clone. Using the same `master` and `salt` multiple time will revert, since
         * the clones cannot be deployed twice at the same address.
         */
        function cloneDeterministic(address master, bytes32 salt) internal returns (address instance) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                let ptr := mload(0x40)
                mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                mstore(add(ptr, 0x14), shl(0x60, master))
                mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
                instance := create2(0, ptr, 0x37, salt)
            }
            require(instance != address(0), "ERC1167: create2 failed");
        }
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
         */
        function predictDeterministicAddress(address master, bytes32 salt, address deployer) internal pure returns (address predicted) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                let ptr := mload(0x40)
                mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
                mstore(add(ptr, 0x14), shl(0x60, master))
                mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
                mstore(add(ptr, 0x38), shl(0x60, deployer))
                mstore(add(ptr, 0x4c), salt)
                mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
                predicted := keccak256(add(ptr, 0x37), 0x55)
            }
        }
        /**
         * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
         */
        function predictDeterministicAddress(address master, bytes32 salt) internal view returns (address predicted) {
            return predictDeterministicAddress(master, salt, address(this));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @dev Library used to query support of an interface declared via {IERC165}.
     *
     * Note that these functions return the actual result of the query: they do not
     * `revert` if an interface is not supported. It is up to the caller to decide
     * what to do in these cases.
     */
    library ERC165Checker {
        // As per the EIP-165 spec, no interface should ever match 0xffffffff
        bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;
        /*
         * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
         */
        bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
        /**
         * @dev Returns true if `account` supports the {IERC165} interface,
         */
        function supportsERC165(address account) internal view returns (bool) {
            // Any contract that implements ERC165 must explicitly indicate support of
            // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
            return _supportsERC165Interface(account, _INTERFACE_ID_ERC165) &&
                !_supportsERC165Interface(account, _INTERFACE_ID_INVALID);
        }
        /**
         * @dev Returns true if `account` supports the interface defined by
         * `interfaceId`. Support for {IERC165} itself is queried automatically.
         *
         * See {IERC165-supportsInterface}.
         */
        function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
            // query support of both ERC165 as per the spec and support of _interfaceId
            return supportsERC165(account) &&
                _supportsERC165Interface(account, interfaceId);
        }
        /**
         * @dev Returns a boolean array where each value corresponds to the
         * interfaces passed in and whether they're supported or not. This allows
         * you to batch check interfaces for a contract where your expectation
         * is that some interfaces may not be supported.
         *
         * See {IERC165-supportsInterface}.
         *
         * _Available since v3.4._
         */
        function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool[] memory) {
            // an array of booleans corresponding to interfaceIds and whether they're supported or not
            bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
            // query support of ERC165 itself
            if (supportsERC165(account)) {
                // query support of each interface in interfaceIds
                for (uint256 i = 0; i < interfaceIds.length; i++) {
                    interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]);
                }
            }
            return interfaceIdsSupported;
        }
        /**
         * @dev Returns true if `account` supports all the interfaces defined in
         * `interfaceIds`. Support for {IERC165} itself is queried automatically.
         *
         * Batch-querying can lead to gas savings by skipping repeated checks for
         * {IERC165} support.
         *
         * See {IERC165-supportsInterface}.
         */
        function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
            // query support of ERC165 itself
            if (!supportsERC165(account)) {
                return false;
            }
            // query support of each interface in _interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                if (!_supportsERC165Interface(account, interfaceIds[i])) {
                    return false;
                }
            }
            // all interfaces supported
            return true;
        }
        /**
         * @notice Query if a contract implements an interface, does not check ERC165 support
         * @param account The address of the contract to query for support of an interface
         * @param interfaceId The interface identifier, as specified in ERC-165
         * @return true if the contract at account indicates support of the interface with
         * identifier interfaceId, false otherwise
         * @dev Assumes that account contains a contract that supports ERC165, otherwise
         * the behavior of this method is undefined. This precondition can be checked
         * with {supportsERC165}.
         * Interface identification is specified in ERC-165.
         */
        function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
            // success determines whether the staticcall succeeded and result determines
            // whether the contract at account indicates support of _interfaceId
            (bool success, bool result) = _callERC165SupportsInterface(account, interfaceId);
            return (success && result);
        }
        /**
         * @notice Calls the function with selector 0x01ffc9a7 (ERC165) and suppresses throw
         * @param account The address of the contract to query for support of an interface
         * @param interfaceId The interface identifier, as specified in ERC-165
         * @return success true if the STATICCALL succeeded, false otherwise
         * @return result true if the STATICCALL succeeded and the contract at account
         * indicates support of the interface with identifier interfaceId, false otherwise
         */
        function _callERC165SupportsInterface(address account, bytes4 interfaceId)
            private
            view
            returns (bool, bool)
        {
            bytes memory encodedParams = abi.encodeWithSelector(_INTERFACE_ID_ERC165, interfaceId);
            (bool success, bytes memory result) = account.staticcall{ gas: 30000 }(encodedParams);
            if (result.length < 32) return (false, false);
            return (success, abi.decode(result, (bool)));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "../../utils/Context.sol";
    import "./IERC721.sol";
    import "./IERC721Metadata.sol";
    import "./IERC721Enumerable.sol";
    import "./IERC721Receiver.sol";
    import "../../introspection/ERC165.sol";
    import "../../math/SafeMath.sol";
    import "../../utils/Address.sol";
    import "../../utils/EnumerableSet.sol";
    import "../../utils/EnumerableMap.sol";
    import "../../utils/Strings.sol";
    /**
     * @title ERC721 Non-Fungible Token Standard basic implementation
     * @dev see https://eips.ethereum.org/EIPS/eip-721
     */
    contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {
        using SafeMath for uint256;
        using Address for address;
        using EnumerableSet for EnumerableSet.UintSet;
        using EnumerableMap for EnumerableMap.UintToAddressMap;
        using Strings for uint256;
        // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
        // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
        bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
        // Mapping from holder address to their (enumerable) set of owned tokens
        mapping (address => EnumerableSet.UintSet) private _holderTokens;
        // Enumerable mapping from token ids to their owners
        EnumerableMap.UintToAddressMap private _tokenOwners;
        // Mapping from token ID to approved address
        mapping (uint256 => address) private _tokenApprovals;
        // Mapping from owner to operator approvals
        mapping (address => mapping (address => bool)) private _operatorApprovals;
        // Token name
        string private _name;
        // Token symbol
        string private _symbol;
        // Optional mapping for token URIs
        mapping (uint256 => string) private _tokenURIs;
        // Base URI
        string private _baseURI;
        /*
         *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
         *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
         *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
         *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
         *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
         *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
         *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
         *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
         *
         *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
         *        0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
         */
        bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
        /*
         *     bytes4(keccak256('name()')) == 0x06fdde03
         *     bytes4(keccak256('symbol()')) == 0x95d89b41
         *     bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
         *
         *     => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
         */
        bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
        /*
         *     bytes4(keccak256('totalSupply()')) == 0x18160ddd
         *     bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
         *     bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
         *
         *     => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
         */
        bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
        /**
         * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
         */
        constructor (string memory name_, string memory symbol_) {
            _name = name_;
            _symbol = symbol_;
            // register the supported interfaces to conform to ERC721 via ERC165
            _registerInterface(_INTERFACE_ID_ERC721);
            _registerInterface(_INTERFACE_ID_ERC721_METADATA);
            _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
        }
        /**
         * @dev See {IERC721-balanceOf}.
         */
        function balanceOf(address owner) public view virtual override returns (uint256) {
            require(owner != address(0), "ERC721: balance query for the zero address");
            return _holderTokens[owner].length();
        }
        /**
         * @dev See {IERC721-ownerOf}.
         */
        function ownerOf(uint256 tokenId) public view virtual override returns (address) {
            return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
        }
        /**
         * @dev See {IERC721Metadata-name}.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev See {IERC721Metadata-symbol}.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev See {IERC721Metadata-tokenURI}.
         */
        function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
            require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
            string memory _tokenURI = _tokenURIs[tokenId];
            string memory base = baseURI();
            // If there is no base URI, return the token URI.
            if (bytes(base).length == 0) {
                return _tokenURI;
            }
            // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
            if (bytes(_tokenURI).length > 0) {
                return string(abi.encodePacked(base, _tokenURI));
            }
            // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
            return string(abi.encodePacked(base, tokenId.toString()));
        }
        /**
        * @dev Returns the base URI set via {_setBaseURI}. This will be
        * automatically added as a prefix in {tokenURI} to each token's URI, or
        * to the token ID if no specific URI is set for that token ID.
        */
        function baseURI() public view virtual returns (string memory) {
            return _baseURI;
        }
        /**
         * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
         */
        function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
            return _holderTokens[owner].at(index);
        }
        /**
         * @dev See {IERC721Enumerable-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
            return _tokenOwners.length();
        }
        /**
         * @dev See {IERC721Enumerable-tokenByIndex}.
         */
        function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
            (uint256 tokenId, ) = _tokenOwners.at(index);
            return tokenId;
        }
        /**
         * @dev See {IERC721-approve}.
         */
        function approve(address to, uint256 tokenId) public virtual override {
            address owner = ERC721.ownerOf(tokenId);
            require(to != owner, "ERC721: approval to current owner");
            require(_msgSender() == owner || ERC721.isApprovedForAll(owner, _msgSender()),
                "ERC721: approve caller is not owner nor approved for all"
            );
            _approve(to, tokenId);
        }
        /**
         * @dev See {IERC721-getApproved}.
         */
        function getApproved(uint256 tokenId) public view virtual override returns (address) {
            require(_exists(tokenId), "ERC721: approved query for nonexistent token");
            return _tokenApprovals[tokenId];
        }
        /**
         * @dev See {IERC721-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual override {
            require(operator != _msgSender(), "ERC721: approve to caller");
            _operatorApprovals[_msgSender()][operator] = approved;
            emit ApprovalForAll(_msgSender(), operator, approved);
        }
        /**
         * @dev See {IERC721-isApprovedForAll}.
         */
        function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[owner][operator];
        }
        /**
         * @dev See {IERC721-transferFrom}.
         */
        function transferFrom(address from, address to, uint256 tokenId) public virtual override {
            //solhint-disable-next-line max-line-length
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
            _transfer(from, to, tokenId);
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
            safeTransferFrom(from, to, tokenId, "");
        }
        /**
         * @dev See {IERC721-safeTransferFrom}.
         */
        function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
            require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
            _safeTransfer(from, to, tokenId, _data);
        }
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * `_data` is additional data, it has no specified format and it is sent in call to `to`.
         *
         * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
         * implement alternative mechanisms to perform token transfer, such as signature-based.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
            _transfer(from, to, tokenId);
            require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
        }
        /**
         * @dev Returns whether `tokenId` exists.
         *
         * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
         *
         * Tokens start existing when they are minted (`_mint`),
         * and stop existing when they are burned (`_burn`).
         */
        function _exists(uint256 tokenId) internal view virtual returns (bool) {
            return _tokenOwners.contains(tokenId);
        }
        /**
         * @dev Returns whether `spender` is allowed to manage `tokenId`.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
            require(_exists(tokenId), "ERC721: operator query for nonexistent token");
            address owner = ERC721.ownerOf(tokenId);
            return (spender == owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(owner, spender));
        }
        /**
         * @dev Safely mints `tokenId` and transfers it to `to`.
         *
         * Requirements:
         d*
         * - `tokenId` must not exist.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function _safeMint(address to, uint256 tokenId) internal virtual {
            _safeMint(to, tokenId, "");
        }
        /**
         * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
         * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
         */
        function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
            _mint(to, tokenId);
            require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
        }
        /**
         * @dev Mints `tokenId` and transfers it to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
         *
         * Requirements:
         *
         * - `tokenId` must not exist.
         * - `to` cannot be the zero address.
         *
         * Emits a {Transfer} event.
         */
        function _mint(address to, uint256 tokenId) internal virtual {
            require(to != address(0), "ERC721: mint to the zero address");
            require(!_exists(tokenId), "ERC721: token already minted");
            _beforeTokenTransfer(address(0), to, tokenId);
            _holderTokens[to].add(tokenId);
            _tokenOwners.set(tokenId, to);
            emit Transfer(address(0), to, tokenId);
        }
        /**
         * @dev Destroys `tokenId`.
         * The approval is cleared when the token is burned.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         *
         * Emits a {Transfer} event.
         */
        function _burn(uint256 tokenId) internal virtual {
            address owner = ERC721.ownerOf(tokenId); // internal owner
            _beforeTokenTransfer(owner, address(0), tokenId);
            // Clear approvals
            _approve(address(0), tokenId);
            // Clear metadata (if any)
            if (bytes(_tokenURIs[tokenId]).length != 0) {
                delete _tokenURIs[tokenId];
            }
            _holderTokens[owner].remove(tokenId);
            _tokenOwners.remove(tokenId);
            emit Transfer(owner, address(0), tokenId);
        }
        /**
         * @dev Transfers `tokenId` from `from` to `to`.
         *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         *
         * Emits a {Transfer} event.
         */
        function _transfer(address from, address to, uint256 tokenId) internal virtual {
            require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner
            require(to != address(0), "ERC721: transfer to the zero address");
            _beforeTokenTransfer(from, to, tokenId);
            // Clear approvals from the previous owner
            _approve(address(0), tokenId);
            _holderTokens[from].remove(tokenId);
            _holderTokens[to].add(tokenId);
            _tokenOwners.set(tokenId, to);
            emit Transfer(from, to, tokenId);
        }
        /**
         * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
            require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
            _tokenURIs[tokenId] = _tokenURI;
        }
        /**
         * @dev Internal function to set the base URI for all token IDs. It is
         * automatically added as a prefix to the value returned in {tokenURI},
         * or to the token ID if {tokenURI} is empty.
         */
        function _setBaseURI(string memory baseURI_) internal virtual {
            _baseURI = baseURI_;
        }
        /**
         * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
         * The call is not executed if the target address is not a contract.
         *
         * @param from address representing the previous owner of the given token ID
         * @param to target address that will receive the tokens
         * @param tokenId uint256 ID of the token to be transferred
         * @param _data bytes optional data to send along with the call
         * @return bool whether the call correctly returned the expected magic value
         */
        function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
            private returns (bool)
        {
            if (!to.isContract()) {
                return true;
            }
            bytes memory returndata = to.functionCall(abi.encodeWithSelector(
                IERC721Receiver(to).onERC721Received.selector,
                _msgSender(),
                from,
                tokenId,
                _data
            ), "ERC721: transfer to non ERC721Receiver implementer");
            bytes4 retval = abi.decode(returndata, (bytes4));
            return (retval == _ERC721_RECEIVED);
        }
        function _approve(address to, uint256 tokenId) private {
            _tokenApprovals[tokenId] = to;
            emit Approval(ERC721.ownerOf(tokenId), to, tokenId); // internal owner
        }
        /**
         * @dev Hook that is called before any token transfer. This includes minting
         * and burning.
         *
         * Calling conditions:
         *
         * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
         * transferred to `to`.
         * - When `from` is zero, `tokenId` will be minted for `to`.
         * - When `to` is zero, ``from``'s `tokenId` will be burned.
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "./IERC721.sol";
    /**
     * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
     * @dev See https://eips.ethereum.org/EIPS/eip-721
     */
    interface IERC721Metadata is IERC721 {
        /**
         * @dev Returns the token collection name.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @dev String operations.
     */
    library Strings {
        /**
         * @dev Converts a `uint256` to its ASCII `string` representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            // Inspired by OraclizeAPI's implementation - MIT licence
            // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
            if (value == 0) {
                return "0";
            }
            uint256 temp = value;
            uint256 digits;
            while (temp != 0) {
                digits++;
                temp /= 10;
            }
            bytes memory buffer = new bytes(digits);
            uint256 index = digits - 1;
            temp = value;
            while (temp != 0) {
                buffer[index--] = bytes1(uint8(48 + temp % 10));
                temp /= 10;
            }
            return string(buffer);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    /**
     * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
     *
     * These functions can be used to verify that a message was signed by the holder
     * of the private keys of a given address.
     */
    library ECDSA {
        /**
         * @dev Returns the address that signed a hashed message (`hash`) with
         * `signature`. This address can then be used for verification purposes.
         *
         * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
         * this function rejects them by requiring the `s` value to be in the lower
         * half order, and the `v` value to be either 27 or 28.
         *
         * IMPORTANT: `hash` _must_ be the result of a hash operation for the
         * verification to be secure: it is possible to craft signatures that
         * recover to arbitrary addresses for non-hashed data. A safe way to ensure
         * this is by receiving a hash of the original message (which may otherwise
         * be too long), and then calling {toEthSignedMessageHash} on it.
         */
        function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
            // Check the signature length
            if (signature.length != 65) {
                revert("ECDSA: invalid signature length");
            }
            // Divide the signature in r, s and v variables
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            // solhint-disable-next-line no-inline-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return recover(hash, v, r, s);
        }
        /**
         * @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
         * `r` and `s` signature fields separately.
         */
        function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
            // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
            // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
            // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
            // signatures from current libraries generate a unique signature with an s-value in the lower half order.
            //
            // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
            // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
            // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
            // these malleable signatures as well.
            require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
            require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
            // If the signature is valid (and not malleable), return the signer address
            address signer = ecrecover(hash, v, r, s);
            require(signer != address(0), "ECDSA: invalid signature");
            return signer;
        }
        /**
         * @dev Returns an Ethereum Signed Message, created from a `hash`. This
         * replicates the behavior of the
         * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
         * JSON-RPC method.
         *
         * See {recover}.
         */
        function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
            // 32 is the length in bytes of hash,
            // enforced by the type signature above
            return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
    32", hash));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    pragma experimental ABIEncoderV2;
    import "./../math/SafeMath.sol";
    import "./AccessControl.sol";
    /**
     * @dev Contract module which acts as a timelocked controller. When set as the
     * owner of an `Ownable` smart contract, it enforces a timelock on all
     * `onlyOwner` maintenance operations. This gives time for users of the
     * controlled contract to exit before a potentially dangerous maintenance
     * operation is applied.
     *
     * By default, this contract is self administered, meaning administration tasks
     * have to go through the timelock process. The proposer (resp executor) role
     * is in charge of proposing (resp executing) operations. A common use case is
     * to position this {TimelockController} as the owner of a smart contract, with
     * a multisig or a DAO as the sole proposer.
     *
     * _Available since v3.3._
     */
    contract TimelockController is AccessControl {
        bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE");
        bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
        bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
        uint256 internal constant _DONE_TIMESTAMP = uint256(1);
        mapping(bytes32 => uint256) private _timestamps;
        uint256 private _minDelay;
        /**
         * @dev Emitted when a call is scheduled as part of operation `id`.
         */
        event CallScheduled(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data, bytes32 predecessor, uint256 delay);
        /**
         * @dev Emitted when a call is performed as part of operation `id`.
         */
        event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data);
        /**
         * @dev Emitted when operation `id` is cancelled.
         */
        event Cancelled(bytes32 indexed id);
        /**
         * @dev Emitted when the minimum delay for future operations is modified.
         */
        event MinDelayChange(uint256 oldDuration, uint256 newDuration);
        /**
         * @dev Initializes the contract with a given `minDelay`.
         */
        constructor(uint256 minDelay, address[] memory proposers, address[] memory executors) {
            _setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE);
            _setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE);
            _setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE);
            // deployer + self administration
            _setupRole(TIMELOCK_ADMIN_ROLE, _msgSender());
            _setupRole(TIMELOCK_ADMIN_ROLE, address(this));
            // register proposers
            for (uint256 i = 0; i < proposers.length; ++i) {
                _setupRole(PROPOSER_ROLE, proposers[i]);
            }
            // register executors
            for (uint256 i = 0; i < executors.length; ++i) {
                _setupRole(EXECUTOR_ROLE, executors[i]);
            }
            _minDelay = minDelay;
            emit MinDelayChange(0, minDelay);
        }
        /**
         * @dev Modifier to make a function callable only by a certain role. In
         * addition to checking the sender's role, `address(0)` 's role is also
         * considered. Granting a role to `address(0)` is equivalent to enabling
         * this role for everyone.
         */
        modifier onlyRole(bytes32 role) {
            require(hasRole(role, _msgSender()) || hasRole(role, address(0)), "TimelockController: sender requires permission");
            _;
        }
        /**
         * @dev Contract might receive/hold ETH as part of the maintenance process.
         */
        receive() external payable {}
        /**
         * @dev Returns whether an id correspond to a registered operation. This
         * includes both Pending, Ready and Done operations.
         */
        function isOperation(bytes32 id) public view virtual returns (bool pending) {
            return getTimestamp(id) > 0;
        }
        /**
         * @dev Returns whether an operation is pending or not.
         */
        function isOperationPending(bytes32 id) public view virtual returns (bool pending) {
            return getTimestamp(id) > _DONE_TIMESTAMP;
        }
        /**
         * @dev Returns whether an operation is ready or not.
         */
        function isOperationReady(bytes32 id) public view virtual returns (bool ready) {
            uint256 timestamp = getTimestamp(id);
            // solhint-disable-next-line not-rely-on-time
            return timestamp > _DONE_TIMESTAMP && timestamp <= block.timestamp;
        }
        /**
         * @dev Returns whether an operation is done or not.
         */
        function isOperationDone(bytes32 id) public view virtual returns (bool done) {
            return getTimestamp(id) == _DONE_TIMESTAMP;
        }
        /**
         * @dev Returns the timestamp at with an operation becomes ready (0 for
         * unset operations, 1 for done operations).
         */
        function getTimestamp(bytes32 id) public view virtual returns (uint256 timestamp) {
            return _timestamps[id];
        }
        /**
         * @dev Returns the minimum delay for an operation to become valid.
         *
         * This value can be changed by executing an operation that calls `updateDelay`.
         */
        function getMinDelay() public view virtual returns (uint256 duration) {
            return _minDelay;
        }
        /**
         * @dev Returns the identifier of an operation containing a single
         * transaction.
         */
        function hashOperation(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt) public pure virtual returns (bytes32 hash) {
            return keccak256(abi.encode(target, value, data, predecessor, salt));
        }
        /**
         * @dev Returns the identifier of an operation containing a batch of
         * transactions.
         */
        function hashOperationBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt) public pure virtual returns (bytes32 hash) {
            return keccak256(abi.encode(targets, values, datas, predecessor, salt));
        }
        /**
         * @dev Schedule an operation containing a single transaction.
         *
         * Emits a {CallScheduled} event.
         *
         * Requirements:
         *
         * - the caller must have the 'proposer' role.
         */
        function schedule(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt, uint256 delay) public virtual onlyRole(PROPOSER_ROLE) {
            bytes32 id = hashOperation(target, value, data, predecessor, salt);
            _schedule(id, delay);
            emit CallScheduled(id, 0, target, value, data, predecessor, delay);
        }
        /**
         * @dev Schedule an operation containing a batch of transactions.
         *
         * Emits one {CallScheduled} event per transaction in the batch.
         *
         * Requirements:
         *
         * - the caller must have the 'proposer' role.
         */
        function scheduleBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt, uint256 delay) public virtual onlyRole(PROPOSER_ROLE) {
            require(targets.length == values.length, "TimelockController: length mismatch");
            require(targets.length == datas.length, "TimelockController: length mismatch");
            bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt);
            _schedule(id, delay);
            for (uint256 i = 0; i < targets.length; ++i) {
                emit CallScheduled(id, i, targets[i], values[i], datas[i], predecessor, delay);
            }
        }
        /**
         * @dev Schedule an operation that is to becomes valid after a given delay.
         */
        function _schedule(bytes32 id, uint256 delay) private {
            require(!isOperation(id), "TimelockController: operation already scheduled");
            require(delay >= getMinDelay(), "TimelockController: insufficient delay");
            // solhint-disable-next-line not-rely-on-time
            _timestamps[id] = SafeMath.add(block.timestamp, delay);
        }
        /**
         * @dev Cancel an operation.
         *
         * Requirements:
         *
         * - the caller must have the 'proposer' role.
         */
        function cancel(bytes32 id) public virtual onlyRole(PROPOSER_ROLE) {
            require(isOperationPending(id), "TimelockController: operation cannot be cancelled");
            delete _timestamps[id];
            emit Cancelled(id);
        }
        /**
         * @dev Execute an (ready) operation containing a single transaction.
         *
         * Emits a {CallExecuted} event.
         *
         * Requirements:
         *
         * - the caller must have the 'executor' role.
         */
        function execute(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt) public payable virtual onlyRole(EXECUTOR_ROLE) {
            bytes32 id = hashOperation(target, value, data, predecessor, salt);
            _beforeCall(predecessor);
            _call(id, 0, target, value, data);
            _afterCall(id);
        }
        /**
         * @dev Execute an (ready) operation containing a batch of transactions.
         *
         * Emits one {CallExecuted} event per transaction in the batch.
         *
         * Requirements:
         *
         * - the caller must have the 'executor' role.
         */
        function executeBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt) public payable virtual onlyRole(EXECUTOR_ROLE) {
            require(targets.length == values.length, "TimelockController: length mismatch");
            require(targets.length == datas.length, "TimelockController: length mismatch");
            bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt);
            _beforeCall(predecessor);
            for (uint256 i = 0; i < targets.length; ++i) {
                _call(id, i, targets[i], values[i], datas[i]);
            }
            _afterCall(id);
        }
        /**
         * @dev Checks before execution of an operation's calls.
         */
        function _beforeCall(bytes32 predecessor) private view {
            require(predecessor == bytes32(0) || isOperationDone(predecessor), "TimelockController: missing dependency");
        }
        /**
         * @dev Checks after execution of an operation's calls.
         */
        function _afterCall(bytes32 id) private {
            require(isOperationReady(id), "TimelockController: operation is not ready");
            _timestamps[id] = _DONE_TIMESTAMP;
        }
        /**
         * @dev Execute an operation's call.
         *
         * Emits a {CallExecuted} event.
         */
        function _call(bytes32 id, uint256 index, address target, uint256 value, bytes calldata data) private {
            // solhint-disable-next-line avoid-low-level-calls
            (bool success,) = target.call{value: value}(data);
            require(success, "TimelockController: underlying transaction reverted");
            emit CallExecuted(id, index, target, value, data);
        }
        /**
         * @dev Changes the minimum timelock duration for future operations.
         *
         * Emits a {MinDelayChange} event.
         *
         * Requirements:
         *
         * - the caller must be the timelock itself. This can only be achieved by scheduling and later executing
         * an operation where the timelock is the target and the data is the ABI-encoded call to this function.
         */
        function updateDelay(uint256 newDelay) external virtual {
            require(msg.sender == address(this), "TimelockController: caller must be timelock");
            emit MinDelayChange(_minDelay, newDelay);
            _minDelay = newDelay;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.7.0;
    import "../utils/EnumerableSet.sol";
    import "../utils/Address.sol";
    import "../utils/Context.sol";
    /**
     * @dev Contract module that allows children to implement role-based access
     * control mechanisms.
     *
     * Roles are referred to by their `bytes32` identifier. These should be exposed
     * in the external API and be unique. The best way to achieve this is by
     * using `public constant` hash digests:
     *
     * ```
     * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
     * ```
     *
     * Roles can be used to represent a set of permissions. To restrict access to a
     * function call, use {hasRole}:
     *
     * ```
     * function foo() public {
     *     require(hasRole(MY_ROLE, msg.sender));
     *     ...
     * }
     * ```
     *
     * Roles can be granted and revoked dynamically via the {grantRole} and
     * {revokeRole} functions. Each role has an associated admin role, and only
     * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
     *
     * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
     * that only accounts with this role will be able to grant or revoke other
     * roles. More complex role relationships can be created by using
     * {_setRoleAdmin}.
     *
     * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
     * grant and revoke this role. Extra precautions should be taken to secure
     * accounts that have been granted it.
     */
    abstract contract AccessControl is Context {
        using EnumerableSet for EnumerableSet.AddressSet;
        using Address for address;
        struct RoleData {
            EnumerableSet.AddressSet members;
            bytes32 adminRole;
        }
        mapping (bytes32 => RoleData) private _roles;
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
        /**
         * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
         *
         * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
         * {RoleAdminChanged} not being emitted signaling this.
         *
         * _Available since v3.1._
         */
        event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
        /**
         * @dev Emitted when `account` is granted `role`.
         *
         * `sender` is the account that originated the contract call, an admin role
         * bearer except when using {_setupRole}.
         */
        event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Emitted when `account` is revoked `role`.
         *
         * `sender` is the account that originated the contract call:
         *   - if using `revokeRole`, it is the admin role bearer
         *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
         */
        event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) public view returns (bool) {
            return _roles[role].members.contains(account);
        }
        /**
         * @dev Returns the number of accounts that have `role`. Can be used
         * together with {getRoleMember} to enumerate all bearers of a role.
         */
        function getRoleMemberCount(bytes32 role) public view returns (uint256) {
            return _roles[role].members.length();
        }
        /**
         * @dev Returns one of the accounts that have `role`. `index` must be a
         * value between 0 and {getRoleMemberCount}, non-inclusive.
         *
         * Role bearers are not sorted in any particular way, and their ordering may
         * change at any point.
         *
         * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
         * you perform all queries on the same block. See the following
         * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
         * for more information.
         */
        function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
            return _roles[role].members.at(index);
        }
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) public view returns (bytes32) {
            return _roles[role].adminRole;
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) public virtual {
            require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
            _grantRole(role, account);
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) public virtual {
            require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
            _revokeRole(role, account);
        }
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been granted `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         */
        function renounceRole(bytes32 role, address account) public virtual {
            require(account == _msgSender(), "AccessControl: can only renounce roles for self");
            _revokeRole(role, account);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event. Note that unlike {grantRole}, this function doesn't perform any
         * checks on the calling account.
         *
         * [WARNING]
         * ====
         * This function should only be called from the constructor when setting
         * up the initial roles for the system.
         *
         * Using this function in any other way is effectively circumventing the admin
         * system imposed by {AccessControl}.
         * ====
         */
        function _setupRole(bytes32 role, address account) internal virtual {
            _grantRole(role, account);
        }
        /**
         * @dev Sets `adminRole` as ``role``'s admin role.
         *
         * Emits a {RoleAdminChanged} event.
         */
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
            emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
            _roles[role].adminRole = adminRole;
        }
        function _grantRole(bytes32 role, address account) private {
            if (_roles[role].members.add(account)) {
                emit RoleGranted(role, account, _msgSender());
            }
        }
        function _revokeRole(bytes32 role, address account) private {
            if (_roles[role].members.remove(account)) {
                emit RoleRevoked(role, account, _msgSender());
            }
        }
    }