false
true
0

Contract Address Details

0x0567CA0dE35606E9C260CC2358404B11DE21DB44

Token
HELGO (HELGO)
Creator
0xb240bf–861ec9 at 0x5773a5–d568b3
Balance
0 PLS ( )
Tokens
Fetching tokens...
Transactions
6,034 Transactions
Transfers
0 Transfers
Gas Used
0
Last Balance Update
25964698
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
Helgo




Optimization enabled
true
Compiler version
v0.8.17+commit.8df45f5f




Optimization runs
639
EVM Version
default




Verified at
2023-06-15T00:37:25.018999Z

Constructor Arguments

0x0000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb39000000000000000000000000165c3410fc91ef562c50559f7d2289febed552d9000000000000000000000000c9cab624882418d8be742b570825a8d5eb8f89da

Arg [0] (address) : 0x2b591e99afe9f32eaa6214f7b7629768c40eeb39
Arg [1] (address) : 0x165c3410fc91ef562c50559f7d2289febed552d9
Arg [2] (address) : 0xc9cab624882418d8be742b570825a8d5eb8f89da

              

contracts/Helgo.sol

// SPDX-License-Identifier: UNLICENSED
/// @custom:security-contact dev@helgo.io

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

import "config/Settings.sol";
import "contracts/Heldao.sol";

import "interfaces/Helgo.sol";
import "interfaces/HEX.sol";
import "interfaces/IHelfactory.sol";
import "interfaces/IEmissions.sol";
import "interfaces/IUniswapV2Factory.sol";
import "interfaces/IUniswapV2Router02.sol";

import { ABDKMath64x64 as Math } from "abdk-libraries-solidity/ABDKMath64x64.sol";

contract Helgo is ERC20, Founders { 

    uint64 immutable public DAYZERO;
    uint64 immutable public DAYLAUNCH;
    bool public launched;

    Heldao immutable public HELDAO;

    IHelfactory immutable private HELFACTORY;
    IEmissions public HELGAS;
    IEmissions public HELLP;

    // HEX contract
    IHEX private HEX;
    IERC20 public PAIR;

    // Uniswap / PulseX factory
    IUniswapV2Router02 private ROUTER;
    IUniswapV2Factory private FACTORY;

    // Addreses that participate in launch phase
    address[] public participants; 

    // Total deposited
    uint256 public totalHEX; 
    uint256 public deposited; 
    uint256 public claimable; 
    uint256 public claimedHELGO; 
    uint256 public claimedHELLP; 

    // Track launch deposits    
    mapping(address => uint256) public deposits;
    mapping(address => bool) public claimers;

    event Deposited(address indexed addr, uint256 amount);    
    event Claimed(address indexed addr, uint256 amount, uint256 amountHELLP);
    event Locked(address indexed addr, uint256 amount);
    event Launched(uint256 timestamp);

    struct Globals {
        uint64 dayzero;
        uint64 daylaunch;
        uint64 today;
        uint64 day;
        bool launched;
        uint256 balance;
        uint256 deposit; 
        uint256 deposited;
        uint256 claimable; 
        uint256 claimedHELGO;
        uint256 claimedHELLP;
        uint256 participants;
    }

    struct Totals {
        uint256 supply;    // 100% HELGO total supply
        uint256 emissions; // 45% HELGO retained for emissions
        uint256 locked;    // 35% HELGO locked for 365 + 5555
        uint256 liquidity; // 10% HELGO for lquidity (contract retains 50%)
        uint256 liquid;    // 5% HELGO liquid 
        uint256 DAO;       // 5% HELGO retained in DAO
        uint256 HEX;       // HEX held by the contract at time of launch (desposited)
    }

    constructor(address payable hexAddr, address routerAddr, address factoryAddr) 
        ERC20("HELGO", "HELGO")
    {
        // Contract has total supply
        _mint(address(this), 55557777369 * 10 ** decimals());

        // Just use IERC20 for HEX
        HEX = IHEX(payable(hexAddr));

        // Set router 
        ROUTER = IUniswapV2Router02(routerAddr);
        FACTORY = IUniswapV2Factory(ROUTER.factory()); 
        
        // Emissions factories
        HELFACTORY = IHelfactory(factoryAddr);

        // Used to lock founders allocation + votes
        HELDAO = new Heldao(address(this), routerAddr, hexAddr);

        // Contract day zero (UTC)
        DAYZERO = today();

        // Launch date
        DAYLAUNCH = today() + HELGO_LAUNCH_LENGTH;
    }

    ///////////////
    // VIEW ONLY //
    ///////////////

    function globals() public view returns (Globals memory) {
        return Globals(
            DAYZERO,
            DAYLAUNCH,
            today(),
            day(),
            launched,
            balanceOf(msg.sender),
            deposits[msg.sender],
            deposited,
            claimable,
            claimedHELGO,
            claimedHELLP,            
            participants.length
        );
    }

    function totals() public view returns (Totals memory) {
        return Totals(
            totalSupply(),
            totalEmissions(),
            totalLocked(),
            totalLiquidity(),
            totalLiquid(),
            totalDAO(),
            totalHEX 
        );
    }

    function today() public view returns (uint64) {
        return uint64((block.timestamp / 1 days) * 1 days);
    }

    function day() public view returns (uint64) {
        uint64 diff = (today() - DAYZERO);
        if(diff == 0) {
            return 0;
        }
        return diff / 1 days;
    }

    function getDeposit(address addr) public view returns(uint256) {
        return deposits[addr];
    }

    function getParticipant(uint i) public view returns (address) {
        return participants[i];
    }

    function totalParticipants() public view returns(uint count){
        return participants.length;
    }

    function totalEmissions() public view returns(uint256){
        return (totalSupply() / 20) * 9; // 45
    }

    function totalLocked() public view returns(uint256){
        return (totalSupply() / 20) * 7; // 35
    }    

    function totalLiquidity() public view returns(uint256){
        return (totalSupply() / 20) * 2; // 10
    }    

    function totalDAO() public view returns(uint256){
        return (totalSupply() / 20) * 1; // 5
    }

    function totalLiquid() public view returns(uint256){
        return (totalSupply() / 20) * 1; // 5
    }    

    function balanceHEX() public view returns(uint256){
        return HEX.balanceOf(address(this));
    }

    /////////////
    // PUBLIC  //
    /////////////

    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    // @dev allow caller to claim
    function claim() external returns(uint256, uint256){
        return _distribute(msg.sender);
    }

    // @dev distribute launch allocation to an address
    function distribute(address addr) external returns(uint256, uint256){
        return _distribute(addr);
    }

    // @notifiy deposit HEX for launch
    function deposit(uint256 amount) public {

        // Deposit
        _deposit(msg.sender, amount);

        // Transfer the HEX
        HEX.transferFrom(msg.sender, address(this), amount);  
    }

    // @notifiy transform Pulse for HEX and deposit
    function transform() public payable {
        _transform(msg.sender, msg.value);
    }

    function launch() public {
        _launch();
    }
    
    function fundHELGAS(uint256 amount) public {
        _transfer(msg.sender, address(this), amount);
        HELGAS.fund(amount);
    }

    function fundHELLP(uint256 amount) public {
        PAIR.transferFrom(msg.sender, address(this), amount);
        HELLP.fund(amount);
    }

    function lock(uint256 amount, uint64 start, uint64 duration) public returns(bool) {
        return _lock(msg.sender, amount, start, duration);
    }

    /****************************
        P r i v a t e
    /****************************/   

    function _lock(address addr, uint256 amount, uint64 start, uint64 duration) private returns(bool) {
        // Get / create a vault for the addr
        address vault = HELDAO.initVault(msg.sender, start, duration);

        // Lock the HELGO
        _transfer(addr, vault, amount);

        emit Locked(addr, amount);

        return true;
    }

    // @dev deposit HEX into launch phase
    function _deposit(address addr, uint256 amount) private {
        require(amount > 0, "Deposit amount must be greater than zero");
        require(!launched, "Already launched, deposits closed");

        // Add address to 
        if(deposits[addr] == 0) {
            participants.push(addr);
        }

        // Internal
        deposited += amount;
        deposits[addr] += amount;

        emit Deposited(addr, amount);
    }

    // @dev deposit Pulse, swapped for HEX and deposited
    function _transform(address addr, uint256 value) private {
        require(value > 0, 'Amount must be greater than zero');

        // Swap path
        address[] memory path;
        path = new address[](2);
        path[0] = address(ROUTER) == ROUTERPULSEX ? WPLS : WETH;
        path[1] = address(HEX);

        // Get amounts
        uint[] memory amounts = ROUTER.getAmountsOut(value, path);

        // Expected amount
        uint256 amount = amounts[amounts.length - 1];

        // Swap for HEX exact amount
        ROUTER.swapExactETHForTokens{value: value}(amount, path, address(this), block.timestamp + 10000);

        // Now deposit
        _deposit(addr, amount);
    }

    // @dev distribute allocation for an address
    function _distribute(address addr) private returns(uint256, uint256){
        require(launched, "Not launched");
        require(!claimers[addr], "Already claimed");
        
        // Record claim
        claimers[addr] = true;

        // Amount of HELLP
        uint256 amountHELLP; 

        // Amount of HELGO
        uint256 amountHELGO; 

        if(deposits[addr] > 0){

            // Share of deposits
            int128 share = Math.divu(deposits[addr], deposited);

            // HELLP
            amountHELLP = Math.mulu(share, claimable / 2);

            // HELGO
            amountHELGO = Math.mulu(share, totalLiquid() / 2);
        }

        // Check if founder
        if(founder[addr]) {

            // Include founder allocations
            amountHELLP += claimable / (2 * HELGO_FOUNDERS.length);
            amountHELGO += totalLiquid() / (2 * HELGO_FOUNDERS.length);

            // Lock founder allocation
            uint256 locked = totalLocked() / HELGO_FOUNDERS.length;
            _transfer(address(this), addr, locked);
            _lock(addr, locked, DAYZERO + FOUNDERS_CLIFF, FOUNDERS_VESTING);
        }
        else {
            require(deposits[addr] > 0, "No deposit");
        }

        // Increased claim totals
        claimedHELLP += amountHELLP;
        claimedHELGO += amountHELGO;

        // Release PAIR
        if(amountHELLP > 0) {
            PAIR.transfer(addr, amountHELLP);
        }

        // Release HELGO
        if(amountHELGO > 0) {
           _transfer(address(this), addr, amountHELGO);
        }

        // Emit claimed event
        emit Claimed(addr, amountHELGO, amountHELLP);

        return (amountHELGO, amountHELLP);
    }

    // @dev end launch phase
    function _launch() private {
        require(!launched, "Already launched");
        require(today() >= DAYLAUNCH, "Too soon to launch");

        launched = true;

        // Snapshot launch HEX
        totalHEX = HEX.balanceOf(address(this));        

        // DAO 
        _launchHELDAO();

        // HELGO
        _launchHELGO();

        // HELLP
        _launchHELLP();

        // Emit event
        emit Launched(block.timestamp);        
    }    

    // @dev launch DAO and fund
    function _launchHELDAO() private {
        // Fund HELDAO with HELGO
        _transfer(address(this), address(HELDAO), totalDAO());

        // Fund HELDAO with HEX
        HEX.transfer(address(HELDAO), totalHEX / 20);
    }

    // @dev launch HELGAS and fund
    function _launchHELGO() private {
        // Populate emissions
        HELGAS = IEmissions(HELFACTORY.newHelgas(address(this)));

        // Approve HELGO for funding emissions
        _approve(address(this), address(HELGAS), type(uint256).max);

        // Fund emissions
        HELGAS.fund(totalEmissions());
    }

    // @dev launch HELLP and fund
    function _launchHELLP() private {
        // Get or create the pair via the factory
        address token0 = address(this);
        address token1 = address(HEX);
        address pair = FACTORY.getPair(token0, token1);

        // Create pair if needed
        if(pair == address(0)) {
            pair = FACTORY.createPair(token0, token1);
        }

        // Approve HELGO for LP 
        _approve(address(this), address(ROUTER), totalLiquidity());

        uint256 liquidityHEX = HEX.balanceOf(address(this));

        // Approve HEX for LP
        HEX.approve(address(ROUTER), liquidityHEX);

        // Add liquidity to pair
        (, , uint liquidity) = ROUTER.addLiquidity(
            token0,
            token1,
            totalLiquidity(),
            liquidityHEX,
            totalLiquidity(),
            liquidityHEX,
            token0,
            block.timestamp + 1000000
        );

        // Populate PAIR emissions
        HELLP = IEmissions(HELFACTORY.newHellp(pair, address(ROUTER), token0, token1));

        //! This could potentially be IUniswapV2ERC20
        PAIR = IERC20(pair);

        // Approve HELLP for funding emissions
        PAIR.approve(address(HELLP), type(uint256).max);
        
        // Fund emissions
        HELLP.fund(liquidity / 2);

        // Retain claimable
        claimable = liquidity - (liquidity / 2);

        // Approve claimable
        PAIR.approve(address(this), claimable);

    }
}
        

@openzeppelin/contracts/proxy/Clones.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol)

pragma solidity ^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 `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(0, 0x09, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(0, 0x09, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}
          

@openzeppelin/contracts/token/ERC20/ERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.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.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead 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, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override 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 default value returned by this function, unless
     * it's overridden.
     *
     * 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 override returns (uint8) {
        return 18;
    }

    /**
     * @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:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, 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}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, 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}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        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) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + 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) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This 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:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, 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:
     *
     * - `account` 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 += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(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);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(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 Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

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

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been 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 _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
          

@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
          

@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

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

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

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

@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

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

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
     * 0 before setting it to a non-zero value.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // 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 cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}
          

@openzeppelin/contracts/utils/Address.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/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.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

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

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

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

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

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

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

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

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

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

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

@openzeppelin/contracts/utils/Context.sol

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

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

abdk-libraries-solidity/ABDKMath64x64.sol

// SPDX-License-Identifier: BSD-4-Clause
/*
 * ABDK Math 64.64 Smart Contract Library.  Copyright © 2019 by ABDK Consulting.
 * Author: Mikhail Vladimirov <mikhail.vladimirov@gmail.com>
 */
pragma solidity ^0.8.0;

/**
 * Smart contract library of mathematical functions operating with signed
 * 64.64-bit fixed point numbers.  Signed 64.64-bit fixed point number is
 * basically a simple fraction whose numerator is signed 128-bit integer and
 * denominator is 2^64.  As long as denominator is always the same, there is no
 * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are
 * represented by int128 type holding only the numerator.
 */

library ABDKMath64x64 {
  /*
   * Minimum value signed 64.64-bit fixed point number may have. 
   */
  int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;

  /*
   * Maximum value signed 64.64-bit fixed point number may have. 
   */
  int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

  /**
   * Convert signed 256-bit integer number into signed 64.64-bit fixed point
   * number.  Revert on overflow.
   *
   * @param x signed 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function fromInt (int256 x) internal pure returns (int128) {
    unchecked {
      require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);
      return int128 (x << 64);
    }
  }

  /**
   * Convert signed 64.64 fixed point number into signed 64-bit integer number
   * rounding down.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64-bit integer number
   */
  function toInt (int128 x) internal pure returns (int64) {
    unchecked {
      return int64 (x >> 64);
    }
  }

  /**
   * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point
   * number.  Revert on overflow.
   *
   * @param x unsigned 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function fromUInt (uint256 x) internal pure returns (int128) {
    unchecked {
      require (x <= 0x7FFFFFFFFFFFFFFF);
      return int128 (int256 (x << 64));
    }
  }

  /**
   * Convert signed 64.64 fixed point number into unsigned 64-bit integer
   * number rounding down.  Revert on underflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return unsigned 64-bit integer number
   */
  function toUInt (int128 x) internal pure returns (uint64) {
    unchecked {
      require (x >= 0);
      return uint64 (uint128 (x >> 64));
    }
  }

  /**
   * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point
   * number rounding down.  Revert on overflow.
   *
   * @param x signed 128.128-bin fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function from128x128 (int256 x) internal pure returns (int128) {
    unchecked {
      int256 result = x >> 64;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Convert signed 64.64 fixed point number into signed 128.128 fixed point
   * number.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 128.128 fixed point number
   */
  function to128x128 (int128 x) internal pure returns (int256) {
    unchecked {
      return int256 (x) << 64;
    }
  }

  /**
   * Calculate x + y.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function add (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) + y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x - y.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function sub (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) - y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x * y rounding down.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function mul (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) * y >> 64;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point
   * number and y is signed 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64 fixed point number
   * @param y signed 256-bit integer number
   * @return signed 256-bit integer number
   */
  function muli (int128 x, int256 y) internal pure returns (int256) {
    unchecked {
      if (x == MIN_64x64) {
        require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&
          y <= 0x1000000000000000000000000000000000000000000000000);
        return -y << 63;
      } else {
        bool negativeResult = false;
        if (x < 0) {
          x = -x;
          negativeResult = true;
        }
        if (y < 0) {
          y = -y; // We rely on overflow behavior here
          negativeResult = !negativeResult;
        }
        uint256 absoluteResult = mulu (x, uint256 (y));
        if (negativeResult) {
          require (absoluteResult <=
            0x8000000000000000000000000000000000000000000000000000000000000000);
          return -int256 (absoluteResult); // We rely on overflow behavior here
        } else {
          require (absoluteResult <=
            0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
          return int256 (absoluteResult);
        }
      }
    }
  }

  /**
   * Calculate x * y rounding down, where x is signed 64.64 fixed point number
   * and y is unsigned 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64 fixed point number
   * @param y unsigned 256-bit integer number
   * @return unsigned 256-bit integer number
   */
  function mulu (int128 x, uint256 y) internal pure returns (uint256) {
    unchecked {
      if (y == 0) return 0;

      require (x >= 0);

      uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;
      uint256 hi = uint256 (int256 (x)) * (y >> 128);

      require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
      hi <<= 64;

      require (hi <=
        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo);
      return hi + lo;
    }
  }

  /**
   * Calculate x / y rounding towards zero.  Revert on overflow or when y is
   * zero.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function div (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);
      int256 result = (int256 (x) << 64) / y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are signed 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x signed 256-bit integer number
   * @param y signed 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function divi (int256 x, int256 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);

      bool negativeResult = false;
      if (x < 0) {
        x = -x; // We rely on overflow behavior here
        negativeResult = true;
      }
      if (y < 0) {
        y = -y; // We rely on overflow behavior here
        negativeResult = !negativeResult;
      }
      uint128 absoluteResult = divuu (uint256 (x), uint256 (y));
      if (negativeResult) {
        require (absoluteResult <= 0x80000000000000000000000000000000);
        return -int128 (absoluteResult); // We rely on overflow behavior here
      } else {
        require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
        return int128 (absoluteResult); // We rely on overflow behavior here
      }
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x unsigned 256-bit integer number
   * @param y unsigned 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function divu (uint256 x, uint256 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);
      uint128 result = divuu (x, y);
      require (result <= uint128 (MAX_64x64));
      return int128 (result);
    }
  }

  /**
   * Calculate -x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function neg (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != MIN_64x64);
      return -x;
    }
  }

  /**
   * Calculate |x|.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function abs (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != MIN_64x64);
      return x < 0 ? -x : x;
    }
  }

  /**
   * Calculate 1 / x rounding towards zero.  Revert on overflow or when x is
   * zero.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function inv (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != 0);
      int256 result = int256 (0x100000000000000000000000000000000) / x;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function avg (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      return int128 ((int256 (x) + int256 (y)) >> 1);
    }
  }

  /**
   * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.
   * Revert on overflow or in case x * y is negative.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function gavg (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 m = int256 (x) * int256 (y);
      require (m >= 0);
      require (m <
          0x4000000000000000000000000000000000000000000000000000000000000000);
      return int128 (sqrtu (uint256 (m)));
    }
  }

  /**
   * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number
   * and y is unsigned 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y uint256 value
   * @return signed 64.64-bit fixed point number
   */
  function pow (int128 x, uint256 y) internal pure returns (int128) {
    unchecked {
      bool negative = x < 0 && y & 1 == 1;

      uint256 absX = uint128 (x < 0 ? -x : x);
      uint256 absResult;
      absResult = 0x100000000000000000000000000000000;

      if (absX <= 0x10000000000000000) {
        absX <<= 63;
        while (y != 0) {
          if (y & 0x1 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x2 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x4 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x8 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          y >>= 4;
        }

        absResult >>= 64;
      } else {
        uint256 absXShift = 63;
        if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; }
        if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; }
        if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; }
        if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; }
        if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; }
        if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; }

        uint256 resultShift = 0;
        while (y != 0) {
          require (absXShift < 64);

          if (y & 0x1 != 0) {
            absResult = absResult * absX >> 127;
            resultShift += absXShift;
            if (absResult > 0x100000000000000000000000000000000) {
              absResult >>= 1;
              resultShift += 1;
            }
          }
          absX = absX * absX >> 127;
          absXShift <<= 1;
          if (absX >= 0x100000000000000000000000000000000) {
              absX >>= 1;
              absXShift += 1;
          }

          y >>= 1;
        }

        require (resultShift < 64);
        absResult >>= 64 - resultShift;
      }
      int256 result = negative ? -int256 (absResult) : int256 (absResult);
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate sqrt (x) rounding down.  Revert if x < 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function sqrt (int128 x) internal pure returns (int128) {
    unchecked {
      require (x >= 0);
      return int128 (sqrtu (uint256 (int256 (x)) << 64));
    }
  }

  /**
   * Calculate binary logarithm of x.  Revert if x <= 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function log_2 (int128 x) internal pure returns (int128) {
    unchecked {
      require (x > 0);

      int256 msb = 0;
      int256 xc = x;
      if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }
      if (xc >= 0x100000000) { xc >>= 32; msb += 32; }
      if (xc >= 0x10000) { xc >>= 16; msb += 16; }
      if (xc >= 0x100) { xc >>= 8; msb += 8; }
      if (xc >= 0x10) { xc >>= 4; msb += 4; }
      if (xc >= 0x4) { xc >>= 2; msb += 2; }
      if (xc >= 0x2) msb += 1;  // No need to shift xc anymore

      int256 result = msb - 64 << 64;
      uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb);
      for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {
        ux *= ux;
        uint256 b = ux >> 255;
        ux >>= 127 + b;
        result += bit * int256 (b);
      }

      return int128 (result);
    }
  }

  /**
   * Calculate natural logarithm of x.  Revert if x <= 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function ln (int128 x) internal pure returns (int128) {
    unchecked {
      require (x > 0);

      return int128 (int256 (
          uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128));
    }
  }

  /**
   * Calculate binary exponent of x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function exp_2 (int128 x) internal pure returns (int128) {
    unchecked {
      require (x < 0x400000000000000000); // Overflow

      if (x < -0x400000000000000000) return 0; // Underflow

      uint256 result = 0x80000000000000000000000000000000;

      if (x & 0x8000000000000000 > 0)
        result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128;
      if (x & 0x4000000000000000 > 0)
        result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128;
      if (x & 0x2000000000000000 > 0)
        result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128;
      if (x & 0x1000000000000000 > 0)
        result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128;
      if (x & 0x800000000000000 > 0)
        result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128;
      if (x & 0x400000000000000 > 0)
        result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128;
      if (x & 0x200000000000000 > 0)
        result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128;
      if (x & 0x100000000000000 > 0)
        result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128;
      if (x & 0x80000000000000 > 0)
        result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128;
      if (x & 0x40000000000000 > 0)
        result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128;
      if (x & 0x20000000000000 > 0)
        result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128;
      if (x & 0x10000000000000 > 0)
        result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128;
      if (x & 0x8000000000000 > 0)
        result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128;
      if (x & 0x4000000000000 > 0)
        result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128;
      if (x & 0x2000000000000 > 0)
        result = result * 0x1000162E525EE054754457D5995292026 >> 128;
      if (x & 0x1000000000000 > 0)
        result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128;
      if (x & 0x800000000000 > 0)
        result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128;
      if (x & 0x400000000000 > 0)
        result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128;
      if (x & 0x200000000000 > 0)
        result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128;
      if (x & 0x100000000000 > 0)
        result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128;
      if (x & 0x80000000000 > 0)
        result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128;
      if (x & 0x40000000000 > 0)
        result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128;
      if (x & 0x20000000000 > 0)
        result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128;
      if (x & 0x10000000000 > 0)
        result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128;
      if (x & 0x8000000000 > 0)
        result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128;
      if (x & 0x4000000000 > 0)
        result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128;
      if (x & 0x2000000000 > 0)
        result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128;
      if (x & 0x1000000000 > 0)
        result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128;
      if (x & 0x800000000 > 0)
        result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128;
      if (x & 0x400000000 > 0)
        result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128;
      if (x & 0x200000000 > 0)
        result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128;
      if (x & 0x100000000 > 0)
        result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128;
      if (x & 0x80000000 > 0)
        result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128;
      if (x & 0x40000000 > 0)
        result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128;
      if (x & 0x20000000 > 0)
        result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128;
      if (x & 0x10000000 > 0)
        result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128;
      if (x & 0x8000000 > 0)
        result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128;
      if (x & 0x4000000 > 0)
        result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128;
      if (x & 0x2000000 > 0)
        result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128;
      if (x & 0x1000000 > 0)
        result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128;
      if (x & 0x800000 > 0)
        result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128;
      if (x & 0x400000 > 0)
        result = result * 0x100000000002C5C85FDF477B662B26945 >> 128;
      if (x & 0x200000 > 0)
        result = result * 0x10000000000162E42FEFA3AE53369388C >> 128;
      if (x & 0x100000 > 0)
        result = result * 0x100000000000B17217F7D1D351A389D40 >> 128;
      if (x & 0x80000 > 0)
        result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128;
      if (x & 0x40000 > 0)
        result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128;
      if (x & 0x20000 > 0)
        result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128;
      if (x & 0x10000 > 0)
        result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128;
      if (x & 0x8000 > 0)
        result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128;
      if (x & 0x4000 > 0)
        result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128;
      if (x & 0x2000 > 0)
        result = result * 0x1000000000000162E42FEFA39F02B772C >> 128;
      if (x & 0x1000 > 0)
        result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128;
      if (x & 0x800 > 0)
        result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128;
      if (x & 0x400 > 0)
        result = result * 0x100000000000002C5C85FDF473DEA871F >> 128;
      if (x & 0x200 > 0)
        result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128;
      if (x & 0x100 > 0)
        result = result * 0x100000000000000B17217F7D1CF79E949 >> 128;
      if (x & 0x80 > 0)
        result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128;
      if (x & 0x40 > 0)
        result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128;
      if (x & 0x20 > 0)
        result = result * 0x100000000000000162E42FEFA39EF366F >> 128;
      if (x & 0x10 > 0)
        result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128;
      if (x & 0x8 > 0)
        result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128;
      if (x & 0x4 > 0)
        result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128;
      if (x & 0x2 > 0)
        result = result * 0x1000000000000000162E42FEFA39EF358 >> 128;
      if (x & 0x1 > 0)
        result = result * 0x10000000000000000B17217F7D1CF79AB >> 128;

      result >>= uint256 (int256 (63 - (x >> 64)));
      require (result <= uint256 (int256 (MAX_64x64)));

      return int128 (int256 (result));
    }
  }

  /**
   * Calculate natural exponent of x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function exp (int128 x) internal pure returns (int128) {
    unchecked {
      require (x < 0x400000000000000000); // Overflow

      if (x < -0x400000000000000000) return 0; // Underflow

      return exp_2 (
          int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128));
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x unsigned 256-bit integer number
   * @param y unsigned 256-bit integer number
   * @return unsigned 64.64-bit fixed point number
   */
  function divuu (uint256 x, uint256 y) private pure returns (uint128) {
    unchecked {
      require (y != 0);

      uint256 result;

      if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
        result = (x << 64) / y;
      else {
        uint256 msb = 192;
        uint256 xc = x >> 192;
        if (xc >= 0x100000000) { xc >>= 32; msb += 32; }
        if (xc >= 0x10000) { xc >>= 16; msb += 16; }
        if (xc >= 0x100) { xc >>= 8; msb += 8; }
        if (xc >= 0x10) { xc >>= 4; msb += 4; }
        if (xc >= 0x4) { xc >>= 2; msb += 2; }
        if (xc >= 0x2) msb += 1;  // No need to shift xc anymore

        result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1);
        require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

        uint256 hi = result * (y >> 128);
        uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

        uint256 xh = x >> 192;
        uint256 xl = x << 64;

        if (xl < lo) xh -= 1;
        xl -= lo; // We rely on overflow behavior here
        lo = hi << 128;
        if (xl < lo) xh -= 1;
        xl -= lo; // We rely on overflow behavior here

        assert (xh == hi >> 128);

        result += xl / y;
      }

      require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
      return uint128 (result);
    }
  }

  /**
   * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer
   * number.
   *
   * @param x unsigned 256-bit integer number
   * @return unsigned 128-bit integer number
   */
  function sqrtu (uint256 x) private pure returns (uint128) {
    unchecked {
      if (x == 0) return 0;
      else {
        uint256 xx = x;
        uint256 r = 1;
        if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; }
        if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; }
        if (xx >= 0x100000000) { xx >>= 32; r <<= 16; }
        if (xx >= 0x10000) { xx >>= 16; r <<= 8; }
        if (xx >= 0x100) { xx >>= 8; r <<= 4; }
        if (xx >= 0x10) { xx >>= 4; r <<= 2; }
        if (xx >= 0x8) { r <<= 1; }
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1; // Seven iterations should be enough
        uint256 r1 = x / r;
        return uint128 (r < r1 ? r : r1);
      }
    }
  }
}
          

config/Settings.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

uint256 constant HELLP_RAMP_FREQ    = 1 days;
 int128 constant HELLP_EMIT_RATE    = 1690 * 10 ** 14;
 int128 constant HELLP_DEPOSIT_FEE  = 69 * 10 ** 14;
 int128 constant HELLP_WITHDRAW_FEE = 69 * 10 ** 14;
 int128 constant HELLP_REWARD       = 1 * 10 ** 14;
 string constant HELLP_NAME         = 'HELLP-Emissions';
 string constant HELLP_TOKEN        = 'HELLP';

uint256 constant HELGAS_RAMP_FREQ    = 1 days;
 int128 constant HELGAS_EMIT_RATE    = 669 * 10 ** 14;
 int128 constant HELGAS_DEPOSIT_FEE  = 69 * 10 ** 14;
 int128 constant HELGAS_WITHDRAW_FEE = 69 * 10 ** 14;
 int128 constant HELGAS_REWARD       = 1 * 10 ** 14;
 string constant HELGAS_NAME         = 'HELGO-Emissions';
 string constant HELGAS_TOKEN        = 'HELGAS';

 uint64 constant FOUNDERS_CLIFF   = 60 * 60 * 24 * 365; 
 uint64 constant FOUNDERS_VESTING = 60 * 60 * 24 * 5555;
 uint64 constant HELGO_LAUNCH_LENGTH = 33 days;

 int128 constant MEME_SMALL_PERC  = 69 * 10 ** 14;
 int128 constant MEME_LARGE_PERC  = 369 * 10 ** 14;

 uint64 constant VOTE_FREQUENCY     = 1 days;
 uint64 constant VOTE_MIN_FREQUENCY = 3 hours;
 uint64 constant VOTE_MAX_FREQUENCY = 4 weeks;

 uint64 constant VOTE_MIN_PRICE_DAYS                             = 3;
   uint constant VOTE_IDIOT_T_MAN                                = 1;
   uint constant VOTE_SHUT_UP_IDIOT_MAN                          = 2;
   uint constant VOTE_I_AM_HELGO                                 = 3;
   uint constant VOTE_PLEASE_EVERY_BODY_REMEMBER_TO_BUY_HEX      = 4;
   uint constant VOTE_DOUBLE_PERIOD                              = 5;
   uint constant VOTE_HALF_PERIOD                                = 6;
 string constant VOTE_DESC_IDIOT_T_MAN                           = 'Use 0.69% of HEX to buy HELGO';
 string constant VOTE_DESC_SHUT_UP_IDIOT_MAN                     = 'Use 0.69% of HELGO to buy HEX';
 string constant VOTE_DESC_I_AM_HELGO                            = 'Uses 3.69% of HEX to buy HELGO (votable when HEX near ATH)';
 string constant VOTE_DESC_PLEASE_EVERY_BODY_REMEMBER_TO_BUY_HEX = 'Uses 3.69% of HELGO to buy HEX (votable when HEX at 3 month low)';
 string constant VOTE_DESC_DOUBLE_PERIOD                         = 'Doubles the length of the vote period';
 string constant VOTE_DESC_HALF_PERIOD                           = 'Halves the length of the vote period';

// Uniswap V2 + PulseX router
address constant ROUTERV2 = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

// LIVE, PulseX router V2
address constant ROUTERPULSEX = 0x165C3410fC91EF562C50559f7d2289fEbed552d9;
address constant WPLS = 0xA1077a294dDE1B09bB078844df40758a5D0f9a27;

contract Founders {
    // Founder addresses
    address[] public HELGO_FOUNDERS = [
        0x892097a6Bbf318e8B3b5d27044782975DcfBc47B,
        0xb7Da80B7db32f57281545d45ffD691914dc2252b,
        0x83559662dE19aaB3BECc817865061aF699421B9a,
        0x13E0A697a1dF2Fc28f6D8f4F98F417be51CA5232,
        0xF999e2E31cE9c70F697545a99dF20D4346B94e26,
        0x19Df3fC60AE23784cb2009C6a32E73999Dd7b4A4,
        0x10F0A702D1FF73d54404BA96A5E114B0A6574d05,
        0x0dC72F41FA93260c2A909408525dF37491d42999,
        0xE3AF5DB5947e4eFea79747C8A5C09DA728313BCC,
        0x728AacBcE90709d8Ee3DD7C42e56A5512CF742c2,
        0xDE0Fb3aF3B72AfD2e39c525E476d8Fbea1b6b006,
        0xA97cA893f62e845b929f65a8d128Cb71D03F3Be3,
        0x4d0e4012F8E0428E90FC36DFF351aF9788e0c60E,
        0x7e04D6194C2ECBf7Ee730B36E17d29726ea9c5FA,
        0x662F84b2f9507cfC02fb259C8c64455fAe8d01b6,
        0x48FcC86Bb079B97dDE0D10026B22069Df5c6630A,
        0x0De4aA116a9B536e9b7c17F2743925117Cdc57E0,
        0xDC6A8C0505EbC3954da4030A135fEe5e568F7d09,
        0xf027324b27Dff4243f13208F1a3aE34b6b514b3A,
        0x4555261e2d5A9A2b9b0FCF26f534a8fd8A8F2f9D,
        0x47C81C1D47742B41fF7d1c04e08d3036aE01F4a1,
        0x9C2f87D3ae338e4fE5f317a4219762dE7CE0eb24,
        0xE33b7F805c97A7f88963CE20629bbe88a9d08986,
        0x307F17350086232FEaa6E17808662A5AEF29841f,
        0x7CC483F2ba233F6F4b52f43b8b657D1071e5F5aB,
        0x2a25da22E94884f3b7B6841a98f21325E131C4b6,
        0x212ff16Ea41fc0394625c9e2D5D45532ea4Cf100,
        0xBeB348B205841be55bF4Fe1732c2a81b390c50d9,
        0x4D30801092100F9E0aA3bf31336B12cBa4dAb29b,
        0xE40569F208Ec96Adad9E0127b5724d50D15E039d,
        0x895A1feFf0ce1DFEB5C15f2c237223537Fc87FE2,
        0x7b156358C4a63a01088CB496212eA02412e34F75,
        0x717B8898860B1d45A621e41360f143DA2C8bc292,
        0x55f7B7286AE9a9Aeabc5cB79Cb8ede034665B20E,
        0x5bC36C7187aA603576CF95108B1107e087Aff64D,
        0x0a93F5Bc1a0aEbFa92180eEc1d4990827446188A,
        0x011df7c4D325290e5D07689E057c5527BdBBcEb0
    ];

    uint public immutable TOTAL_FOUNDERS;

    mapping(address => bool) public founder;

    constructor(){    
        for (uint i=0; i < HELGO_FOUNDERS.length; i++) {
            founder[HELGO_FOUNDERS[i]] = true;
        }
        TOTAL_FOUNDERS = HELGO_FOUNDERS.length;
    }
}
          

contracts/Heldao.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "contracts/Vault.sol";
import "interfaces/Helgo.sol";
import "interfaces/HEX.sol";
import "interfaces/IUniswapV2Router01.sol";
import "contracts/Votable.sol";
import "config/Settings.sol";

/**
 * @dev HELDAO
 *
 * Used to lock HELGO
 * Used for MEME votes
 * Creates Vault when necessary
 * Provides vote relative to time locked
 */

import { ABDKMath64x64 as Math } from "abdk-libraries-solidity/ABDKMath64x64.sol";

contract Heldao is Votable {

    IUniswapV2Router01 private ROUTER;
    IHEX immutable private HEX;
    IHELGO immutable private HELGO;
    Vault immutable private VAULT;

    uint64 private priceChecked;

    uint256[] public priceHistory;

    constructor(address _helgo, address _router, address _hex)
        Votable(VOTE_FREQUENCY)
    {
        ROUTER = IUniswapV2Router01(_router);
        HELGO = IHELGO(_helgo);
        HEX = IHEX(payable(_hex)); 
        VAULT = new Vault();

        // Add default proposals
        addProposal(VOTE_IDIOT_T_MAN, VOTE_DESC_IDIOT_T_MAN);
        addProposal(VOTE_SHUT_UP_IDIOT_MAN, VOTE_DESC_SHUT_UP_IDIOT_MAN);
        addProposal(VOTE_PLEASE_EVERY_BODY_REMEMBER_TO_BUY_HEX, VOTE_DESC_PLEASE_EVERY_BODY_REMEMBER_TO_BUY_HEX);
        addProposal(VOTE_I_AM_HELGO, VOTE_DESC_I_AM_HELGO);
        addProposal(VOTE_DOUBLE_PERIOD, VOTE_DESC_DOUBLE_PERIOD);
        addProposal(VOTE_HALF_PERIOD, VOTE_DESC_HALF_PERIOD);
        lockProposals();
    }

    mapping (address => Vault) public vaults;

    // @returns midnight unix utc
    function today() public view returns (uint64) {
        return uint64((block.timestamp / 1 days) * 1 days);
    }    

    /////////////////////
    // VAULT READ ONLY //
    /////////////////////

    // @returns HELGO balance in vault
    function balanceOf(address addr) public view returns(uint256) {
        Vault vault = vaults[addr];
        // zero when nothing locked
        if(address(vault) == address(0)) {
            return 0;
        }
        return HELGO.balanceOf(address(vault));
    }

    // @returns HELGO balance in vault, less vested amount
    function locked(address addr) public view returns(uint256) {
        Vault vault = vaults[addr];
        // zero when nothing locked
        if(address(vault) == address(0)) {
            return 0;
        }

        uint256 vest = vault.vestedAmount(address(HELGO), uint64(block.timestamp));
        uint256 balance = HELGO.balanceOf(address(vault));

        return balance - vest;
    }

    // @returns total HELGO that can be released
    function vested(address addr) public view returns(uint256) {
        Vault vault = vaults[addr];
        // zero when nothing locked
        if(address(vault) == address(0)) {
            return 0;
        }
        return vault.vestedAmount(address(HELGO), uint64(block.timestamp));
    }

    // @returns total HELGO released so far
    function released(address addr) public view returns(uint256) {
        Vault vault = vaults[addr];
        return vault.releasedToken(address(HELGO));
    }

    // @returns total votes (HELGO balance * duriation)
    function votes(address addr) public view returns(uint256) {
        Vault vault = vaults[addr];
        // No votes when nothing locked
        if(address(vault) == address(0)) {
            return 0;
        }
        // Multiply by duration of lock
        return HELGO.balanceOf(address(vault)) * vault.duration();
    }

    /////////////////
    // VAULT CALLS //
    /////////////////    

    // @notice creates or returns a vault for an address
    function initVault(address addr, uint64 start, uint64 duration) external returns(address) {
        Vault vault = vaults[addr];
        // Create vault when needed
        if(address(vault) == address(0)) {

            // Clone the vault
            address payable clone = payable(Clones.clone(address(VAULT)));

            vault = Vault(clone);

            // Initialise
            vault.initialise(addr, start, duration);

            // Changed to a clonable
            vaults[addr] = vault;
        }
        return address(vault);
    } 

    ////////////////////////
    // VOTABLE BEHAVIOURS //
    ////////////////////////  

    // @dev override votable getVotes
    // @returns votes for user
    function getVotes(address addr) override public view returns(uint256) {
        return votes(addr);
    }

    // @dev override votable castVote
    // @dev casts votes for a poposal
    function castVote(uint proposal, uint256 _votes) public override {

        // Update price history
        _updatePrice();

        // uses 3.69% of HELGO to buy HEX (votable when HEX at 3 month low)
        if(proposal == VOTE_PLEASE_EVERY_BODY_REMEMBER_TO_BUY_HEX) {
            require(alltimelowHEX(), 'HEX not at 90 day low');
        }

        // uses 3.69% of HEX to buy HELGO (votable when HEX near ATH)
        else if(proposal == VOTE_I_AM_HELGO) {
            require(alltimehighHEX(), 'HEX not at 90 day high');
        }        

        super.castVote(proposal, _votes);
    }

    // @dev overrides invokeVote — called on vote tally
    // @dev invokes votes
    function _invokeVote(uint proposal, uint256 _votes) override internal {

        // Update price history
        _updatePrice();

        // Do nothing when no votes
        if(_votes > 0) {
            if(proposal == VOTE_IDIOT_T_MAN) {
                idiot_t_man();
            }
            else if(proposal == VOTE_SHUT_UP_IDIOT_MAN) {
                shut_up_idiot_man();
            }
            else if(proposal == VOTE_I_AM_HELGO) {
                i_am_helgo();
            }
            else if(proposal == VOTE_PLEASE_EVERY_BODY_REMEMBER_TO_BUY_HEX) {
                please_every_body_remember_to_buy_hex();
            }
            else if(proposal == VOTE_DOUBLE_PERIOD) {
                double_vote_period();
            }
            else if(proposal == VOTE_HALF_PERIOD) {
                half_vote_period();
            }
        }
    }

    //////////////////////
    // HELGO MEME VOTES //
    //////////////////////

    // @notice uses small % of HEX to buy HELGO
    function idiot_t_man() internal {
        // Get HEX balance     
        uint256 amount = Math.mulu(Math.div(MEME_SMALL_PERC, 10 ** 18) + 1, HEX.balanceOf(address(this)));

        // Buy if balance
        if(amount > 0) {
            buyHELGO(amount);
        }
    }

    // @notice Use small % of HELGO to buy HEX
    function shut_up_idiot_man() internal {
        // Get HEX balance
        uint256 amount = Math.mulu(Math.div(MEME_SMALL_PERC, 10 ** 18) + 1, HELGO.balanceOf(address(this)));

        // Buy if balance
        if(amount > 0) {
            buyHEX(amount);
        }
    }

    // @notice uses large % of HEX to buy HELGO (votable when HEX near ATH)
    function i_am_helgo() internal {
        // Get HEX balance
        uint256 amount = Math.mulu(Math.div(MEME_LARGE_PERC, 10 ** 18) + 1, HEX.balanceOf(address(this)));

        // Buy if balance
        if(amount > 0) {
            buyHELGO(amount);
        }
    }

    // @notice uses large % of HEX to buy HELGO
    function please_every_body_remember_to_buy_hex() internal {
        // Get HEX balance
        uint256 amount = Math.mulu(Math.div(MEME_LARGE_PERC, 10 ** 18) + 1, HELGO.balanceOf(address(this)));

        // Buy if balance
        if(amount > 0) {
            buyHEX(amount);
        }
    }

    // @notice increases vote period
    function double_vote_period() internal {
        if(_voteFrequency < VOTE_MAX_FREQUENCY) {
            _voteFrequency = ((_voteFrequency * 2) / 1 hours) * 1 hours;
            _votePeriod = currentPeriod();
        }
    }

    // @notice decreases vote period
    function half_vote_period() internal {
        if(_voteFrequency > VOTE_MIN_FREQUENCY) {
            _voteFrequency = ((_voteFrequency / 2) / 1 hours) * 1 hours;
            _votePeriod = currentPeriod();
        }
    }

    ///////////////////
    // PRICE HISTORY //
    ///////////////////

    // @returns true if HEX at a < 90 day low
    function alltimelowHEX() public view returns(bool) {

        uint totaldays = priceHistory.length;

        if(totaldays > 90) {
            totaldays = 90;
        }

        if(totaldays == 0) {
            return false;
        }
        
        uint end = priceHistory.length - totaldays;
        uint256 low;

        // Tally and reset votes
        for (uint i = priceHistory.length - 1; i > end; i--) {
            if(priceHistory[i] < low || low == 0) {
                low = priceHistory[i];
            }
        }

        return priceHistory.length >= VOTE_MIN_PRICE_DAYS && low == priceHistory[priceHistory.length - 1];
    }

    // @returns true if HEX at a < 90 day high
    function alltimehighHEX() public view returns(bool) {

        uint totaldays = priceHistory.length;

        if(totaldays == 0) {
            return false;
        }

        if(totaldays > 90) {
            totaldays = 90;
        }

        uint end = priceHistory.length - totaldays;
        uint256 high;

        // Tally and reset votes
        for (uint i = priceHistory.length - 1; i > end; i--) {
            if(priceHistory[i] > high || high == 0) {
                high = priceHistory[i];
            }
        }

        return priceHistory.length >= VOTE_MIN_PRICE_DAYS && high == priceHistory[priceHistory.length - 1];
    }

    // @dev check if price should be recorded
    function _updatePrice() private {
        // update the price
        if( today() != priceChecked ) {
            priceHistory.push(
                priceHEX()
            );

            priceChecked = today();
        }        
    }

    //////////////////
    // ROUTER CALLS //
    //////////////////

    // @notice buys HELGO using HEX
    function buyHELGO(uint256 amountHex) private {
        swap(address(HEX), address(HELGO), amountHex, address(this));
    }

    // @notice buys HEX using HELGO
    function buyHEX(uint256 amountHelgo) private {
        swap(address(HELGO), address(HEX), amountHelgo, address(this));
    }

    // @notice swaps tokens using router
    function swap(address token0, address token1, uint256 amount, address to) private {

        address[] memory path;
        path = new address[](2);
        path[0] = token0;
        path[1] = token1;

        IERC20 erc = IERC20(token0);

        // check if ROUTER allowance, reset if required
        if(erc.allowance(address(this), address(ROUTER)) < amount){
            erc.approve(address(ROUTER), type(uint256).max);
        }                        
        ROUTER.swapExactTokensForTokens(
            amount, 0, path, to, block.timestamp + 10000
        );
    }

    // @returns price of token
    function price(address token0, address token1, uint decimals) private view returns(uint256 amount) {

        // Swap path
        address[] memory path;
        path = new address[](2);
        path[0] = token0;
        path[1] = token1;

        // Get amounts
        uint[] memory amounts = ROUTER.getAmountsOut(1 * 10 ** decimals, path);

        return amounts[1];
    }

    // @returns HEX price in ETH or PULSE
    function priceHEX() public view returns(uint256 amount) {
        return price(address(HEX), address(ROUTER) == ROUTERPULSEX ? WPLS : WETH, 10);
    }

    // @returns HELGO price in HEX
    function priceHELGO() public view returns(uint256 amount) {
        return price(address(HELGO), address(HEX), 18);
    }

}
          

contracts/Vault.sol

// SPDX-License-Identifier: MIT
// Based on OpenZeppelin Contracts v4.4.1 (finance/VestingWallet.sol)
// Modified to support: cloning, increase start, increase duration, 
// only beneficiary can increase / release

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";

pragma solidity ^0.8.17;

contract Vault is Context {
    event Released(uint256 amount);
    event TokenReleased(address indexed token, uint256 amount);

    event IncreasedStart(uint64 timestamp);
    event IncreasedDuration(uint64 duration);

    uint256 private _released;
    mapping(address => uint256) private _erc20Released;

    address private _beneficiary;
    uint64 private  _start;
    uint64 private  _duration;
    bool private _intialised;

    // @dev Set the beneficiary, start timestamp and vesting duration of the vesting wallet.
    function initialise(
        address beneficiaryAddress,
        uint64 startTimestamp,
        uint64 durationSeconds
    ) public virtual {
        require(beneficiaryAddress != address(0), "Vault: beneficiary is zero address");
        require(_intialised != true, "Vault: already initialised");
        _beneficiary = beneficiaryAddress;
        _start = startTimestamp;
        _duration = durationSeconds;
        _intialised = true;
    }

    // @dev only allow beneficiary 
    modifier onlyBeneficiary() {
        require(beneficiary() == _msgSender(), "Vault: caller is not the beneficiary");
        _;
    }

    ///////////////
    // VIEW ONLY //
    ///////////////

    // @dev Getter for the beneficiary address.
    function beneficiary() public view virtual returns (address) {
        return _beneficiary;
    }

    // @dev Getter for the start timestamp.
    function start() public view virtual returns (uint256) {
        return _start;
    }

    // @dev Getter for the vesting duration.
    function duration() public view virtual returns (uint256) {
        return _duration;
    }

    // @dev Amount of eth already released
    function released() public view virtual returns (uint256) {
        return _released;
    }

    // @dev Amount of token already released
    function releasedToken(address token) public view virtual returns (uint256) {
        return _erc20Released[token];
    }

    // @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve.
    function vestedAmount(uint64 timestamp) public view virtual returns (uint256) {
        return _vestingSchedule(address(this).balance + released(), timestamp);
    }

    // @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve.
    function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) {
        return _vestingSchedule(IERC20(token).balanceOf(address(this)) + releasedToken(token), timestamp);
    }

    ////////////
    // PUBLIC //
    ////////////

    // @dev The contract should be able to receive Eth.
    receive() external payable virtual {}

    // @dev Release the native token (ether) that have already vested.
    function release() public virtual onlyBeneficiary {
        uint256 releasable = vestedAmount(uint64(block.timestamp)) - released();
        _released += releasable;
        emit Released(releasable);
        Address.sendValue(payable(beneficiary()), releasable);
    }

    // @dev Release the tokens that have already vested.
    function releaseToken(address token) public virtual onlyBeneficiary {
        uint256 releasable = vestedAmount(token, uint64(block.timestamp)) - releasedToken(token);
        _erc20Released[token] += releasable;
        emit TokenReleased(token, releasable);
        SafeERC20.safeTransfer(IERC20(token), beneficiary(), releasable);
    }

    // @dev allow vault owner to increase start 
    function increaseStart(uint64 newStart) public virtual onlyBeneficiary {
        require(newStart > _start, 'New start must be greater than previous');
        emit IncreasedStart(newStart);
        _start = newStart;
    }

    // @dev allow vault owner to increase duration 
    function increaseDuration(uint64 newDuration) public virtual onlyBeneficiary {
        require(newDuration > _duration, 'New duration must be greater than previous');
        emit IncreasedDuration(newDuration);
        _duration = newDuration;
    }

    //////////////
    // INTERNAL //
    //////////////

    // @dev Virtual implementation of the vesting formula. This returns the amout vested, as a function of time, for
    // an asset given its total historical allocation.
    function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) {
        if (timestamp < start()) {
            return 0;
        } else if (timestamp > start() + duration()) {
            return totalAllocation;
        } else {
            return (totalAllocation * (timestamp - start())) / duration();
        }
    }
}


          

contracts/Votable.sol

// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.17;

/**
 * @dev Votable
 * 
 * 1x vote per voting votePeriod
 * vote count and eligibility handled by parent
 * override: getVotes, invokeVote, votePeriod, addProposal
 *
 */

contract Votable {

    event Proposed(uint id, string desc);
    event Voted(uint proposal, address addr, uint256 votes,  uint256 period);
    event Winner(uint proposal, uint256 votes, uint256 total);

    mapping(address => uint256) private voted;

    struct Proposal {
        uint id;
        string desc;
        uint256 votes;
    }

    struct Result {
        uint proposal;
        uint256 votes;
        uint256 total;
        uint64 timestamp;
    }

    Proposal[] public proposals;
    Result[] public results;

    bool private proposalsLocked;
    uint64 internal _votePeriod;
    uint64 internal _voteFrequency;

    constructor(uint64 frequency){
        // Start current vote votePeriod
        require(frequency >= 1 hours, 'initial voteFrequency less than 1 hour');
        _voteFrequency = frequency;
        _votePeriod = currentPeriod();
    }

    function currentPeriod() public virtual view returns(uint64) {
        return uint64(block.timestamp / _voteFrequency) * _voteFrequency;
    }

    function votePeriod() public virtual view returns(uint64) {
        return _votePeriod;
    }

    function voteFrequency() public virtual view returns(uint64) {
        return _voteFrequency;
    }

    function getProposals() public virtual view returns(Proposal[] memory){
        return proposals;
    }

    function totalProposals() public virtual view returns(uint256){
        return proposals.length;
    }

    function totalResults() public virtual view returns(uint256){
        return results.length;
    }

    function shouldTally() public view virtual returns(bool) {
        return _votePeriod != currentPeriod();
    }

    /**
     * @dev maximum votes for address
     * defaults to 1 per address
     * override for custom logic
     */     
    function getVotes(address addr) public virtual view returns(uint256) {
        if(addr != address(0)) {
            return 1;
        }
        else {
            return 0;
        }
    }

    function canVote(address addr) public view virtual returns(bool) {
        return _canVote(addr);
    }

    /**
     * @dev add a voting proposal
     * id must be unique and greater than zero
     */
    function addProposal(uint id, string memory desc) public virtual {
        require(!proposalsLocked, 'Proposals locked');
        _addProposal(id, desc);
    }

    function castVote(uint proposal, uint256 votes) public virtual {
        require(proposal > 0, 'Proposal required');
        require(votes > 0, 'Votes required');
        _castVote(msg.sender, proposal, votes);
    }

    function tallyVotes() public returns(uint256) {
        require(_votePeriod != currentPeriod(), 'Voting still underway');
        return _tallyVotes();
    }

    function lockProposals() internal {
        proposalsLocked = true;
    }

    /**
     * @dev invoke hook
     * called when vote is tallied 
     * override to handle result
     */
    function _invokeVote(uint proposal, uint256 votes) internal virtual {
    }

    function _addProposal(uint id, string memory desc) internal {
        // Check unique
        for (uint i = 0; i < proposals.length; i++) {
            require(proposals[i].id != id, 'Proposal id is not unique');
        }

        // Add proposal
        proposals.push(Proposal(id, desc, 0));

        // Emit event
        emit Proposed(
            id,
            desc
        );
    }

    function _canVote(address addr) internal virtual view returns(bool){
        return voted[addr] != currentPeriod() && getVotes(addr) > 0; 
    }

    function _castVote(address addr, uint proposal, uint256 votes) internal returns(uint256) {
        require( voted[addr] != currentPeriod(), 'Already voted');
        require( getVotes(addr) > 0, 'No votes');
        require( getVotes(addr) >= votes, 'Not enough votes');

        // Check if we should tally votes
        if(shouldTally()){
            _tallyVotes();
        }

        // One vote per votePeriod
        voted[addr] = currentPeriod();

        // Vote for option
        for (uint i = 0; i < proposals.length; i++) {
            if(proposals[i].id == proposal) {
                emit Voted(proposals[i].id, addr, votes, currentPeriod());
                return proposals[i].votes += votes;
            }
        }

        // No proposal matched
        revert('Not a valid proposal');
    }

    function _tallyVotes() private returns(uint256) {
  
        uint winner;
        uint256 max;
        uint256 total;
        uint256 draw;

        // Tally and reset votes
        for (uint i = 0; i < proposals.length; i++) {
            if(proposals[i].votes > max) {
                winner = proposals[i].id;
                max = proposals[i].votes;
                draw = 0;
            }
            else if(proposals[i].votes == max) {
                draw = 1;
            }

            total += proposals[i].votes;
            proposals[i].votes = 0;
        }

        // Disallow draws
        if(draw == 1) {
            winner = 0;
        }

        // Set new votePeriod
        _votePeriod = currentPeriod();

        // Emit result
        emit Winner(winner, max, total);

        // Record winndow
        results.push(Result(
            winner,
            max,
            total,
            uint64(block.timestamp)
        ));

        // Invoke winner (if any)
        _invokeVote(winner, max);

        return winner;
    }
}


          

interfaces/HEX.sol

// SPDX-License-Identifier: MIT
// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.5.2. SEE SOURCE BELOW. !!
// Launch claim events removed
pragma solidity >=0.7.0 <0.9.0;

interface IHEX {
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
    event DailyDataUpdate(uint256 data0, address indexed updaterAddr);
    event ShareRateChange(uint256 data0, uint40 indexed stakeId);
    event StakeEnd(
        uint256 data0,
        uint256 data1,
        address indexed stakerAddr,
        uint40 indexed stakeId
    );
    event StakeGoodAccounting(
        uint256 data0,
        uint256 data1,
        address indexed stakerAddr,
        uint40 indexed stakeId,
        address indexed senderAddr
    );
    event StakeStart(
        uint256 data0,
        address indexed stakerAddr,
        uint40 indexed stakeId
    );
    event Transfer(address indexed from, address indexed to, uint256 value);

    function allocatedSupply() external view returns (uint256);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function balanceOf(address account) external view returns (uint256);

    function currentDay() external view returns (uint256);

    function dailyData(uint256)
        external
        view
        returns (
            uint72 dayPayoutTotal,
            uint72 dayStakeSharesTotal,
            uint56 dayUnclaimedSatoshisTotal
        );

    function dailyDataRange(uint256 beginDay, uint256 endDay)
        external
        view
        returns (uint256[] memory list);

    function dailyDataUpdate(uint256 beforeDay) external;

    function decimals() external view returns (uint8);

    function decreaseAllowance(address spender, uint256 subtractedValue)
        external
        returns (bool);

    function globalInfo() external view returns (uint256[13] memory);

    function globals()
        external
        view
        returns (
            uint72 lockedHeartsTotal,
            uint72 nextStakeSharesTotal,
            uint40 shareRate,
            uint72 stakePenaltyTotal,
            uint16 dailyDataCount,
            uint72 stakeSharesTotal,
            uint40 latestStakeId,
            uint128 claimStats
        );

    function increaseAllowance(address spender, uint256 addedValue)
        external
        returns (bool);

    function name() external view returns (string memory);

    function stakeCount(address stakerAddr) external view returns (uint256);

    function stakeEnd(uint256 stakeIndex, uint40 stakeIdParam) external;

    function stakeGoodAccounting(
        address stakerAddr,
        uint256 stakeIndex,
        uint40 stakeIdParam
    ) external;

    function stakeLists(address, uint256)
        external
        view
        returns (
            uint40 stakeId,
            uint72 stakedHearts,
            uint72 stakeShares,
            uint16 lockedDay,
            uint16 stakedDays,
            uint16 unlockedDay,
            bool isAutoStake
        );

    function stakeStart(uint256 newStakedHearts, uint256 newStakedDays)
        external;

    function symbol() external view returns (string memory);

    function totalSupply() external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);
}
          

interfaces/Helgo.sol

// SPDX-License-Identifier: UNLICENSED
// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.5.2. SEE SOURCE BELOW. !!
pragma solidity >=0.7.0 <0.9.0;
pragma experimental ABIEncoderV2;

interface IHELGO {
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
    event Claimed(address indexed addr, uint256 amount, uint256 amountHELLP);
    event Deposited(address indexed addr, uint256 amount);
    event Launched(uint256 timestamp);
    event Locked(address indexed addr, uint256 amount);
    event Transfer(address indexed from, address indexed to, uint256 value);

    function DAYLAUNCH() external view returns (uint64);

    function DAYZERO() external view returns (uint64);

    function HELDAO() external view returns (address);

    function HELGAS() external view returns (address);

    function HELGO_FOUNDERS(uint256) external view returns (address);

    function HELLP() external view returns (address);

    function PAIR() external view returns (address);

    function TOTAL_FOUNDERS() external view returns (uint256);

    function _distribute(address addr) external returns (uint256, uint256);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function balanceHEX() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function burn(uint256 amount) external;

    function claim() external returns (uint256, uint256);

    function claimable() external view returns (uint256);

    function claimedHELGO() external view returns (uint256);

    function claimedHELLP() external view returns (uint256);

    function claimers(address) external view returns (bool);

    function day() external view returns (uint64);

    function decimals() external view returns (uint8);

    function decreaseAllowance(address spender, uint256 subtractedValue)
        external
        returns (bool);

    function deposit(uint256 amount) external;

    function deposited() external view returns (uint256);

    function deposits(address) external view returns (uint256);

    function distribute(address addr) external returns (uint256, uint256);

    function founder(address) external view returns (bool);

    function fundHELGAS(uint256 amount) external;

    function fundHELLP(uint256 amount) external;

    function getDeposit(address addr) external view returns (uint256);

    function getParticipant(uint256 i) external view returns (address);

    function globals() external view returns (IHELGO_STRUCTS.Globals memory);

    function increaseAllowance(address spender, uint256 addedValue)
        external
        returns (bool);

    function launch() external;

    function launched() external view returns (bool);

    function lock(
        uint256 amount,
        uint64 start,
        uint64 duration
    ) external returns (bool);

    function name() external view returns (string memory);

    function participants(uint256) external view returns (address);

    function symbol() external view returns (string memory);

    function today() external view returns (uint64);

    function totalDAO() external view returns (uint256);

    function totalEmissions() external view returns (uint256);

    function totalHEX() external view returns (uint256);

    function totalLiquid() external view returns (uint256);

    function totalLiquidity() external view returns (uint256);

    function totalLocked() external view returns (uint256);

    function totalParticipants() external view returns (uint256 count);

    function totalSupply() external view returns (uint256);

    function totals() external view returns (IHELGO_STRUCTS.Totals memory);

    function transfer(address to, uint256 amount) external returns (bool);

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);

    function transform() external payable;
}

interface IHELGO_STRUCTS {
    struct Globals {
        uint64 dayzero;
        uint64 daylaunch;
        uint64 today;
        uint64 day;
        bool launched;
        uint256 balance;
        uint256 deposit;
        uint256 deposited;
        uint256 claimable;
        uint256 claimedHELGO;
        uint256 claimedHELLP;
        uint256 participants;
    }

    struct Totals {
        uint256 supply;
        uint256 emissions;
        uint256 locked;
        uint256 liquidity;
        uint256 liquid;
        uint256 DAO;
        uint256 HEX;
    }
}

// THIS FILE WAS AUTOGENERATED FROM THE FOLLOWING ABI JSON:
/*
[{"inputs":[{"internalType":"address payable","name":"hexAddr","type":"address"},{"internalType":"address","name":"routerAddr","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountHELLP","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Launched","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Locked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DAYLAUNCH","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DAYZERO","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"HELDAO","outputs":[{"internalType":"contract Heldao","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"HELGAS","outputs":[{"internalType":"contract Helgas","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"HELGO_FOUNDERS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"HELLP","outputs":[{"internalType":"contract Hellp","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAIR","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOTAL_FOUNDERS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"_distribute","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"balanceHEX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimedHELGO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimedHELLP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"day","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deposited","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"deposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"distribute","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"founder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"fundHELGAS","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"fundHELLP","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"}],"name":"getParticipant","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globals","outputs":[{"components":[{"internalType":"uint64","name":"dayzero","type":"uint64"},{"internalType":"uint64","name":"daylaunch","type":"uint64"},{"internalType":"uint64","name":"today","type":"uint64"},{"internalType":"uint64","name":"day","type":"uint64"},{"internalType":"bool","name":"launched","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"deposit","type":"uint256"},{"internalType":"uint256","name":"deposited","type":"uint256"},{"internalType":"uint256","name":"claimable","type":"uint256"},{"internalType":"uint256","name":"claimedHELGO","type":"uint256"},{"internalType":"uint256","name":"claimedHELLP","type":"uint256"},{"internalType":"uint256","name":"participants","type":"uint256"}],"internalType":"struct Helgo.Globals","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"launch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"launched","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"start","type":"uint64"},{"internalType":"uint64","name":"duration","type":"uint64"}],"name":"lock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"participants","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"today","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDAO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalEmissions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalHEX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLiquid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalParticipants","outputs":[{"internalType":"uint256","name":"count","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totals","outputs":[{"components":[{"internalType":"uint256","name":"supply","type":"uint256"},{"internalType":"uint256","name":"emissions","type":"uint256"},{"internalType":"uint256","name":"locked","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"liquid","type":"uint256"},{"internalType":"uint256","name":"DAO","type":"uint256"},{"internalType":"uint256","name":"HEX","type":"uint256"}],"internalType":"struct Helgo.Totals","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transform","outputs":[],"stateMutability":"payable","type":"function"}]
*/
          

interfaces/IEmissions.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.7.0 <0.9.0;
pragma experimental ABIEncoderV2;

interface IEmissions {
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
    event Deposited(address indexed addr, uint256 amount, uint256 net);
    event Funded(address indexed addr, uint256 amount);
    event Ramped(uint256 timestamp, uint256 emission, int128 yield);
    event Reward(address indexed addr, uint256 amount);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Withdrawed(address indexed addr, uint256 amount, uint256 net);

    function DEPOSITFEE() external view returns (int128);

    function FREQUENCY() external view returns (uint256);

    function RATE() external view returns (int128);

    function REWARD() external view returns (int128);

    function TOKEN() external view returns (address);

    function WITHDRAWFEE() external view returns (int128);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function balanceOf(address addr) external view returns (uint256);

    function basis() external view returns (int128);

    function decimals() external view returns (uint8);

    function decreaseAllowance(address spender, uint256 subtractedValue)
        external
        returns (bool);

    function deposit(uint256 amount) external;

    function deposited() external view returns (uint256);

    function deposits() external view returns (uint256);

    function emissions() external view returns (uint256);

    function emitting() external view returns (IEmissionsStructs.Emission memory);

    function fees() external view returns (uint256);

    function fund(uint256 amount) external;

    function globals() external view returns (IEmissionsStructs.Globals memory);

    function history(uint256)
        external
        view
        returns (
            uint256 timestamp,
            uint256 amount,
            int128 yield
        );

    function increaseAllowance(address spender, uint256 addedValue)
        external
        returns (bool);

    function lastramp() external view returns (uint256);

    function name() external view returns (string memory);

    function period() external view returns (uint256);

    function ramp() external returns (IEmissionsStructs.Emission memory);

    function reserves() external view returns (uint256);

    function should() external view returns (bool);

    function symbol() external view returns (string memory);

    function token() external view returns (address);

    function totalSupply() external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    function withdraw(uint256 amount) external;

    function withdrawAll() external;

    function withdrawn() external view returns (uint256);
}

interface IEmissionsStructs {
    struct Emission {
        uint256 timestamp;
        uint256 amount;
        int128 yield;
    }

    struct Globals {
        uint256 reserves;
        uint256 emissions;
        uint256 deposited;
        uint256 withdrawn;
        uint256 deposits;
        uint256 fees;
        uint256 lastramp;
        int128 basis;
        uint256 frequency;
        int128 rate;
        int128 depositfee;
        int128 withdrawfee;
    }
}


          

interfaces/IHelfactory.sol

// SPDX-License-Identifier: UNLICENSED
// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.7.1. SEE SOURCE BELOW. !!
pragma solidity >=0.7.0 <0.9.0;

interface IHelfactory {
    function newHelgas(address helgo) external returns (address);

    function newHellp(
        address pair,
        address router,
        address token0,
        address token1
    ) external returns (address);
}
          

interfaces/IUniswapV2Factory.sol

// SPDX-License-Identifier: UNLICENSED
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;
}
          

interfaces/IUniswapV2Router01.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.6.2;

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
          

interfaces/IUniswapV2Router02.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol'; 

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}
          

Compiler Settings

{"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers"]}},"optimizer":{"runs":639,"enabled":true},"libraries":{}}
              

Contract ABI

[{"type":"constructor","inputs":[{"type":"address","name":"hexAddr","internalType":"address payable"},{"type":"address","name":"routerAddr","internalType":"address"},{"type":"address","name":"factoryAddr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"","internalType":"uint64"}],"name":"DAYLAUNCH","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"","internalType":"uint64"}],"name":"DAYZERO","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract Heldao"}],"name":"HELDAO","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IEmissions"}],"name":"HELGAS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"HELGO_FOUNDERS","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IEmissions"}],"name":"HELLP","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"PAIR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TOTAL_FOUNDERS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceHEX","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"burn","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"claim","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"claimable","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"claimedHELGO","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"claimedHELLP","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"claimers","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"","internalType":"uint64"}],"name":"day","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"decreaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"subtractedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deposit","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"deposited","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"deposits","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"distribute","inputs":[{"type":"address","name":"addr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"founder","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fundHELGAS","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"fundHELLP","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getDeposit","inputs":[{"type":"address","name":"addr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getParticipant","inputs":[{"type":"uint256","name":"i","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct Helgo.Globals","components":[{"type":"uint64"},{"type":"uint64"},{"type":"uint64"},{"type":"uint64"},{"type":"bool"},{"type":"uint256"},{"type":"uint256"},{"type":"uint256"},{"type":"uint256"},{"type":"uint256"},{"type":"uint256"},{"type":"uint256"}]}],"name":"globals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"increaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"addedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"launch","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"launched","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"lock","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"uint64","name":"start","internalType":"uint64"},{"type":"uint64","name":"duration","internalType":"uint64"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"participants","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"","internalType":"uint64"}],"name":"today","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalDAO","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalEmissions","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalHEX","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalLiquid","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalLiquidity","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalLocked","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"count","internalType":"uint256"}],"name":"totalParticipants","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct Helgo.Totals","components":[{"type":"uint256"},{"type":"uint256"},{"type":"uint256"},{"type":"uint256"},{"type":"uint256"},{"type":"uint256"},{"type":"uint256"}]}],"name":"totals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"transform","inputs":[]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","indexed":true},{"type":"address","name":"spender","indexed":true},{"type":"uint256","name":"value","indexed":false}],"anonymous":false},{"type":"event","name":"Claimed","inputs":[{"type":"address","name":"addr","indexed":true},{"type":"uint256","name":"amount","indexed":false},{"type":"uint256","name":"amountHELLP","indexed":false}],"anonymous":false},{"type":"event","name":"Deposited","inputs":[{"type":"address","name":"addr","indexed":true},{"type":"uint256","name":"amount","indexed":false}],"anonymous":false},{"type":"event","name":"Launched","inputs":[{"type":"uint256","name":"timestamp","indexed":false}],"anonymous":false},{"type":"event","name":"Locked","inputs":[{"type":"address","name":"addr","indexed":true},{"type":"uint256","name":"amount","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","indexed":true},{"type":"address","name":"to","indexed":true},{"type":"uint256","name":"value","indexed":false}],"anonymous":false}]
              

Contract Creation Code

0x6105c060405273892097a6bbf318e8b3b5d27044782975dcfbc47b61012090815273b7da80b7db32f57281545d45ffd691914dc2252b610140527383559662de19aab3becc817865061af699421b9a610160527313e0a697a1df2fc28f6d8f4f98f417be51ca52326101805273f999e2e31ce9c70f697545a99df20d4346b94e266101a0527319df3fc60ae23784cb2009c6a32e73999dd7b4a46101c0527310f0a702d1ff73d54404ba96a5e114b0a6574d056101e052730dc72f41fa93260c2a909408525df37491d429996102005273e3af5db5947e4efea79747c8a5c09da728313bcc6102205273728aacbce90709d8ee3dd7c42e56a5512cf742c26102405273de0fb3af3b72afd2e39c525e476d8fbea1b6b0066102605273a97ca893f62e845b929f65a8d128cb71d03f3be361028052734d0e4012f8e0428e90fc36dff351af9788e0c60e6102a052737e04d6194c2ecbf7ee730b36e17d29726ea9c5fa6102c05273662f84b2f9507cfc02fb259c8c64455fae8d01b66102e0527348fcc86bb079b97dde0d10026b22069df5c6630a61030052730de4aa116a9b536e9b7c17f2743925117cdc57e06103205273dc6a8c0505ebc3954da4030a135fee5e568f7d096103405273f027324b27dff4243f13208f1a3ae34b6b514b3a61036052734555261e2d5a9a2b9b0fcf26f534a8fd8a8f2f9d610380527347c81c1d47742b41ff7d1c04e08d3036ae01f4a16103a052739c2f87d3ae338e4fe5f317a4219762de7ce0eb246103c05273e33b7f805c97a7f88963ce20629bbe88a9d089866103e05273307f17350086232feaa6e17808662a5aef29841f61040052737cc483f2ba233f6f4b52f43b8b657d1071e5f5ab61042052732a25da22e94884f3b7b6841a98f21325e131c4b66104405273212ff16ea41fc0394625c9e2d5d45532ea4cf1006104605273beb348b205841be55bf4fe1732c2a81b390c50d961048052734d30801092100f9e0aa3bf31336b12cba4dab29b6104a05273e40569f208ec96adad9e0127b5724d50d15e039d6104c05273895a1feff0ce1dfeb5c15f2c237223537fc87fe26104e052737b156358c4a63a01088cb496212ea02412e34f756105005273717b8898860b1d45a621e41360f143da2c8bc292610520527355f7b7286ae9a9aeabc5cb79cb8ede034665b20e61054052735bc36c7187aa603576cf95108b1107e087aff64d61056052730a93f5bc1a0aebfa92180eec1d4990827446188a6105805273011df7c4d325290e5d07689e057c5527bdbbceb06105a052620003b49060059060256200071e565b50348015620003c257600080fd5b5060405162007def38038062007def833981016040819052620003e591620007c6565b60408051808201825260058082526448454c474f60d81b6020808401829052845180860190955291845290830152906003620004228382620008be565b506004620004318282620008be565b50505060005b600554811015620004a757600160066000600584815481106200045e576200045e6200098a565b6000918252602080832091909101546001600160a01b031683528201929092526040019020805460ff1916911515919091179055806200049e81620009b6565b91505062000437565b50600554608052620004d830620004c16012600a62000ad1565b620004d290640cef8067d962000ae9565b6200062e565b600980546001600160a01b038086166001600160a01b031992831617909255600b805492851692909116821790556040805163c45a015560e01b8152905163c45a0155916004808201926020929091908290030181865afa15801562000542573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000568919062000b03565b600c80546001600160a01b0319166001600160a01b039283161790558116610100526040513090839085906200059e9062000788565b6001600160a01b03938416815291831660208301529091166040820152606001604051809103906000f080158015620005db573d6000803e3d6000fd5b506001600160a01b031660e052620005f2620006f4565b6001600160401b031660a052622b81806200060c620006f4565b62000618919062000b23565b6001600160401b031660c0525062000b86915050565b6001600160a01b038216620006895760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390fd5b80600260008282546200069d919062000b4d565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600062000705620151804262000b63565b62000714906201518062000ae9565b905090565b505050565b82805482825590600052602060002090810192821562000776579160200282015b828111156200077657825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906200073f565b506200078492915062000796565b5090565b6141688062003c8783390190565b5b8082111562000784576000815560010162000797565b6001600160a01b0381168114620007c357600080fd5b50565b600080600060608486031215620007dc57600080fd5b8351620007e981620007ad565b6020850151909350620007fc81620007ad565b60408501519092506200080f81620007ad565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200084557607f821691505b6020821081036200086657634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200071957600081815260208120601f850160051c81016020861015620008955750805b601f850160051c820191505b81811015620008b657828155600101620008a1565b505050505050565b81516001600160401b03811115620008da57620008da6200081a565b620008f281620008eb845462000830565b846200086c565b602080601f8311600181146200092a5760008415620009115750858301515b600019600386901b1c1916600185901b178555620008b6565b600085815260208120601f198616915b828110156200095b578886015182559484019460019091019084016200093a565b50858210156200097a5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201620009cb57620009cb620009a0565b5060010190565b600181815b8085111562000a13578160001904821115620009f757620009f7620009a0565b8085161562000a0557918102915b93841c9390800290620009d7565b509250929050565b60008262000a2c5750600162000acb565b8162000a3b5750600062000acb565b816001811462000a54576002811462000a5f5762000a7f565b600191505062000acb565b60ff84111562000a735762000a73620009a0565b50506001821b62000acb565b5060208310610133831016604e8410600b841016171562000aa4575081810a62000acb565b62000ab08383620009d2565b806000190482111562000ac75762000ac7620009a0565b0290505b92915050565b600062000ae260ff84168362000a1b565b9392505050565b808202811582820484141762000acb5762000acb620009a0565b60006020828403121562000b1657600080fd5b815162000ae281620007ad565b6001600160401b0381811683821601908082111562000b465762000b46620009a0565b5092915050565b8082018082111562000acb5762000acb620009a0565b60008262000b8157634e487b7160e01b600052601260045260246000fd5b500490565b60805160a05160c05160e0516101005161307d62000c0a60003960008181612204015261264c0152600081816106ca015281816115280152818161210e01526121540152600081816103e001528181611153015261127701526000818161066c01528181610c9e015281816111230152611da5015260006109b4015261307d6000f3fe6080604052600436106103345760003560e01c806386514a2e116101b0578063b74e452b116100ec578063da62fba911610095578063eef49ee31161006f578063eef49ee31461098c578063f027bf1b146109a2578063f53352b2146109d6578063fc7e286d14610a0657600080fd5b8063da62fba9146108e0578063dd62ed3e14610910578063e1254fba1461095657600080fd5b8063c3124525116100c6578063c312452514610892578063d3b7ec81146108b4578063d5a1d691146108ca57600080fd5b8063b74e452b146107f2578063c038a38e14610807578063c1bbfb611461087257600080fd5b8063a52b9a8a11610159578063ace3a8a711610133578063ace3a8a714610786578063af38d757146107a6578063b5caf888146107bc578063b6b55f25146107d257600080fd5b8063a52b9a8a14610721578063a9059cbb14610746578063aa97527f1461076657600080fd5b8063999ec52a1161018a578063999ec52a146106b8578063a26dbf26146106ec578063a457c2d71461070157600080fd5b806386514a2e1461058b5780639225dfe81461068e57806395d89b41146106a357600080fd5b8063395093511161027f57806356891412116102285780637b76ac91116102025780637b76ac911461060b5780637c8c12e1146106205780638091f3bf1461064057806381b3b1fb1461065a57600080fd5b806356891412146105a057806363453ae1146105b557806370a08231146105d557600080fd5b8063450db90b11610259578063450db90b1461054c5780634e71d92d14610561578063513107631461058b57600080fd5b806339509351146104ec57806340d2609e1461050c57806342966c681461052c57600080fd5b80631b9db2ef116102e15780633054c79a116102bb5780633054c79a146104a8578063313ce567146104b057806335c1d349146104cc57600080fd5b80631b9db2ef146104305780631d8eb19f1461046857806323b872dd1461048857600080fd5b806315770f921161031257806315770f92146103ab578063160e4152146103ce57806318160ddd1461041b57600080fd5b806301339c211461033957806306fdde0314610350578063095ea7b31461037b575b600080fd5b34801561034557600080fd5b5061034e610a33565b005b34801561035c57600080fd5b50610365610a3d565b6040516103729190612aa1565b60405180910390f35b34801561038757600080fd5b5061039b610396366004612b04565b610acf565b6040519015158152602001610372565b3480156103b757600080fd5b506103c0610ae9565b604051908152602001610372565b3480156103da57600080fd5b506104027f000000000000000000000000000000000000000000000000000000000000000081565b60405167ffffffffffffffff9091168152602001610372565b34801561042757600080fd5b506002546103c0565b34801561043c57600080fd5b5061045061044b366004612b30565b610b10565b6040516001600160a01b039091168152602001610372565b34801561047457600080fd5b5061039b610483366004612b66565b610b40565b34801561049457600080fd5b5061039b6104a3366004612ba2565b610b56565b61034e610b7a565b3480156104bc57600080fd5b5060405160128152602001610372565b3480156104d857600080fd5b506104506104e7366004612b30565b610b84565b3480156104f857600080fd5b5061039b610507366004612b04565b610bae565b34801561051857600080fd5b50610450610527366004612b30565b610bed565b34801561053857600080fd5b5061034e610547366004612b30565b610bfd565b34801561055857600080fd5b506103c0610c0a565b34801561056d57600080fd5b50610576610c2c565b60408051928352602083019190915201610372565b34801561059757600080fd5b506103c0610c40565b3480156105ac57600080fd5b506103c0610c62565b3480156105c157600080fd5b506105766105d0366004612be3565b610c84565b3480156105e157600080fd5b506103c06105f0366004612be3565b6001600160a01b031660009081526020819052604090205490565b34801561061757600080fd5b50610402610c99565b34801561062c57600080fd5b50600854610450906001600160a01b031681565b34801561064c57600080fd5b5060075461039b9060ff1681565b34801561066657600080fd5b506104027f000000000000000000000000000000000000000000000000000000000000000081565b34801561069a57600080fd5b506103c0610cfe565b3480156106af57600080fd5b50610365610d6b565b3480156106c457600080fd5b506104507f000000000000000000000000000000000000000000000000000000000000000081565b3480156106f857600080fd5b50600d546103c0565b34801561070d57600080fd5b5061039b61071c366004612b04565b610d7a565b34801561072d57600080fd5b506007546104509061010090046001600160a01b031681565b34801561075257600080fd5b5061039b610761366004612b04565b610e11565b34801561077257600080fd5b5061034e610781366004612b30565b610e1f565b34801561079257600080fd5b50600a54610450906001600160a01b031681565b3480156107b257600080fd5b506103c060105481565b3480156107c857600080fd5b506103c0600e5481565b3480156107de57600080fd5b5061034e6107ed366004612b30565b610efd565b3480156107fe57600080fd5b50610402610f86565b34801561081357600080fd5b5061081c610fa2565b6040516103729190600060e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015292915050565b34801561087e57600080fd5b5061034e61088d366004612b30565b611047565b34801561089e57600080fd5b506108a7611087565b6040516103729190612c00565b3480156108c057600080fd5b506103c060125481565b3480156108d657600080fd5b506103c060115481565b3480156108ec57600080fd5b5061039b6108fb366004612be3565b60146020526000908152604090205460ff1681565b34801561091c57600080fd5b506103c061092b366004612ccf565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b34801561096257600080fd5b506103c0610971366004612be3565b6001600160a01b031660009081526013602052604090205490565b34801561099857600080fd5b506103c0600f5481565b3480156109ae57600080fd5b506103c07f000000000000000000000000000000000000000000000000000000000000000081565b3480156109e257600080fd5b5061039b6109f1366004612be3565b60066020526000908152604090205460ff1681565b348015610a1257600080fd5b506103c0610a21366004612be3565b60136020526000908152604090205481565b610a3b611222565b565b606060038054610a4c90612d08565b80601f0160208091040260200160405190810160405280929190818152602001828054610a7890612d08565b8015610ac55780601f10610a9a57610100808354040283529160200191610ac5565b820191906000526020600020905b815481529060010190602001808311610aa857829003601f168201915b5050505050905090565b600033610add8185856113c9565b60019150505b92915050565b60006014610af660025490565b610b009190612d6e565b610b0b906002612d82565b905090565b6000600d8281548110610b2557610b25612d99565b6000918252602090912001546001600160a01b031692915050565b6000610b4e338585856114ee565b949350505050565b600033610b648582856115f1565b610b6f858585611683565b506001949350505050565b610a3b3334611827565b600d8181548110610b9457600080fd5b6000918252602090912001546001600160a01b0316905081565b3360008181526001602090815260408083206001600160a01b0387168452909152812054909190610add9082908690610be8908790612daf565b6113c9565b60058181548110610b9457600080fd5b610c073382611a7e565b50565b60006014610c1760025490565b610c219190612d6e565b610b0b906009612d82565b600080610c3833611ba8565b915091509091565b60006014610c4d60025490565b610c579190612d6e565b610b0b906001612d82565b60006014610c6f60025490565b610c799190612d6e565b610b0b906007612d82565b600080610c9083611ba8565b91509150915091565b6000807f0000000000000000000000000000000000000000000000000000000000000000610cc5610f86565b610ccf9190612dc2565b90508067ffffffffffffffff16600003610ceb57600091505090565b610cf86201518082612dea565b91505090565b6009546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015610d47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0b9190612e11565b606060048054610a4c90612d08565b3360008181526001602090815260408083206001600160a01b038716845290915281205490919083811015610e045760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084015b60405180910390fd5b610b6f82868684036113c9565b600033610add818585611683565b600a546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610e76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9a9190612e2a565b5060085460405163ca1d209d60e01b8152600481018390526001600160a01b039091169063ca1d209d906024015b600060405180830381600087803b158015610ee257600080fd5b505af1158015610ef6573d6000803e3d6000fd5b5050505050565b610f073382611f4c565b6009546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610f5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f829190612e2a565b5050565b6000610f956201518042612d6e565b610b0b9062015180612d82565b610fe26040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060e00160405280610ff660025490565b8152602001611003610c0a565b8152602001611010610c62565b815260200161101d610ae9565b815260200161102a610c40565b8152602001611037610c40565b8152602001600e54815250905090565b611052333083611683565b60075460405163ca1d209d60e01b8152600481018390526101009091046001600160a01b03169063ca1d209d90602401610ec8565b611115604051806101800160405280600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600015158152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518061018001604052807f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff168152602001611189610f86565b67ffffffffffffffff1681526020016111a0610c99565b67ffffffffffffffff16815260075460ff16151560208201526040016111db336001600160a01b031660009081526020819052604090205490565b8152336000908152601360209081526040918290205490830152600f54908201526010546060820152601154608082015260125460a0820152600d5460c090910152919050565b60075460ff16156112755760405162461bcd60e51b815260206004820152601060248201527f416c7265616479206c61756e63686564000000000000000000000000000000006044820152606401610dfb565b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff166112a8610f86565b67ffffffffffffffff1610156113005760405162461bcd60e51b815260206004820152601260248201527f546f6f20736f6f6e20746f206c61756e636800000000000000000000000000006044820152606401610dfb565b6007805460ff191660011790556009546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611355573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113799190612e11565b600e55611384612108565b61138c6121ef565b61139461232a565b6040514281527feca0dfbda743d141662328d4d48393e344b2c60df0430297221df823f2926dba9060200160405180910390a1565b6001600160a01b03831661142b5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610dfb565b6001600160a01b03821661148c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610dfb565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6040516313d4515360e11b815233600482015267ffffffffffffffff80841660248301528216604482015260009081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906327a8a2a6906064016020604051808303816000875af1158015611571573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115959190612e4c565b90506115a2868287611683565b856001600160a01b03167f9f1ec8c880f76798e7b793325d625e9b60e4082a553c98f42b6cda368dd60008866040516115dd91815260200190565b60405180910390a250600195945050505050565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461167d57818110156116705760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610dfb565b61167d84848484036113c9565b50505050565b6001600160a01b0383166116e75760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610dfb565b6001600160a01b0382166117495760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610dfb565b6001600160a01b038316600090815260208190526040902054818110156117c15760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610dfb565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a361167d565b600081116118775760405162461bcd60e51b815260206004820181905260248201527f416d6f756e74206d7573742062652067726561746572207468616e207a65726f6044820152606401610dfb565b604080516002808252606080830184529260208301908036833701905050600b549091506001600160a01b031673165c3410fc91ef562c50559f7d2289febed552d9146118d85773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26118ee565b73a1077a294dde1b09bb078844df40758a5d0f9a275b8160008151811061190157611901612d99565b6001600160a01b03928316602091820292909201015260095482519116908290600190811061193257611932612d99565b6001600160a01b039283166020918202929092010152600b5460405163d06ca61f60e01b8152600092919091169063d06ca61f906119769086908690600401612ec3565b600060405180830381865afa158015611993573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119bb9190810190612edc565b9050600081600183516119ce9190612f9a565b815181106119de576119de612d99565b6020908102919091010151600b549091506001600160a01b0316637ff36ab585838630611a0d42612710612daf565b6040518663ffffffff1660e01b8152600401611a2c9493929190612fad565b60006040518083038185885af1158015611a4a573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f19168201604052611a739190810190612edc565b50610ef68582611f4c565b6001600160a01b038216611ade5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610dfb565b6001600160a01b03821660009081526020819052604090205481811015611b525760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610dfb565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91016114e1565b600754600090819060ff16611bff5760405162461bcd60e51b815260206004820152600c60248201527f4e6f74206c61756e6368656400000000000000000000000000000000000000006044820152606401610dfb565b6001600160a01b03831660009081526014602052604090205460ff1615611c685760405162461bcd60e51b815260206004820152600f60248201527f416c726561647920636c61696d656400000000000000000000000000000000006044820152606401610dfb565b6001600160a01b0383166000908152601460209081526040808320805460ff191660011790556013909152812054819015611cfb576001600160a01b038516600090815260136020526040812054600f54611cc39190612865565b9050611cdd816002601054611cd89190612d6e565b6128af565b9250611cf7816002611ced610c40565b611cd89190612d6e565b9150505b6001600160a01b03851660009081526006602052604090205460ff1615611dda57600554611d2a906002612d82565b601054611d379190612d6e565b611d419083612daf565b600554909250611d52906002612d82565b611d5a610c40565b611d649190612d6e565b611d6e9082612daf565b600554909150600090611d7f610c62565b611d899190612d6e565b9050611d96308783611683565b611dd38682611dc96301e133807f0000000000000000000000000000000000000000000000000000000000000000612fe2565b631c9b7c806114ee565b5050611e3f565b6001600160a01b038516600090815260136020526040902054611e3f5760405162461bcd60e51b815260206004820152600a60248201527f4e6f206465706f736974000000000000000000000000000000000000000000006044820152606401610dfb565b8160126000828254611e519190612daf565b925050819055508060116000828254611e6a9190612daf565b90915550508115611eee57600a5460405163a9059cbb60e01b81526001600160a01b038781166004830152602482018590529091169063a9059cbb906044016020604051808303816000875af1158015611ec8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eec9190612e2a565b505b8015611eff57611eff308683611683565b60408051828152602081018490526001600160a01b038716917f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a910160405180910390a294909350915050565b60008111611fad5760405162461bcd60e51b815260206004820152602860248201527f4465706f73697420616d6f756e74206d7573742062652067726561746572207460448201526768616e207a65726f60c01b6064820152608401610dfb565b60075460ff161561200a5760405162461bcd60e51b815260206004820152602160248201527f416c7265616479206c61756e636865642c206465706f7369747320636c6f73656044820152601960fa1b6064820152608401610dfb565b6001600160a01b038216600090815260136020526040812054900361208257600d80546001810182556000919091527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb501805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384161790555b80600f60008282546120949190612daf565b90915550506001600160a01b038216600090815260136020526040812080548392906120c1908490612daf565b90915550506040518181526001600160a01b038316907f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c49060200160405180910390a25050565b61213a307f0000000000000000000000000000000000000000000000000000000000000000612135610c40565b611683565b600954600e546001600160a01b039091169063a9059cbb907f00000000000000000000000000000000000000000000000000000000000000009061218090601490612d6e565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af11580156121cb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c079190612e2a565b604051630431b6ad60e41b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063431b6ad0906024016020604051808303816000875af1158015612255573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122799190612e4c565b6007805474ffffffffffffffffffffffffffffffffffffffff0019166101006001600160a01b03938416810291909117918290556122c092309291909104166000196113c9565b60075461010090046001600160a01b031663ca1d209d6122de610c0a565b6040518263ffffffff1660e01b81526004016122fc91815260200190565b600060405180830381600087803b15801561231657600080fd5b505af115801561167d573d6000803e3d6000fd5b600954600c5460405163e6a4390560e01b815230600482018190526001600160a01b03938416602483018190529093909260009291169063e6a4390590604401602060405180830381865afa158015612387573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ab9190612e4c565b90506001600160a01b03811661243657600c546040516364e329cb60e11b81526001600160a01b03858116600483015284811660248301529091169063c9c65396906044016020604051808303816000875af115801561240f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124339190612e4c565b90505b600b546124509030906001600160a01b0316610be8610ae9565b6009546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015612499573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124bd9190612e11565b600954600b5460405163095ea7b360e01b81526001600160a01b03918216600482015260248101849052929350169063095ea7b3906044016020604051808303816000875af1158015612514573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125389190612e2a565b50600b546000906001600160a01b031663e8e337008686612557610ae9565b86612560610ae9565b888c61256f42620f4240612daf565b60405160e08a901b6001600160e01b03191681526001600160a01b039889166004820152968816602488015260448701959095526064860193909352608485019190915260a484015290921660c482015260e4810191909152610104016060604051808303816000875af11580156125eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061260f9190613003565b600b54604051631022ac8760e31b81526001600160a01b0388811660048301529182166024820152898216604482015288821660648201529194507f0000000000000000000000000000000000000000000000000000000000000000169250638115643891506084016020604051808303816000875af1158015612697573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126bb9190612e4c565b600880546001600160a01b0392831673ffffffffffffffffffffffffffffffffffffffff199182168117909255600a8054938716939091168317905560405163095ea7b360e01b81526004810191909152600019602482015263095ea7b3906044016020604051808303816000875af115801561273c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127609190612e2a565b506008546001600160a01b031663ca1d209d61277d600284612d6e565b6040518263ffffffff1660e01b815260040161279b91815260200190565b600060405180830381600087803b1580156127b557600080fd5b505af11580156127c9573d6000803e3d6000fd5b505050506002816127da9190612d6e565b6127e49082612f9a565b6010819055600a5460405163095ea7b360e01b815230600482015260248101929092526001600160a01b03169063095ea7b3906044016020604051808303816000875af1158015612839573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285d9190612e2a565b505050505050565b60008160000361287457600080fd5b6000612880848461292b565b90506f7fffffffffffffffffffffffffffffff6001600160801b03821611156128a857600080fd5b9392505050565b6000816000036128c157506000610ae3565b600083600f0b12156128d257600080fd5b600f83900b6001600160801b038316810260401c90608084901c0277ffffffffffffffffffffffffffffffffffffffffffffffff81111561291257600080fd5b60401b811981111561292357600080fd5b019392505050565b60008160000361293a57600080fd5b600077ffffffffffffffffffffffffffffffffffffffffffffffff84116129765782604085901b8161296e5761296e612d42565b049050612a8d565b60c084811c640100000000811061298f576020918201911c5b6201000081106129a1576010918201911c5b61010081106129b2576008918201911c5b601081106129c2576004918201911c5b600481106129d2576002918201911c5b600281106129e1576001820191505b60bf820360018603901c6001018260ff0387901b81612a0257612a02612d42565b0492506001600160801b03831115612a1957600080fd5b608085901c83026001600160801b038616840260c088901c604089901b82811015612a45576001820391505b608084901b92900382811015612a5c576001820391505b829003608084901c8214612a7257612a72613031565b888181612a8157612a81612d42565b04870196505050505050505b6001600160801b038111156128a857600080fd5b600060208083528351808285015260005b81811015612ace57858101830151858201604001528201612ab2565b506000604082860101526040601f19601f8301168501019250505092915050565b6001600160a01b0381168114610c0757600080fd5b60008060408385031215612b1757600080fd5b8235612b2281612aef565b946020939093013593505050565b600060208284031215612b4257600080fd5b5035919050565b803567ffffffffffffffff81168114612b6157600080fd5b919050565b600080600060608486031215612b7b57600080fd5b83359250612b8b60208501612b49565b9150612b9960408501612b49565b90509250925092565b600080600060608486031215612bb757600080fd5b8335612bc281612aef565b92506020840135612bd281612aef565b929592945050506040919091013590565b600060208284031215612bf557600080fd5b81356128a881612aef565b815167ffffffffffffffff16815261018081016020830151612c2e602084018267ffffffffffffffff169052565b506040830151612c4a604084018267ffffffffffffffff169052565b506060830151612c66606084018267ffffffffffffffff169052565b506080830151612c7a608084018215159052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151818401525061014080840151818401525061016080840151818401525092915050565b60008060408385031215612ce257600080fd5b8235612ced81612aef565b91506020830135612cfd81612aef565b809150509250929050565b600181811c90821680612d1c57607f821691505b602082108103612d3c57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600082612d7d57612d7d612d42565b500490565b8082028115828204841417610ae357610ae3612d58565b634e487b7160e01b600052603260045260246000fd5b80820180821115610ae357610ae3612d58565b67ffffffffffffffff828116828216039080821115612de357612de3612d58565b5092915050565b600067ffffffffffffffff80841680612e0557612e05612d42565b92169190910492915050565b600060208284031215612e2357600080fd5b5051919050565b600060208284031215612e3c57600080fd5b815180151581146128a857600080fd5b600060208284031215612e5e57600080fd5b81516128a881612aef565b634e487b7160e01b600052604160045260246000fd5b600081518084526020808501945080840160005b83811015612eb85781516001600160a01b031687529582019590820190600101612e93565b509495945050505050565b828152604060208201526000610b4e6040830184612e7f565b60006020808385031215612eef57600080fd5b825167ffffffffffffffff80821115612f0757600080fd5b818501915085601f830112612f1b57600080fd5b815181811115612f2d57612f2d612e69565b8060051b604051601f19603f83011681018181108582111715612f5257612f52612e69565b604052918252848201925083810185019188831115612f7057600080fd5b938501935b82851015612f8e57845184529385019392850192612f75565b98975050505050505050565b81810381811115610ae357610ae3612d58565b848152608060208201526000612fc66080830186612e7f565b6001600160a01b03949094166040830152506060015292915050565b67ffffffffffffffff818116838216019080821115612de357612de3612d58565b60008060006060848603121561301857600080fd5b8351925060208401519150604084015190509250925092565b634e487b7160e01b600052600160045260246000fdfea26469706673582212201fa36733c7604deb0e365dcf2b654a18dcd15e562f9a82757217606afc147a8664736f6c6343000811003360e06040523480156200001157600080fd5b506040516200416838038062004168833981016040819052620000349162000473565b6201518062000047565b60405180910390fd5b60038054600160481b600160881b03191669010000000000000000006001600160401b038416021790556200007b62000245565b60038054610100600160481b0319166101006001600160401b03939093169290920291909117905550600480546001600160a01b0319166001600160a01b038481169190911790915583811660a0528116608052604051620000dd9062000448565b604051809103906000f080158015620000fa573d6000803e3d6000fd5b506001600160a01b031660c05260408051808201909152601d81527f55736520302e363925206f662048455820746f206275792048454c474f000000602082015262000149906001906200027e565b6200019160026040518060400160405280601d81526020017f55736520302e363925206f662048454c474f20746f20627579204845580000008152506200027e60201b60201c565b620001b7600460405180606001604052806040815260200162004128604091396200027e565b620001dd60036040518060600160405280603a8152602001620040ee603a91396200027e565b620002036005604051806060016040528060258152602001620040c9602591396200027e565b620002296006604051806060016040528060248152602001620040a5602491396200027e565b6200023c6003805460ff19166001179055565b5050506200071f565b600354600090690100000000000000000090046001600160401b03166200026d8142620004d3565b620002799190620004f6565b905090565b60035460ff1615620002c65760405162461bcd60e51b815260206004820152601060248201526f141c9bdc1bdcd85b1cc81b1bd8dad95960821b60448201526064016200003e565b620002d28282620002d6565b5050565b60005b60015481101562000371578260018281548110620002fb57620002fb62000524565b906000526020600020906003020160000154036200035c5760405162461bcd60e51b815260206004820152601960248201527f50726f706f73616c206964206973206e6f7420756e697175650000000000000060448201526064016200003e565b8062000368816200053a565b915050620002d9565b50604080516060810182528381526020810183815260009282018390526001805480820182559352815160039093027fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6810193845590519192917fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf790910190620003fc9082620005fb565b506040820151816002015550507fbb9ae1d099646048224bf08217de5a99638b686eeaaf0eead766fcf8eb24309182826040516200043c929190620006c7565b60405180910390a15050565b611025806200308083390190565b80516001600160a01b03811681146200046e57600080fd5b919050565b6000806000606084860312156200048957600080fd5b620004948462000456565b9250620004a46020850162000456565b9150620004b46040850162000456565b90509250925092565b634e487b7160e01b600052601160045260246000fd5b600082620004f157634e487b7160e01b600052601260045260246000fd5b500490565b6001600160401b038181168382160280821691908281146200051c576200051c620004bd565b505092915050565b634e487b7160e01b600052603260045260246000fd5b6000600182016200054f576200054f620004bd565b5060010190565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200058157607f821691505b602082108103620005a257634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620005f657600081815260208120601f850160051c81016020861015620005d15750805b601f850160051c820191505b81811015620005f257828155600101620005dd565b5050505b505050565b81516001600160401b0381111562000617576200061762000556565b6200062f816200062884546200056c565b84620005a8565b602080601f8311600181146200066757600084156200064e5750858301515b600019600386901b1c1916600185901b178555620005f2565b600085815260208120601f198616915b82811015620006985788860151825594840194600190910190840162000677565b5085821015620006b75787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b82815260006020604081840152835180604085015260005b81811015620006fd57858101830151858201606001528201620006df565b506000606082860101526060601f19601f830116850101925050509392505050565b60805160a05160c0516128d6620007aa60003960006106150152600081816105a501528181610bee01528181610c9c01528181610d0c01528181610de101528181610e8d01528181610fb901528181611bdb01528181611e680152611e930152600081816105c6015281816109fe01528181611b1001528181611e470152611eb401526128d66000f3fe608060405234801561001057600080fd5b50600436106101b95760003560e01c806362564c48116100f9578063a78d80fc11610097578063b74e452b11610071578063b74e452b146103d0578063c0a6ab79146103d8578063cbf9fe5f146103e0578063d8bff5a5146103f357600080fd5b8063a78d80fc146103a2578063adfaa72e146103aa578063b27a0484146103bd57600080fd5b80639852595c116100d35780639852595c1461033c5780639ab24eb01461034f578063a622ee7c14610362578063a78135871461038b57600080fd5b806362564c481461030157806370a08231146103165780637102b7281461032957600080fd5b806327a8a2a611610166578063372eafea11610140578063372eafea146102e1578063378a2178146102e95780634d347872146102f1578063616e42b8146102f957600080fd5b806327a8a2a61461028b5780632c0a3f89146102b65780633039addf146102c957600080fd5b80631b0c27da116101975780631b0c27da1461021f5780631f68e23b1461025c57806326db9d3a1461027257600080fd5b8063013cf08b146101be578063051097ef146101e957806306040618146101fe575b600080fd5b6101d16101cc3660046120da565b610406565b6040516101e093929190612139565b60405180910390f35b6101fc6101f73660046121a9565b6104c4565b005b61020661052a565b60405167ffffffffffffffff90911681526020016101e0565b61023261022d3660046120da565b61055a565b6040805194855260208501939093529183015267ffffffffffffffff1660608201526080016101e0565b61026461059e565b6040519081526020016101e0565b600354600160481b900467ffffffffffffffff16610206565b61029e610299366004612277565b6105ec565b6040516001600160a01b0390911681526020016101e0565b6101fc6102c43660046122ba565b6106fa565b6102d16107c9565b60405190151581526020016101e0565b6102d16108c5565b61026461097b565b6102646109f2565b6102d1610a78565b610309610a9f565b6040516101e091906122dc565b61026461032436600461235d565b610ba4565b61026461033736600461235d565b610c5a565b61026461034a36600461235d565b610ce3565b61026461035d36600461235d565b610d46565b61029e61037036600461235d565b6006602052600090815260409020546001600160a01b031681565b600354610100900467ffffffffffffffff16610206565b600154610264565b6102d16103b836600461235d565b610d57565b6102646103cb3660046120da565b610d62565b610206610d83565b600254610264565b6102646103ee36600461235d565b610d9f565b61026461040136600461235d565b610f0d565b6001818154811061041657600080fd5b6000918252602090912060039091020180546001820180549193509061043b90612378565b80601f016020809104026020016040519081016040528092919081815260200182805461046790612378565b80156104b45780601f10610489576101008083540402835291602001916104b4565b820191906000526020600020905b81548152906001019060200180831161049757829003601f168201915b5050505050908060020154905083565b60035460ff161561051c5760405162461bcd60e51b815260206004820152601060248201527f50726f706f73616c73206c6f636b65640000000000000000000000000000000060448201526064015b60405180910390fd5b610526828261102e565b5050565b600354600090600160481b900467ffffffffffffffff1661054b81426123de565b61055591906123f2565b905090565b6002818154811061056a57600080fd5b60009182526020909120600490910201805460018201546002830154600390930154919350919067ffffffffffffffff1684565b60006105557f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006012611193565b6001600160a01b03808416600090815260066020526040812054909116806106f05760006106397f00000000000000000000000000000000000000000000000000000000000000006112c4565b60405163389071af60e21b81526001600160a01b03888116600483015267ffffffffffffffff8089166024840152871660448301529193508392509082169063e241c6bc90606401600060405180830381600087803b15801561069b57600080fd5b505af11580156106af573d6000803e3d6000fd5b505050506001600160a01b038681166000908152600660205260409020805473ffffffffffffffffffffffffffffffffffffffff1916918416919091179055505b90505b9392505050565b610702611365565b60048203610763576107126107c9565b61075e5760405162461bcd60e51b815260206004820152601560248201527f484558206e6f7420617420393020646179206c6f7700000000000000000000006044820152606401610513565b6107bf565b600382036107bf576107736108c5565b6107bf5760405162461bcd60e51b815260206004820152601660248201527f484558206e6f74206174203930206461792068696768000000000000000000006044820152606401610513565b61052682826113f4565b600554600090605a8111156107dc5750605a5b806000036107ec57600091505090565b6005546000906107fd90839061241e565b90506000806001600580549050610814919061241e565b90505b8281111561088057816005828154811061083357610833612431565b90600052602060002001541080610848575081155b1561086e576005818154811061086057610860612431565b906000526020600020015491505b8061087881612447565b915050610817565b506005546003118015906108bd57506005805461089f9060019061241e565b815481106108af576108af612431565b906000526020600020015481145b935050505090565b6005546000908082036108da57600091505090565b605a8111156108e75750605a5b6005546000906108f890839061241e565b9050600080600160058054905061090f919061241e565b90505b8281111561088057816005828154811061092e5761092e612431565b90600052602060002001541180610943575081155b15610969576005818154811061095b5761095b612431565b906000526020600020015491505b8061097381612447565b915050610912565b600061098561052a565b60035467ffffffffffffffff918216610100909104909116036109ea5760405162461bcd60e51b815260206004820152601560248201527f566f74696e67207374696c6c20756e64657277617900000000000000000000006044820152606401610513565b6105556114a4565b600454600090610555907f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b031673165c3410fc91ef562c50559f7d2289febed552d914610a5b5773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2610a71565b73a1077a294dde1b09bb078844df40758a5d0f9a275b600a611193565b6000610a8261052a565b600354610100900467ffffffffffffffff90811691161415919050565b60606001805480602002602001604051908101604052809291908181526020016000905b82821015610b9b578382906000526020600020906003020160405180606001604052908160008201548152602001600182018054610b0090612378565b80601f0160208091040260200160405190810160405280929190818152602001828054610b2c90612378565b8015610b795780601f10610b4e57610100808354040283529160200191610b79565b820191906000526020600020905b815481529060010190602001808311610b5c57829003601f168201915b5050505050815260200160028201548152505081526020019060010190610ac3565b50505050905090565b6001600160a01b0380821660009081526006602052604081205490911680610bcf5750600092915050565b6040516370a0823160e01b81526001600160a01b0382811660048301527f000000000000000000000000000000000000000000000000000000000000000016906370a08231906024015b602060405180830381865afa158015610c36573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f3919061245e565b6001600160a01b0380821660009081526006602052604081205490911680610c855750600092915050565b60405163810ec23b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301524267ffffffffffffffff16602483015282169063810ec23b90604401610c19565b6001600160a01b03818116600090815260066020526040808220549051631aa7287360e21b81527f000000000000000000000000000000000000000000000000000000000000000084166004820152919216908190636a9ca1cc90602401610c19565b6000610d5182610f0d565b92915050565b6000610d5182611759565b60058181548110610d7257600080fd5b600091825260209091200154905081565b6000610d9262015180426123de565b6105559062015180612477565b6001600160a01b0380821660009081526006602052604081205490911680610dca5750600092915050565b60405163810ec23b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301524267ffffffffffffffff1660248301526000919083169063810ec23b90604401602060405180830381865afa158015610e44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e68919061245e565b6040516370a0823160e01b81526001600160a01b0384811660048301529192506000917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610ed4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef8919061245e565b9050610f04828261241e565b95945050505050565b6001600160a01b0380821660009081526006602052604081205490911680610f385750600092915050565b806001600160a01b0316630fb5a6b46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9a919061245e565b6040516370a0823160e01b81526001600160a01b0383811660048301527f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611000573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611024919061245e565b6106f39190612477565b60005b6001548110156110c057826001828154811061104f5761104f612431565b906000526020600020906003020160000154036110ae5760405162461bcd60e51b815260206004820152601960248201527f50726f706f73616c206964206973206e6f7420756e69717565000000000000006044820152606401610513565b806110b88161248e565b915050611031565b50604080516060810182528381526020810183815260009282018390526001805480820182559352815160039093027fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6810193845590519192917fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf79091019061114990826124f5565b506040820151816002015550507fbb9ae1d099646048224bf08217de5a99638b686eeaaf0eead766fcf8eb24309182826040516111879291906125b5565b60405180910390a15050565b604080516002808252606080830184526000939092919060208301908036833701905050905084816000815181106111cd576111cd612431565b60200260200101906001600160a01b031690816001600160a01b031681525050838160018151811061120157611201612431565b6001600160a01b0392831660209182029290920101526004546000911663d06ca61f61122e86600a6126ba565b611239906001612477565b846040518363ffffffff1660e01b815260040161125792919061270a565b600060405180830381865afa158015611274573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261129c9190810190612723565b9050806001815181106112b1576112b1612431565b6020026020010151925050509392505050565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c176000526e5af43d82803e903d91602b57fd5bf38260781b17602052603760096000f090506001600160a01b0381166113605760405162461bcd60e51b815260206004820152601660248201527f455243313136373a20637265617465206661696c6564000000000000000000006044820152606401610513565b919050565b60045474010000000000000000000000000000000000000000900467ffffffffffffffff16611392610d83565b67ffffffffffffffff16146113f25760056113ab6109f2565b815460018101835560009283526020909220909101556113c9610d83565b600460146101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055505b565b600082116114445760405162461bcd60e51b815260206004820152601160248201527f50726f706f73616c2072657175697265640000000000000000000000000000006044820152606401610513565b600081116114945760405162461bcd60e51b815260206004820152600e60248201527f566f7465732072657175697265640000000000000000000000000000000000006044820152606401610513565b61149f3383836117a4565b505050565b600080808080805b6001548110156115d65783600182815481106114ca576114ca612431565b906000526020600020906003020160020154111561153957600181815481106114f5576114f5612431565b90600052602060002090600302016000015494506001818154811061151c5761151c612431565b906000526020600020906003020160020154935060009150611569565b836001828154811061154d5761154d612431565b9060005260206000209060030201600201540361156957600191505b6001818154811061157c5761157c612431565b9060005260206000209060030201600201548361159991906127c9565b92506000600182815481106115b0576115b0612431565b6000918252602090912060026003909202010155806115ce8161248e565b9150506114ac565b50806001036115e457600093505b6115ec61052a565b6003805467ffffffffffffffff929092166101000268ffffffffffffffff001990921691909117905560408051858152602081018590529081018390527f35373aa77b533e0f418d94b45286b19b27edc63a06e8a7b127a18a83b1a3527d9060600160405180910390a160408051608081018252858152602081018581529181018481524267ffffffffffffffff908116606084019081526002805460018101825560009190915293517f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace60049095029485015593517f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf84015590517f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ad083015591517f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ad1909101805467ffffffffffffffff1916919092161790556117508484611a64565b50919392505050565b600061176361052a565b6001600160a01b03831660009081526020819052604090205467ffffffffffffffff9190911614801590610d515750600061179d83610d46565b1192915050565b60006117ae61052a565b67ffffffffffffffff16600080866001600160a01b03166001600160a01b03168152602001908152602001600020540361182a5760405162461bcd60e51b815260206004820152600d60248201527f416c726561647920766f746564000000000000000000000000000000000000006044820152606401610513565b600061183585610d46565b1161186d5760405162461bcd60e51b81526020600482015260086024820152674e6f20766f74657360c01b6044820152606401610513565b8161187785610d46565b10156118c55760405162461bcd60e51b815260206004820152601060248201527f4e6f7420656e6f75676820766f746573000000000000000000000000000000006044820152606401610513565b6118cd610a78565b156118dc576118da6114a4565b505b6118e461052a565b6001600160a01b038516600090815260208190526040812067ffffffffffffffff929092169091555b600154811015611a1b57836001828154811061192b5761192b612431565b90600052602060002090600302016000015403611a09577fb4c54afd73915f9f756c8dee39fe0b311937871a92261a47747f7a3d32f63a0c6001828154811061197657611976612431565b906000526020600020906003020160000154868561199261052a565b604080519485526001600160a01b0390931660208501529183015267ffffffffffffffff16606082015260800160405180910390a182600182815481106119db576119db612431565b906000526020600020906003020160020160008282546119fb91906127c9565b9250508190559150506106f3565b80611a138161248e565b91505061190d565b5060405162461bcd60e51b815260206004820152601460248201527f4e6f7420612076616c69642070726f706f73616c0000000000000000000000006044820152606401610513565b611a6c611365565b80156105265760018203611a8257610526611ad2565b60028203611a9257610526611b9d565b60038203611aa257610526611c23565b60048203611ab257610526611c41565b60058203611ac257610526611c5f565b6006820361052657610526611d11565b6000611b89611af06618838370f34000670de0b6b3a7640000611d53565b611afb9060016127dc565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a08231906024015b602060405180830381865afa158015611b60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b84919061245e565b611dbd565b90508015611b9a57611b9a81611e42565b50565b6000611c12611bbb6618838370f34000670de0b6b3a7640000611d53565b611bc69060016127dc565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401611b43565b90508015611b9a57611b9a81611e8e565b6000611b89611af06683185ac0364000670de0b6b3a7640000611d53565b6000611c12611bbb6683185ac0364000670de0b6b3a7640000611d53565b6003546224ea00600160481b90910467ffffffffffffffff1610156113f257600354610e1090611ca190600160481b900467ffffffffffffffff1660026123f2565b611cab919061281b565b611cb790610e106123f2565b600360096101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550611ce761052a565b600360016101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550565b600354612a30600160481b90910467ffffffffffffffff1611156113f257600354610e1090611ca190600290600160481b900467ffffffffffffffff1661281b565b600081600f0b600003611d6557600080fd5b600082600f0b604085600f0b901b81611d8057611d806123b2565b0590506f7fffffffffffffffffffffffffffffff198112801590611db457506f7fffffffffffffffffffffffffffffff8113155b6106f357600080fd5b600081600003611dcf57506000610d51565b600083600f0b1215611de057600080fd5b600f83900b6fffffffffffffffffffffffffffffffff8316810260401c90608084901c0277ffffffffffffffffffffffffffffffffffffffffffffffff811115611e2957600080fd5b60401b8119811115611e3a57600080fd5b019392505050565b611b9a7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008330611ed6565b611b9a7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083305b60408051600280825260608083018452926020830190803683370190505090508481600081518110611f0a57611f0a612431565b60200260200101906001600160a01b031690816001600160a01b0316815250508381600181518110611f3e57611f3e612431565b6001600160a01b03928316602091820292909201015260048054604051636eb1769f60e11b8152309281019290925282166024820152869185919083169063dd62ed3e90604401602060405180830381865afa158015611fa2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc6919061245e565b1015612048576004805460405163095ea7b360e01b81526001600160a01b0391821692810192909252600019602483015282169063095ea7b3906044016020604051808303816000875af1158015612022573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120469190612842565b505b6004546001600160a01b03166338ed1739856000858761206a426127106127c9565b6040518663ffffffff1660e01b815260040161208a959493929190612864565b6000604051808303816000875af11580156120a9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120d19190810190612723565b50505050505050565b6000602082840312156120ec57600080fd5b5035919050565b6000815180845260005b81811015612119576020818501810151868301820152016120fd565b506000602082860101526020601f19601f83011685010191505092915050565b83815260606020820152600061215260608301856120f3565b9050826040830152949350505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156121a1576121a1612162565b604052919050565b600080604083850312156121bc57600080fd5b8235915060208084013567ffffffffffffffff808211156121dc57600080fd5b818601915086601f8301126121f057600080fd5b81358181111561220257612202612162565b612214601f8201601f19168501612178565b9150808252878482850101111561222a57600080fd5b80848401858401376000848284010152508093505050509250929050565b80356001600160a01b038116811461136057600080fd5b803567ffffffffffffffff8116811461136057600080fd5b60008060006060848603121561228c57600080fd5b61229584612248565b92506122a36020850161225f565b91506122b16040850161225f565b90509250925092565b600080604083850312156122cd57600080fd5b50508035926020909101359150565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561234f57603f19898403018552815160608151855288820151818a870152612331828701826120f3565b92890151958901959095525094870194925090860190600101612303565b509098975050505050505050565b60006020828403121561236f57600080fd5b6106f382612248565b600181811c9082168061238c57607f821691505b6020821081036123ac57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000826123ed576123ed6123b2565b500490565b67ffffffffffffffff818116838216028082169190828114612416576124166123c8565b505092915050565b81810381811115610d5157610d516123c8565b634e487b7160e01b600052603260045260246000fd5b600081612456576124566123c8565b506000190190565b60006020828403121561247057600080fd5b5051919050565b8082028115828204841417610d5157610d516123c8565b6000600182016124a0576124a06123c8565b5060010190565b601f82111561149f57600081815260208120601f850160051c810160208610156124ce5750805b601f850160051c820191505b818110156124ed578281556001016124da565b505050505050565b815167ffffffffffffffff81111561250f5761250f612162565b6125238161251d8454612378565b846124a7565b602080601f83116001811461255857600084156125405750858301515b600019600386901b1c1916600185901b1785556124ed565b600085815260208120601f198616915b8281101561258757888601518255948401946001909101908401612568565b50858210156125a55787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8281526040602082015260006125ce60408301846120f3565b949350505050565b600181815b808511156126115781600019048211156125f7576125f76123c8565b8085161561260457918102915b93841c93908002906125db565b509250929050565b60008261262857506001610d51565b8161263557506000610d51565b816001811461264b576002811461265557612671565b6001915050610d51565b60ff841115612666576126666123c8565b50506001821b610d51565b5060208310610133831016604e8410600b8410161715612694575081810a610d51565b61269e83836125d6565b80600019048211156126b2576126b26123c8565b029392505050565b60006106f38383612619565b600081518084526020808501945080840160005b838110156126ff5781516001600160a01b0316875295820195908201906001016126da565b509495945050505050565b8281526040602082015260006125ce60408301846126c6565b6000602080838503121561273657600080fd5b825167ffffffffffffffff8082111561274e57600080fd5b818501915085601f83011261276257600080fd5b81518181111561277457612774612162565b8060051b9150612785848301612178565b818152918301840191848101908884111561279f57600080fd5b938501935b838510156127bd578451825293850193908501906127a4565b98975050505050505050565b80820180821115610d5157610d516123c8565b600f81810b9083900b016f7fffffffffffffffffffffffffffffff81136f7fffffffffffffffffffffffffffffff1982121715610d5157610d516123c8565b600067ffffffffffffffff80841680612836576128366123b2565b92169190910492915050565b60006020828403121561285457600080fd5b815180151581146106f357600080fd5b85815284602082015260a06040820152600061288360a08301866126c6565b6001600160a01b039490941660608301525060800152939250505056fea264697066735822122086035217f1b690f6ecb845f49b46cc41c123b309277cf7ecd4b1811d3d3410a064736f6c63430008110033608060405234801561001057600080fd5b50611005806100206000396000f3fe6080604052600436106100cb5760003560e01c80639613252111610074578063e241c6bc1161004e578063e241c6bc14610219578063e545f94114610239578063f4b3238a1461025957600080fd5b806396132521146101be578063be9a6555146101d3578063d571100d146101f957600080fd5b80636a9ca1cc116100a55780636a9ca1cc14610151578063810ec23b1461018757806386d1a69f146101a757600080fd5b80630a17b06b146100d75780630fb5a6b41461010a57806338af3eed1461012957600080fd5b366100d257005b600080fd5b3480156100e357600080fd5b506100f76100f2366004610de9565b610279565b6040519081526020015b60405180910390f35b34801561011657600080fd5b5060035467ffffffffffffffff166100f7565b34801561013557600080fd5b506002546040516001600160a01b039091168152602001610101565b34801561015d57600080fd5b506100f761016c366004610e1b565b6001600160a01b031660009081526001602052604090205490565b34801561019357600080fd5b506100f76101a2366004610e36565b61029d565b3480156101b357600080fd5b506101bc610333565b005b3480156101ca57600080fd5b506000546100f7565b3480156101df57600080fd5b50600254600160a01b900467ffffffffffffffff166100f7565b34801561020557600080fd5b506101bc610214366004610de9565b61041e565b34801561022557600080fd5b506101bc610234366004610e69565b610579565b34801561024557600080fd5b506101bc610254366004610e1b565b6106b4565b34801561026557600080fd5b506101bc610274366004610de9565b6107d7565b600061029761028760005490565b6102919047610ec2565b83610923565b92915050565b6001600160a01b03821660009081526001602052604081205461032c906040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa1580156102fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103229190610ed5565b6102919190610ec2565b9392505050565b6002546001600160a01b0316331461039e5760405162461bcd60e51b8152602060048201526024808201527f5661756c743a2063616c6c6572206973206e6f74207468652062656e656669636044820152636961727960e01b60648201526084015b60405180910390fd5b600080546103ab42610279565b6103b59190610eee565b9050806000808282546103c89190610ec2565b90915550506040518181527ffb81f9b30d73d830c3544b34d827c08142579ee75710b490bab0b3995468c5659060200160405180910390a161041b6104156002546001600160a01b031690565b826109e6565b50565b6002546001600160a01b031633146104845760405162461bcd60e51b8152602060048201526024808201527f5661756c743a2063616c6c6572206973206e6f74207468652062656e656669636044820152636961727960e01b6064820152608401610395565b60025467ffffffffffffffff600160a01b9091048116908216116104fa5760405162461bcd60e51b815260206004820152602760248201527f4e6577207374617274206d7573742062652067726561746572207468616e2070604482015266726576696f757360c81b6064820152608401610395565b60405167ffffffffffffffff821681527f821528367497dd987acc493072f294a1f12dc214e00ac4536bee892ba99b0ca29060200160405180910390a16002805467ffffffffffffffff909216600160a01b027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b6001600160a01b0383166105da5760405162461bcd60e51b815260206004820152602260248201527f5661756c743a2062656e6566696369617279206973207a65726f206164647265604482015261737360f01b6064820152608401610395565b60035468010000000000000000900460ff16151560010361063d5760405162461bcd60e51b815260206004820152601a60248201527f5661756c743a20616c726561647920696e697469616c697365640000000000006044820152606401610395565b6002805467ffffffffffffffff938416600160a01b027fffffffff000000000000000000000000000000000000000000000000000000009091166001600160a01b0390951694909417939093179092556003805468ffffffffffffffffff1916929091169190911768010000000000000000179055565b6002546001600160a01b0316331461071a5760405162461bcd60e51b8152602060048201526024808201527f5661756c743a2063616c6c6572206973206e6f74207468652062656e656669636044820152636961727960e01b6064820152608401610395565b6001600160a01b03811660009081526001602052604081205461073d834261029d565b6107479190610eee565b6001600160a01b038316600090815260016020526040812080549293508392909190610774908490610ec2565b90915550506040518181526001600160a01b038316907f9cf9e3ab58b33f06d81842ea0ad850b6640c6430d6396973312e1715792e7a919060200160405180910390a26107d3826107cd6002546001600160a01b031690565b83610b04565b5050565b6002546001600160a01b0316331461083d5760405162461bcd60e51b8152602060048201526024808201527f5661756c743a2063616c6c6572206973206e6f74207468652062656e656669636044820152636961727960e01b6064820152608401610395565b60035467ffffffffffffffff908116908216116108c25760405162461bcd60e51b815260206004820152602a60248201527f4e6577206475726174696f6e206d75737420626520677265617465722074686160448201527f6e2070726576696f7573000000000000000000000000000000000000000000006064820152608401610395565b60405167ffffffffffffffff821681527fb0d47a45bab34df24d10acb7012cfa00c3d7ac0f1d4940837577243a37f241379060200160405180910390a16003805467ffffffffffffffff191667ffffffffffffffff92909216919091179055565b600254600090600160a01b900467ffffffffffffffff168267ffffffffffffffff16101561095357506000610297565b60035467ffffffffffffffff16600254600160a01b900467ffffffffffffffff1661097e9190610ec2565b8267ffffffffffffffff161115610996575081610297565b60035467ffffffffffffffff16600254600160a01b900467ffffffffffffffff166109cb9067ffffffffffffffff8516610eee565b6109d59085610f01565b6109df9190610f18565b9050610297565b80471015610a365760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610395565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114610a83576040519150601f19603f3d011682016040523d82523d6000602084013e610a88565b606091505b5050905080610aff5760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610395565b505050565b604080516001600160a01b03848116602483015260448083018590528351808403909101815260649092018352602080830180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663a9059cbb60e01b17905283518085019094528084527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656490840152610aff92869291600091610ba9918516908490610c3c565b9050805160001480610bca575080806020019051810190610bca9190610f3a565b610aff5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610395565b6060610c4b8484600085610c53565b949350505050565b606082471015610cb45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610395565b600080866001600160a01b03168587604051610cd09190610f80565b60006040518083038185875af1925050503d8060008114610d0d576040519150601f19603f3d011682016040523d82523d6000602084013e610d12565b606091505b5091509150610d2387838387610d2e565b979650505050505050565b60608315610d9d578251600003610d96576001600160a01b0385163b610d965760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610395565b5081610c4b565b610c4b8383815115610db25781518083602001fd5b8060405162461bcd60e51b81526004016103959190610f9c565b803567ffffffffffffffff81168114610de457600080fd5b919050565b600060208284031215610dfb57600080fd5b61032c82610dcc565b80356001600160a01b0381168114610de457600080fd5b600060208284031215610e2d57600080fd5b61032c82610e04565b60008060408385031215610e4957600080fd5b610e5283610e04565b9150610e6060208401610dcc565b90509250929050565b600080600060608486031215610e7e57600080fd5b610e8784610e04565b9250610e9560208501610dcc565b9150610ea360408501610dcc565b90509250925092565b634e487b7160e01b600052601160045260246000fd5b8082018082111561029757610297610eac565b600060208284031215610ee757600080fd5b5051919050565b8181038181111561029757610297610eac565b808202811582820484141761029757610297610eac565b600082610f3557634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215610f4c57600080fd5b8151801515811461032c57600080fd5b60005b83811015610f77578181015183820152602001610f5f565b50506000910152565b60008251610f92818460208701610f5c565b9190910192915050565b6020815260008251806020840152610fbb816040850160208701610f5c565b601f01601f1916919091016040019291505056fea2646970667358221220ffaa8acb43c3c624885938af774f63b22ca40e4d1f0520406cf0dd3cd762c1f164736f6c6343000811003348616c76657320746865206c656e677468206f662074686520766f746520706572696f64446f75626c657320746865206c656e677468206f662074686520766f746520706572696f645573657320332e363925206f662048455820746f206275792048454c474f2028766f7461626c65207768656e20484558206e65617220415448295573657320332e363925206f662048454c474f20746f20627579204845582028766f7461626c65207768656e204845582061742033206d6f6e7468206c6f77290000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb39000000000000000000000000165c3410fc91ef562c50559f7d2289febed552d9000000000000000000000000c9cab624882418d8be742b570825a8d5eb8f89da

Deployed ByteCode

0x6080604052600436106103345760003560e01c806386514a2e116101b0578063b74e452b116100ec578063da62fba911610095578063eef49ee31161006f578063eef49ee31461098c578063f027bf1b146109a2578063f53352b2146109d6578063fc7e286d14610a0657600080fd5b8063da62fba9146108e0578063dd62ed3e14610910578063e1254fba1461095657600080fd5b8063c3124525116100c6578063c312452514610892578063d3b7ec81146108b4578063d5a1d691146108ca57600080fd5b8063b74e452b146107f2578063c038a38e14610807578063c1bbfb611461087257600080fd5b8063a52b9a8a11610159578063ace3a8a711610133578063ace3a8a714610786578063af38d757146107a6578063b5caf888146107bc578063b6b55f25146107d257600080fd5b8063a52b9a8a14610721578063a9059cbb14610746578063aa97527f1461076657600080fd5b8063999ec52a1161018a578063999ec52a146106b8578063a26dbf26146106ec578063a457c2d71461070157600080fd5b806386514a2e1461058b5780639225dfe81461068e57806395d89b41146106a357600080fd5b8063395093511161027f57806356891412116102285780637b76ac91116102025780637b76ac911461060b5780637c8c12e1146106205780638091f3bf1461064057806381b3b1fb1461065a57600080fd5b806356891412146105a057806363453ae1146105b557806370a08231146105d557600080fd5b8063450db90b11610259578063450db90b1461054c5780634e71d92d14610561578063513107631461058b57600080fd5b806339509351146104ec57806340d2609e1461050c57806342966c681461052c57600080fd5b80631b9db2ef116102e15780633054c79a116102bb5780633054c79a146104a8578063313ce567146104b057806335c1d349146104cc57600080fd5b80631b9db2ef146104305780631d8eb19f1461046857806323b872dd1461048857600080fd5b806315770f921161031257806315770f92146103ab578063160e4152146103ce57806318160ddd1461041b57600080fd5b806301339c211461033957806306fdde0314610350578063095ea7b31461037b575b600080fd5b34801561034557600080fd5b5061034e610a33565b005b34801561035c57600080fd5b50610365610a3d565b6040516103729190612aa1565b60405180910390f35b34801561038757600080fd5b5061039b610396366004612b04565b610acf565b6040519015158152602001610372565b3480156103b757600080fd5b506103c0610ae9565b604051908152602001610372565b3480156103da57600080fd5b506104027f0000000000000000000000000000000000000000000000000000000064b5d60081565b60405167ffffffffffffffff9091168152602001610372565b34801561042757600080fd5b506002546103c0565b34801561043c57600080fd5b5061045061044b366004612b30565b610b10565b6040516001600160a01b039091168152602001610372565b34801561047457600080fd5b5061039b610483366004612b66565b610b40565b34801561049457600080fd5b5061039b6104a3366004612ba2565b610b56565b61034e610b7a565b3480156104bc57600080fd5b5060405160128152602001610372565b3480156104d857600080fd5b506104506104e7366004612b30565b610b84565b3480156104f857600080fd5b5061039b610507366004612b04565b610bae565b34801561051857600080fd5b50610450610527366004612b30565b610bed565b34801561053857600080fd5b5061034e610547366004612b30565b610bfd565b34801561055857600080fd5b506103c0610c0a565b34801561056d57600080fd5b50610576610c2c565b60408051928352602083019190915201610372565b34801561059757600080fd5b506103c0610c40565b3480156105ac57600080fd5b506103c0610c62565b3480156105c157600080fd5b506105766105d0366004612be3565b610c84565b3480156105e157600080fd5b506103c06105f0366004612be3565b6001600160a01b031660009081526020819052604090205490565b34801561061757600080fd5b50610402610c99565b34801561062c57600080fd5b50600854610450906001600160a01b031681565b34801561064c57600080fd5b5060075461039b9060ff1681565b34801561066657600080fd5b506104027f00000000000000000000000000000000000000000000000000000000648a548081565b34801561069a57600080fd5b506103c0610cfe565b3480156106af57600080fd5b50610365610d6b565b3480156106c457600080fd5b506104507f0000000000000000000000001fd1bb3afcb6223c7a9398ffcef24f9077c749b581565b3480156106f857600080fd5b50600d546103c0565b34801561070d57600080fd5b5061039b61071c366004612b04565b610d7a565b34801561072d57600080fd5b506007546104509061010090046001600160a01b031681565b34801561075257600080fd5b5061039b610761366004612b04565b610e11565b34801561077257600080fd5b5061034e610781366004612b30565b610e1f565b34801561079257600080fd5b50600a54610450906001600160a01b031681565b3480156107b257600080fd5b506103c060105481565b3480156107c857600080fd5b506103c0600e5481565b3480156107de57600080fd5b5061034e6107ed366004612b30565b610efd565b3480156107fe57600080fd5b50610402610f86565b34801561081357600080fd5b5061081c610fa2565b6040516103729190600060e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015292915050565b34801561087e57600080fd5b5061034e61088d366004612b30565b611047565b34801561089e57600080fd5b506108a7611087565b6040516103729190612c00565b3480156108c057600080fd5b506103c060125481565b3480156108d657600080fd5b506103c060115481565b3480156108ec57600080fd5b5061039b6108fb366004612be3565b60146020526000908152604090205460ff1681565b34801561091c57600080fd5b506103c061092b366004612ccf565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b34801561096257600080fd5b506103c0610971366004612be3565b6001600160a01b031660009081526013602052604090205490565b34801561099857600080fd5b506103c0600f5481565b3480156109ae57600080fd5b506103c07f000000000000000000000000000000000000000000000000000000000000002581565b3480156109e257600080fd5b5061039b6109f1366004612be3565b60066020526000908152604090205460ff1681565b348015610a1257600080fd5b506103c0610a21366004612be3565b60136020526000908152604090205481565b610a3b611222565b565b606060038054610a4c90612d08565b80601f0160208091040260200160405190810160405280929190818152602001828054610a7890612d08565b8015610ac55780601f10610a9a57610100808354040283529160200191610ac5565b820191906000526020600020905b815481529060010190602001808311610aa857829003601f168201915b5050505050905090565b600033610add8185856113c9565b60019150505b92915050565b60006014610af660025490565b610b009190612d6e565b610b0b906002612d82565b905090565b6000600d8281548110610b2557610b25612d99565b6000918252602090912001546001600160a01b031692915050565b6000610b4e338585856114ee565b949350505050565b600033610b648582856115f1565b610b6f858585611683565b506001949350505050565b610a3b3334611827565b600d8181548110610b9457600080fd5b6000918252602090912001546001600160a01b0316905081565b3360008181526001602090815260408083206001600160a01b0387168452909152812054909190610add9082908690610be8908790612daf565b6113c9565b60058181548110610b9457600080fd5b610c073382611a7e565b50565b60006014610c1760025490565b610c219190612d6e565b610b0b906009612d82565b600080610c3833611ba8565b915091509091565b60006014610c4d60025490565b610c579190612d6e565b610b0b906001612d82565b60006014610c6f60025490565b610c799190612d6e565b610b0b906007612d82565b600080610c9083611ba8565b91509150915091565b6000807f00000000000000000000000000000000000000000000000000000000648a5480610cc5610f86565b610ccf9190612dc2565b90508067ffffffffffffffff16600003610ceb57600091505090565b610cf86201518082612dea565b91505090565b6009546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015610d47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0b9190612e11565b606060048054610a4c90612d08565b3360008181526001602090815260408083206001600160a01b038716845290915281205490919083811015610e045760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084015b60405180910390fd5b610b6f82868684036113c9565b600033610add818585611683565b600a546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610e76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9a9190612e2a565b5060085460405163ca1d209d60e01b8152600481018390526001600160a01b039091169063ca1d209d906024015b600060405180830381600087803b158015610ee257600080fd5b505af1158015610ef6573d6000803e3d6000fd5b5050505050565b610f073382611f4c565b6009546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610f5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f829190612e2a565b5050565b6000610f956201518042612d6e565b610b0b9062015180612d82565b610fe26040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060e00160405280610ff660025490565b8152602001611003610c0a565b8152602001611010610c62565b815260200161101d610ae9565b815260200161102a610c40565b8152602001611037610c40565b8152602001600e54815250905090565b611052333083611683565b60075460405163ca1d209d60e01b8152600481018390526101009091046001600160a01b03169063ca1d209d90602401610ec8565b611115604051806101800160405280600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600015158152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518061018001604052807f00000000000000000000000000000000000000000000000000000000648a548067ffffffffffffffff1681526020017f0000000000000000000000000000000000000000000000000000000064b5d60067ffffffffffffffff168152602001611189610f86565b67ffffffffffffffff1681526020016111a0610c99565b67ffffffffffffffff16815260075460ff16151560208201526040016111db336001600160a01b031660009081526020819052604090205490565b8152336000908152601360209081526040918290205490830152600f54908201526010546060820152601154608082015260125460a0820152600d5460c090910152919050565b60075460ff16156112755760405162461bcd60e51b815260206004820152601060248201527f416c7265616479206c61756e63686564000000000000000000000000000000006044820152606401610dfb565b7f0000000000000000000000000000000000000000000000000000000064b5d60067ffffffffffffffff166112a8610f86565b67ffffffffffffffff1610156113005760405162461bcd60e51b815260206004820152601260248201527f546f6f20736f6f6e20746f206c61756e636800000000000000000000000000006044820152606401610dfb565b6007805460ff191660011790556009546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611355573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113799190612e11565b600e55611384612108565b61138c6121ef565b61139461232a565b6040514281527feca0dfbda743d141662328d4d48393e344b2c60df0430297221df823f2926dba9060200160405180910390a1565b6001600160a01b03831661142b5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610dfb565b6001600160a01b03821661148c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610dfb565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6040516313d4515360e11b815233600482015267ffffffffffffffff80841660248301528216604482015260009081906001600160a01b037f0000000000000000000000001fd1bb3afcb6223c7a9398ffcef24f9077c749b516906327a8a2a6906064016020604051808303816000875af1158015611571573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115959190612e4c565b90506115a2868287611683565b856001600160a01b03167f9f1ec8c880f76798e7b793325d625e9b60e4082a553c98f42b6cda368dd60008866040516115dd91815260200190565b60405180910390a250600195945050505050565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461167d57818110156116705760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610dfb565b61167d84848484036113c9565b50505050565b6001600160a01b0383166116e75760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610dfb565b6001600160a01b0382166117495760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610dfb565b6001600160a01b038316600090815260208190526040902054818110156117c15760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610dfb565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a361167d565b600081116118775760405162461bcd60e51b815260206004820181905260248201527f416d6f756e74206d7573742062652067726561746572207468616e207a65726f6044820152606401610dfb565b604080516002808252606080830184529260208301908036833701905050600b549091506001600160a01b031673165c3410fc91ef562c50559f7d2289febed552d9146118d85773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26118ee565b73a1077a294dde1b09bb078844df40758a5d0f9a275b8160008151811061190157611901612d99565b6001600160a01b03928316602091820292909201015260095482519116908290600190811061193257611932612d99565b6001600160a01b039283166020918202929092010152600b5460405163d06ca61f60e01b8152600092919091169063d06ca61f906119769086908690600401612ec3565b600060405180830381865afa158015611993573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119bb9190810190612edc565b9050600081600183516119ce9190612f9a565b815181106119de576119de612d99565b6020908102919091010151600b549091506001600160a01b0316637ff36ab585838630611a0d42612710612daf565b6040518663ffffffff1660e01b8152600401611a2c9493929190612fad565b60006040518083038185885af1158015611a4a573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f19168201604052611a739190810190612edc565b50610ef68582611f4c565b6001600160a01b038216611ade5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610dfb565b6001600160a01b03821660009081526020819052604090205481811015611b525760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610dfb565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91016114e1565b600754600090819060ff16611bff5760405162461bcd60e51b815260206004820152600c60248201527f4e6f74206c61756e6368656400000000000000000000000000000000000000006044820152606401610dfb565b6001600160a01b03831660009081526014602052604090205460ff1615611c685760405162461bcd60e51b815260206004820152600f60248201527f416c726561647920636c61696d656400000000000000000000000000000000006044820152606401610dfb565b6001600160a01b0383166000908152601460209081526040808320805460ff191660011790556013909152812054819015611cfb576001600160a01b038516600090815260136020526040812054600f54611cc39190612865565b9050611cdd816002601054611cd89190612d6e565b6128af565b9250611cf7816002611ced610c40565b611cd89190612d6e565b9150505b6001600160a01b03851660009081526006602052604090205460ff1615611dda57600554611d2a906002612d82565b601054611d379190612d6e565b611d419083612daf565b600554909250611d52906002612d82565b611d5a610c40565b611d649190612d6e565b611d6e9082612daf565b600554909150600090611d7f610c62565b611d899190612d6e565b9050611d96308783611683565b611dd38682611dc96301e133807f00000000000000000000000000000000000000000000000000000000648a5480612fe2565b631c9b7c806114ee565b5050611e3f565b6001600160a01b038516600090815260136020526040902054611e3f5760405162461bcd60e51b815260206004820152600a60248201527f4e6f206465706f736974000000000000000000000000000000000000000000006044820152606401610dfb565b8160126000828254611e519190612daf565b925050819055508060116000828254611e6a9190612daf565b90915550508115611eee57600a5460405163a9059cbb60e01b81526001600160a01b038781166004830152602482018590529091169063a9059cbb906044016020604051808303816000875af1158015611ec8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eec9190612e2a565b505b8015611eff57611eff308683611683565b60408051828152602081018490526001600160a01b038716917f987d620f307ff6b94d58743cb7a7509f24071586a77759b77c2d4e29f75a2f9a910160405180910390a294909350915050565b60008111611fad5760405162461bcd60e51b815260206004820152602860248201527f4465706f73697420616d6f756e74206d7573742062652067726561746572207460448201526768616e207a65726f60c01b6064820152608401610dfb565b60075460ff161561200a5760405162461bcd60e51b815260206004820152602160248201527f416c7265616479206c61756e636865642c206465706f7369747320636c6f73656044820152601960fa1b6064820152608401610dfb565b6001600160a01b038216600090815260136020526040812054900361208257600d80546001810182556000919091527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb501805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384161790555b80600f60008282546120949190612daf565b90915550506001600160a01b038216600090815260136020526040812080548392906120c1908490612daf565b90915550506040518181526001600160a01b038316907f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c49060200160405180910390a25050565b61213a307f0000000000000000000000001fd1bb3afcb6223c7a9398ffcef24f9077c749b5612135610c40565b611683565b600954600e546001600160a01b039091169063a9059cbb907f0000000000000000000000001fd1bb3afcb6223c7a9398ffcef24f9077c749b59061218090601490612d6e565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af11580156121cb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c079190612e2a565b604051630431b6ad60e41b81523060048201527f000000000000000000000000c9cab624882418d8be742b570825a8d5eb8f89da6001600160a01b03169063431b6ad0906024016020604051808303816000875af1158015612255573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122799190612e4c565b6007805474ffffffffffffffffffffffffffffffffffffffff0019166101006001600160a01b03938416810291909117918290556122c092309291909104166000196113c9565b60075461010090046001600160a01b031663ca1d209d6122de610c0a565b6040518263ffffffff1660e01b81526004016122fc91815260200190565b600060405180830381600087803b15801561231657600080fd5b505af115801561167d573d6000803e3d6000fd5b600954600c5460405163e6a4390560e01b815230600482018190526001600160a01b03938416602483018190529093909260009291169063e6a4390590604401602060405180830381865afa158015612387573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ab9190612e4c565b90506001600160a01b03811661243657600c546040516364e329cb60e11b81526001600160a01b03858116600483015284811660248301529091169063c9c65396906044016020604051808303816000875af115801561240f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124339190612e4c565b90505b600b546124509030906001600160a01b0316610be8610ae9565b6009546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015612499573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124bd9190612e11565b600954600b5460405163095ea7b360e01b81526001600160a01b03918216600482015260248101849052929350169063095ea7b3906044016020604051808303816000875af1158015612514573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125389190612e2a565b50600b546000906001600160a01b031663e8e337008686612557610ae9565b86612560610ae9565b888c61256f42620f4240612daf565b60405160e08a901b6001600160e01b03191681526001600160a01b039889166004820152968816602488015260448701959095526064860193909352608485019190915260a484015290921660c482015260e4810191909152610104016060604051808303816000875af11580156125eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061260f9190613003565b600b54604051631022ac8760e31b81526001600160a01b0388811660048301529182166024820152898216604482015288821660648201529194507f000000000000000000000000c9cab624882418d8be742b570825a8d5eb8f89da169250638115643891506084016020604051808303816000875af1158015612697573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126bb9190612e4c565b600880546001600160a01b0392831673ffffffffffffffffffffffffffffffffffffffff199182168117909255600a8054938716939091168317905560405163095ea7b360e01b81526004810191909152600019602482015263095ea7b3906044016020604051808303816000875af115801561273c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127609190612e2a565b506008546001600160a01b031663ca1d209d61277d600284612d6e565b6040518263ffffffff1660e01b815260040161279b91815260200190565b600060405180830381600087803b1580156127b557600080fd5b505af11580156127c9573d6000803e3d6000fd5b505050506002816127da9190612d6e565b6127e49082612f9a565b6010819055600a5460405163095ea7b360e01b815230600482015260248101929092526001600160a01b03169063095ea7b3906044016020604051808303816000875af1158015612839573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285d9190612e2a565b505050505050565b60008160000361287457600080fd5b6000612880848461292b565b90506f7fffffffffffffffffffffffffffffff6001600160801b03821611156128a857600080fd5b9392505050565b6000816000036128c157506000610ae3565b600083600f0b12156128d257600080fd5b600f83900b6001600160801b038316810260401c90608084901c0277ffffffffffffffffffffffffffffffffffffffffffffffff81111561291257600080fd5b60401b811981111561292357600080fd5b019392505050565b60008160000361293a57600080fd5b600077ffffffffffffffffffffffffffffffffffffffffffffffff84116129765782604085901b8161296e5761296e612d42565b049050612a8d565b60c084811c640100000000811061298f576020918201911c5b6201000081106129a1576010918201911c5b61010081106129b2576008918201911c5b601081106129c2576004918201911c5b600481106129d2576002918201911c5b600281106129e1576001820191505b60bf820360018603901c6001018260ff0387901b81612a0257612a02612d42565b0492506001600160801b03831115612a1957600080fd5b608085901c83026001600160801b038616840260c088901c604089901b82811015612a45576001820391505b608084901b92900382811015612a5c576001820391505b829003608084901c8214612a7257612a72613031565b888181612a8157612a81612d42565b04870196505050505050505b6001600160801b038111156128a857600080fd5b600060208083528351808285015260005b81811015612ace57858101830151858201604001528201612ab2565b506000604082860101526040601f19601f8301168501019250505092915050565b6001600160a01b0381168114610c0757600080fd5b60008060408385031215612b1757600080fd5b8235612b2281612aef565b946020939093013593505050565b600060208284031215612b4257600080fd5b5035919050565b803567ffffffffffffffff81168114612b6157600080fd5b919050565b600080600060608486031215612b7b57600080fd5b83359250612b8b60208501612b49565b9150612b9960408501612b49565b90509250925092565b600080600060608486031215612bb757600080fd5b8335612bc281612aef565b92506020840135612bd281612aef565b929592945050506040919091013590565b600060208284031215612bf557600080fd5b81356128a881612aef565b815167ffffffffffffffff16815261018081016020830151612c2e602084018267ffffffffffffffff169052565b506040830151612c4a604084018267ffffffffffffffff169052565b506060830151612c66606084018267ffffffffffffffff169052565b506080830151612c7a608084018215159052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151818401525061014080840151818401525061016080840151818401525092915050565b60008060408385031215612ce257600080fd5b8235612ced81612aef565b91506020830135612cfd81612aef565b809150509250929050565b600181811c90821680612d1c57607f821691505b602082108103612d3c57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600082612d7d57612d7d612d42565b500490565b8082028115828204841417610ae357610ae3612d58565b634e487b7160e01b600052603260045260246000fd5b80820180821115610ae357610ae3612d58565b67ffffffffffffffff828116828216039080821115612de357612de3612d58565b5092915050565b600067ffffffffffffffff80841680612e0557612e05612d42565b92169190910492915050565b600060208284031215612e2357600080fd5b5051919050565b600060208284031215612e3c57600080fd5b815180151581146128a857600080fd5b600060208284031215612e5e57600080fd5b81516128a881612aef565b634e487b7160e01b600052604160045260246000fd5b600081518084526020808501945080840160005b83811015612eb85781516001600160a01b031687529582019590820190600101612e93565b509495945050505050565b828152604060208201526000610b4e6040830184612e7f565b60006020808385031215612eef57600080fd5b825167ffffffffffffffff80821115612f0757600080fd5b818501915085601f830112612f1b57600080fd5b815181811115612f2d57612f2d612e69565b8060051b604051601f19603f83011681018181108582111715612f5257612f52612e69565b604052918252848201925083810185019188831115612f7057600080fd5b938501935b82851015612f8e57845184529385019392850192612f75565b98975050505050505050565b81810381811115610ae357610ae3612d58565b848152608060208201526000612fc66080830186612e7f565b6001600160a01b03949094166040830152506060015292915050565b67ffffffffffffffff818116838216019080821115612de357612de3612d58565b60008060006060848603121561301857600080fd5b8351925060208401519150604084015190509250925092565b634e487b7160e01b600052600160045260246000fdfea26469706673582212201fa36733c7604deb0e365dcf2b654a18dcd15e562f9a82757217606afc147a8664736f6c63430008110033