Contract 0x64c9c8f5e06f6149302a1098030295f1e37bc8dc

 
Txn Hash Method
Block
From
To
Value
0xfbb671d41a3c15fb09d47cd549b0c7431327462dffaa10a202191007c03c8c290x60806040108347462022-06-07 10:46:47660 days 3 hrs agoThales: Deployer IN  Create: ThalesAMM0 ETH0.0082270573440.001
[ Download CSV Export 
View more zero value Internal Transactions in Advanced View mode
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ThalesAMM

Compiler Version
v0.5.16+commit.9c3226ce

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 17 : ThalesAMM.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.5.16;

import "openzeppelin-solidity-2.3.0/contracts/token/ERC20/SafeERC20.sol";
import "openzeppelin-solidity-2.3.0/contracts/math/SafeMath.sol";
import "openzeppelin-solidity-2.3.0/contracts/math/Math.sol";
import "@openzeppelin/upgrades-core/contracts/Initializable.sol";

import "../utils/proxy/ProxyReentrancyGuard.sol";
import "../utils/proxy/ProxyOwned.sol";
import "../utils/proxy/ProxyPausable.sol";

import "../interfaces/IPriceFeed.sol";
import "../interfaces/IPositionalMarket.sol";
import "../interfaces/IPositionalMarketManager.sol";
import "../interfaces/IPosition.sol";
import "../interfaces/IStakingThales.sol";
import "../interfaces/IReferrals.sol";
import "./DeciMath.sol";

contract ThalesAMM is ProxyOwned, ProxyPausable, ProxyReentrancyGuard, Initializable {
    using SafeMath for uint;
    using SafeERC20 for IERC20;

    DeciMath public deciMath;

    uint private constant ONE = 1e18;
    uint private constant ONE_PERCENT = 1e16;

    IPriceFeed public priceFeed;
    IERC20 public sUSD;
    address public manager;

    uint public capPerMarket;
    uint public min_spread;
    uint public max_spread;

    mapping(bytes32 => uint) public impliedVolatilityPerAsset;

    uint public minimalTimeLeftToMaturity;

    enum Position {Up, Down}

    mapping(address => uint) public spentOnMarket;

    address public safeBox;
    uint public safeBoxImpact;

    IStakingThales public stakingThales;

    uint public minSupportedPrice;
    uint public maxSupportedPrice;

    mapping(bytes32 => uint) private _capPerAsset;

    mapping(address => bool) public whitelistedAddresses;

    address public referrals;
    uint public referrerFee;

    address public previousManager;

    function initialize(
        address _owner,
        IPriceFeed _priceFeed,
        IERC20 _sUSD,
        uint _capPerMarket,
        DeciMath _deciMath,
        uint _min_spread,
        uint _max_spread,
        uint _minimalTimeLeftToMaturity
    ) public initializer {
        setOwner(_owner);
        initNonReentrant();
        priceFeed = _priceFeed;
        sUSD = _sUSD;
        capPerMarket = _capPerMarket;
        deciMath = _deciMath;
        min_spread = _min_spread;
        max_spread = _max_spread;
        minimalTimeLeftToMaturity = _minimalTimeLeftToMaturity;
    }

    function availableToBuyFromAMM(address market, Position position) public view returns (uint) {
        if (isMarketInAMMTrading(market)) {
            uint basePrice = price(market, position).add(min_spread);
            // ignore extremes
            return _availableToBuyFromAMMWithBasePrice(market, position, basePrice);
        } else {
            return 0;
        }
    }

    function _availableToBuyFromAMMWithBasePrice(
        address market,
        Position position,
        uint basePrice
    ) internal view returns (uint) {
        // ignore extremes
        if (basePrice <= minSupportedPrice || basePrice >= maxSupportedPrice) {
            return 0;
        }
        uint balance = _balanceOfPositionOnMarket(market, position);
        uint midImpactPriceIncrease = ONE.sub(basePrice).mul(max_spread.div(2)).div(ONE);

        uint divider_price = ONE.sub(basePrice.add(midImpactPriceIncrease));

        uint additionalBufferFromSelling = balance.mul(basePrice).div(ONE);

        if (_capOnMarket(market).add(additionalBufferFromSelling) <= spentOnMarket[market]) {
            return 0;
        }
        uint availableUntilCapSUSD = _capOnMarket(market).add(additionalBufferFromSelling).sub(spentOnMarket[market]);

        return balance.add(availableUntilCapSUSD.mul(ONE).div(divider_price));
    }

    function buyFromAmmQuote(
        address market,
        Position position,
        uint amount
    ) public view returns (uint) {
        uint basePrice = price(market, position).add(min_spread);
        return _buyFromAmmQuoteWithBasePrice(market, position, amount, basePrice);
    }

    function _buyFromAmmQuoteWithBasePrice(
        address market,
        Position position,
        uint amount,
        uint basePrice
    ) internal view returns (uint) {
        if (amount < 1 || amount > _availableToBuyFromAMMWithBasePrice(market, position, basePrice)) {
            return 0;
        }
        uint impactPriceIncrease = ONE.sub(basePrice).mul(_buyPriceImpact(market, position, amount)).div(ONE);
        // add 2% to the price increase to avoid edge cases on the extremes
        impactPriceIncrease = impactPriceIncrease.mul(ONE.add(ONE_PERCENT * 2)).div(ONE);
        uint tempAmount = amount.mul(basePrice.add(impactPriceIncrease)).div(ONE);
        uint returnQuote = tempAmount.mul(ONE.add(safeBoxImpact)).div(ONE);
        return IPositionalMarketManager(manager).transformCollateral(returnQuote);
    }

    function buyPriceImpact(
        address market,
        Position position,
        uint amount
    ) public view returns (uint) {
        if (amount < 1 || amount > availableToBuyFromAMM(market, position)) {
            return 0;
        }
        return _buyPriceImpact(market, position, amount);
    }

    function availableToSellToAMM(address market, Position position) public view returns (uint) {
        if (isMarketInAMMTrading(market)) {
            uint sell_max_price = _getSellMaxPrice(market, position);
            if (sell_max_price == 0) {
                return 0;
            }

            (IPosition up, IPosition down) = IPositionalMarket(market).getOptions();
            uint balanceOfTheOtherSide =
                position == Position.Up ? down.getBalanceOf(address(this)) : up.getBalanceOf(address(this));

            // can burn straight away balanceOfTheOtherSide
            uint willPay = balanceOfTheOtherSide.mul(sell_max_price).div(ONE);
            if (_capOnMarket(market).add(balanceOfTheOtherSide) < spentOnMarket[market].add(willPay)) {
                return 0;
            }
            uint usdAvailable = _capOnMarket(market).add(balanceOfTheOtherSide).sub(spentOnMarket[market]).sub(willPay);
            return usdAvailable.div(sell_max_price).mul(ONE).add(balanceOfTheOtherSide);
        } else return 0;
    }

    function _getSellMaxPrice(address market, Position position) internal view returns (uint) {
        uint basePrice = price(market, position);
        // ignore extremes
        if (basePrice <= minSupportedPrice || basePrice >= maxSupportedPrice) {
            return 0;
        }
        uint sell_max_price = basePrice.sub(min_spread).mul(ONE.sub(max_spread.div(2))).div(ONE);
        return sell_max_price;
    }

    function sellToAmmQuote(
        address market,
        Position position,
        uint amount
    ) public view returns (uint) {
        if (amount > availableToSellToAMM(market, position)) {
            return 0;
        }
        uint basePrice = price(market, position).sub(min_spread);

        uint tempAmount = amount.mul(basePrice.mul(ONE.sub(_sellPriceImpact(market, position, amount))).div(ONE)).div(ONE);

        uint returnQuote = tempAmount.mul(ONE.sub(safeBoxImpact)).div(ONE);
        return IPositionalMarketManager(manager).transformCollateral(returnQuote);
    }

    function sellPriceImpact(
        address market,
        Position position,
        uint amount
    ) public view returns (uint) {
        if (amount > availableToSellToAMM(market, position)) {
            return 0;
        }
        return _sellPriceImpact(market, position, amount);
    }

    function price(address market, Position position) public view returns (uint) {
        if (isMarketInAMMTrading(market)) {
            // add price calculation
            IPositionalMarket marketContract = IPositionalMarket(market);
            (uint maturity, ) = marketContract.times();

            uint timeLeftToMaturity = maturity - block.timestamp;
            uint timeLeftToMaturityInDays = timeLeftToMaturity.mul(ONE).div(86400);
            uint oraclePrice = marketContract.oraclePrice();

            (bytes32 key, uint strikePrice, ) = marketContract.getOracleDetails();

            if (position == Position.Up) {
                return
                    calculateOdds(oraclePrice, strikePrice, timeLeftToMaturityInDays, impliedVolatilityPerAsset[key]).div(
                        1e2
                    );
            } else {
                return
                    ONE.sub(
                        calculateOdds(oraclePrice, strikePrice, timeLeftToMaturityInDays, impliedVolatilityPerAsset[key])
                            .div(1e2)
                    );
            }
        } else return 0;
    }

    function calculateOdds(
        uint _price,
        uint strike,
        uint timeLeftInDays,
        uint volatility
    ) public view returns (uint) {
        uint vt = volatility.div(100).mul(sqrt(timeLeftInDays.div(365))).div(1e9);
        bool direction = strike >= _price;
        uint lnBase = strike >= _price ? strike.mul(ONE).div(_price) : _price.mul(ONE).div(strike);
        uint d1 = deciMath.ln(lnBase, 99).mul(ONE).div(vt);
        uint y = ONE.mul(ONE).div(ONE.add(d1.mul(2316419).div(1e7)));
        uint d2 = d1.mul(d1).div(2).div(ONE);
        uint z = _expneg(d2).mul(3989423).div(1e7);

        uint y5 = deciMath.pow(y, 5 * ONE).mul(1330274).div(1e6);
        uint y4 = deciMath.pow(y, 4 * ONE).mul(1821256).div(1e6);
        uint y3 = deciMath.pow(y, 3 * ONE).mul(1781478).div(1e6);
        uint y2 = deciMath.pow(y, 2 * ONE).mul(356538).div(1e6);
        uint y1 = y.mul(3193815).div(1e7);
        uint x1 = y5.add(y3).add(y1).sub(y4).sub(y2);
        uint x = ONE.sub(z.mul(x1).div(ONE));
        uint result = ONE.mul(1e2).sub(x.mul(1e2));
        if (direction) {
            return result;
        } else {
            return ONE.mul(1e2).sub(result);
        }
    }

    function isMarketInAMMTrading(address market) public view returns (bool) {
        if (IPositionalMarketManager(manager).isActiveMarket(market)) {
            IPositionalMarket marketContract = IPositionalMarket(market);
            (bytes32 key, , ) = marketContract.getOracleDetails();
            //check if asset is supported
            if (impliedVolatilityPerAsset[key] == 0) {
                return false;
            }
            // add price calculation
            (uint maturity, ) = marketContract.times();

            if (maturity < block.timestamp) {
                return false;
            }

            uint timeLeftToMaturity = maturity - block.timestamp;
            return timeLeftToMaturity > minimalTimeLeftToMaturity;
        } else {
            return false;
        }
    }

    function canExerciseMaturedMarket(address market) public view returns (bool) {
        if (
            IPositionalMarketManager(manager).isKnownMarket(market) &&
            (IPositionalMarket(market).phase() == IPositionalMarket.Phase.Maturity)
        ) {
            (IPosition up, IPosition down) = IPositionalMarket(market).getOptions();
            if ((up.getBalanceOf(address(this)) > 0) || (down.getBalanceOf(address(this)) > 0)) {
                return true;
            }
        }
        return false;
    }

    function getCapPerAsset(bytes32 asset) public view returns (uint) {
        if (priceFeed.rateForCurrency(asset) == 0) {
            return 0;
        }
        if (_capPerAsset[asset] == 0) {
            return capPerMarket;
        }
        return _capPerAsset[asset];
    }

    // write methods

    function buyFromAMMWithReferrer(
        address market,
        Position position,
        uint amount,
        uint expectedPayout,
        uint additionalSlippage,
        address _referrer
    ) public nonReentrant notPaused {
        IReferrals(referrals).setReferrer(_referrer, msg.sender);
        _buyFromAMM(market, position, amount, expectedPayout, additionalSlippage);
    }

    function buyFromAMM(
        address market,
        Position position,
        uint amount,
        uint expectedPayout,
        uint additionalSlippage
    ) public nonReentrant notPaused {
        _buyFromAMM(market, position, amount, expectedPayout, additionalSlippage);
    }

    function _buyFromAMM(
        address market,
        Position position,
        uint amount,
        uint expectedPayout,
        uint additionalSlippage
    ) internal {
        require(isMarketInAMMTrading(market), "Market is not in Trading phase");

        uint basePrice = price(market, position).add(min_spread);

        uint availableToBuyFromAMMatm = _availableToBuyFromAMMWithBasePrice(market, position, basePrice);
        require(amount <= availableToBuyFromAMMatm, "Not enough liquidity.");

        uint sUSDPaid = _buyFromAmmQuoteWithBasePrice(market, position, amount, basePrice);
        require(sUSD.balanceOf(msg.sender) >= sUSDPaid, "You dont have enough sUSD.");
        require(sUSD.allowance(msg.sender, address(this)) >= sUSDPaid, "No allowance.");
        require(sUSDPaid.mul(ONE).div(expectedPayout) <= ONE.add(additionalSlippage), "Slippage too high");

        sUSD.safeTransferFrom(msg.sender, address(this), sUSDPaid);

        uint toMint = _getMintableAmount(market, position, amount);
        if (toMint > 0) {
            require(
                sUSD.balanceOf(address(this)) >= IPositionalMarketManager(manager).transformCollateral(toMint),
                "Not enough sUSD in contract."
            );
            IPositionalMarket(market).mint(toMint);
            spentOnMarket[market] = spentOnMarket[market].add(toMint);
        }

        (IPosition up, IPosition down) = IPositionalMarket(market).getOptions();
        IPosition target = position == Position.Up ? up : down;
        IERC20(address(target)).transfer(msg.sender, amount);

        if (address(stakingThales) != address(0)) {
            stakingThales.updateVolume(msg.sender, sUSDPaid);
        }
        _updateSpentOnOnMarketOnBuy(market, sUSDPaid, msg.sender);

        emit BoughtFromAmm(msg.sender, market, position, amount, sUSDPaid, address(sUSD), address(target));
    }

    function sellToAMM(
        address market,
        Position position,
        uint amount,
        uint expectedPayout,
        uint additionalSlippage
    ) public nonReentrant notPaused {
        require(isMarketInAMMTrading(market), "Market is not in Trading phase");

        uint availableToSellToAMMATM = availableToSellToAMM(market, position);
        require(availableToSellToAMMATM > 0 && amount <= availableToSellToAMMATM, "Not enough liquidity.");

        uint pricePaid = sellToAmmQuote(market, position, amount);
        require(expectedPayout.mul(ONE).div(pricePaid) <= (ONE.add(additionalSlippage)), "Slippage too high");

        (IPosition up, IPosition down) = IPositionalMarket(market).getOptions();
        IPosition target = position == Position.Up ? up : down;

        require(target.getBalanceOf(msg.sender) >= amount, "You dont have enough options.");
        require(IERC20(address(target)).allowance(msg.sender, address(this)) >= amount, "No allowance.");

        //transfer options first to have max burn available
        IERC20(address(target)).safeTransferFrom(msg.sender, address(this), amount);
        uint sUSDFromBurning =
            IPositionalMarketManager(manager).transformCollateral(
                IPositionalMarket(market).getMaximumBurnable(address(this))
            );
        if (sUSDFromBurning > 0) {
            IPositionalMarket(market).burnOptionsMaximum();
        }

        require(sUSD.balanceOf(address(this)) >= pricePaid, "Not enough sUSD in contract.");

        sUSD.transfer(msg.sender, pricePaid);

        if (address(stakingThales) != address(0)) {
            stakingThales.updateVolume(msg.sender, pricePaid);
        }
        _updateSpentOnMarketOnSell(market, pricePaid, sUSDFromBurning, msg.sender);

        emit SoldToAMM(msg.sender, market, position, amount, pricePaid, address(sUSD), address(target));
    }

    function exerciseMaturedMarket(address market) external {
        require(canExerciseMaturedMarket(market), "No options to exercise");
        IPositionalMarket(market).exerciseOptions();
    }

    // setters
    function setMinimalTimeLeftToMaturity(uint _minimalTimeLeftToMaturity) external onlyOwner {
        minimalTimeLeftToMaturity = _minimalTimeLeftToMaturity;
        emit SetMinimalTimeLeftToMaturity(_minimalTimeLeftToMaturity);
    }

    function setMinSpread(uint _spread) external onlyOwner {
        min_spread = _spread;
        emit SetMinSpread(_spread);
    }

    function setSafeBoxImpact(uint _safeBoxImpact) external onlyOwner {
        safeBoxImpact = _safeBoxImpact;
        emit SetSafeBoxImpact(_safeBoxImpact);
    }

    function setSafeBox(address _safeBox) external onlyOwner {
        safeBox = _safeBox;
        emit SetSafeBox(_safeBox);
    }

    function setMaxSpread(uint _spread) external onlyOwner {
        max_spread = _spread;
        emit SetMaxSpread(_spread);
    }

    function setMinSupportedPrice(uint _minSupportedPrice) external onlyOwner {
        minSupportedPrice = _minSupportedPrice;
        emit SetMinSupportedPrice(_minSupportedPrice);
    }

    function setMaxSupportedPrice(uint _maxSupportedPrice) external onlyOwner {
        maxSupportedPrice = _maxSupportedPrice;
        emit SetMaxSupportedPrice(_maxSupportedPrice);
    }

    function setImpliedVolatilityPerAsset(bytes32 asset, uint _impliedVolatility) external {
        require(
            whitelistedAddresses[msg.sender] || owner == msg.sender,
            "Only whitelisted addresses or owner can change IV!"
        );
        require(_impliedVolatility > ONE.mul(60) && _impliedVolatility < ONE.mul(300), "IV outside min/max range!");
        require(priceFeed.rateForCurrency(asset) != 0, "Asset has no price!");
        impliedVolatilityPerAsset[asset] = _impliedVolatility;
        emit SetImpliedVolatilityPerAsset(asset, _impliedVolatility);
    }

    function setCapPerMarket(uint _capPerMarket) external onlyOwner {
        capPerMarket = _capPerMarket;
        emit SetCapPerMarket(_capPerMarket);
    }

    function setPriceFeed(IPriceFeed _priceFeed) external onlyOwner {
        priceFeed = _priceFeed;
        emit SetPriceFeed(address(_priceFeed));
    }

    function setSUSD(IERC20 _sUSD) external onlyOwner {
        sUSD = _sUSD;
        emit SetSUSD(address(sUSD));
    }

    function setStakingThales(IStakingThales _stakingThales) external onlyOwner {
        stakingThales = _stakingThales;
        emit SetStakingThales(address(_stakingThales));
    }

    function setReferrals(address _referrals, uint _referrerFee) external onlyOwner {
        referrals = _referrals;
        referrerFee = _referrerFee;
    }

    function setPositionalMarketManager(address _manager) external onlyOwner {
        if (address(manager) != address(0)) {
            sUSD.approve(address(manager), 0);
        }
        manager = _manager;
        sUSD.approve(manager, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        emit SetPositionalMarketManager(_manager);
    }

    function setCapPerAsset(bytes32 asset, uint _cap) external onlyOwner {
        _capPerAsset[asset] = _cap;
        emit SetCapPerAsset(asset, _cap);
    }

    // Internal

    function _updateSpentOnMarketOnSell(
        address market,
        uint sUSDPaid,
        uint sUSDFromBurning,
        address seller
    ) internal {
        uint safeBoxShare = sUSDPaid.mul(ONE).div(ONE.sub(safeBoxImpact)).sub(sUSDPaid);

        if (safeBoxImpact > 0) {
            sUSD.transfer(safeBox, safeBoxShare);
        } else {
            safeBoxShare = 0;
        }

        spentOnMarket[market] = spentOnMarket[market].add(
            IPositionalMarketManager(manager).reverseTransformCollateral(sUSDPaid.add(safeBoxShare))
        );
        if (spentOnMarket[market] <= IPositionalMarketManager(manager).reverseTransformCollateral(sUSDFromBurning)) {
            spentOnMarket[market] = 0;
        } else {
            spentOnMarket[market] = spentOnMarket[market].sub(
                IPositionalMarketManager(manager).reverseTransformCollateral(sUSDFromBurning)
            );
        }

        if (referrerFee > 0 && referrals != address(0)) {
            uint referrerShare = sUSDPaid.mul(ONE).div(ONE.sub(referrerFee)).sub(sUSDPaid);
            _handleReferrer(seller, referrerShare, sUSDPaid);
        }
    }

    function _updateSpentOnOnMarketOnBuy(
        address market,
        uint sUSDPaid,
        address buyer
    ) internal {
        uint safeBoxShare = sUSDPaid.sub(sUSDPaid.mul(ONE).div(ONE.add(safeBoxImpact)));
        if (safeBoxImpact > 0) {
            sUSD.transfer(safeBox, safeBoxShare);
        } else {
            safeBoxShare = 0;
        }

        if (
            spentOnMarket[market] <= IPositionalMarketManager(manager).reverseTransformCollateral(sUSDPaid.sub(safeBoxShare))
        ) {
            spentOnMarket[market] = 0;
        } else {
            spentOnMarket[market] = spentOnMarket[market].sub(
                IPositionalMarketManager(manager).reverseTransformCollateral(sUSDPaid.sub(safeBoxShare))
            );
        }

        if (referrerFee > 0 && referrals != address(0)) {
            uint referrerShare = sUSDPaid.sub(sUSDPaid.mul(ONE).div(ONE.add(referrerFee)));
            _handleReferrer(buyer, referrerShare, sUSDPaid);
        }
    }

    function _buyPriceImpact(
        address market,
        Position position,
        uint amount
    ) internal view returns (uint) {
        (uint balancePosition, uint balanceOtherSide) = _balanceOfPositionsOnMarket(market, position);
        uint balancePositionAfter = balancePosition > amount ? balancePosition.sub(amount) : 0;
        uint balanceOtherSideAfter =
            balancePosition > amount ? balanceOtherSide : balanceOtherSide.add(amount.sub(balancePosition));
        if (balancePositionAfter >= balanceOtherSideAfter) {
            //minimal price impact as it will balance the AMM exposure
            return 0;
        } else {
            return
                _buyPriceImpactElse(
                    market,
                    position,
                    amount,
                    balanceOtherSide,
                    balancePosition,
                    balanceOtherSideAfter,
                    balancePositionAfter
                );
        }
    }

    function _buyPriceImpactElse(
        address market,
        Position position,
        uint amount,
        uint balanceOtherSide,
        uint balancePosition,
        uint balanceOtherSideAfter,
        uint balancePositionAfter
    ) internal view returns (uint) {
        uint maxPossibleSkew = balanceOtherSide.add(availableToBuyFromAMM(market, position)).sub(balancePosition);
        uint skew = balanceOtherSideAfter.sub(balancePositionAfter);
        uint newImpact = max_spread.mul(skew.mul(ONE).div(maxPossibleSkew)).div(ONE);
        if (balancePosition > 0) {
            uint newPriceForMintedOnes = newImpact.div(2);
            uint tempMultiplier = amount.sub(balancePosition).mul(newPriceForMintedOnes);
            return tempMultiplier.div(amount);
        } else {
            uint previousSkew = balanceOtherSide;
            uint previousImpact = max_spread.mul(previousSkew.mul(ONE).div(maxPossibleSkew)).div(ONE);
            return newImpact.add(previousImpact).div(2);
        }
    }

    function _handleReferrer(
        address buyer,
        uint referrerShare,
        uint volume
    ) internal {
        address referrer = IReferrals(referrals).referrals(buyer);
        if (referrer != address(0)) {
            if (referrerFee > 0) {
                sUSD.transfer(referrer, referrerShare);
                emit ReferrerPaid(referrer, buyer, referrerShare, volume);
            }
        }
    }

    function balancePosition(address market, Position position) public view returns (uint) {
        (uint _balancePosition, ) = _balanceOfPositionsOnMarket(market, position);
        return _balancePosition;
    }

    function _sellPriceImpact(
        address market,
        Position position,
        uint amount
    ) internal view returns (uint) {
        (uint _balancePosition, uint balanceOtherSide) = _balanceOfPositionsOnMarket(market, position);
        uint balancePositionAfter =
            _balancePosition > 0 ? _balancePosition.add(amount) : balanceOtherSide > amount
                ? 0
                : amount.sub(balanceOtherSide);
        uint balanceOtherSideAfter = balanceOtherSide > amount ? balanceOtherSide.sub(amount) : 0;
        if (balancePositionAfter < balanceOtherSideAfter) {
            //minimal price impact as it will balance the AMM exposure
            return 0;
        } else {
            return
                _sellPriceImpactElse(
                    market,
                    position,
                    amount,
                    balanceOtherSide,
                    _balancePosition,
                    balanceOtherSideAfter,
                    balancePositionAfter
                );
        }
    }

    function _sellPriceImpactElse(
        address market,
        Position position,
        uint amount,
        uint balanceOtherSide,
        uint _balancePosition,
        uint balanceOtherSideAfter,
        uint balancePositionAfter
    ) internal view returns (uint) {
        uint maxPossibleSkew = _balancePosition.add(availableToSellToAMM(market, position)).sub(balanceOtherSide);
        uint skew = balancePositionAfter.sub(balanceOtherSideAfter);
        uint newImpact = max_spread.mul(skew.mul(ONE).div(maxPossibleSkew)).div(ONE);

        if (balanceOtherSide > 0) {
            uint newPriceForMintedOnes = newImpact.div(2);
            uint tempMultiplier = amount.sub(_balancePosition).mul(newPriceForMintedOnes);
            return tempMultiplier.div(amount);
        } else {
            uint previousSkew = _balancePosition;
            uint previousImpact = max_spread.mul(previousSkew.mul(ONE).div(maxPossibleSkew)).div(ONE);
            return newImpact.add(previousImpact).div(2);
        }
    }

    function _getMintableAmount(
        address market,
        Position position,
        uint amount
    ) internal view returns (uint mintable) {
        uint availableInContract = _balanceOfPositionOnMarket(market, position);
        mintable = 0;
        if (availableInContract < amount) {
            mintable = amount.sub(availableInContract);
        }
    }

    function _balanceOfPositionOnMarket(address market, Position position) internal view returns (uint) {
        (IPosition up, IPosition down) = IPositionalMarket(market).getOptions();
        uint balance = position == Position.Up ? up.getBalanceOf(address(this)) : down.getBalanceOf(address(this));
        return balance;
    }

    function _balanceOfPositionsOnMarket(address market, Position position) internal view returns (uint, uint) {
        (IPosition up, IPosition down) = IPositionalMarket(market).getOptions();
        uint balance = position == Position.Up ? up.getBalanceOf(address(this)) : down.getBalanceOf(address(this));
        uint balanceOtherSide = position == Position.Up ? down.getBalanceOf(address(this)) : up.getBalanceOf(address(this));
        return (balance, balanceOtherSide);
    }

    function _capOnMarket(address market) internal view returns (uint) {
        (bytes32 key, , ) = IPositionalMarket(market).getOracleDetails();
        return getCapPerAsset(key);
    }

    function _expneg(uint x) internal view returns (uint result) {
        result = (ONE * ONE) / _expNegPow(x);
    }

    function _expNegPow(uint x) internal view returns (uint result) {
        uint e = 2718280000000000000;
        result = deciMath.pow(e, x);
    }

    function sqrt(uint y) internal pure returns (uint z) {
        if (y > 3) {
            z = y;
            uint x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }

    function retrieveSUSD(address payable account) external onlyOwner {
        sUSD.transfer(account, sUSD.balanceOf(address(this)));
    }

    function retrieveSUSDAmount(address payable account, uint amount) external onlyOwner {
        sUSD.transfer(account, amount);
    }

    function setWhitelistedAddress(address _address, bool enabled) external onlyOwner {
        whitelistedAddresses[_address] = enabled;
    }

    // events
    event SoldToAMM(
        address seller,
        address market,
        Position position,
        uint amount,
        uint sUSDPaid,
        address susd,
        address asset
    );
    event BoughtFromAmm(
        address buyer,
        address market,
        Position position,
        uint amount,
        uint sUSDPaid,
        address susd,
        address asset
    );

    event SetPositionalMarketManager(address _manager);
    event SetSUSD(address sUSD);
    event SetPriceFeed(address _priceFeed);
    event SetCapPerMarket(uint _capPerMarket);
    event SetImpliedVolatilityPerAsset(bytes32 asset, uint _impliedVolatility);
    event SetCapPerAsset(bytes32 asset, uint _cap);
    event SetMaxSpread(uint _spread);
    event SetMinSpread(uint _spread);
    event SetSafeBoxImpact(uint _safeBoxImpact);
    event SetSafeBox(address _safeBox);
    event SetMinimalTimeLeftToMaturity(uint _minimalTimeLeftToMaturity);
    event SetStakingThales(address _stakingThales);
    event SetMinSupportedPrice(uint _spread);
    event SetMaxSupportedPrice(uint _spread);
    event ReferrerPaid(address refferer, address trader, uint amount, uint volume);
}

File 2 of 17 : SafeERC20.sol
pragma solidity ^0.5.0;

import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";

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

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

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

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

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

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value);
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves.

        // A Solidity high level call has three parts:
        //  1. The target address is checked to verify it contains contract code
        //  2. The call itself is made, and success asserted
        //  3. The return value is decoded, which in turn checks the size of the returned data.
        // solhint-disable-next-line max-line-length
        require(address(token).isContract(), "SafeERC20: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success, "SafeERC20: low-level call failed");

        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 3 of 17 : SafeMath.sol
pragma solidity ^0.5.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, "SafeMath: division by zero");
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0, "SafeMath: modulo by zero");
        return a % b;
    }
}

File 4 of 17 : Math.sol
pragma solidity ^0.5.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow, so we distribute
        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
    }
}

File 5 of 17 : Initializable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.4.24 <0.7.0;


/**
 * @title Initializable
 *
 * @dev Helper contract to support initializer functions. To use it, replace
 * the constructor with a function that has the `initializer` modifier.
 * WARNING: Unlike constructors, initializer functions must be manually
 * invoked. This applies both to deploying an Initializable contract, as well
 * as extending an Initializable contract via inheritance.
 * WARNING: When used with inheritance, manual care must be taken to not invoke
 * a parent initializer twice, or ensure that all initializers are idempotent,
 * because this is not dealt with automatically as with constructors.
 */
contract Initializable {

  /**
   * @dev Indicates that the contract has been initialized.
   */
  bool private initialized;

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

  /**
   * @dev Modifier to use in the initializer function of a contract.
   */
  modifier initializer() {
    require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");

    bool isTopLevelCall = !initializing;
    if (isTopLevelCall) {
      initializing = true;
      initialized = true;
    }

    _;

    if (isTopLevelCall) {
      initializing = false;
    }
  }

  /// @dev Returns true if and only if the function is running in the constructor
  function isConstructor() private view returns (bool) {
    // extcodesize checks the size of the code stored in an address, and
    // address returns the current address. Since the code is still not
    // deployed when running a constructor, any checks on its code size will
    // yield zero, making it an effective way to detect if a contract is
    // under construction or not.
    address self = address(this);
    uint256 cs;
    assembly { cs := extcodesize(self) }
    return cs == 0;
  }

  // Reserved storage space to allow for layout changes in the future.
  uint256[50] private ______gap;
}

File 6 of 17 : ProxyReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.5.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier
 * available, which can be aplied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 */
contract ProxyReentrancyGuard {
    /// @dev counter to allow mutex lock with only one SSTORE operation
    uint256 private _guardCounter;
    bool private _initialized;

    function initNonReentrant() public {
        require(!_initialized, "Already initialized");
        _initialized = true;
        _guardCounter = 1;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _guardCounter += 1;
        uint256 localCounter = _guardCounter;
        _;
        require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
    }
}

File 7 of 17 : ProxyOwned.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.5.16;

// Clone of syntetix contract without constructor
contract ProxyOwned {
    address public owner;
    address public nominatedOwner;
    bool private _initialized;
    bool private _transferredAtInit;

    function setOwner(address _owner) public {
        require(_owner != address(0), "Owner address cannot be 0");
        require(!_initialized, "Already initialized, use nominateNewOwner");
        _initialized = true;
        owner = _owner;
        emit OwnerChanged(address(0), _owner);
    }

    function nominateNewOwner(address _owner) external onlyOwner {
        nominatedOwner = _owner;
        emit OwnerNominated(_owner);
    }

    function acceptOwnership() external {
        require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
        emit OwnerChanged(owner, nominatedOwner);
        owner = nominatedOwner;
        nominatedOwner = address(0);
    }

    function transferOwnershipAtInit(address proxyAddress) external onlyOwner {
        require(proxyAddress != address(0), "Invalid address");
        require(!_transferredAtInit, "Already transferred");
        owner = proxyAddress;
        _transferredAtInit = true;
        emit OwnerChanged(owner, proxyAddress);
    }

    modifier onlyOwner {
        _onlyOwner();
        _;
    }

    function _onlyOwner() private view {
        require(msg.sender == owner, "Only the contract owner may perform this action");
    }

    event OwnerNominated(address newOwner);
    event OwnerChanged(address oldOwner, address newOwner);
}

File 8 of 17 : ProxyPausable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.5.16;

// Inheritance
import "./ProxyOwned.sol";

// Clone of syntetix contract without constructor

contract ProxyPausable is ProxyOwned {
    uint public lastPauseTime;
    bool public paused;

    

    /**
     * @notice Change the paused state of the contract
     * @dev Only the contract owner may call this.
     */
    function setPaused(bool _paused) external onlyOwner {
        // Ensure we're actually changing the state before we do anything
        if (_paused == paused) {
            return;
        }

        // Set our paused state.
        paused = _paused;

        // If applicable, set the last pause time.
        if (paused) {
            lastPauseTime = block.timestamp;
        }

        // Let everyone know that our pause state has changed.
        emit PauseChanged(paused);
    }

    event PauseChanged(bool isPaused);

    modifier notPaused {
        require(!paused, "This action cannot be performed while the contract is paused");
        _;
    }
}

File 9 of 17 : IPriceFeed.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.5.16;

interface IPriceFeed {
     // Structs
    struct RateAndUpdatedTime {
        uint216 rate;
        uint40 time;
    }
    
    // Mutative functions
    function addAggregator(bytes32 currencyKey, address aggregatorAddress) external;

    function removeAggregator(bytes32 currencyKey) external;

    // Views

    function rateForCurrency(bytes32 currencyKey) external view returns (uint);

    function rateAndUpdatedTime(bytes32 currencyKey) external view returns (uint rate, uint time);

    function getRates() external view returns (uint[] memory);

    function getCurrencies() external view returns (bytes32[] memory);
}

File 10 of 17 : IPositionalMarket.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.5.16;

import "../interfaces/IPositionalMarketManager.sol";
import "../interfaces/IPosition.sol";
import "../interfaces/IPriceFeed.sol";

interface IPositionalMarket {
    /* ========== TYPES ========== */

    enum Phase {Trading, Maturity, Expiry}
    enum Side {Up, Down}

    /* ========== VIEWS / VARIABLES ========== */

    function getOptions() external view returns (IPosition up, IPosition down);

    function times() external view returns (uint maturity, uint destructino);

    function getOracleDetails()
        external
        view
        returns (
            bytes32 key,
            uint strikePrice,
            uint finalPrice
        );

    function fees() external view returns (uint poolFee, uint creatorFee);

    function deposited() external view returns (uint);

    function creator() external view returns (address);

    function resolved() external view returns (bool);

    function phase() external view returns (Phase);

    function oraclePrice() external view returns (uint);

    function oraclePriceAndTimestamp() external view returns (uint price, uint updatedAt);

    function canResolve() external view returns (bool);

    function result() external view returns (Side);

    function balancesOf(address account) external view returns (uint up, uint down);

    function totalSupplies() external view returns (uint up, uint down);

    function getMaximumBurnable(address account) external view returns (uint amount);

    /* ========== MUTATIVE FUNCTIONS ========== */

    function mint(uint value) external;

    function exerciseOptions() external returns (uint);

    function burnOptions(uint amount) external;

    function burnOptionsMaximum() external;
}

File 11 of 17 : IPositionalMarketManager.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.5.16;

import "../interfaces/IPositionalMarket.sol";

interface IPositionalMarketManager {
    /* ========== VIEWS / VARIABLES ========== */

    function durations() external view returns (uint expiryDuration, uint maxTimeToMaturity);

    function capitalRequirement() external view returns (uint);

    function marketCreationEnabled() external view returns (bool);

    function transformCollateral(uint value) external view returns (uint);

    function reverseTransformCollateral(uint value) external view returns (uint);

    function totalDeposited() external view returns (uint);

    function numActiveMarkets() external view returns (uint);

    function activeMarkets(uint index, uint pageSize) external view returns (address[] memory);

    function numMaturedMarkets() external view returns (uint);

    function maturedMarkets(uint index, uint pageSize) external view returns (address[] memory);

    function isActiveMarket(address candidate) external view returns (bool);

    function isKnownMarket(address candidate) external view returns (bool);

    /* ========== MUTATIVE FUNCTIONS ========== */

    function createMarket(
        bytes32 oracleKey,
        uint strikePrice,
        uint maturity,
        uint initialMint, // initial sUSD to mint options for,
        bool customMarket,
        address customOracle
    ) external returns (IPositionalMarket);

    function resolveMarket(address market) external;

    function expireMarkets(address[] calldata market) external;

    function transferSusdTo(
        address sender,
        address receiver,
        uint amount
    ) external;
}

File 12 of 17 : IPosition.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.5.16;

import "./IPositionalMarket.sol";

interface IPosition {
    /* ========== VIEWS / VARIABLES ========== */

    function getBalanceOf(address account) external view returns (uint);

    function getTotalSupply() external view returns (uint);

}

File 13 of 17 : IStakingThales.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.5.16;

interface IStakingThales {
    function updateVolume(address account, uint amount) external;
    
    /* ========== VIEWS / VARIABLES ========== */
    function totalStakedAmount() external view returns (uint);

    function stakedBalanceOf(address account) external view returns (uint); 

    function currentPeriodRewards() external view returns (uint);

    function currentPeriodFees() external view returns (uint);

    function getLastPeriodOfClaimedRewards(address account) external view returns (uint);

    function getRewardsAvailable(address account) external view returns (uint);

    function getRewardFeesAvailable(address account) external view returns (uint);

    function getAlreadyClaimedRewards(address account) external view returns (uint);

    function getAlreadyClaimedFees(address account) external view returns (uint);

    function getContractRewardFunds() external view returns (uint);

    function getContractFeeFunds() external view returns (uint);

    
}

File 14 of 17 : IReferrals.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;

interface IReferrals {
    function referrals(address) external view returns (address);

    function setReferrer(address, address) external;
}

File 15 of 17 : DeciMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.5.16;

contract DeciMath {
    // Abbreviation: DP stands for 'Decimal Places'

    uint constant TEN38 = 10**38;
    uint constant TEN30 = 10**30;
    uint constant TEN20 = 10**20;
    uint constant TEN19 = 10**19;
    uint constant TEN18 = 10**18;
    uint constant TEN17 = 10**17;
    uint constant TEN12 = 10**12;
    uint constant TEN11 = 10**11;
    uint constant TEN10 = 10**10;
    uint constant TEN9 = 10**9;
    uint constant TEN8 = 10**8;
    uint constant TEN7 = 10**7;

    // ln(2) - used in ln(x). 30 DP.
    uint constant LN2 = 693147180559945309417232121458;

    // 1 / ln(2) - used in exp(x). 30 DP.
    uint constant ONE_OVER_LN2 = 1442695040888963407359924681002;

    /***** LOOKUP TABLES *****/

    // Lookup table arrays (LUTs) for log_2(x)
    uint[100] public table_log_2;
    uint[100] public table2_log_2;

    // Lookup table for pow2(). Table contains 39 arrays, each array contains 10 uint slots.
    uint[10][39] public table_pow2;

    // LUT flags
    bool LUT1_isSet = false;
    bool LUT2_isSet = false;
    bool LUT3_1_isSet = false;
    bool LUT3_2_isSet = false;
    bool LUT3_3_isSet = false;
    bool LUT3_4_isSet = false;

    /******  BASIC MATH OPERATORS ******/

    // Integer math operators. Identical to Zeppelin's SafeMath
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }
        uint256 c = a * b;
        require(c / a == b, "uint overflow from multiplication");
        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "division by zero");
        uint256 c = a / b;
        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "uint underflow from subtraction");
        uint256 c = a - b;
        return c;
    }

    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "uint overflow from multiplication");
        return c;
    }

    // Basic decimal math operators. Inputs and outputs are uint representations of fixed-point decimals.

    // 18 Decimal places
    function decMul18(uint x, uint y) public pure returns (uint decProd) {
        uint prod_xy = mul(x, y);
        decProd = add(prod_xy, TEN18 / 2) / TEN18;
    }

    function decDiv18(uint x, uint y) public pure returns (uint decQuotient) {
        uint prod_xTEN18 = mul(x, TEN18);
        decQuotient = add(prod_xTEN18, y / 2) / y;
    }

    // 30 Decimal places
    function decMul30(uint x, uint y) public pure returns (uint decProd) {
        uint prod_xy = mul(x, y);
        decProd = add(prod_xy, TEN30 / 2) / TEN30;
    }

    // 38 Decimal places
    function decMul38(uint x, uint y) public pure returns (uint decProd) {
        uint prod_xy = mul(x, y);
        decProd = add(prod_xy, TEN38 / 2) / TEN38;
    }

    /****** HELPER FUNCTIONS ******/

    function convert38To18DP(uint x) public pure returns (uint y) {
        uint digit = (x % TEN20) / TEN19; // grab 20th digit from-right
        return chopAndRound(x, digit, 20);
    }

    function convert38To30DP(uint x) public pure returns (uint y) {
        uint digit = (x % TEN8) / TEN7; // grab 8th digit from-right
        return chopAndRound(x, digit, 8);
    }

    function convert30To20DP(uint x) public pure returns (uint y) {
        uint digit = (x % TEN10) / TEN9; // grab 10th digit from-right
        return chopAndRound(x, digit, 10);
    }

    function convert30To18DP(uint x) public pure returns (uint y) {
        uint digit = (x % TEN12) / TEN11; // grab 12th digit from-right
        return chopAndRound(x, digit, 12);
    }

    // Chop the last digits, and round the resulting number
    function chopAndRound(
        uint num,
        uint digit,
        uint positionOfChop
    ) public pure returns (uint chopped) {
        if (digit < 5) {
            chopped = div(num, 10**positionOfChop); // round down
        } else if (digit >= 5) {
            chopped = div(num, 10**positionOfChop) + 1; // round up
        }
        return chopped;
    }

    // return the floor of a fixed-point 20DP number
    function floor(uint x) public pure returns (uint num) {
        num = x - (x % TEN20);
        return num;
    }

    function countDigits(uint num) public pure returns (uint) {
        uint digits = 0;

        while (num != 0) {
            num /= 10; // When num < 10, yields 0 due to EVM floor division
            digits++;
        }
        return digits;
    }

    /****** MATH FUNCTIONS ******/

    // b^x for integer exponent. Use highly performant 'exponentiation-by-squaring' algorithm. O(log(n)) operations.

    // b^x - integer base, integer exponent
    function powBySquare(uint x, uint n) public pure returns (uint) {
        if (n == 0) return 1;

        uint y = 1;

        while (n > 1)
            if (n % 2 == 0) {
                x = mul(x, x);
                n = n / 2;
            } else if (n % 2 != 0) {
                y = mul(x, y);
                x = mul(x, x);
                n = (n - 1) / 2;
            }
        return mul(x, y);
    }

    // b^x - fixed-point 18 DP base, integer exponent
    function powBySquare18(uint base, uint n) public pure returns (uint) {
        if (n == 0) return TEN18;

        uint y = TEN18;

        while (n > 1) {
            if (n % 2 == 0) {
                base = decMul18(base, base);
                n = n / 2;
            } else if (n % 2 != 0) {
                y = decMul18(base, y);
                base = decMul18(base, base);
                n = (n - 1) / 2;
            }
        }
        return decMul18(base, y);
    }

    // b^x - fixed-point 38 DP base, integer exponent n
    function powBySquare38(uint base, uint n) public pure returns (uint) {
        if (n == 0) return TEN38;

        uint y = TEN38;

        while (n > 1) {
            if (n % 2 == 0) {
                base = decMul38(base, base);
                n = n / 2;
            } else if (n % 2 != 0) {
                y = decMul38(base, y);
                base = decMul38(base, base);
                n = (n - 1) / 2;
            }
        }
        return decMul38(base, y);
    }

    /* exp(x) function. Input 18 DP, output 18 DP.  Uses identities:

    A) e^x = 2^(x / ln(2))

    and

    B) 2^y = (2^r) * 2^(y - r); where r = floor(y) - 1, and (y - r) is in range [1,2[

    */
    function exp(uint x) public view returns (uint num) {
        uint intExponent; // 20 DP
        uint decExponent; // 20 DP
        uint coefficient; // 38 DP

        x = mul(x, TEN12); // make x 30DP
        x = decMul30(ONE_OVER_LN2, x);
        x = convert30To20DP(x);

        // if x < 1, do: (2^-1) * 2^(1 + x)
        if (x < TEN20 && x >= 0) {
            decExponent = add(TEN20, x);
            coefficient = TEN38 / 2;
            num = decMul38(coefficient, pow2(decExponent));
        }
        // Use identity B)
        else {
            intExponent = floor(x) - TEN20;
            decExponent = x - intExponent; // decimal exponent in range [1,2[
            coefficient = powBySquare(2, div(intExponent, TEN20));
            num = mul(coefficient, pow2(decExponent)); //  use normal mul to avoid overflow, as coeff. is an int
        }

        return convert38To18DP(num);
    }

    // Base-2 logarithm function, for x in range [1,2[. For use in ln(x). Input 18 DP, output 30 DP.
    function log_2(uint x, uint accuracy) public view _onlyLUT1andLUT2AreSet returns (uint) {
        require(x >= TEN18 && x < 2 * TEN18, "input x must be within range [1,2[");
        uint prod = mul(x, TEN20); // make x 38 DP
        uint newProd = TEN38;
        uint output = 0;

        for (uint i = 1; i <= accuracy; i++) {
            newProd = decMul38(table_log_2[i], prod);

            if (newProd >= TEN38) {
                prod = newProd;
                output += table2_log_2[i];
            }
        }
        return convert38To30DP(output);
    }

    // pow2(x) function, for use in exp(x). Uses 2D-array LUT. Valid for x in range [1,2[. Input 20DP, output 38DP
    function pow2(uint x) public view _onlyLUT3isSet returns (uint) {
        require(x >= TEN20 && x < 2 * TEN20, "input x must be within range [1,2[");
        uint x_38dp = x * TEN18;
        uint prod = 2 * TEN38;
        uint fractPart = x_38dp % TEN38;
        uint digitsLength = countDigits(fractPart);

        // loop backwards through mantissa digits - multiply each by the Lookup-table value
        for (uint i = 0; i < digitsLength; i++) {
            uint digit = (fractPart % (10**(i + 1))) / (10**i); // grab the i'th digit from right

            if (digit == 0) continue; // Save gas - skip the step if digit = 0 and there would be no resultant change to prod

            // computer i'th term, and new product
            uint term = table_pow2[37 - i][digit];
            prod = decMul38(prod, term);
        }
        return prod;
    }

    /* Natural log function ln(x). Input 18 DP, output 18 DP. Uses identities:

    A) ln(x) = log_2(x) * ln(2)

    and

    B) log_2(x) = log_2(2^q * y)           y in range [1,2[
                = q + log_2(y)

    The algorithm finds q and y by repeated division by powers-of-two.
    */
    function ln(uint x, uint accuracy) public view returns (uint) {
        require(x >= TEN18, "input must be >= 1");
        uint count = 0; // track

        /* Calculate q. Use branches to divide by powers-of-two, until output is in range [1,2[. Branch approach is more performant
        than simple successive division by 2. As max input of ln(x) is ~= 2^132, starting division at 2^30 yields sufficiently few operations for large x. */
        while (x >= 2 * TEN18) {
            if (x >= 1073741824 * TEN18) {
                // start at 2^30
                x = decDiv18(x, 1073741824 * TEN18);
                count += 30;
            } else if (x >= 1048576 * TEN18) {
                x = decDiv18(x, 1048576 * TEN18);
                count += 20;
            } else if (x >= 32768 * TEN18) {
                x = decDiv18(x, 32768 * TEN18);
                count += 15;
            } else if (x >= 1024 * TEN18) {
                x = decDiv18(x, 1024 * TEN18);
                count += 10;
            } else if (x >= 512 * TEN18) {
                x = decDiv18(x, 512 * TEN18);
                count += 9;
            } else if (x >= 256 * TEN18) {
                x = decDiv18(x, 256 * TEN18);
                count += 8;
            } else if (x >= 128 * TEN18) {
                x = decDiv18(x, 128 * TEN18);
                count += 7;
            } else if (x >= 64 * TEN18) {
                x = decDiv18(x, 64 * TEN18);
                count += 6;
            } else if (x >= 32 * TEN18) {
                x = decDiv18(x, 32 * TEN18);
                count += 5;
            } else if (x >= 16 * TEN18) {
                x = decDiv18(x, 16 * TEN18);
                count += 4;
            } else if (x >= 8 * TEN18) {
                x = decDiv18(x, 8 * TEN18);
                count += 3;
            } else if (x >= 4 * TEN18) {
                x = decDiv18(x, 4 * TEN18);
                count += 2;
            } else if (x >= 2 * TEN18) {
                x = decDiv18(x, 2 * TEN18);
                count += 1;
            }
        }

        uint q = count * TEN30;
        uint output = decMul30(LN2, add(q, log_2(x, accuracy)));

        return convert30To18DP(output);
    }

    /* pow(b, x) function for 18 DP base and exponent. Output 18 DP.

    Uses identity:  b^x = exp (x * ln(b)).

    For b < 1, rewrite b^x as:
    b^x = exp( x * (-ln(1/b)) ) = 1/exp(x * ln(1/b)).

     Thus, we avoid passing argument y < 1 to ln(y), and z < 0 to exp(z).   */
    function pow(uint base, uint x) public view returns (uint power) {
        if (base >= TEN18) {
            return exp(decMul18(x, ln(base, 70)));
        }

        if (base < TEN18) {
            uint exponent = decMul18(x, ln(decDiv18(TEN18, base), 70));
            return decDiv18(TEN18, exp(exponent));
        }
    }

    // Taylor series implementation of exp(x) - lower accuracy and higher gas cost than exp(x). 18 DP input and output.
    function exp_taylor(uint x) public pure returns (uint) {
        uint tolerance = 1;
        uint term = TEN18;
        uint sum = TEN18;
        uint i = 0;

        while (term > tolerance) {
            i += TEN18;
            term = decDiv18(decMul18(term, x), i);
            sum += term;
        }
        return sum;
    }

    /* Lookup Tables (LUTs). 38 DP fixed-point numbers. */

    // LUT1 for log_2(x). The i'th term is 1/(2^(1/2^i))
    function setLUT1() public {
        table_log_2[0] = 0;
        table_log_2[1] = 70710678118654752440084436210484903928;
        table_log_2[2] = 84089641525371454303112547623321489504;
        table_log_2[3] = 91700404320467123174354159479414442804;
        table_log_2[4] = 95760328069857364693630563514791544393;
        table_log_2[5] = 97857206208770013450916112581343574560;
        table_log_2[6] = 98922801319397548412912495906558366777;
        table_log_2[7] = 99459942348363317565247768622216631446;
        table_log_2[8] = 99729605608547012625765991384792260112;
        table_log_2[9] = 99864711289097017358812131808592040806;
        table_log_2[10] = 99932332750265075236028365984373804116;
        table_log_2[11] = 99966160649624368394219686876281565561;
        table_log_2[12] = 99983078893192906311748078019767389868;
        table_log_2[13] = 99991539088661349753372497156418872723;
        table_log_2[14] = 99995769454843113254396753730099797524;
        table_log_2[15] = 99997884705049192982650067113039327478;
        table_log_2[16] = 99998942346931446424221059225315431670;
        table_log_2[17] = 99999471172067428300770241277030532519;
        table_log_2[18] = 99999735585684139498225234636504270993;
        table_log_2[19] = 99999867792754675970531776759801063698;
        table_log_2[20] = 99999933896355489526178052900624509795;
        table_log_2[21] = 99999966948172282646511738368820575117;
        table_log_2[22] = 99999983474084775793885880947314828005;
        table_log_2[23] = 99999991737042046514572235133214264694;
        table_log_2[24] = 99999995868520937911689915196095249000;
        table_log_2[25] = 99999997934260447619445466250978583193;
        table_log_2[26] = 99999998967130218475622805194415901619;
        table_log_2[27] = 99999999483565107904286413727651274869;
        table_log_2[28] = 99999999741782553618761958785587923503;
        table_log_2[29] = 99999999870891276726035667265628464908;
        table_log_2[30] = 99999999935445638342181505587572099682;
        table_log_2[31] = 99999999967722819165881670780794171827;
        table_log_2[32] = 99999999983861409581638564886938948308;
        table_log_2[33] = 99999999991930704790493714817578668739;
        table_log_2[34] = 99999999995965352395165465502313349139;
        table_log_2[35] = 99999999997982676197562384774537267778;
        table_log_2[36] = 99999999998991338098776105393113730880;
        table_log_2[37] = 99999999999495669049386780948018133274;
        table_log_2[38] = 99999999999747834524693072536874382794;
        table_log_2[39] = 99999999999873917262346456784153520336;
        table_log_2[40] = 99999999999936958631173208521005842390;
        table_log_2[41] = 99999999999968479315586599292735191749;
        table_log_2[42] = 99999999999984239657793298404425663513;
        table_log_2[43] = 99999999999992119828896648891727348666;
        table_log_2[44] = 99999999999996059914448324368242303560;
        table_log_2[45] = 99999999999998029957224162164715809087;
        table_log_2[46] = 99999999999999014978612081077506568870;
        table_log_2[47] = 99999999999999507489306040537540450517;
        table_log_2[48] = 99999999999999753744653020268467016779;
        table_log_2[49] = 99999999999999876872326510134157706270;
        table_log_2[50] = 99999999999999938436163255067059902605;
        table_log_2[51] = 99999999999999969218081627533525213670;
        table_log_2[52] = 99999999999999984609040813766761422427;
        table_log_2[53] = 99999999999999992304520406883380415111;
        table_log_2[54] = 99999999999999996152260203441690133530;
        table_log_2[55] = 99999999999999998076130101720845048259;
        table_log_2[56] = 99999999999999999038065050860422519503;
        table_log_2[57] = 99999999999999999519032525430211258595;
        table_log_2[58] = 99999999999999999759516262715105629008;
        table_log_2[59] = 99999999999999999879758131357552814432;
        table_log_2[60] = 99999999999999999939879065678776407198;
        table_log_2[61] = 99999999999999999969939532839388203594;
        table_log_2[62] = 99999999999999999984969766419694101796;
        table_log_2[63] = 99999999999999999992484883209847050898;
        table_log_2[64] = 99999999999999999996242441604923525449;
        table_log_2[65] = 99999999999999999998121220802461762724;
        table_log_2[66] = 99999999999999999999060610401230881362;
        table_log_2[67] = 99999999999999999999530305200615440681;
        table_log_2[68] = 99999999999999999999765152600307720341;
        table_log_2[69] = 99999999999999999999882576300153860170;
        table_log_2[70] = 99999999999999999999941288150076930085;
        table_log_2[71] = 99999999999999999999970644075038465043;
        table_log_2[72] = 99999999999999999999985322037519232521;
        table_log_2[73] = 99999999999999999999992661018759616261;
        table_log_2[74] = 99999999999999999999996330509379808130;
        table_log_2[75] = 99999999999999999999998165254689904065;
        table_log_2[76] = 99999999999999999999999082627344952033;
        table_log_2[77] = 99999999999999999999999541313672476016;
        table_log_2[78] = 99999999999999999999999770656836238008;
        table_log_2[79] = 99999999999999999999999885328418119004;
        table_log_2[80] = 99999999999999999999999942664209059502;
        table_log_2[81] = 99999999999999999999999971332104529751;
        table_log_2[82] = 99999999999999999999999985666052264876;
        table_log_2[83] = 99999999999999999999999992833026132438;
        table_log_2[84] = 99999999999999999999999996416513066219;
        table_log_2[85] = 99999999999999999999999998208256533109;
        table_log_2[86] = 99999999999999999999999999104128266555;
        table_log_2[87] = 99999999999999999999999999552064133277;
        table_log_2[88] = 99999999999999999999999999776032066639;
        table_log_2[89] = 99999999999999999999999999888016033319;
        table_log_2[90] = 99999999999999999999999999944008016660;
        table_log_2[91] = 99999999999999999999999999972004008330;
        table_log_2[92] = 99999999999999999999999999986002004165;
        table_log_2[93] = 99999999999999999999999999993001002082;
        table_log_2[94] = 99999999999999999999999999996500501041;
        table_log_2[95] = 99999999999999999999999999998250250521;
        table_log_2[96] = 99999999999999999999999999999125125260;
        table_log_2[97] = 99999999999999999999999999999562562630;
        table_log_2[98] = 99999999999999999999999999999781281315;
        table_log_2[99] = 99999999999999999999999999999890640658;

        LUT1_isSet = true;
    }

    // LUT2 for log_2(x). The i'th term is 1/(2^i)
    function setLUT2() public {
        table2_log_2[0] = 200000000000000000000000000000000000000;
        table2_log_2[1] = 50000000000000000000000000000000000000;
        table2_log_2[2] = 25000000000000000000000000000000000000;
        table2_log_2[3] = 12500000000000000000000000000000000000;
        table2_log_2[4] = 6250000000000000000000000000000000000;
        table2_log_2[5] = 3125000000000000000000000000000000000;
        table2_log_2[6] = 1562500000000000000000000000000000000;
        table2_log_2[7] = 781250000000000000000000000000000000;
        table2_log_2[8] = 390625000000000000000000000000000000;
        table2_log_2[9] = 195312500000000000000000000000000000;
        table2_log_2[10] = 97656250000000000000000000000000000;
        table2_log_2[11] = 48828125000000000000000000000000000;
        table2_log_2[12] = 24414062500000000000000000000000000;
        table2_log_2[13] = 12207031250000000000000000000000000;
        table2_log_2[14] = 6103515625000000000000000000000000;
        table2_log_2[15] = 3051757812500000000000000000000000;
        table2_log_2[16] = 1525878906250000000000000000000000;
        table2_log_2[17] = 762939453125000000000000000000000;
        table2_log_2[18] = 381469726562500000000000000000000;
        table2_log_2[19] = 190734863281250000000000000000000;
        table2_log_2[20] = 95367431640625000000000000000000;
        table2_log_2[21] = 47683715820312500000000000000000;
        table2_log_2[22] = 23841857910156250000000000000000;
        table2_log_2[23] = 11920928955078125000000000000000;
        table2_log_2[24] = 5960464477539062500000000000000;
        table2_log_2[25] = 2980232238769531250000000000000;
        table2_log_2[26] = 1490116119384765625000000000000;
        table2_log_2[27] = 745058059692382812500000000000;
        table2_log_2[28] = 372529029846191406250000000000;
        table2_log_2[29] = 186264514923095703125000000000;
        table2_log_2[30] = 93132257461547851562500000000;
        table2_log_2[31] = 46566128730773925781250000000;
        table2_log_2[32] = 23283064365386962890625000000;
        table2_log_2[33] = 11641532182693481445312500000;
        table2_log_2[34] = 5820766091346740722656250000;
        table2_log_2[35] = 2910383045673370361328125000;
        table2_log_2[36] = 1455191522836685180664062500;
        table2_log_2[37] = 727595761418342590332031250;
        table2_log_2[38] = 363797880709171295166015625;
        table2_log_2[39] = 181898940354585647583007812;
        table2_log_2[40] = 90949470177292823791503906;
        table2_log_2[41] = 45474735088646411895751953;
        table2_log_2[42] = 22737367544323205947875976;
        table2_log_2[43] = 11368683772161602973937988;
        table2_log_2[44] = 5684341886080801486968994;
        table2_log_2[45] = 2842170943040400743484497;
        table2_log_2[46] = 1421085471520200371742248;
        table2_log_2[47] = 710542735760100185871124;
        table2_log_2[48] = 355271367880050092935562;
        table2_log_2[49] = 177635683940025046467781;
        table2_log_2[50] = 88817841970012523233890;
        table2_log_2[51] = 44408920985006261616945;
        table2_log_2[52] = 22204460492503130808472;
        table2_log_2[53] = 11102230246251565404236;
        table2_log_2[54] = 5551115123125782702118;
        table2_log_2[55] = 2775557561562891351059;
        table2_log_2[56] = 1387778780781445675529;
        table2_log_2[57] = 693889390390722837764;
        table2_log_2[58] = 346944695195361418882;
        table2_log_2[59] = 173472347597680709441;
        table2_log_2[60] = 86736173798840354720;
        table2_log_2[61] = 43368086899420177360;
        table2_log_2[62] = 21684043449710088680;
        table2_log_2[63] = 10842021724855044340;
        table2_log_2[64] = 5421010862427522170;
        table2_log_2[65] = 2710505431213761085;
        table2_log_2[66] = 1355252715606880542;
        table2_log_2[67] = 677626357803440271;
        table2_log_2[68] = 338813178901720135;
        table2_log_2[69] = 169406589450860067;
        table2_log_2[70] = 84703294725430033;
        table2_log_2[71] = 42351647362715016;
        table2_log_2[72] = 21175823681357508;
        table2_log_2[73] = 10587911840678754;
        table2_log_2[74] = 5293955920339377;
        table2_log_2[75] = 2646977960169688;
        table2_log_2[76] = 1323488980084844;
        table2_log_2[77] = 661744490042422;
        table2_log_2[78] = 330872245021211;
        table2_log_2[79] = 165436122510605;
        table2_log_2[80] = 82718061255302;
        table2_log_2[81] = 41359030627651;
        table2_log_2[82] = 20679515313825;
        table2_log_2[83] = 10339757656912;
        table2_log_2[84] = 5169878828456;
        table2_log_2[85] = 2584939414228;
        table2_log_2[86] = 1292469707114;
        table2_log_2[87] = 646234853557;
        table2_log_2[88] = 323117426778;
        table2_log_2[89] = 161558713389;
        table2_log_2[90] = 80779356694;
        table2_log_2[91] = 40389678347;
        table2_log_2[92] = 20194839173;
        table2_log_2[93] = 10097419586;
        table2_log_2[94] = 5048709793;
        table2_log_2[95] = 2524354896;
        table2_log_2[96] = 1262177448;
        table2_log_2[97] = 631088724;
        table2_log_2[98] = 315544362;
        table2_log_2[99] = 157772181;

        LUT2_isSet = true;
    }

    /* LUT for pow2() function. Table contains 39 arrays, each array contains 10 uint slots.

    table_pow2[i][d] = (2^(1 / 10^(i + 1))) ** d.
    d ranges from 0 to 9.

    LUT-setting is split into four separate setter functions to keep gas costs under block limit.
    */
    function setLUT3_1() public {
        table_pow2[0][0] = 100000000000000000000000000000000000000;
        table_pow2[0][1] = 107177346253629316421300632502334202291;
        table_pow2[0][2] = 114869835499703500679862694677792758944;
        table_pow2[0][3] = 123114441334491628449939306916774310988;
        table_pow2[0][4] = 131950791077289425937400197122964013303;
        table_pow2[0][5] = 141421356237309504880168872420969807857;
        table_pow2[0][6] = 151571656651039808234725980130644523868;
        table_pow2[0][7] = 162450479271247104521941876555056330257;
        table_pow2[0][8] = 174110112659224827827254003495949219796;
        table_pow2[0][9] = 186606598307361483196268653229988433405;
        table_pow2[1][0] = 100000000000000000000000000000000000000;
        table_pow2[1][1] = 100695555005671880883269821411323978545;
        table_pow2[1][2] = 101395947979002913869016599962823042584;
        table_pow2[1][3] = 102101212570719324976409517478306437354;
        table_pow2[1][4] = 102811382665606650934634495879263497655;
        table_pow2[1][5] = 103526492384137750434778819421124619773;
        table_pow2[1][6] = 104246576084112139095471141872690784007;
        table_pow2[1][7] = 104971668362306726904934732174028851665;
        table_pow2[1][8] = 105701804056138037449949421408611430989;
        table_pow2[1][9] = 106437018245335988793865835140404338206;
        table_pow2[2][0] = 100000000000000000000000000000000000000;
        table_pow2[2][1] = 100069338746258063253756863930385919571;
        table_pow2[2][2] = 100138725571133452908322477441877746756;
        table_pow2[2][3] = 100208160507963279436035132489114568295;
        table_pow2[2][4] = 100277643590107768843673305907248072983;
        table_pow2[2][5] = 100347174850950278700477431086959080340;
        table_pow2[2][6] = 100416754323897314177285298995922943429;
        table_pow2[2][7] = 100486382042378544096788794597976421668;
        table_pow2[2][8] = 100556058039846816994919680064517944020;
        table_pow2[2][9] = 100625782349778177193372141519657470417;
        table_pow2[3][0] = 100000000000000000000000000000000000000;
        table_pow2[3][1] = 100006931712037656919243991260264256542;
        table_pow2[3][2] = 100013863904561631568466376833067115945;
        table_pow2[3][3] = 100020796577605229875592540103010552992;
        table_pow2[3][4] = 100027729731201760077218879711834041246;
        table_pow2[3][5] = 100034663365384532718772839985089028270;
        table_pow2[3][6] = 100041597480186860654672952451661760537;
        table_pow2[3][7] = 100048532075642059048488888456913382370;
        table_pow2[3][8] = 100055467151783445373101522870206286485;
        table_pow2[3][9] = 100062402708644339410863008887585747065;
        table_pow2[4][0] = 100000000000000000000000000000000000000;
        table_pow2[4][1] = 100000693149582830565320908980056168150;
        table_pow2[4][2] = 100001386303970224572423685307245831542;
        table_pow2[4][3] = 100002079463162215324119782522433627138;
        table_pow2[4][4] = 100002772627158836123451492465145260129;
        table_pow2[4][5] = 100003465795960120273691946873622208121;
        table_pow2[4][6] = 100004158969566101078345118984887516084;
        table_pow2[4][7] = 100004852147976811841145825134822682163;
        table_pow2[4][8] = 100005545331192285866059726358255634403;
        table_pow2[4][9] = 100006238519212556457283329989059798485;
        table_pow2[5][0] = 100000000000000000000000000000000000000;
        table_pow2[5][1] = 100000069314742078650777263622740703038;
        table_pow2[5][2] = 100000138629532202636248826052225815048;
        table_pow2[5][3] = 100000207944370371989717187112633071811;
        table_pow2[5][4] = 100000277259256586744484869711682067979;
        table_pow2[5][5] = 100000346574190846933854419840650257373;
        table_pow2[5][6] = 100000415889173152591128406574388953292;
        table_pow2[5][7] = 100000485204203503749609422071339328833;
        table_pow2[5][8] = 100000554519281900442600081573548417222;
        table_pow2[5][9] = 100000623834408342703403023406685112154;
        table_pow2[6][0] = 100000000000000000000000000000000000000;
        table_pow2[6][1] = 100000006931472045825965603683996211583;
        table_pow2[6][2] = 100000013862944572104978428035962521332;
        table_pow2[6][3] = 100000020794417578837071775524560348874;
        table_pow2[6][4] = 100000027725891066022278948620759465140;
        table_pow2[6][5] = 100000034657365033660633249797837992529;
        table_pow2[6][6] = 100000041588839481752167981531382405066;
        table_pow2[6][7] = 100000048520314410296916446299287528561;
        table_pow2[6][8] = 100000055451789819294911946581756540768;
        table_pow2[6][9] = 100000062383265708746187784861300971552;
        table_pow2[7][0] = 100000000000000000000000000000000000000;
        table_pow2[7][1] = 100000000693147182962210384558650120894;
        table_pow2[7][2] = 100000001386294370728950941601779822006;
        table_pow2[7][3] = 100000002079441563300221704431854648481;
        table_pow2[7][4] = 100000002772588760676022706351340376300;
        table_pow2[7][5] = 100000003465735962856353980662703012279;
        table_pow2[7][6] = 100000004158883169841215560668408794069;
        table_pow2[7][7] = 100000004852030381630607479670924190156;
        table_pow2[7][8] = 100000005545177598224529770972715899860;
        table_pow2[7][9] = 100000006238324819622982467876250853339;
        table_pow2[8][0] = 100000000000000000000000000000000000000;
        table_pow2[8][1] = 100000000069314718080017181643183694247;
        table_pow2[8][2] = 100000000138629436208079664711489996172;
        table_pow2[8][3] = 100000000207944154384187449238221371011;
        table_pow2[8][4] = 100000000277258872608340535256680284018;
        table_pow2[8][5] = 100000000346573590880538922800169200474;
        table_pow2[8][6] = 100000000415888309200782611901990585682;
        table_pow2[8][7] = 100000000485203027569071602595446904968;
        table_pow2[8][8] = 100000000554517745985405894913840623680;
        table_pow2[8][9] = 100000000623832464449785488890474207190;
        table_pow2[9][0] = 100000000000000000000000000000000000000;
        table_pow2[9][1] = 100000000006931471805839679601136972338;
        table_pow2[9][2] = 100000000013862943612159812216225448565;
        table_pow2[9][3] = 100000000020794415418960397845298731148;
        table_pow2[9][4] = 100000000027725887226241436488390122551;
        table_pow2[9][5] = 100000000034657359034002928145532925240;
        table_pow2[9][6] = 100000000041588830842244872816760441679;
        table_pow2[9][7] = 100000000048520302650967270502105974334;
        table_pow2[9][8] = 100000000055451774460170121201602825670;
        table_pow2[9][9] = 100000000062383246269853424915284298153;
        table_pow2[10][0] = 100000000000000000000000000000000000000;
        table_pow2[10][1] = 100000000000693147180562347574486828679;
        table_pow2[10][2] = 100000000001386294361129499679112872675;
        table_pow2[10][3] = 100000000002079441541701456313878165290;
        table_pow2[10][4] = 100000000002772588722278217478782739826;
        table_pow2[10][5] = 100000000003465735902859783173826629587;
        table_pow2[10][6] = 100000000004158883083446153399009867874;
        table_pow2[10][7] = 100000000004852030264037328154332487990;
        table_pow2[10][8] = 100000000005545177444633307439794523238;
        table_pow2[10][9] = 100000000006238324625234091255396006920;

        LUT3_1_isSet = true;
    }

    function setLUT3_2() public {
        table_pow2[11][0] = 100000000000000000000000000000000000000;
        table_pow2[11][1] = 100000000000069314718056018553592419128;
        table_pow2[11][2] = 100000000000138629436112085152486230109;
        table_pow2[11][3] = 100000000000207944154168199796681432977;
        table_pow2[11][4] = 100000000000277258872224362486178027765;
        table_pow2[11][5] = 100000000000346573590280573220976014506;
        table_pow2[11][6] = 100000000000415888308336832001075393234;
        table_pow2[11][7] = 100000000000485203026393138826476163982;
        table_pow2[11][8] = 100000000000554517744449493697178326784;
        table_pow2[11][9] = 100000000000623832462505896613181881671;
        table_pow2[12][0] = 100000000000000000000000000000000000000;
        table_pow2[12][1] = 100000000000006931471805599693320679280;
        table_pow2[12][2] = 100000000000013862943611199867094372479;
        table_pow2[12][3] = 100000000000020794415416800521321079596;
        table_pow2[12][4] = 100000000000027725887222401656000800631;
        table_pow2[12][5] = 100000000000034657359028003271133535584;
        table_pow2[12][6] = 100000000000041588830833605366719284456;
        table_pow2[12][7] = 100000000000048520302639207942758047246;
        table_pow2[12][8] = 100000000000055451774444810999249823955;
        table_pow2[12][9] = 100000000000062383246250414536194614582;
        table_pow2[13][0] = 100000000000000000000000000000000000000;
        table_pow2[13][1] = 100000000000000693147180559947711682302;
        table_pow2[13][2] = 100000000000001386294361119900227894743;
        table_pow2[13][3] = 100000000000002079441541679857548637323;
        table_pow2[13][4] = 100000000000002772588722239819673910042;
        table_pow2[13][5] = 100000000000003465735902799786603712900;
        table_pow2[13][6] = 100000000000004158883083359758338045898;
        table_pow2[13][7] = 100000000000004852030263919734876909035;
        table_pow2[13][8] = 100000000000005545177444479716220302311;
        table_pow2[13][9] = 100000000000006238324625039702368225726;
        table_pow2[14][0] = 100000000000000000000000000000000000000;
        table_pow2[14][1] = 100000000000000069314718055994554964374;
        table_pow2[14][2] = 100000000000000138629436111989157974049;
        table_pow2[14][3] = 100000000000000207944154167983809029026;
        table_pow2[14][4] = 100000000000000277258872223978508129304;
        table_pow2[14][5] = 100000000000000346573590279973255274883;
        table_pow2[14][6] = 100000000000000415888308335968050465764;
        table_pow2[14][7] = 100000000000000485203026391962893701947;
        table_pow2[14][8] = 100000000000000554517744447957784983430;
        table_pow2[14][9] = 100000000000000623832462503952724310215;
        table_pow2[15][0] = 100000000000000000000000000000000000000;
        table_pow2[15][1] = 100000000000000006931471805599453334399;
        table_pow2[15][2] = 100000000000000013862943611198907149251;
        table_pow2[15][3] = 100000000000000020794415416798361444556;
        table_pow2[15][4] = 100000000000000027725887222397816220313;
        table_pow2[15][5] = 100000000000000034657359027997271476524;
        table_pow2[15][6] = 100000000000000041588830833596727213188;
        table_pow2[15][7] = 100000000000000048520302639196183430305;
        table_pow2[15][8] = 100000000000000055451774444795640127875;
        table_pow2[15][9] = 100000000000000062383246250395097305898;
        table_pow2[16][0] = 100000000000000000000000000000000000000;
        table_pow2[16][1] = 100000000000000000693147180559945311819;
        table_pow2[16][2] = 100000000000000001386294361119890628444;
        table_pow2[16][3] = 100000000000000002079441541679835949872;
        table_pow2[16][4] = 100000000000000002772588722239781276105;
        table_pow2[16][5] = 100000000000000003465735902799726607143;
        table_pow2[16][6] = 100000000000000004158883083359671942985;
        table_pow2[16][7] = 100000000000000004852030263919617283632;
        table_pow2[16][8] = 100000000000000005545177444479562629083;
        table_pow2[16][9] = 100000000000000006238324625039507979339;
        table_pow2[17][0] = 100000000000000000000000000000000000000;
        table_pow2[17][1] = 100000000000000000069314718055994530966;
        table_pow2[17][2] = 100000000000000000138629436111989061980;
        table_pow2[17][3] = 100000000000000000207944154167983593041;
        table_pow2[17][4] = 100000000000000000277258872223978124151;
        table_pow2[17][5] = 100000000000000000346573590279972655309;
        table_pow2[17][6] = 100000000000000000415888308335967186515;
        table_pow2[17][7] = 100000000000000000485203026391961717769;
        table_pow2[17][8] = 100000000000000000554517744447956249071;
        table_pow2[17][9] = 100000000000000000623832462503950780421;
        table_pow2[18][0] = 100000000000000000000000000000000000000;
        table_pow2[18][1] = 100000000000000000006931471805599453094;
        table_pow2[18][2] = 100000000000000000013862943611198906189;
        table_pow2[18][3] = 100000000000000000020794415416798359285;
        table_pow2[18][4] = 100000000000000000027725887222397812381;
        table_pow2[18][5] = 100000000000000000034657359027997265477;
        table_pow2[18][6] = 100000000000000000041588830833596718574;
        table_pow2[18][7] = 100000000000000000048520302639196171671;
        table_pow2[18][8] = 100000000000000000055451774444795624769;
        table_pow2[18][9] = 100000000000000000062383246250395077867;
        table_pow2[19][0] = 100000000000000000000000000000000000000;
        table_pow2[19][1] = 100000000000000000000693147180559945309;
        table_pow2[19][2] = 100000000000000000001386294361119890619;
        table_pow2[19][3] = 100000000000000000002079441541679835928;
        table_pow2[19][4] = 100000000000000000002772588722239781238;
        table_pow2[19][5] = 100000000000000000003465735902799726547;
        table_pow2[19][6] = 100000000000000000004158883083359671857;
        table_pow2[19][7] = 100000000000000000004852030263919617166;
        table_pow2[19][8] = 100000000000000000005545177444479562475;
        table_pow2[19][9] = 100000000000000000006238324625039507785;
        table_pow2[20][0] = 100000000000000000000000000000000000000;
        table_pow2[20][1] = 100000000000000000000069314718055994531;
        table_pow2[20][2] = 100000000000000000000138629436111989062;
        table_pow2[20][3] = 100000000000000000000207944154167983593;
        table_pow2[20][4] = 100000000000000000000277258872223978124;
        table_pow2[20][5] = 100000000000000000000346573590279972655;
        table_pow2[20][6] = 100000000000000000000415888308335967186;
        table_pow2[20][7] = 100000000000000000000485203026391961717;
        table_pow2[20][8] = 100000000000000000000554517744447956248;
        table_pow2[20][9] = 100000000000000000000623832462503950778;

        LUT3_2_isSet = true;
    }

    function setLUT3_3() public {
        table_pow2[21][0] = 100000000000000000000000000000000000000;
        table_pow2[21][1] = 100000000000000000000006931471805599453;
        table_pow2[21][2] = 100000000000000000000013862943611198906;
        table_pow2[21][3] = 100000000000000000000020794415416798359;
        table_pow2[21][4] = 100000000000000000000027725887222397812;
        table_pow2[21][5] = 100000000000000000000034657359027997265;
        table_pow2[21][6] = 100000000000000000000041588830833596719;
        table_pow2[21][7] = 100000000000000000000048520302639196172;
        table_pow2[21][8] = 100000000000000000000055451774444795625;
        table_pow2[21][9] = 100000000000000000000062383246250395078;
        table_pow2[22][0] = 100000000000000000000000000000000000000;
        table_pow2[22][1] = 100000000000000000000000693147180559945;
        table_pow2[22][2] = 100000000000000000000001386294361119891;
        table_pow2[22][3] = 100000000000000000000002079441541679836;
        table_pow2[22][4] = 100000000000000000000002772588722239781;
        table_pow2[22][5] = 100000000000000000000003465735902799727;
        table_pow2[22][6] = 100000000000000000000004158883083359672;
        table_pow2[22][7] = 100000000000000000000004852030263919617;
        table_pow2[22][8] = 100000000000000000000005545177444479562;
        table_pow2[22][9] = 100000000000000000000006238324625039508;
        table_pow2[23][0] = 100000000000000000000000000000000000000;
        table_pow2[23][1] = 100000000000000000000000069314718055995;
        table_pow2[23][2] = 100000000000000000000000138629436111989;
        table_pow2[23][3] = 100000000000000000000000207944154167984;
        table_pow2[23][4] = 100000000000000000000000277258872223978;
        table_pow2[23][5] = 100000000000000000000000346573590279973;
        table_pow2[23][6] = 100000000000000000000000415888308335967;
        table_pow2[23][7] = 100000000000000000000000485203026391962;
        table_pow2[23][8] = 100000000000000000000000554517744447956;
        table_pow2[23][9] = 100000000000000000000000623832462503951;
        table_pow2[24][0] = 100000000000000000000000000000000000000;
        table_pow2[24][1] = 100000000000000000000000006931471805599;
        table_pow2[24][2] = 100000000000000000000000013862943611199;
        table_pow2[24][3] = 100000000000000000000000020794415416798;
        table_pow2[24][4] = 100000000000000000000000027725887222398;
        table_pow2[24][5] = 100000000000000000000000034657359027997;
        table_pow2[24][6] = 100000000000000000000000041588830833597;
        table_pow2[24][7] = 100000000000000000000000048520302639196;
        table_pow2[24][8] = 100000000000000000000000055451774444796;
        table_pow2[24][9] = 100000000000000000000000062383246250395;
        table_pow2[25][0] = 100000000000000000000000000000000000000;
        table_pow2[25][1] = 100000000000000000000000000693147180560;
        table_pow2[25][2] = 100000000000000000000000001386294361120;
        table_pow2[25][3] = 100000000000000000000000002079441541680;
        table_pow2[25][4] = 100000000000000000000000002772588722240;
        table_pow2[25][5] = 100000000000000000000000003465735902800;
        table_pow2[25][6] = 100000000000000000000000004158883083360;
        table_pow2[25][7] = 100000000000000000000000004852030263920;
        table_pow2[25][8] = 100000000000000000000000005545177444480;
        table_pow2[25][9] = 100000000000000000000000006238324625040;
        table_pow2[26][0] = 100000000000000000000000000000000000000;
        table_pow2[26][1] = 100000000000000000000000000069314718056;
        table_pow2[26][2] = 100000000000000000000000000138629436112;
        table_pow2[26][3] = 100000000000000000000000000207944154168;
        table_pow2[26][4] = 100000000000000000000000000277258872224;
        table_pow2[26][5] = 100000000000000000000000000346573590280;
        table_pow2[26][6] = 100000000000000000000000000415888308336;
        table_pow2[26][7] = 100000000000000000000000000485203026392;
        table_pow2[26][8] = 100000000000000000000000000554517744448;
        table_pow2[26][9] = 100000000000000000000000000623832462504;
        table_pow2[27][0] = 100000000000000000000000000000000000000;
        table_pow2[27][1] = 100000000000000000000000000006931471806;
        table_pow2[27][2] = 100000000000000000000000000013862943611;
        table_pow2[27][3] = 100000000000000000000000000020794415417;
        table_pow2[27][4] = 100000000000000000000000000027725887222;
        table_pow2[27][5] = 100000000000000000000000000034657359028;
        table_pow2[27][6] = 100000000000000000000000000041588830834;
        table_pow2[27][7] = 100000000000000000000000000048520302639;
        table_pow2[27][8] = 100000000000000000000000000055451774445;
        table_pow2[27][9] = 100000000000000000000000000062383246250;
        table_pow2[28][0] = 100000000000000000000000000000000000000;
        table_pow2[28][1] = 100000000000000000000000000000693147181;
        table_pow2[28][2] = 100000000000000000000000000001386294361;
        table_pow2[28][3] = 100000000000000000000000000002079441542;
        table_pow2[28][4] = 100000000000000000000000000002772588722;
        table_pow2[28][5] = 100000000000000000000000000003465735903;
        table_pow2[28][6] = 100000000000000000000000000004158883083;
        table_pow2[28][7] = 100000000000000000000000000004852030264;
        table_pow2[28][8] = 100000000000000000000000000005545177444;
        table_pow2[28][9] = 100000000000000000000000000006238324625;
        table_pow2[29][0] = 100000000000000000000000000000000000000;
        table_pow2[29][1] = 100000000000000000000000000000069314718;
        table_pow2[29][2] = 100000000000000000000000000000138629436;
        table_pow2[29][3] = 100000000000000000000000000000207944154;
        table_pow2[29][4] = 100000000000000000000000000000277258872;
        table_pow2[29][5] = 100000000000000000000000000000346573590;
        table_pow2[29][6] = 100000000000000000000000000000415888308;
        table_pow2[29][7] = 100000000000000000000000000000485203026;
        table_pow2[29][8] = 100000000000000000000000000000554517744;
        table_pow2[29][9] = 100000000000000000000000000000623832463;
        table_pow2[30][0] = 100000000000000000000000000000000000000;
        table_pow2[30][1] = 100000000000000000000000000000006931472;
        table_pow2[30][2] = 100000000000000000000000000000013862944;
        table_pow2[30][3] = 100000000000000000000000000000020794415;
        table_pow2[30][4] = 100000000000000000000000000000027725887;
        table_pow2[30][5] = 100000000000000000000000000000034657359;
        table_pow2[30][6] = 100000000000000000000000000000041588831;
        table_pow2[30][7] = 100000000000000000000000000000048520303;
        table_pow2[30][8] = 100000000000000000000000000000055451774;
        table_pow2[30][9] = 100000000000000000000000000000062383246;

        LUT3_3_isSet = true;
    }

    function setLUT3_4() public {
        table_pow2[31][0] = 100000000000000000000000000000000000000;
        table_pow2[31][1] = 100000000000000000000000000000000693147;
        table_pow2[31][2] = 100000000000000000000000000000001386294;
        table_pow2[31][3] = 100000000000000000000000000000002079442;
        table_pow2[31][4] = 100000000000000000000000000000002772589;
        table_pow2[31][5] = 100000000000000000000000000000003465736;
        table_pow2[31][6] = 100000000000000000000000000000004158883;
        table_pow2[31][7] = 100000000000000000000000000000004852030;
        table_pow2[31][8] = 100000000000000000000000000000005545177;
        table_pow2[31][9] = 100000000000000000000000000000006238325;
        table_pow2[32][0] = 100000000000000000000000000000000000000;
        table_pow2[32][1] = 100000000000000000000000000000000069315;
        table_pow2[32][2] = 100000000000000000000000000000000138629;
        table_pow2[32][3] = 100000000000000000000000000000000207944;
        table_pow2[32][4] = 100000000000000000000000000000000277259;
        table_pow2[32][5] = 100000000000000000000000000000000346574;
        table_pow2[32][6] = 100000000000000000000000000000000415888;
        table_pow2[32][7] = 100000000000000000000000000000000485203;
        table_pow2[32][8] = 100000000000000000000000000000000554518;
        table_pow2[32][9] = 100000000000000000000000000000000623832;
        table_pow2[33][0] = 100000000000000000000000000000000000000;
        table_pow2[33][1] = 100000000000000000000000000000000006931;
        table_pow2[33][2] = 100000000000000000000000000000000013863;
        table_pow2[33][3] = 100000000000000000000000000000000020794;
        table_pow2[33][4] = 100000000000000000000000000000000027726;
        table_pow2[33][5] = 100000000000000000000000000000000034657;
        table_pow2[33][6] = 100000000000000000000000000000000041589;
        table_pow2[33][7] = 100000000000000000000000000000000048520;
        table_pow2[33][8] = 100000000000000000000000000000000055452;
        table_pow2[33][9] = 100000000000000000000000000000000062383;
        table_pow2[34][0] = 100000000000000000000000000000000000000;
        table_pow2[34][1] = 100000000000000000000000000000000000693;
        table_pow2[34][2] = 100000000000000000000000000000000001386;
        table_pow2[34][3] = 100000000000000000000000000000000002079;
        table_pow2[34][4] = 100000000000000000000000000000000002773;
        table_pow2[34][5] = 100000000000000000000000000000000003466;
        table_pow2[34][6] = 100000000000000000000000000000000004159;
        table_pow2[34][7] = 100000000000000000000000000000000004852;
        table_pow2[34][8] = 100000000000000000000000000000000005545;
        table_pow2[34][9] = 100000000000000000000000000000000006238;
        table_pow2[35][0] = 100000000000000000000000000000000000000;
        table_pow2[35][1] = 100000000000000000000000000000000000069;
        table_pow2[35][2] = 100000000000000000000000000000000000139;
        table_pow2[35][3] = 100000000000000000000000000000000000208;
        table_pow2[35][4] = 100000000000000000000000000000000000277;
        table_pow2[35][5] = 100000000000000000000000000000000000347;
        table_pow2[35][6] = 100000000000000000000000000000000000416;
        table_pow2[35][7] = 100000000000000000000000000000000000485;
        table_pow2[35][8] = 100000000000000000000000000000000000555;
        table_pow2[35][9] = 100000000000000000000000000000000000624;
        table_pow2[36][0] = 100000000000000000000000000000000000000;
        table_pow2[36][1] = 100000000000000000000000000000000000007;
        table_pow2[36][2] = 100000000000000000000000000000000000014;
        table_pow2[36][3] = 100000000000000000000000000000000000021;
        table_pow2[36][4] = 100000000000000000000000000000000000028;
        table_pow2[36][5] = 100000000000000000000000000000000000035;
        table_pow2[36][6] = 100000000000000000000000000000000000042;
        table_pow2[36][7] = 100000000000000000000000000000000000049;
        table_pow2[36][8] = 100000000000000000000000000000000000055;
        table_pow2[36][9] = 100000000000000000000000000000000000062;
        table_pow2[37][0] = 100000000000000000000000000000000000000;
        table_pow2[37][1] = 100000000000000000000000000000000000001;
        table_pow2[37][2] = 100000000000000000000000000000000000001;
        table_pow2[37][3] = 100000000000000000000000000000000000002;
        table_pow2[37][4] = 100000000000000000000000000000000000003;
        table_pow2[37][5] = 100000000000000000000000000000000000003;
        table_pow2[37][6] = 100000000000000000000000000000000000004;
        table_pow2[37][7] = 100000000000000000000000000000000000005;
        table_pow2[37][8] = 100000000000000000000000000000000000006;
        table_pow2[37][9] = 100000000000000000000000000000000000006;
        table_pow2[38][0] = 100000000000000000000000000000000000000;
        table_pow2[38][1] = 100000000000000000000000000000000000000;
        table_pow2[38][2] = 100000000000000000000000000000000000000;
        table_pow2[38][3] = 100000000000000000000000000000000000000;
        table_pow2[38][4] = 100000000000000000000000000000000000000;
        table_pow2[38][5] = 100000000000000000000000000000000000000;
        table_pow2[38][6] = 100000000000000000000000000000000000000;
        table_pow2[38][7] = 100000000000000000000000000000000000000;
        table_pow2[38][8] = 100000000000000000000000000000000000001;
        table_pow2[38][9] = 100000000000000000000000000000000000001;

        LUT3_4_isSet = true;
    }

    /***** MODIFIERS *****/

    modifier _onlyLUT1andLUT2AreSet() {
        require(LUT1_isSet == true && LUT2_isSet == true, "Lookup tables 1 & 2 must first be set");
        _;
    }

    modifier _onlyLUT3isSet() {
        require(
            LUT3_1_isSet == true && LUT3_2_isSet == true && LUT3_3_isSet == true && LUT3_4_isSet == true,
            "Lookup table 3 must first be set"
        );
        _;
    }
}

File 16 of 17 : IERC20.sol
pragma solidity ^0.5.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see `ERC20Detailed`.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

File 17 of 17 : Address.sol
pragma solidity ^0.5.0;

/**
 * @dev Collection of functions related to the address type,
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * This test is non-exhaustive, and there may be false-negatives: during the
     * execution of a contract's constructor, its address will be reported as
     * not containing a contract.
     *
     * > It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies in extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"address","name":"market","type":"address"},{"indexed":false,"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sUSDPaid","type":"uint256"},{"indexed":false,"internalType":"address","name":"susd","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"BoughtFromAmm","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"PauseChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"refferer","type":"address"},{"indexed":false,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"volume","type":"uint256"}],"name":"ReferrerPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_cap","type":"uint256"}],"name":"SetCapPerAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_capPerMarket","type":"uint256"}],"name":"SetCapPerMarket","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_impliedVolatility","type":"uint256"}],"name":"SetImpliedVolatilityPerAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_spread","type":"uint256"}],"name":"SetMaxSpread","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_spread","type":"uint256"}],"name":"SetMaxSupportedPrice","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_spread","type":"uint256"}],"name":"SetMinSpread","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_spread","type":"uint256"}],"name":"SetMinSupportedPrice","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_minimalTimeLeftToMaturity","type":"uint256"}],"name":"SetMinimalTimeLeftToMaturity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_manager","type":"address"}],"name":"SetPositionalMarketManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_priceFeed","type":"address"}],"name":"SetPriceFeed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sUSD","type":"address"}],"name":"SetSUSD","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_safeBox","type":"address"}],"name":"SetSafeBox","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_safeBoxImpact","type":"uint256"}],"name":"SetSafeBoxImpact","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_stakingThales","type":"address"}],"name":"SetStakingThales","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"address","name":"market","type":"address"},{"indexed":false,"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sUSDPaid","type":"uint256"},{"indexed":false,"internalType":"address","name":"susd","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"SoldToAMM","type":"event"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"}],"name":"availableToBuyFromAMM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"}],"name":"availableToSellToAMM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"}],"name":"balancePosition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expectedPayout","type":"uint256"},{"internalType":"uint256","name":"additionalSlippage","type":"uint256"}],"name":"buyFromAMM","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expectedPayout","type":"uint256"},{"internalType":"uint256","name":"additionalSlippage","type":"uint256"},{"internalType":"address","name":"_referrer","type":"address"}],"name":"buyFromAMMWithReferrer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"buyFromAmmQuote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"buyPriceImpact","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"timeLeftInDays","type":"uint256"},{"internalType":"uint256","name":"volatility","type":"uint256"}],"name":"calculateOdds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"canExerciseMaturedMarket","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"capPerMarket","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"deciMath","outputs":[{"internalType":"contract DeciMath","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"exerciseMaturedMarket","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"asset","type":"bytes32"}],"name":"getCapPerAsset","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"impliedVolatilityPerAsset","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"initNonReentrant","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"contract IPriceFeed","name":"_priceFeed","type":"address"},{"internalType":"contract IERC20","name":"_sUSD","type":"address"},{"internalType":"uint256","name":"_capPerMarket","type":"uint256"},{"internalType":"contract DeciMath","name":"_deciMath","type":"address"},{"internalType":"uint256","name":"_min_spread","type":"uint256"},{"internalType":"uint256","name":"_max_spread","type":"uint256"},{"internalType":"uint256","name":"_minimalTimeLeftToMaturity","type":"uint256"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"isMarketInAMMTrading","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastPauseTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxSupportedPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"max_spread","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"minSupportedPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"min_spread","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"minimalTimeLeftToMaturity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"previousManager","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"}],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"priceFeed","outputs":[{"internalType":"contract IPriceFeed","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"referrals","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"referrerFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"account","type":"address"}],"name":"retrieveSUSD","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"retrieveSUSDAmount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"sUSD","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"safeBox","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"safeBoxImpact","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sellPriceImpact","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expectedPayout","type":"uint256"},{"internalType":"uint256","name":"additionalSlippage","type":"uint256"}],"name":"sellToAMM","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ThalesAMM.Position","name":"position","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sellToAmmQuote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint256","name":"_cap","type":"uint256"}],"name":"setCapPerAsset","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_capPerMarket","type":"uint256"}],"name":"setCapPerMarket","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint256","name":"_impliedVolatility","type":"uint256"}],"name":"setImpliedVolatilityPerAsset","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_spread","type":"uint256"}],"name":"setMaxSpread","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_maxSupportedPrice","type":"uint256"}],"name":"setMaxSupportedPrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_spread","type":"uint256"}],"name":"setMinSpread","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_minSupportedPrice","type":"uint256"}],"name":"setMinSupportedPrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_minimalTimeLeftToMaturity","type":"uint256"}],"name":"setMinimalTimeLeftToMaturity","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setPaused","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_manager","type":"address"}],"name":"setPositionalMarketManager","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IPriceFeed","name":"_priceFeed","type":"address"}],"name":"setPriceFeed","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_referrals","type":"address"},{"internalType":"uint256","name":"_referrerFee","type":"uint256"}],"name":"setReferrals","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"_sUSD","type":"address"}],"name":"setSUSD","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_safeBox","type":"address"}],"name":"setSafeBox","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_safeBoxImpact","type":"uint256"}],"name":"setSafeBoxImpact","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IStakingThales","name":"_stakingThales","type":"address"}],"name":"setStakingThales","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setWhitelistedAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"spentOnMarket","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakingThales","outputs":[{"internalType":"contract IStakingThales","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"proxyAddress","type":"address"}],"name":"transferOwnershipAtInit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedAddresses","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b506155aa806100206000396000f3fe608060405234801561001057600080fd5b50600436106103d05760003560e01c80638a249778116101ff578063c4dc27d71161011a578063efb1fe35116100ad578063f85de9fa1161007c578063f85de9fa14610b64578063f8debeb714610b81578063fb91d41c14610b9e578063fd8a8cc614610ba6576103d0565b8063efb1fe3514610acf578063efc1525114610afb578063f502b00314610b2a578063f598df8214610b47576103d0565b8063d3dc7539116100e9578063d3dc753914610aaf578063d69fb66814610ab7578063df8974d014610abf578063ebc7977214610ac7576103d0565b8063c4dc27d714610a11578063ca1d578e14610a37578063ce696e5114610a5d578063d162492414610a8c576103d0565b8063a996798c11610192578063bf996ae311610161578063bf996ae314610970578063c2783f9214610996578063c3b83f5f146109c5578063c402055f146109eb576103d0565b8063a996798c146108a1578063ad18f0da146108c7578063bef648db1461091e578063bf46c0b41461093b576103d0565b8063931b2040116101ce578063931b2040146108195780639324cac7146108215780639f916c9f14610829578063a8cd06e814610872576103d0565b80638a249778146107c05780638da5cb5b146107ec5780638f816652146107f457806391b4ded914610811576103d0565b806353a47bb7116102ef578063724e78da116102825780637f852582116102515780637f8525821461071f578063808226d31461074557806385170209146107625780638875eb841461077f576103d0565b8063724e78da146106bb578063741bef1a146106e157806379ba5097146106e95780637b337a36146106f1576103d0565b80636aaa81b6116102be5780636aaa81b6146106745780636cfa636e1461067c5780636e88a7bd146106845780636ed033f81461068c576103d0565b806353a47bb71461061b5780635727a0f3146106235780635c975abb146106495780635ef85b2614610651576103d0565b80631c37d04b116103675780633ce1108d116103365780633ce1108d146105a4578063481c6a75146105e557806348663e95146105ed57806351b9181f146105f5576103d0565b80631c37d04b1461050c57806321ef44c614610541578063270e13ef14610567578063316425c31461059c576103d0565b806313af4035116103a357806313af4035146104995780631627540c146104bf57806316c38b3c146104e557806319b844a614610504576103d0565b806306c933d8146103d557806306f58fe41461040f5780630d0c8ca7146104335780630f13aae814610452575b600080fd5b6103fb600480360360208110156103eb57600080fd5b50356001600160a01b0316610bae565b604080519115158252519081900360200190f35b610417610bc3565b604080516001600160a01b039092168252519081900360200190f35b6104506004803603602081101561044957600080fd5b5035610bd2565b005b6104876004803603606081101561046857600080fd5b506001600160a01b038135169060ff6020820135169060400135610c15565b60408051918252519081900360200190f35b610450600480360360208110156104af57600080fd5b50356001600160a01b0316610d7e565b610450600480360360208110156104d557600080fd5b50356001600160a01b0316610e91565b610450600480360360208110156104fb57600080fd5b50351515610eed565b610487610f67565b6104876004803603606081101561052257600080fd5b506001600160a01b038135169060ff6020820135169060400135610f6d565b6104506004803603602081101561055757600080fd5b50356001600160a01b0316610f9b565b6104876004803603606081101561057d57600080fd5b506001600160a01b038135169060ff6020820135169060400135610ffd565b610487611033565b610450600480360360a08110156105ba57600080fd5b506001600160a01b038135169060ff6020820135169060408101359060608101359060800135611039565b61041761185b565b61041761186a565b6104506004803603602081101561060b57600080fd5b50356001600160a01b0316611879565b610417611936565b6103fb6004803603602081101561063957600080fd5b50356001600160a01b0316611945565b6103fb611aeb565b6104506004803603604081101561066757600080fd5b5080359060200135611af4565b610487611cf7565b610417611cfd565b610487611d0c565b610487600480360360408110156106a257600080fd5b5080356001600160a01b0316906020013560ff16611d12565b610450600480360360208110156106d157600080fd5b50356001600160a01b0316611f14565b610417611f70565b610450611f7f565b6104506004803603604081101561070757600080fd5b506001600160a01b038135169060200135151561203b565b6104506004803603602081101561073557600080fd5b50356001600160a01b031661206e565b6104506004803603602081101561075b57600080fd5b50356120ca565b6104506004803603602081101561077857600080fd5b503561210d565b610450600480360360a081101561079557600080fd5b506001600160a01b038135169060ff602082013516906040810135906060810135906080013561214e565b610450600480360360408110156107d657600080fd5b506001600160a01b0381351690602001356121fe565b61041761222c565b6104506004803603602081101561080a57600080fd5b503561223b565b61048761227e565b610487612284565b61041761228a565b610450600480360360c081101561083f57600080fd5b506001600160a01b03813581169160ff6020820135169160408201359160608101359160808201359160a0013516612299565b6104876004803603608081101561088857600080fd5b50803590602081013590604081013590606001356123be565b610450600480360360208110156108b757600080fd5b50356001600160a01b031661281e565b61045060048036036101008110156108de57600080fd5b506001600160a01b038135811691602081013582169160408201358116916060810135916080820135169060a08101359060c08101359060e001356128f5565b6104506004803603602081101561093457600080fd5b5035612a0d565b6104876004803603606081101561095157600080fd5b506001600160a01b038135169060ff6020820135169060400135612a50565b6104506004803603602081101561098657600080fd5b50356001600160a01b0316612a81565b610487600480360360408110156109ac57600080fd5b5080356001600160a01b0316906020013560ff16612bfa565b610450600480360360208110156109db57600080fd5b50356001600160a01b0316612e62565b61045060048036036020811015610a0157600080fd5b50356001600160a01b0316612f81565b61048760048036036020811015610a2757600080fd5b50356001600160a01b0316612fdd565b6103fb60048036036020811015610a4d57600080fd5b50356001600160a01b0316612fef565b61048760048036036040811015610a7357600080fd5b5080356001600160a01b0316906020013560ff1661326b565b61045060048036036040811015610aa257600080fd5b5080359060200135613281565b6104176132da565b6104876132e9565b6104876132ef565b6104506132f5565b61045060048036036040811015610ae557600080fd5b506001600160a01b038135169060200135613358565b61048760048036036040811015610b1157600080fd5b5080356001600160a01b0316906020013560ff166133e6565b61048760048036036020811015610b4057600080fd5b503561341d565b61045060048036036020811015610b5d57600080fd5b503561342f565b61045060048036036020811015610b7a57600080fd5b5035613472565b61048760048036036020811015610b9757600080fd5b50356134b5565b610487613568565b61041761356e565b60486020526000908152604090205460ff1681565b604b546001600160a01b031681565b610bda61357d565b60458190556040805182815290517fa36b45ea010fcb2e70d33048b7d3f27c265915709bf40fc7e239459424933d9f9181900360200190a150565b6000610c218484612bfa565b821115610c3057506000610d77565b6000610c4e603d54610c428787611d12565b9063ffffffff6135c816565b90506000610cb9670de0b6b3a7640000610ca0610cac670de0b6b3a7640000610ca0610c93610c7e8d8d8d613625565b670de0b6b3a76400009063ffffffff6135c816565b889063ffffffff6136c816565b9063ffffffff61372116565b879063ffffffff6136c816565b90506000610cf6670de0b6b3a7640000610ca0610ce9604354670de0b6b3a76400006135c890919063ffffffff16565b859063ffffffff6136c816565b603b546040805163edc892e160e01b81526004810184905290519293506001600160a01b039091169163edc892e191602480820192602092909190829003018186803b158015610d4557600080fd5b505afa158015610d59573d6000803e3d6000fd5b505050506040513d6020811015610d6f57600080fd5b505193505050505b9392505050565b6001600160a01b038116610dd9576040805162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f74206265203000000000000000604482015290519081900360640190fd5b600154600160a01b900460ff1615610e225760405162461bcd60e51b81526004018080602001828103825260298152602001806154696029913960400191505060405180910390fd5b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b03199091168117825560408051928352602083019190915280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150565b610e9961357d565b600180546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229181900360200190a150565b610ef561357d565b60035460ff1615158115151415610f0b57610f64565b6003805460ff1916821515179081905560ff1615610f2857426002555b6003546040805160ff90921615158252517f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec59181900360200190a15b50565b60465481565b6000610f798484612bfa565b821115610f8857506000610d77565b610f93848484613625565b949350505050565b610fa361357d565b603a80546001600160a01b0319166001600160a01b03838116919091179182905560408051929091168252517f74a8764fc8d62d2d844c8c54426bd94ad034e0e92abdf5280ff75e2cbd678fb6916020908290030190a150565b60008061101c603d546110108787611d12565b9063ffffffff61378b16565b905061102a858585846137e5565b95945050505050565b603e5481565b600480546001019081905560035460ff16156110865760405162461bcd60e51b815260040180806020018281038252603c815260200180615510603c913960400191505060405180910390fd5b61108f86611945565b6110e0576040805162461bcd60e51b815260206004820152601e60248201527f4d61726b6574206973206e6f7420696e2054726164696e672070686173650000604482015290519081900360640190fd5b60006110ec8787612bfa565b90506000811180156110fe5750808511155b611147576040805162461bcd60e51b81526020600482015260156024820152742737ba1032b737bab3b4103634b8bab4b234ba3c9760591b604482015290519081900360640190fd5b6000611154888888610c15565b905061116e670de0b6b3a76400008563ffffffff61378b16565b61118a82610ca088670de0b6b3a764000063ffffffff6136c816565b11156111d1576040805162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b604482015290519081900360640190fd5b600080896001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b15801561120c57600080fd5b505afa158015611220573d6000803e3d6000fd5b505050506040513d604081101561123657600080fd5b50805160209091015190925090506000808a600181111561125357fe5b1461125e5781611260565b825b60408051634dcb776760e11b815233600482015290519192508a916001600160a01b03841691639b96eece916024808301926020929190829003018186803b1580156112ab57600080fd5b505afa1580156112bf573d6000803e3d6000fd5b505050506040513d60208110156112d557600080fd5b5051101561132a576040805162461bcd60e51b815260206004820152601d60248201527f596f7520646f6e74206861766520656e6f756768206f7074696f6e732e000000604482015290519081900360640190fd5b60408051636eb1769f60e11b815233600482015230602482015290518a916001600160a01b0384169163dd62ed3e91604480820192602092909190829003018186803b15801561137957600080fd5b505afa15801561138d573d6000803e3d6000fd5b505050506040513d60208110156113a357600080fd5b505110156113e8576040805162461bcd60e51b815260206004820152600d60248201526c27379030b63637bbb0b731b29760991b604482015290519081900360640190fd5b6114036001600160a01b03821633308c63ffffffff61394f16565b6000603b60009054906101000a90046001600160a01b03166001600160a01b031663edc892e18d6001600160a01b031663a76df708306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561147f57600080fd5b505afa158015611493573d6000803e3d6000fd5b505050506040513d60208110156114a957600080fd5b5051604080516001600160e01b031960e085901b1681526004810192909252516024808301926020929190829003018186803b1580156114e857600080fd5b505afa1580156114fc573d6000803e3d6000fd5b505050506040513d602081101561151257600080fd5b505190508015611570578b6001600160a01b031663f5571beb6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561155757600080fd5b505af115801561156b573d6000803e3d6000fd5b505050505b603a54604080516370a0823160e01b8152306004820152905187926001600160a01b0316916370a08231916024808301926020929190829003018186803b1580156115ba57600080fd5b505afa1580156115ce573d6000803e3d6000fd5b505050506040513d60208110156115e457600080fd5b50511015611639576040805162461bcd60e51b815260206004820152601c60248201527f4e6f7420656e6f756768207355534420696e20636f6e74726163742e00000000604482015290519081900360640190fd5b603a546040805163a9059cbb60e01b81523360048201526024810188905290516001600160a01b039092169163a9059cbb916044808201926020929091908290030181600087803b15801561168d57600080fd5b505af11580156116a1573d6000803e3d6000fd5b505050506040513d60208110156116b757600080fd5b50506044546001600160a01b0316156117335760448054604080516302c7739b60e01b81523360048201526024810189905290516001600160a01b03909216926302c7739b92828201926000929082900301818387803b15801561171a57600080fd5b505af115801561172e573d6000803e3d6000fd5b505050505b61173f8c8683336139a9565b7f1d6ff70c632edb1e6aba7fbc0148db68c8392e30f9dfaadae2543a2543757cf6338d8d8d89603a60009054906101000a90046001600160a01b03168860405180886001600160a01b03166001600160a01b03168152602001876001600160a01b03166001600160a01b031681526020018660018111156117bc57fe5b60ff1681526020810195909552506040808501939093526001600160a01b039182166060850152166080830152519081900360a0019350915050a15050505050506004548114611853576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b505050505050565b603b546001600160a01b031681565b6042546001600160a01b031681565b61188281612fef565b6118cc576040805162461bcd60e51b81526020600482015260166024820152754e6f206f7074696f6e7320746f20657865726369736560501b604482015290519081900360640190fd5b806001600160a01b031663851492586040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561190757600080fd5b505af115801561191b573d6000803e3d6000fd5b505050506040513d602081101561193157600080fd5b505050565b6001546001600160a01b031681565b603b5460408051633761c52760e11b81526001600160a01b03848116600483015291516000939290921691636ec38a4e91602480820192602092909190829003018186803b15801561199657600080fd5b505afa1580156119aa573d6000803e3d6000fd5b505050506040513d60208110156119c057600080fd5b505115611ae25760008290506000816001600160a01b03166341bc7b1f6040518163ffffffff1660e01b815260040160606040518083038186803b158015611a0757600080fd5b505afa158015611a1b573d6000803e3d6000fd5b505050506040513d6060811015611a3157600080fd5b50516000818152603f6020526040902054909150611a5457600092505050611ae6565b6000826001600160a01b0316639e3b34bf6040518163ffffffff1660e01b8152600401604080518083038186803b158015611a8e57600080fd5b505afa158015611aa2573d6000803e3d6000fd5b505050506040513d6040811015611ab857600080fd5b5051905042811015611ad05760009350505050611ae6565b60405442909103119250611ae6915050565b5060005b919050565b60035460ff1681565b3360009081526048602052604090205460ff1680611b1c57506000546001600160a01b031633145b611b575760405162461bcd60e51b81526004018080602001828103825260328152602001806154026032913960400191505060405180910390fd5b611b70670de0b6b3a7640000603c63ffffffff6136c816565b81118015611b965750611b93670de0b6b3a764000061012c63ffffffff6136c816565b81105b611be7576040805162461bcd60e51b815260206004820152601960248201527f4956206f757473696465206d696e2f6d61782072616e67652100000000000000604482015290519081900360640190fd5b603954604080516315905ec160e31b81526004810185905290516001600160a01b039092169163ac82f60891602480820192602092909190829003018186803b158015611c3357600080fd5b505afa158015611c47573d6000803e3d6000fd5b505050506040513d6020811015611c5d57600080fd5b5051611ca6576040805162461bcd60e51b8152602060048201526013602482015272417373657420686173206e6f2070726963652160681b604482015290519081900360640190fd5b6000828152603f6020908152604091829020839055815184815290810183905281517f715e0a52c0b74c77d2d2012a363ac95b494302ad2abb78ac7406ec93451f1adb929181900390910190a15050565b603d5481565b6038546001600160a01b031681565b604a5481565b6000611d1d83611945565b15611f0a5760008390506000816001600160a01b0316639e3b34bf6040518163ffffffff1660e01b8152600401604080518083038186803b158015611d6157600080fd5b505afa158015611d75573d6000803e3d6000fd5b505050506040513d6040811015611d8b57600080fd5b505190504281036000611db362015180610ca084670de0b6b3a764000063ffffffff6136c816565b90506000846001600160a01b031663668aa8246040518163ffffffff1660e01b815260040160206040518083038186803b158015611df057600080fd5b505afa158015611e04573d6000803e3d6000fd5b505050506040513d6020811015611e1a57600080fd5b5051604080516341bc7b1f60e01b8152905191925060009182916001600160a01b038916916341bc7b1f91600480820192606092909190829003018186803b158015611e6557600080fd5b505afa158015611e79573d6000803e3d6000fd5b505050506040513d6060811015611e8f57600080fd5b50805160209091015190925090506000896001811115611eab57fe5b1415611ee357611ed56064610ca0858488603f6000898152602001908152602001600020546123be565b975050505050505050611f0e565b611ed5610c7e6064610ca0868589603f60008a8152602001908152602001600020546123be565b5060005b92915050565b611f1c61357d565b603980546001600160a01b0383166001600160a01b0319909116811790915560408051918252517ff724a45d041687842411f2b977ef22ab8f43c8f1104f4592b42a00f9b34a643d9181900360200190a150565b6039546001600160a01b031681565b6001546001600160a01b03163314611fc85760405162461bcd60e51b81526004018080602001828103825260358152602001806154346035913960400191505060405180910390fd5b600054600154604080516001600160a01b03938416815292909116602083015280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b61204361357d565b6001600160a01b03919091166000908152604860205260409020805460ff1916911515919091179055565b61207661357d565b604480546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f475a2179b9b6a155e7ba3f46a461beb6798279b265026364944e38dcb4bacafe9181900360200190a150565b6120d261357d565b60468190556040805182815290517fafb2511d843afc28083da6c608f400d0262aef83b3cd885abd205b3e64f544bb9181900360200190a150565b61211561357d565b6040818155805182815290517fdc469b5583fa9b7ebd3245e1665334cd758c4bef4c5a132c62baca85effacfec9181900360200190a150565b600480546001019081905560035460ff161561219b5760405162461bcd60e51b815260040180806020018281038252603c815260200180615510603c913960400191505060405180910390fd5b6121a88686868686613d1a565b6004548114611853576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b61220661357d565b604980546001600160a01b0319166001600160a01b039390931692909217909155604a55565b6000546001600160a01b031681565b61224361357d565b60438190556040805182815290517f01edd423db862fb00774918e3b06d9c1dd3db9a99b5a194c439d2f141876f4449181900360200190a150565b60025481565b60455481565b603a546001600160a01b031681565b600480546001019081905560035460ff16156122e65760405162461bcd60e51b815260040180806020018281038252603c815260200180615510603c913960400191505060405180910390fd5b6049546040805163bbddaca360e01b81526001600160a01b0385811660048301523360248301529151919092169163bbddaca391604480830192600092919082900301818387803b15801561233a57600080fd5b505af115801561234e573d6000803e3d6000fd5b5050505061235f8787878787613d1a565b60045481146123b5576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b50505050505050565b600080612403633b9aca00610ca06123e66123e18861016d63ffffffff61372116565b61446a565b6123f787606463ffffffff61372116565b9063ffffffff6136c816565b9050858510156000816124315761242c87610ca08a670de0b6b3a764000063ffffffff6136c816565b61244d565b61244d88610ca089670de0b6b3a764000063ffffffff6136c816565b603854604080516344f7787f60e01b8152600481018490526063602482015290519293506000926124ef928792610ca092670de0b6b3a7640000926001600160a01b0316916344f7787f916044808301926020929190829003018186803b1580156124b757600080fd5b505afa1580156124cb573d6000803e3d6000fd5b505050506040513d60208110156124e157600080fd5b50519063ffffffff6136c816565b9050600061254061252861251362989680610ca0866223588363ffffffff6136c816565b670de0b6b3a76400009063ffffffff61378b16565b610ca0670de0b6b3a76400008063ffffffff6136c816565b90506000612563670de0b6b3a7640000610ca0600281878063ffffffff6136c816565b9050600061257e62989680610ca0623cdfaf6123f7866144bb565b60385460408051632e4c697f60e01b815260048101879052674563918244f40000602482015290519293506000926125ed92620f424092610ca09262144c62926001600160a01b031691632e4c697f916044808301926020929190829003018186803b1580156124b757600080fd5b60385460408051632e4c697f60e01b815260048101889052673782dace9d9000006024820152905192935060009261265c92620f424092610ca092621bca48926001600160a01b031691632e4c697f916044808301926020929190829003018186803b1580156124b757600080fd5b60385460408051632e4c697f60e01b8152600481018990526729a2241af62c0000602482015290519293506000926126cb92620f424092610ca092621b2ee6926001600160a01b031691632e4c697f916044808301926020929190829003018186803b1580156124b757600080fd5b60385460408051632e4c697f60e01b8152600481018a9052671bc16d674ec800006024820152905192935060009261273a92620f424092610ca092620570ba926001600160a01b031691632e4c697f916044808301926020929190829003018186803b1580156124b757600080fd5b9050600061275862989680610ca08a6230bbd763ffffffff6136c816565b9050600061277683610c428781866110108c8b63ffffffff61378b16565b90506000612799610c7e670de0b6b3a7640000610ca08b8663ffffffff6136c816565b905060006127ca6127b183606463ffffffff6136c816565b610c42670de0b6b3a7640000606463ffffffff6136c816565b90508d156127e8579e50610f939d5050505050505050505050505050565b61280581610c42670de0b6b3a7640000606463ffffffff6136c816565b9f50505050505050505050505050505050949350505050565b61282661357d565b603a54604080516370a0823160e01b815230600482015290516001600160a01b039092169163a9059cbb91849184916370a08231916024808301926020929190829003018186803b15801561287a57600080fd5b505afa15801561288e573d6000803e3d6000fd5b505050506040513d60208110156128a457600080fd5b5051604080516001600160e01b031960e086901b1681526001600160a01b03909316600484015260248301919091525160448083019260209291908290030181600087803b15801561190757600080fd5b60055462010000900460ff168061290f575061290f6144e4565b806129225750600554610100900460ff16155b61295d5760405162461bcd60e51b815260040180806020018281038252602e8152602001806154e2602e913960400191505060405180910390fd5b60055462010000900460ff1615801561298d576005805461ff001962ff0000199091166201000017166101001790555b61299689610d7e565b61299e6132f5565b603980546001600160a01b03808b166001600160a01b031992831617909255603a80548a8416908316179055603c8890556038805492881692909116919091179055603d849055603e83905560408290558015612a02576005805462ff0000191690555b505050505050505050565b612a1561357d565b603e8190556040805182815290517f4af6d03c4624e0a6b868b8f6453e047f23f3ea15e9d08c938bd4c445d7ef19b39181900360200190a150565b60006001821080612a695750612a6684846133e6565b82115b15612a7657506000610d77565b610f938484846144ea565b612a8961357d565b603b546001600160a01b031615612b2257603a54603b546040805163095ea7b360e01b81526001600160a01b0392831660048201526000602482018190529151929093169263095ea7b39260448083019360209383900390910190829087803b158015612af557600080fd5b505af1158015612b09573d6000803e3d6000fd5b505050506040513d6020811015612b1f57600080fd5b50505b603b80546001600160a01b0319166001600160a01b038381169190911791829055603a546040805163095ea7b360e01b8152938316600485015260001960248501525191169163095ea7b39160448083019260209291908290030181600087803b158015612b8f57600080fd5b505af1158015612ba3573d6000803e3d6000fd5b505050506040513d6020811015612bb957600080fd5b5050604080516001600160a01b038316815290517f9987372437ace1af79923f26b948aa04afef92b2b7786144c5aae621ea84eb0a9181900360200190a150565b6000612c0583611945565b15611f0a576000612c168484614574565b905080612c27576000915050611f0e565b600080856001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b158015612c6257600080fd5b505afa158015612c76573d6000803e3d6000fd5b505050506040513d6040811015612c8c57600080fd5b5080516020909101519092509050600080866001811115612ca957fe5b14612d255760408051634dcb776760e11b815230600482015290516001600160a01b03851691639b96eece916024808301926020929190829003018186803b158015612cf457600080fd5b505afa158015612d08573d6000803e3d6000fd5b505050506040513d6020811015612d1e57600080fd5b5051612d98565b60408051634dcb776760e11b815230600482015290516001600160a01b03841691639b96eece916024808301926020929190829003018186803b158015612d6b57600080fd5b505afa158015612d7f573d6000803e3d6000fd5b505050506040513d6020811015612d9557600080fd5b50515b90506000612db8670de0b6b3a7640000610ca0848863ffffffff6136c816565b6001600160a01b038916600090815260416020526040902054909150612de4908263ffffffff61378b16565b612df1836110108b6145e3565b1015612e0557600095505050505050611f0e565b6001600160a01b038816600090815260416020526040812054612e33908390610c429081876110108f6145e3565b9050612e5583611010670de0b6b3a76400006123f7858b63ffffffff61372116565b9650505050505050611f0e565b612e6a61357d565b6001600160a01b038116612eb7576040805162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b604482015290519081900360640190fd5b600154600160a81b900460ff1615612f0c576040805162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b604482015290519081900360640190fd5b600080546001600160a01b038084166001600160a01b03199092168217928390556001805460ff60a81b1916600160a81b17905560408051939091168352602083019190915280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150565b612f8961357d565b604280546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f49a4e3b1ee2ad2bfb1455dfad2d72e84e71a35c756d2c8296f305f12e1828bf59181900360200190a150565b60416020526000908152604090205481565b603b546040805163e62b888960e01b81526001600160a01b0384811660048301529151600093929092169163e62b888991602480820192602092909190829003018186803b15801561304057600080fd5b505afa158015613054573d6000803e3d6000fd5b505050506040513d602081101561306a57600080fd5b505180156130e757506001826001600160a01b031663b1c9fe6e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156130ae57600080fd5b505afa1580156130c2573d6000803e3d6000fd5b505050506040513d60208110156130d857600080fd5b505160028111156130e557fe5b145b15611ae257600080836001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b15801561312757600080fd5b505afa15801561313b573d6000803e3d6000fd5b505050506040513d604081101561315157600080fd5b50805160209182015160408051634dcb776760e11b815230600482015290519295509093506000926001600160a01b03861692639b96eece926024808201939291829003018186803b1580156131a657600080fd5b505afa1580156131ba573d6000803e3d6000fd5b505050506040513d60208110156131d057600080fd5b50511180613251575060408051634dcb776760e11b815230600482015290516000916001600160a01b03841691639b96eece91602480820192602092909190829003018186803b15801561322357600080fd5b505afa158015613237573d6000803e3d6000fd5b505050506040513d602081101561324d57600080fd5b5051115b1561326157600192505050611ae6565b5050506000919050565b6000806132788484614656565b50949350505050565b61328961357d565b600082815260476020908152604091829020839055815184815290810183905281517f5af395595015797b4d0f26b77c38dd4831298dabc7906ee3d62b80fa75d35c1e929181900390910190a15050565b6049546001600160a01b031681565b60435481565b60405481565b60055460ff1615613343576040805162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b6005805460ff19166001908117909155600455565b61336061357d565b603a546040805163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529151919092169163a9059cbb9160448083019260209291908290030181600087803b1580156133b657600080fd5b505af11580156133ca573d6000803e3d6000fd5b505050506040513d60208110156133e057600080fd5b50505050565b60006133f183611945565b15611f0a576000613408603d546110108686611d12565b90506134158484836148d9565b915050611f0e565b603f6020526000908152604090205481565b61343761357d565b603c8190556040805182815290517fcc72495e91bfc45ec11465a752fc2866b8a5ef8960b0925774ede927ee7cbdbe9181900360200190a150565b61347a61357d565b603d8190556040805182815290517f1e6a338a58debcc786781a079c4459466b102ad0156cc84f51d25ef7dd8cb9b09181900360200190a150565b603954604080516315905ec160e31b81526004810184905290516000926001600160a01b03169163ac82f608916024808301926020929190829003018186803b15801561350157600080fd5b505afa158015613515573d6000803e3d6000fd5b505050506040513d602081101561352b57600080fd5b505161353957506000611ae6565b6000828152604760205260409020546135555750603c54611ae6565b5060009081526047602052604090205490565b603c5481565b6044546001600160a01b031681565b6000546001600160a01b031633146135c65760405162461bcd60e51b815260040180806020018281038252602f815260200180615492602f913960400191505060405180910390fd5b565b60008282111561361f576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60008060006136348686614656565b9150915060008083116136655784821161365d57613658858363ffffffff6135c816565b613660565b60005b613675565b613675838663ffffffff61378b16565b90506000858311613687576000613697565b613697838763ffffffff6135c816565b9050808210156136ae576000945050505050610d77565b6136bd88888886888688614a1f565b945050505050610d77565b6000826136d757506000611f0e565b828202828482816136e457fe5b0414610d775760405162461bcd60e51b81526004018080602001828103825260218152602001806154c16021913960400191505060405180910390fd5b6000808211613777576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b600082848161378257fe5b04949350505050565b600082820183811015610d77576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600060018310806137ff57506137fc8585846148d9565b83115b1561380c57506000610f93565b6000613840670de0b6b3a7640000610ca06138288989896144ea565b6123f7670de0b6b3a76400008863ffffffff6135c816565b9050613875670de0b6b3a7640000610ca06138688266470de4df82000063ffffffff61378b16565b849063ffffffff6136c816565b90506000613898670de0b6b3a7640000610ca0610c93878663ffffffff61378b16565b905060006138c8670de0b6b3a7640000610ca0610ce9604354670de0b6b3a764000061378b90919063ffffffff16565b603b546040805163edc892e160e01b81526004810184905290519293506001600160a01b039091169163edc892e191602480820192602092909190829003018186803b15801561391757600080fd5b505afa15801561392b573d6000803e3d6000fd5b505050506040513d602081101561394157600080fd5b505198975050505050505050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526133e0908590614b1f565b60006139e784610c426139cf604354670de0b6b3a76400006135c890919063ffffffff16565b610ca088670de0b6b3a764000063ffffffff6136c816565b60435490915015613a7d57603a546042546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018590529051919092169163a9059cbb9160448083019260209291908290030181600087803b158015613a4b57600080fd5b505af1158015613a5f573d6000803e3d6000fd5b505050506040513d6020811015613a7557600080fd5b50613a819050565b5060005b603b54613b2b906001600160a01b03166317fd849a613aa6878563ffffffff61378b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015613ada57600080fd5b505afa158015613aee573d6000803e3d6000fd5b505050506040513d6020811015613b0457600080fd5b50516001600160a01b0387166000908152604160205260409020549063ffffffff61378b16565b6001600160a01b0380871660009081526041602090815260409182902093909355603b548151630bfec24d60e11b81526004810188905291519216926317fd849a92602480840193829003018186803b158015613b8757600080fd5b505afa158015613b9b573d6000803e3d6000fd5b505050506040513d6020811015613bb157600080fd5b50516001600160a01b03861660009081526041602052604090205411613bef576001600160a01b038516600090815260416020526040812055613ca7565b603b5460408051630bfec24d60e11b8152600481018690529051613c8d926001600160a01b0316916317fd849a916024808301926020929190829003018186803b158015613c3c57600080fd5b505afa158015613c50573d6000803e3d6000fd5b505050506040513d6020811015613c6657600080fd5b50516001600160a01b0387166000908152604160205260409020549063ffffffff6135c816565b6001600160a01b0386166000908152604160205260409020555b6000604a54118015613cc357506049546001600160a01b031615155b15613d13576000613d0685610c42613cee604a54670de0b6b3a76400006135c890919063ffffffff16565b610ca089670de0b6b3a764000063ffffffff6136c816565b9050611853838287614cd7565b5050505050565b613d2385611945565b613d74576040805162461bcd60e51b815260206004820152601e60248201527f4d61726b6574206973206e6f7420696e2054726164696e672070686173650000604482015290519081900360640190fd5b6000613d86603d546110108888611d12565b90506000613d958787846148d9565b905080851115613de4576040805162461bcd60e51b81526020600482015260156024820152742737ba1032b737bab3b4103634b8bab4b234ba3c9760591b604482015290519081900360640190fd5b6000613df2888888866137e5565b603a54604080516370a0823160e01b8152336004820152905192935083926001600160a01b03909216916370a0823191602480820192602092909190829003018186803b158015613e4257600080fd5b505afa158015613e56573d6000803e3d6000fd5b505050506040513d6020811015613e6c57600080fd5b50511015613ec1576040805162461bcd60e51b815260206004820152601a60248201527f596f7520646f6e74206861766520656e6f75676820735553442e000000000000604482015290519081900360640190fd5b603a5460408051636eb1769f60e11b8152336004820152306024820152905183926001600160a01b03169163dd62ed3e916044808301926020929190829003018186803b158015613f1157600080fd5b505afa158015613f25573d6000803e3d6000fd5b505050506040513d6020811015613f3b57600080fd5b50511015613f80576040805162461bcd60e51b815260206004820152600d60248201526c27379030b63637bbb0b731b29760991b604482015290519081900360640190fd5b613f98670de0b6b3a76400008563ffffffff61378b16565b613fb486610ca084670de0b6b3a764000063ffffffff6136c816565b1115613ffb576040805162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b604482015290519081900360640190fd5b603a54614019906001600160a01b031633308463ffffffff61394f16565b6000614026898989614e46565b9050801561421157603b546040805163edc892e160e01b81526004810184905290516001600160a01b039092169163edc892e191602480820192602092909190829003018186803b15801561407a57600080fd5b505afa15801561408e573d6000803e3d6000fd5b505050506040513d60208110156140a457600080fd5b5051603a54604080516370a0823160e01b815230600482015290516001600160a01b03909216916370a0823191602480820192602092909190829003018186803b1580156140f157600080fd5b505afa158015614105573d6000803e3d6000fd5b505050506040513d602081101561411b57600080fd5b50511015614170576040805162461bcd60e51b815260206004820152601c60248201527f4e6f7420656e6f756768207355534420696e20636f6e74726163742e00000000604482015290519081900360640190fd5b886001600160a01b031663a0712d68826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156141b657600080fd5b505af11580156141ca573d6000803e3d6000fd5b5050506001600160a01b038a166000908152604160205260409020546141f791508263ffffffff61378b16565b6001600160a01b038a166000908152604160205260409020555b6000808a6001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b15801561424c57600080fd5b505afa158015614260573d6000803e3d6000fd5b505050506040513d604081101561427657600080fd5b50805160209091015190925090506000808b600181111561429357fe5b1461429e57816142a0565b825b6040805163a9059cbb60e01b8152336004820152602481018d905290519192506001600160a01b0383169163a9059cbb916044808201926020929091908290030181600087803b1580156142f357600080fd5b505af1158015614307573d6000803e3d6000fd5b505050506040513d602081101561431d57600080fd5b50506044546001600160a01b0316156143995760448054604080516302c7739b60e01b81523360048201526024810189905290516001600160a01b03909216926302c7739b92828201926000929082900301818387803b15801561438057600080fd5b505af1158015614394573d6000803e3d6000fd5b505050505b6143a48c8633614e79565b7ff3bfbc0822d1ed667a2b298e71e0304f2c1f4685398189d7c39e412f733150f4338d8d8d89603a60009054906101000a90046001600160a01b03168760405180886001600160a01b03166001600160a01b03168152602001876001600160a01b03166001600160a01b0316815260200186600181111561442157fe5b60ff1681526020810195909552506040808501939093526001600160a01b039182166060850152166080830152519081900360a0019350915050a1505050505050505050505050565b600060038211156144ad575080600160028204015b818110156144a75780915060028182858161449657fe5b04018161449f57fe5b04905061447f565b50611ae6565b8115611ae657506001919050565b60006144c682615156565b6ec097ce7bc90715b34b9f1000000000816144dd57fe5b0492915050565b303b1590565b60008060006144f98686614656565b91509150600084831161450d57600061451d565b61451d838663ffffffff6135c816565b9050600085841161454d5761454861453b878663ffffffff6135c816565b849063ffffffff61378b16565b61454f565b825b9050808210614565576000945050505050610d77565b6136bd888888868886886151e5565b6000806145818484611d12565b90506045548111158061459657506046548110155b156145a5576000915050611f0e565b600061102a670de0b6b3a7640000610ca06145cf610c7e6002603e5461372190919063ffffffff16565b603d546123f790879063ffffffff6135c816565b600080826001600160a01b03166341bc7b1f6040518163ffffffff1660e01b815260040160606040518083038186803b15801561461f57600080fd5b505afa158015614633573d6000803e3d6000fd5b505050506040513d606081101561464957600080fd5b50519050610d77816134b5565b600080600080856001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b15801561469457600080fd5b505afa1580156146a8573d6000803e3d6000fd5b505050506040513d60408110156146be57600080fd5b50805160209091015190925090506000808660018111156146db57fe5b146147575760408051634dcb776760e11b815230600482015290516001600160a01b03841691639b96eece916024808301926020929190829003018186803b15801561472657600080fd5b505afa15801561473a573d6000803e3d6000fd5b505050506040513d602081101561475057600080fd5b50516147ca565b60408051634dcb776760e11b815230600482015290516001600160a01b03851691639b96eece916024808301926020929190829003018186803b15801561479d57600080fd5b505afa1580156147b1573d6000803e3d6000fd5b505050506040513d60208110156147c757600080fd5b50515b90506000808760018111156147db57fe5b146148575760408051634dcb776760e11b815230600482015290516001600160a01b03861691639b96eece916024808301926020929190829003018186803b15801561482657600080fd5b505afa15801561483a573d6000803e3d6000fd5b505050506040513d602081101561485057600080fd5b50516148ca565b60408051634dcb776760e11b815230600482015290516001600160a01b03851691639b96eece916024808301926020929190829003018186803b15801561489d57600080fd5b505afa1580156148b1573d6000803e3d6000fd5b505050506040513d60208110156148c757600080fd5b50515b91989197509095505050505050565b6000604554821115806148ee57506046548210155b156148fb57506000610d77565b6000614907858561527e565b90506000614948670de0b6b3a7640000610ca06149306002603e5461372190919063ffffffff16565b6123f7670de0b6b3a76400008963ffffffff6135c816565b9050600061495f610c7e868463ffffffff61378b16565b9050600061497f670de0b6b3a7640000610ca0868963ffffffff6136c816565b6001600160a01b0389166000908152604160205260409020549091506149a8826110108b6145e3565b116149ba576000945050505050610d77565b6001600160a01b0388166000908152604160205260408120546149e490610c42846110108d6145e3565b9050614a12614a0584610ca084670de0b6b3a764000063ffffffff6136c816565b869063ffffffff61378b16565b9998505050505050505050565b600080614a4086610c42614a338c8c612bfa565b889063ffffffff61378b16565b90506000614a54848663ffffffff6135c816565b90506000614a88670de0b6b3a7640000610ca0614a798682878563ffffffff6136c816565b603e549063ffffffff6136c816565b90508715614ad9576000614aa382600263ffffffff61372116565b90506000614abb826123f78d8c63ffffffff6135c816565b9050614acd818c63ffffffff61372116565b95505050505050614b14565b866000614afd670de0b6b3a7640000610ca0614a798882878563ffffffff6136c816565b9050614acd6002610ca0858463ffffffff61378b16565b979650505050505050565b614b31826001600160a01b03166153fb565b614b82576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310614bc05780518252601f199092019160209182019101614ba1565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114614c22576040519150601f19603f3d011682016040523d82523d6000602084013e614c27565b606091505b509150915081614c7e576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b8051156133e057808060200190516020811015614c9a57600080fd5b50516133e05760405162461bcd60e51b815260040180806020018281038252602a81526020018061554c602a913960400191505060405180910390fd5b60495460408051639ca423b360e01b81526001600160a01b03868116600483015291516000939290921691639ca423b391602480820192602092909190829003018186803b158015614d2857600080fd5b505afa158015614d3c573d6000803e3d6000fd5b505050506040513d6020811015614d5257600080fd5b505190506001600160a01b038116156133e057604a54156133e057603a546040805163a9059cbb60e01b81526001600160a01b038481166004830152602482018790529151919092169163a9059cbb9160448083019260209291908290030181600087803b158015614dc357600080fd5b505af1158015614dd7573d6000803e3d6000fd5b505050506040513d6020811015614ded57600080fd5b5050604080516001600160a01b038084168252861660208201528082018590526060810184905290517f8fa68a6a8e2fc9ff758a6e64afba8bc2f66fb082999a2c5225c8c49633faded49181900360800190a150505050565b600080614e53858561527e565b90506000915082811015614e715761102a838263ffffffff6135c816565b509392505050565b6000614ec3614eb6614e9e604354670de0b6b3a764000061378b90919063ffffffff16565b610ca086670de0b6b3a764000063ffffffff6136c816565b849063ffffffff6135c816565b60435490915015614f5957603a546042546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018590529051919092169163a9059cbb9160448083019260209291908290030181600087803b158015614f2757600080fd5b505af1158015614f3b573d6000803e3d6000fd5b505050506040513d6020811015614f5157600080fd5b50614f5d9050565b5060005b603b546001600160a01b03166317fd849a614f7e858463ffffffff6135c816565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015614fb257600080fd5b505afa158015614fc6573d6000803e3d6000fd5b505050506040513d6020811015614fdc57600080fd5b50516001600160a01b0385166000908152604160205260409020541161501a576001600160a01b0384166000908152604160205260408120556150de565b603b546150c4906001600160a01b03166317fd849a61503f868563ffffffff6135c816565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561507357600080fd5b505afa158015615087573d6000803e3d6000fd5b505050506040513d602081101561509d57600080fd5b50516001600160a01b0386166000908152604160205260409020549063ffffffff6135c816565b6001600160a01b0385166000908152604160205260409020555b6000604a541180156150fa57506049546001600160a01b031615155b156133e057600061514961513c615124604a54670de0b6b3a764000061378b90919063ffffffff16565b610ca087670de0b6b3a764000063ffffffff6136c816565b859063ffffffff6135c816565b9050613d13838286614cd7565b60385460408051632e4c697f60e01b81526725b94542080c8000600482018190526024820185905291516000936001600160a01b031691632e4c697f916044808301926020929190829003018186803b1580156151b257600080fd5b505afa1580156151c6573d6000803e3d6000fd5b505050506040513d60208110156151dc57600080fd5b50519392505050565b60008061520685610c426151f98c8c6133e6565b899063ffffffff61378b16565b9050600061521a858563ffffffff6135c816565b9050600061523f670de0b6b3a7640000610ca0614a798682878563ffffffff6136c816565b9050861561525a576000614aa382600263ffffffff61372116565b876000614afd670de0b6b3a7640000610ca0614a798882878563ffffffff6136c816565b6000806000846001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b1580156152bb57600080fd5b505afa1580156152cf573d6000803e3d6000fd5b505050506040513d60408110156152e557600080fd5b508051602090910151909250905060008085600181111561530257fe5b1461537e5760408051634dcb776760e11b815230600482015290516001600160a01b03841691639b96eece916024808301926020929190829003018186803b15801561534d57600080fd5b505afa158015615361573d6000803e3d6000fd5b505050506040513d602081101561537757600080fd5b50516153f1565b60408051634dcb776760e11b815230600482015290516001600160a01b03851691639b96eece916024808301926020929190829003018186803b1580156153c457600080fd5b505afa1580156153d8573d6000803e3d6000fd5b505050506040513d60208110156153ee57600080fd5b50515b9695505050505050565b3b15159056fe4f6e6c792077686974656c697374656420616464726573736573206f72206f776e65722063616e206368616e676520495621596f75206d757374206265206e6f6d696e61746564206265666f726520796f752063616e20616363657074206f776e657273686970416c726561647920696e697469616c697a65642c20757365206e6f6d696e6174654e65774f776e65724f6e6c792074686520636f6e7472616374206f776e6572206d617920706572666f726d207468697320616374696f6e536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a65645468697320616374696f6e2063616e6e6f7420626520706572666f726d6564207768696c652074686520636f6e7472616374206973207061757365645361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a265627a7a7231582034e1e06e9bbdf270fd11894e3ce3fcf42b01429e9ff746b38a58496abf1592d564736f6c63430005100032

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103d05760003560e01c80638a249778116101ff578063c4dc27d71161011a578063efb1fe35116100ad578063f85de9fa1161007c578063f85de9fa14610b64578063f8debeb714610b81578063fb91d41c14610b9e578063fd8a8cc614610ba6576103d0565b8063efb1fe3514610acf578063efc1525114610afb578063f502b00314610b2a578063f598df8214610b47576103d0565b8063d3dc7539116100e9578063d3dc753914610aaf578063d69fb66814610ab7578063df8974d014610abf578063ebc7977214610ac7576103d0565b8063c4dc27d714610a11578063ca1d578e14610a37578063ce696e5114610a5d578063d162492414610a8c576103d0565b8063a996798c11610192578063bf996ae311610161578063bf996ae314610970578063c2783f9214610996578063c3b83f5f146109c5578063c402055f146109eb576103d0565b8063a996798c146108a1578063ad18f0da146108c7578063bef648db1461091e578063bf46c0b41461093b576103d0565b8063931b2040116101ce578063931b2040146108195780639324cac7146108215780639f916c9f14610829578063a8cd06e814610872576103d0565b80638a249778146107c05780638da5cb5b146107ec5780638f816652146107f457806391b4ded914610811576103d0565b806353a47bb7116102ef578063724e78da116102825780637f852582116102515780637f8525821461071f578063808226d31461074557806385170209146107625780638875eb841461077f576103d0565b8063724e78da146106bb578063741bef1a146106e157806379ba5097146106e95780637b337a36146106f1576103d0565b80636aaa81b6116102be5780636aaa81b6146106745780636cfa636e1461067c5780636e88a7bd146106845780636ed033f81461068c576103d0565b806353a47bb71461061b5780635727a0f3146106235780635c975abb146106495780635ef85b2614610651576103d0565b80631c37d04b116103675780633ce1108d116103365780633ce1108d146105a4578063481c6a75146105e557806348663e95146105ed57806351b9181f146105f5576103d0565b80631c37d04b1461050c57806321ef44c614610541578063270e13ef14610567578063316425c31461059c576103d0565b806313af4035116103a357806313af4035146104995780631627540c146104bf57806316c38b3c146104e557806319b844a614610504576103d0565b806306c933d8146103d557806306f58fe41461040f5780630d0c8ca7146104335780630f13aae814610452575b600080fd5b6103fb600480360360208110156103eb57600080fd5b50356001600160a01b0316610bae565b604080519115158252519081900360200190f35b610417610bc3565b604080516001600160a01b039092168252519081900360200190f35b6104506004803603602081101561044957600080fd5b5035610bd2565b005b6104876004803603606081101561046857600080fd5b506001600160a01b038135169060ff6020820135169060400135610c15565b60408051918252519081900360200190f35b610450600480360360208110156104af57600080fd5b50356001600160a01b0316610d7e565b610450600480360360208110156104d557600080fd5b50356001600160a01b0316610e91565b610450600480360360208110156104fb57600080fd5b50351515610eed565b610487610f67565b6104876004803603606081101561052257600080fd5b506001600160a01b038135169060ff6020820135169060400135610f6d565b6104506004803603602081101561055757600080fd5b50356001600160a01b0316610f9b565b6104876004803603606081101561057d57600080fd5b506001600160a01b038135169060ff6020820135169060400135610ffd565b610487611033565b610450600480360360a08110156105ba57600080fd5b506001600160a01b038135169060ff6020820135169060408101359060608101359060800135611039565b61041761185b565b61041761186a565b6104506004803603602081101561060b57600080fd5b50356001600160a01b0316611879565b610417611936565b6103fb6004803603602081101561063957600080fd5b50356001600160a01b0316611945565b6103fb611aeb565b6104506004803603604081101561066757600080fd5b5080359060200135611af4565b610487611cf7565b610417611cfd565b610487611d0c565b610487600480360360408110156106a257600080fd5b5080356001600160a01b0316906020013560ff16611d12565b610450600480360360208110156106d157600080fd5b50356001600160a01b0316611f14565b610417611f70565b610450611f7f565b6104506004803603604081101561070757600080fd5b506001600160a01b038135169060200135151561203b565b6104506004803603602081101561073557600080fd5b50356001600160a01b031661206e565b6104506004803603602081101561075b57600080fd5b50356120ca565b6104506004803603602081101561077857600080fd5b503561210d565b610450600480360360a081101561079557600080fd5b506001600160a01b038135169060ff602082013516906040810135906060810135906080013561214e565b610450600480360360408110156107d657600080fd5b506001600160a01b0381351690602001356121fe565b61041761222c565b6104506004803603602081101561080a57600080fd5b503561223b565b61048761227e565b610487612284565b61041761228a565b610450600480360360c081101561083f57600080fd5b506001600160a01b03813581169160ff6020820135169160408201359160608101359160808201359160a0013516612299565b6104876004803603608081101561088857600080fd5b50803590602081013590604081013590606001356123be565b610450600480360360208110156108b757600080fd5b50356001600160a01b031661281e565b61045060048036036101008110156108de57600080fd5b506001600160a01b038135811691602081013582169160408201358116916060810135916080820135169060a08101359060c08101359060e001356128f5565b6104506004803603602081101561093457600080fd5b5035612a0d565b6104876004803603606081101561095157600080fd5b506001600160a01b038135169060ff6020820135169060400135612a50565b6104506004803603602081101561098657600080fd5b50356001600160a01b0316612a81565b610487600480360360408110156109ac57600080fd5b5080356001600160a01b0316906020013560ff16612bfa565b610450600480360360208110156109db57600080fd5b50356001600160a01b0316612e62565b61045060048036036020811015610a0157600080fd5b50356001600160a01b0316612f81565b61048760048036036020811015610a2757600080fd5b50356001600160a01b0316612fdd565b6103fb60048036036020811015610a4d57600080fd5b50356001600160a01b0316612fef565b61048760048036036040811015610a7357600080fd5b5080356001600160a01b0316906020013560ff1661326b565b61045060048036036040811015610aa257600080fd5b5080359060200135613281565b6104176132da565b6104876132e9565b6104876132ef565b6104506132f5565b61045060048036036040811015610ae557600080fd5b506001600160a01b038135169060200135613358565b61048760048036036040811015610b1157600080fd5b5080356001600160a01b0316906020013560ff166133e6565b61048760048036036020811015610b4057600080fd5b503561341d565b61045060048036036020811015610b5d57600080fd5b503561342f565b61045060048036036020811015610b7a57600080fd5b5035613472565b61048760048036036020811015610b9757600080fd5b50356134b5565b610487613568565b61041761356e565b60486020526000908152604090205460ff1681565b604b546001600160a01b031681565b610bda61357d565b60458190556040805182815290517fa36b45ea010fcb2e70d33048b7d3f27c265915709bf40fc7e239459424933d9f9181900360200190a150565b6000610c218484612bfa565b821115610c3057506000610d77565b6000610c4e603d54610c428787611d12565b9063ffffffff6135c816565b90506000610cb9670de0b6b3a7640000610ca0610cac670de0b6b3a7640000610ca0610c93610c7e8d8d8d613625565b670de0b6b3a76400009063ffffffff6135c816565b889063ffffffff6136c816565b9063ffffffff61372116565b879063ffffffff6136c816565b90506000610cf6670de0b6b3a7640000610ca0610ce9604354670de0b6b3a76400006135c890919063ffffffff16565b859063ffffffff6136c816565b603b546040805163edc892e160e01b81526004810184905290519293506001600160a01b039091169163edc892e191602480820192602092909190829003018186803b158015610d4557600080fd5b505afa158015610d59573d6000803e3d6000fd5b505050506040513d6020811015610d6f57600080fd5b505193505050505b9392505050565b6001600160a01b038116610dd9576040805162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f74206265203000000000000000604482015290519081900360640190fd5b600154600160a01b900460ff1615610e225760405162461bcd60e51b81526004018080602001828103825260298152602001806154696029913960400191505060405180910390fd5b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b03199091168117825560408051928352602083019190915280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150565b610e9961357d565b600180546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229181900360200190a150565b610ef561357d565b60035460ff1615158115151415610f0b57610f64565b6003805460ff1916821515179081905560ff1615610f2857426002555b6003546040805160ff90921615158252517f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec59181900360200190a15b50565b60465481565b6000610f798484612bfa565b821115610f8857506000610d77565b610f93848484613625565b949350505050565b610fa361357d565b603a80546001600160a01b0319166001600160a01b03838116919091179182905560408051929091168252517f74a8764fc8d62d2d844c8c54426bd94ad034e0e92abdf5280ff75e2cbd678fb6916020908290030190a150565b60008061101c603d546110108787611d12565b9063ffffffff61378b16565b905061102a858585846137e5565b95945050505050565b603e5481565b600480546001019081905560035460ff16156110865760405162461bcd60e51b815260040180806020018281038252603c815260200180615510603c913960400191505060405180910390fd5b61108f86611945565b6110e0576040805162461bcd60e51b815260206004820152601e60248201527f4d61726b6574206973206e6f7420696e2054726164696e672070686173650000604482015290519081900360640190fd5b60006110ec8787612bfa565b90506000811180156110fe5750808511155b611147576040805162461bcd60e51b81526020600482015260156024820152742737ba1032b737bab3b4103634b8bab4b234ba3c9760591b604482015290519081900360640190fd5b6000611154888888610c15565b905061116e670de0b6b3a76400008563ffffffff61378b16565b61118a82610ca088670de0b6b3a764000063ffffffff6136c816565b11156111d1576040805162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b604482015290519081900360640190fd5b600080896001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b15801561120c57600080fd5b505afa158015611220573d6000803e3d6000fd5b505050506040513d604081101561123657600080fd5b50805160209091015190925090506000808a600181111561125357fe5b1461125e5781611260565b825b60408051634dcb776760e11b815233600482015290519192508a916001600160a01b03841691639b96eece916024808301926020929190829003018186803b1580156112ab57600080fd5b505afa1580156112bf573d6000803e3d6000fd5b505050506040513d60208110156112d557600080fd5b5051101561132a576040805162461bcd60e51b815260206004820152601d60248201527f596f7520646f6e74206861766520656e6f756768206f7074696f6e732e000000604482015290519081900360640190fd5b60408051636eb1769f60e11b815233600482015230602482015290518a916001600160a01b0384169163dd62ed3e91604480820192602092909190829003018186803b15801561137957600080fd5b505afa15801561138d573d6000803e3d6000fd5b505050506040513d60208110156113a357600080fd5b505110156113e8576040805162461bcd60e51b815260206004820152600d60248201526c27379030b63637bbb0b731b29760991b604482015290519081900360640190fd5b6114036001600160a01b03821633308c63ffffffff61394f16565b6000603b60009054906101000a90046001600160a01b03166001600160a01b031663edc892e18d6001600160a01b031663a76df708306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561147f57600080fd5b505afa158015611493573d6000803e3d6000fd5b505050506040513d60208110156114a957600080fd5b5051604080516001600160e01b031960e085901b1681526004810192909252516024808301926020929190829003018186803b1580156114e857600080fd5b505afa1580156114fc573d6000803e3d6000fd5b505050506040513d602081101561151257600080fd5b505190508015611570578b6001600160a01b031663f5571beb6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561155757600080fd5b505af115801561156b573d6000803e3d6000fd5b505050505b603a54604080516370a0823160e01b8152306004820152905187926001600160a01b0316916370a08231916024808301926020929190829003018186803b1580156115ba57600080fd5b505afa1580156115ce573d6000803e3d6000fd5b505050506040513d60208110156115e457600080fd5b50511015611639576040805162461bcd60e51b815260206004820152601c60248201527f4e6f7420656e6f756768207355534420696e20636f6e74726163742e00000000604482015290519081900360640190fd5b603a546040805163a9059cbb60e01b81523360048201526024810188905290516001600160a01b039092169163a9059cbb916044808201926020929091908290030181600087803b15801561168d57600080fd5b505af11580156116a1573d6000803e3d6000fd5b505050506040513d60208110156116b757600080fd5b50506044546001600160a01b0316156117335760448054604080516302c7739b60e01b81523360048201526024810189905290516001600160a01b03909216926302c7739b92828201926000929082900301818387803b15801561171a57600080fd5b505af115801561172e573d6000803e3d6000fd5b505050505b61173f8c8683336139a9565b7f1d6ff70c632edb1e6aba7fbc0148db68c8392e30f9dfaadae2543a2543757cf6338d8d8d89603a60009054906101000a90046001600160a01b03168860405180886001600160a01b03166001600160a01b03168152602001876001600160a01b03166001600160a01b031681526020018660018111156117bc57fe5b60ff1681526020810195909552506040808501939093526001600160a01b039182166060850152166080830152519081900360a0019350915050a15050505050506004548114611853576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b505050505050565b603b546001600160a01b031681565b6042546001600160a01b031681565b61188281612fef565b6118cc576040805162461bcd60e51b81526020600482015260166024820152754e6f206f7074696f6e7320746f20657865726369736560501b604482015290519081900360640190fd5b806001600160a01b031663851492586040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561190757600080fd5b505af115801561191b573d6000803e3d6000fd5b505050506040513d602081101561193157600080fd5b505050565b6001546001600160a01b031681565b603b5460408051633761c52760e11b81526001600160a01b03848116600483015291516000939290921691636ec38a4e91602480820192602092909190829003018186803b15801561199657600080fd5b505afa1580156119aa573d6000803e3d6000fd5b505050506040513d60208110156119c057600080fd5b505115611ae25760008290506000816001600160a01b03166341bc7b1f6040518163ffffffff1660e01b815260040160606040518083038186803b158015611a0757600080fd5b505afa158015611a1b573d6000803e3d6000fd5b505050506040513d6060811015611a3157600080fd5b50516000818152603f6020526040902054909150611a5457600092505050611ae6565b6000826001600160a01b0316639e3b34bf6040518163ffffffff1660e01b8152600401604080518083038186803b158015611a8e57600080fd5b505afa158015611aa2573d6000803e3d6000fd5b505050506040513d6040811015611ab857600080fd5b5051905042811015611ad05760009350505050611ae6565b60405442909103119250611ae6915050565b5060005b919050565b60035460ff1681565b3360009081526048602052604090205460ff1680611b1c57506000546001600160a01b031633145b611b575760405162461bcd60e51b81526004018080602001828103825260328152602001806154026032913960400191505060405180910390fd5b611b70670de0b6b3a7640000603c63ffffffff6136c816565b81118015611b965750611b93670de0b6b3a764000061012c63ffffffff6136c816565b81105b611be7576040805162461bcd60e51b815260206004820152601960248201527f4956206f757473696465206d696e2f6d61782072616e67652100000000000000604482015290519081900360640190fd5b603954604080516315905ec160e31b81526004810185905290516001600160a01b039092169163ac82f60891602480820192602092909190829003018186803b158015611c3357600080fd5b505afa158015611c47573d6000803e3d6000fd5b505050506040513d6020811015611c5d57600080fd5b5051611ca6576040805162461bcd60e51b8152602060048201526013602482015272417373657420686173206e6f2070726963652160681b604482015290519081900360640190fd5b6000828152603f6020908152604091829020839055815184815290810183905281517f715e0a52c0b74c77d2d2012a363ac95b494302ad2abb78ac7406ec93451f1adb929181900390910190a15050565b603d5481565b6038546001600160a01b031681565b604a5481565b6000611d1d83611945565b15611f0a5760008390506000816001600160a01b0316639e3b34bf6040518163ffffffff1660e01b8152600401604080518083038186803b158015611d6157600080fd5b505afa158015611d75573d6000803e3d6000fd5b505050506040513d6040811015611d8b57600080fd5b505190504281036000611db362015180610ca084670de0b6b3a764000063ffffffff6136c816565b90506000846001600160a01b031663668aa8246040518163ffffffff1660e01b815260040160206040518083038186803b158015611df057600080fd5b505afa158015611e04573d6000803e3d6000fd5b505050506040513d6020811015611e1a57600080fd5b5051604080516341bc7b1f60e01b8152905191925060009182916001600160a01b038916916341bc7b1f91600480820192606092909190829003018186803b158015611e6557600080fd5b505afa158015611e79573d6000803e3d6000fd5b505050506040513d6060811015611e8f57600080fd5b50805160209091015190925090506000896001811115611eab57fe5b1415611ee357611ed56064610ca0858488603f6000898152602001908152602001600020546123be565b975050505050505050611f0e565b611ed5610c7e6064610ca0868589603f60008a8152602001908152602001600020546123be565b5060005b92915050565b611f1c61357d565b603980546001600160a01b0383166001600160a01b0319909116811790915560408051918252517ff724a45d041687842411f2b977ef22ab8f43c8f1104f4592b42a00f9b34a643d9181900360200190a150565b6039546001600160a01b031681565b6001546001600160a01b03163314611fc85760405162461bcd60e51b81526004018080602001828103825260358152602001806154346035913960400191505060405180910390fd5b600054600154604080516001600160a01b03938416815292909116602083015280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b61204361357d565b6001600160a01b03919091166000908152604860205260409020805460ff1916911515919091179055565b61207661357d565b604480546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f475a2179b9b6a155e7ba3f46a461beb6798279b265026364944e38dcb4bacafe9181900360200190a150565b6120d261357d565b60468190556040805182815290517fafb2511d843afc28083da6c608f400d0262aef83b3cd885abd205b3e64f544bb9181900360200190a150565b61211561357d565b6040818155805182815290517fdc469b5583fa9b7ebd3245e1665334cd758c4bef4c5a132c62baca85effacfec9181900360200190a150565b600480546001019081905560035460ff161561219b5760405162461bcd60e51b815260040180806020018281038252603c815260200180615510603c913960400191505060405180910390fd5b6121a88686868686613d1a565b6004548114611853576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b61220661357d565b604980546001600160a01b0319166001600160a01b039390931692909217909155604a55565b6000546001600160a01b031681565b61224361357d565b60438190556040805182815290517f01edd423db862fb00774918e3b06d9c1dd3db9a99b5a194c439d2f141876f4449181900360200190a150565b60025481565b60455481565b603a546001600160a01b031681565b600480546001019081905560035460ff16156122e65760405162461bcd60e51b815260040180806020018281038252603c815260200180615510603c913960400191505060405180910390fd5b6049546040805163bbddaca360e01b81526001600160a01b0385811660048301523360248301529151919092169163bbddaca391604480830192600092919082900301818387803b15801561233a57600080fd5b505af115801561234e573d6000803e3d6000fd5b5050505061235f8787878787613d1a565b60045481146123b5576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b50505050505050565b600080612403633b9aca00610ca06123e66123e18861016d63ffffffff61372116565b61446a565b6123f787606463ffffffff61372116565b9063ffffffff6136c816565b9050858510156000816124315761242c87610ca08a670de0b6b3a764000063ffffffff6136c816565b61244d565b61244d88610ca089670de0b6b3a764000063ffffffff6136c816565b603854604080516344f7787f60e01b8152600481018490526063602482015290519293506000926124ef928792610ca092670de0b6b3a7640000926001600160a01b0316916344f7787f916044808301926020929190829003018186803b1580156124b757600080fd5b505afa1580156124cb573d6000803e3d6000fd5b505050506040513d60208110156124e157600080fd5b50519063ffffffff6136c816565b9050600061254061252861251362989680610ca0866223588363ffffffff6136c816565b670de0b6b3a76400009063ffffffff61378b16565b610ca0670de0b6b3a76400008063ffffffff6136c816565b90506000612563670de0b6b3a7640000610ca0600281878063ffffffff6136c816565b9050600061257e62989680610ca0623cdfaf6123f7866144bb565b60385460408051632e4c697f60e01b815260048101879052674563918244f40000602482015290519293506000926125ed92620f424092610ca09262144c62926001600160a01b031691632e4c697f916044808301926020929190829003018186803b1580156124b757600080fd5b60385460408051632e4c697f60e01b815260048101889052673782dace9d9000006024820152905192935060009261265c92620f424092610ca092621bca48926001600160a01b031691632e4c697f916044808301926020929190829003018186803b1580156124b757600080fd5b60385460408051632e4c697f60e01b8152600481018990526729a2241af62c0000602482015290519293506000926126cb92620f424092610ca092621b2ee6926001600160a01b031691632e4c697f916044808301926020929190829003018186803b1580156124b757600080fd5b60385460408051632e4c697f60e01b8152600481018a9052671bc16d674ec800006024820152905192935060009261273a92620f424092610ca092620570ba926001600160a01b031691632e4c697f916044808301926020929190829003018186803b1580156124b757600080fd5b9050600061275862989680610ca08a6230bbd763ffffffff6136c816565b9050600061277683610c428781866110108c8b63ffffffff61378b16565b90506000612799610c7e670de0b6b3a7640000610ca08b8663ffffffff6136c816565b905060006127ca6127b183606463ffffffff6136c816565b610c42670de0b6b3a7640000606463ffffffff6136c816565b90508d156127e8579e50610f939d5050505050505050505050505050565b61280581610c42670de0b6b3a7640000606463ffffffff6136c816565b9f50505050505050505050505050505050949350505050565b61282661357d565b603a54604080516370a0823160e01b815230600482015290516001600160a01b039092169163a9059cbb91849184916370a08231916024808301926020929190829003018186803b15801561287a57600080fd5b505afa15801561288e573d6000803e3d6000fd5b505050506040513d60208110156128a457600080fd5b5051604080516001600160e01b031960e086901b1681526001600160a01b03909316600484015260248301919091525160448083019260209291908290030181600087803b15801561190757600080fd5b60055462010000900460ff168061290f575061290f6144e4565b806129225750600554610100900460ff16155b61295d5760405162461bcd60e51b815260040180806020018281038252602e8152602001806154e2602e913960400191505060405180910390fd5b60055462010000900460ff1615801561298d576005805461ff001962ff0000199091166201000017166101001790555b61299689610d7e565b61299e6132f5565b603980546001600160a01b03808b166001600160a01b031992831617909255603a80548a8416908316179055603c8890556038805492881692909116919091179055603d849055603e83905560408290558015612a02576005805462ff0000191690555b505050505050505050565b612a1561357d565b603e8190556040805182815290517f4af6d03c4624e0a6b868b8f6453e047f23f3ea15e9d08c938bd4c445d7ef19b39181900360200190a150565b60006001821080612a695750612a6684846133e6565b82115b15612a7657506000610d77565b610f938484846144ea565b612a8961357d565b603b546001600160a01b031615612b2257603a54603b546040805163095ea7b360e01b81526001600160a01b0392831660048201526000602482018190529151929093169263095ea7b39260448083019360209383900390910190829087803b158015612af557600080fd5b505af1158015612b09573d6000803e3d6000fd5b505050506040513d6020811015612b1f57600080fd5b50505b603b80546001600160a01b0319166001600160a01b038381169190911791829055603a546040805163095ea7b360e01b8152938316600485015260001960248501525191169163095ea7b39160448083019260209291908290030181600087803b158015612b8f57600080fd5b505af1158015612ba3573d6000803e3d6000fd5b505050506040513d6020811015612bb957600080fd5b5050604080516001600160a01b038316815290517f9987372437ace1af79923f26b948aa04afef92b2b7786144c5aae621ea84eb0a9181900360200190a150565b6000612c0583611945565b15611f0a576000612c168484614574565b905080612c27576000915050611f0e565b600080856001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b158015612c6257600080fd5b505afa158015612c76573d6000803e3d6000fd5b505050506040513d6040811015612c8c57600080fd5b5080516020909101519092509050600080866001811115612ca957fe5b14612d255760408051634dcb776760e11b815230600482015290516001600160a01b03851691639b96eece916024808301926020929190829003018186803b158015612cf457600080fd5b505afa158015612d08573d6000803e3d6000fd5b505050506040513d6020811015612d1e57600080fd5b5051612d98565b60408051634dcb776760e11b815230600482015290516001600160a01b03841691639b96eece916024808301926020929190829003018186803b158015612d6b57600080fd5b505afa158015612d7f573d6000803e3d6000fd5b505050506040513d6020811015612d9557600080fd5b50515b90506000612db8670de0b6b3a7640000610ca0848863ffffffff6136c816565b6001600160a01b038916600090815260416020526040902054909150612de4908263ffffffff61378b16565b612df1836110108b6145e3565b1015612e0557600095505050505050611f0e565b6001600160a01b038816600090815260416020526040812054612e33908390610c429081876110108f6145e3565b9050612e5583611010670de0b6b3a76400006123f7858b63ffffffff61372116565b9650505050505050611f0e565b612e6a61357d565b6001600160a01b038116612eb7576040805162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b604482015290519081900360640190fd5b600154600160a81b900460ff1615612f0c576040805162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b604482015290519081900360640190fd5b600080546001600160a01b038084166001600160a01b03199092168217928390556001805460ff60a81b1916600160a81b17905560408051939091168352602083019190915280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150565b612f8961357d565b604280546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f49a4e3b1ee2ad2bfb1455dfad2d72e84e71a35c756d2c8296f305f12e1828bf59181900360200190a150565b60416020526000908152604090205481565b603b546040805163e62b888960e01b81526001600160a01b0384811660048301529151600093929092169163e62b888991602480820192602092909190829003018186803b15801561304057600080fd5b505afa158015613054573d6000803e3d6000fd5b505050506040513d602081101561306a57600080fd5b505180156130e757506001826001600160a01b031663b1c9fe6e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156130ae57600080fd5b505afa1580156130c2573d6000803e3d6000fd5b505050506040513d60208110156130d857600080fd5b505160028111156130e557fe5b145b15611ae257600080836001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b15801561312757600080fd5b505afa15801561313b573d6000803e3d6000fd5b505050506040513d604081101561315157600080fd5b50805160209182015160408051634dcb776760e11b815230600482015290519295509093506000926001600160a01b03861692639b96eece926024808201939291829003018186803b1580156131a657600080fd5b505afa1580156131ba573d6000803e3d6000fd5b505050506040513d60208110156131d057600080fd5b50511180613251575060408051634dcb776760e11b815230600482015290516000916001600160a01b03841691639b96eece91602480820192602092909190829003018186803b15801561322357600080fd5b505afa158015613237573d6000803e3d6000fd5b505050506040513d602081101561324d57600080fd5b5051115b1561326157600192505050611ae6565b5050506000919050565b6000806132788484614656565b50949350505050565b61328961357d565b600082815260476020908152604091829020839055815184815290810183905281517f5af395595015797b4d0f26b77c38dd4831298dabc7906ee3d62b80fa75d35c1e929181900390910190a15050565b6049546001600160a01b031681565b60435481565b60405481565b60055460ff1615613343576040805162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b6005805460ff19166001908117909155600455565b61336061357d565b603a546040805163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529151919092169163a9059cbb9160448083019260209291908290030181600087803b1580156133b657600080fd5b505af11580156133ca573d6000803e3d6000fd5b505050506040513d60208110156133e057600080fd5b50505050565b60006133f183611945565b15611f0a576000613408603d546110108686611d12565b90506134158484836148d9565b915050611f0e565b603f6020526000908152604090205481565b61343761357d565b603c8190556040805182815290517fcc72495e91bfc45ec11465a752fc2866b8a5ef8960b0925774ede927ee7cbdbe9181900360200190a150565b61347a61357d565b603d8190556040805182815290517f1e6a338a58debcc786781a079c4459466b102ad0156cc84f51d25ef7dd8cb9b09181900360200190a150565b603954604080516315905ec160e31b81526004810184905290516000926001600160a01b03169163ac82f608916024808301926020929190829003018186803b15801561350157600080fd5b505afa158015613515573d6000803e3d6000fd5b505050506040513d602081101561352b57600080fd5b505161353957506000611ae6565b6000828152604760205260409020546135555750603c54611ae6565b5060009081526047602052604090205490565b603c5481565b6044546001600160a01b031681565b6000546001600160a01b031633146135c65760405162461bcd60e51b815260040180806020018281038252602f815260200180615492602f913960400191505060405180910390fd5b565b60008282111561361f576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60008060006136348686614656565b9150915060008083116136655784821161365d57613658858363ffffffff6135c816565b613660565b60005b613675565b613675838663ffffffff61378b16565b90506000858311613687576000613697565b613697838763ffffffff6135c816565b9050808210156136ae576000945050505050610d77565b6136bd88888886888688614a1f565b945050505050610d77565b6000826136d757506000611f0e565b828202828482816136e457fe5b0414610d775760405162461bcd60e51b81526004018080602001828103825260218152602001806154c16021913960400191505060405180910390fd5b6000808211613777576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b600082848161378257fe5b04949350505050565b600082820183811015610d77576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600060018310806137ff57506137fc8585846148d9565b83115b1561380c57506000610f93565b6000613840670de0b6b3a7640000610ca06138288989896144ea565b6123f7670de0b6b3a76400008863ffffffff6135c816565b9050613875670de0b6b3a7640000610ca06138688266470de4df82000063ffffffff61378b16565b849063ffffffff6136c816565b90506000613898670de0b6b3a7640000610ca0610c93878663ffffffff61378b16565b905060006138c8670de0b6b3a7640000610ca0610ce9604354670de0b6b3a764000061378b90919063ffffffff16565b603b546040805163edc892e160e01b81526004810184905290519293506001600160a01b039091169163edc892e191602480820192602092909190829003018186803b15801561391757600080fd5b505afa15801561392b573d6000803e3d6000fd5b505050506040513d602081101561394157600080fd5b505198975050505050505050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526133e0908590614b1f565b60006139e784610c426139cf604354670de0b6b3a76400006135c890919063ffffffff16565b610ca088670de0b6b3a764000063ffffffff6136c816565b60435490915015613a7d57603a546042546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018590529051919092169163a9059cbb9160448083019260209291908290030181600087803b158015613a4b57600080fd5b505af1158015613a5f573d6000803e3d6000fd5b505050506040513d6020811015613a7557600080fd5b50613a819050565b5060005b603b54613b2b906001600160a01b03166317fd849a613aa6878563ffffffff61378b16565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015613ada57600080fd5b505afa158015613aee573d6000803e3d6000fd5b505050506040513d6020811015613b0457600080fd5b50516001600160a01b0387166000908152604160205260409020549063ffffffff61378b16565b6001600160a01b0380871660009081526041602090815260409182902093909355603b548151630bfec24d60e11b81526004810188905291519216926317fd849a92602480840193829003018186803b158015613b8757600080fd5b505afa158015613b9b573d6000803e3d6000fd5b505050506040513d6020811015613bb157600080fd5b50516001600160a01b03861660009081526041602052604090205411613bef576001600160a01b038516600090815260416020526040812055613ca7565b603b5460408051630bfec24d60e11b8152600481018690529051613c8d926001600160a01b0316916317fd849a916024808301926020929190829003018186803b158015613c3c57600080fd5b505afa158015613c50573d6000803e3d6000fd5b505050506040513d6020811015613c6657600080fd5b50516001600160a01b0387166000908152604160205260409020549063ffffffff6135c816565b6001600160a01b0386166000908152604160205260409020555b6000604a54118015613cc357506049546001600160a01b031615155b15613d13576000613d0685610c42613cee604a54670de0b6b3a76400006135c890919063ffffffff16565b610ca089670de0b6b3a764000063ffffffff6136c816565b9050611853838287614cd7565b5050505050565b613d2385611945565b613d74576040805162461bcd60e51b815260206004820152601e60248201527f4d61726b6574206973206e6f7420696e2054726164696e672070686173650000604482015290519081900360640190fd5b6000613d86603d546110108888611d12565b90506000613d958787846148d9565b905080851115613de4576040805162461bcd60e51b81526020600482015260156024820152742737ba1032b737bab3b4103634b8bab4b234ba3c9760591b604482015290519081900360640190fd5b6000613df2888888866137e5565b603a54604080516370a0823160e01b8152336004820152905192935083926001600160a01b03909216916370a0823191602480820192602092909190829003018186803b158015613e4257600080fd5b505afa158015613e56573d6000803e3d6000fd5b505050506040513d6020811015613e6c57600080fd5b50511015613ec1576040805162461bcd60e51b815260206004820152601a60248201527f596f7520646f6e74206861766520656e6f75676820735553442e000000000000604482015290519081900360640190fd5b603a5460408051636eb1769f60e11b8152336004820152306024820152905183926001600160a01b03169163dd62ed3e916044808301926020929190829003018186803b158015613f1157600080fd5b505afa158015613f25573d6000803e3d6000fd5b505050506040513d6020811015613f3b57600080fd5b50511015613f80576040805162461bcd60e51b815260206004820152600d60248201526c27379030b63637bbb0b731b29760991b604482015290519081900360640190fd5b613f98670de0b6b3a76400008563ffffffff61378b16565b613fb486610ca084670de0b6b3a764000063ffffffff6136c816565b1115613ffb576040805162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b604482015290519081900360640190fd5b603a54614019906001600160a01b031633308463ffffffff61394f16565b6000614026898989614e46565b9050801561421157603b546040805163edc892e160e01b81526004810184905290516001600160a01b039092169163edc892e191602480820192602092909190829003018186803b15801561407a57600080fd5b505afa15801561408e573d6000803e3d6000fd5b505050506040513d60208110156140a457600080fd5b5051603a54604080516370a0823160e01b815230600482015290516001600160a01b03909216916370a0823191602480820192602092909190829003018186803b1580156140f157600080fd5b505afa158015614105573d6000803e3d6000fd5b505050506040513d602081101561411b57600080fd5b50511015614170576040805162461bcd60e51b815260206004820152601c60248201527f4e6f7420656e6f756768207355534420696e20636f6e74726163742e00000000604482015290519081900360640190fd5b886001600160a01b031663a0712d68826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156141b657600080fd5b505af11580156141ca573d6000803e3d6000fd5b5050506001600160a01b038a166000908152604160205260409020546141f791508263ffffffff61378b16565b6001600160a01b038a166000908152604160205260409020555b6000808a6001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b15801561424c57600080fd5b505afa158015614260573d6000803e3d6000fd5b505050506040513d604081101561427657600080fd5b50805160209091015190925090506000808b600181111561429357fe5b1461429e57816142a0565b825b6040805163a9059cbb60e01b8152336004820152602481018d905290519192506001600160a01b0383169163a9059cbb916044808201926020929091908290030181600087803b1580156142f357600080fd5b505af1158015614307573d6000803e3d6000fd5b505050506040513d602081101561431d57600080fd5b50506044546001600160a01b0316156143995760448054604080516302c7739b60e01b81523360048201526024810189905290516001600160a01b03909216926302c7739b92828201926000929082900301818387803b15801561438057600080fd5b505af1158015614394573d6000803e3d6000fd5b505050505b6143a48c8633614e79565b7ff3bfbc0822d1ed667a2b298e71e0304f2c1f4685398189d7c39e412f733150f4338d8d8d89603a60009054906101000a90046001600160a01b03168760405180886001600160a01b03166001600160a01b03168152602001876001600160a01b03166001600160a01b0316815260200186600181111561442157fe5b60ff1681526020810195909552506040808501939093526001600160a01b039182166060850152166080830152519081900360a0019350915050a1505050505050505050505050565b600060038211156144ad575080600160028204015b818110156144a75780915060028182858161449657fe5b04018161449f57fe5b04905061447f565b50611ae6565b8115611ae657506001919050565b60006144c682615156565b6ec097ce7bc90715b34b9f1000000000816144dd57fe5b0492915050565b303b1590565b60008060006144f98686614656565b91509150600084831161450d57600061451d565b61451d838663ffffffff6135c816565b9050600085841161454d5761454861453b878663ffffffff6135c816565b849063ffffffff61378b16565b61454f565b825b9050808210614565576000945050505050610d77565b6136bd888888868886886151e5565b6000806145818484611d12565b90506045548111158061459657506046548110155b156145a5576000915050611f0e565b600061102a670de0b6b3a7640000610ca06145cf610c7e6002603e5461372190919063ffffffff16565b603d546123f790879063ffffffff6135c816565b600080826001600160a01b03166341bc7b1f6040518163ffffffff1660e01b815260040160606040518083038186803b15801561461f57600080fd5b505afa158015614633573d6000803e3d6000fd5b505050506040513d606081101561464957600080fd5b50519050610d77816134b5565b600080600080856001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b15801561469457600080fd5b505afa1580156146a8573d6000803e3d6000fd5b505050506040513d60408110156146be57600080fd5b50805160209091015190925090506000808660018111156146db57fe5b146147575760408051634dcb776760e11b815230600482015290516001600160a01b03841691639b96eece916024808301926020929190829003018186803b15801561472657600080fd5b505afa15801561473a573d6000803e3d6000fd5b505050506040513d602081101561475057600080fd5b50516147ca565b60408051634dcb776760e11b815230600482015290516001600160a01b03851691639b96eece916024808301926020929190829003018186803b15801561479d57600080fd5b505afa1580156147b1573d6000803e3d6000fd5b505050506040513d60208110156147c757600080fd5b50515b90506000808760018111156147db57fe5b146148575760408051634dcb776760e11b815230600482015290516001600160a01b03861691639b96eece916024808301926020929190829003018186803b15801561482657600080fd5b505afa15801561483a573d6000803e3d6000fd5b505050506040513d602081101561485057600080fd5b50516148ca565b60408051634dcb776760e11b815230600482015290516001600160a01b03851691639b96eece916024808301926020929190829003018186803b15801561489d57600080fd5b505afa1580156148b1573d6000803e3d6000fd5b505050506040513d60208110156148c757600080fd5b50515b91989197509095505050505050565b6000604554821115806148ee57506046548210155b156148fb57506000610d77565b6000614907858561527e565b90506000614948670de0b6b3a7640000610ca06149306002603e5461372190919063ffffffff16565b6123f7670de0b6b3a76400008963ffffffff6135c816565b9050600061495f610c7e868463ffffffff61378b16565b9050600061497f670de0b6b3a7640000610ca0868963ffffffff6136c816565b6001600160a01b0389166000908152604160205260409020549091506149a8826110108b6145e3565b116149ba576000945050505050610d77565b6001600160a01b0388166000908152604160205260408120546149e490610c42846110108d6145e3565b9050614a12614a0584610ca084670de0b6b3a764000063ffffffff6136c816565b869063ffffffff61378b16565b9998505050505050505050565b600080614a4086610c42614a338c8c612bfa565b889063ffffffff61378b16565b90506000614a54848663ffffffff6135c816565b90506000614a88670de0b6b3a7640000610ca0614a798682878563ffffffff6136c816565b603e549063ffffffff6136c816565b90508715614ad9576000614aa382600263ffffffff61372116565b90506000614abb826123f78d8c63ffffffff6135c816565b9050614acd818c63ffffffff61372116565b95505050505050614b14565b866000614afd670de0b6b3a7640000610ca0614a798882878563ffffffff6136c816565b9050614acd6002610ca0858463ffffffff61378b16565b979650505050505050565b614b31826001600160a01b03166153fb565b614b82576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310614bc05780518252601f199092019160209182019101614ba1565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114614c22576040519150601f19603f3d011682016040523d82523d6000602084013e614c27565b606091505b509150915081614c7e576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b8051156133e057808060200190516020811015614c9a57600080fd5b50516133e05760405162461bcd60e51b815260040180806020018281038252602a81526020018061554c602a913960400191505060405180910390fd5b60495460408051639ca423b360e01b81526001600160a01b03868116600483015291516000939290921691639ca423b391602480820192602092909190829003018186803b158015614d2857600080fd5b505afa158015614d3c573d6000803e3d6000fd5b505050506040513d6020811015614d5257600080fd5b505190506001600160a01b038116156133e057604a54156133e057603a546040805163a9059cbb60e01b81526001600160a01b038481166004830152602482018790529151919092169163a9059cbb9160448083019260209291908290030181600087803b158015614dc357600080fd5b505af1158015614dd7573d6000803e3d6000fd5b505050506040513d6020811015614ded57600080fd5b5050604080516001600160a01b038084168252861660208201528082018590526060810184905290517f8fa68a6a8e2fc9ff758a6e64afba8bc2f66fb082999a2c5225c8c49633faded49181900360800190a150505050565b600080614e53858561527e565b90506000915082811015614e715761102a838263ffffffff6135c816565b509392505050565b6000614ec3614eb6614e9e604354670de0b6b3a764000061378b90919063ffffffff16565b610ca086670de0b6b3a764000063ffffffff6136c816565b849063ffffffff6135c816565b60435490915015614f5957603a546042546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018590529051919092169163a9059cbb9160448083019260209291908290030181600087803b158015614f2757600080fd5b505af1158015614f3b573d6000803e3d6000fd5b505050506040513d6020811015614f5157600080fd5b50614f5d9050565b5060005b603b546001600160a01b03166317fd849a614f7e858463ffffffff6135c816565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b158015614fb257600080fd5b505afa158015614fc6573d6000803e3d6000fd5b505050506040513d6020811015614fdc57600080fd5b50516001600160a01b0385166000908152604160205260409020541161501a576001600160a01b0384166000908152604160205260408120556150de565b603b546150c4906001600160a01b03166317fd849a61503f868563ffffffff6135c816565b6040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561507357600080fd5b505afa158015615087573d6000803e3d6000fd5b505050506040513d602081101561509d57600080fd5b50516001600160a01b0386166000908152604160205260409020549063ffffffff6135c816565b6001600160a01b0385166000908152604160205260409020555b6000604a541180156150fa57506049546001600160a01b031615155b156133e057600061514961513c615124604a54670de0b6b3a764000061378b90919063ffffffff16565b610ca087670de0b6b3a764000063ffffffff6136c816565b859063ffffffff6135c816565b9050613d13838286614cd7565b60385460408051632e4c697f60e01b81526725b94542080c8000600482018190526024820185905291516000936001600160a01b031691632e4c697f916044808301926020929190829003018186803b1580156151b257600080fd5b505afa1580156151c6573d6000803e3d6000fd5b505050506040513d60208110156151dc57600080fd5b50519392505050565b60008061520685610c426151f98c8c6133e6565b899063ffffffff61378b16565b9050600061521a858563ffffffff6135c816565b9050600061523f670de0b6b3a7640000610ca0614a798682878563ffffffff6136c816565b9050861561525a576000614aa382600263ffffffff61372116565b876000614afd670de0b6b3a7640000610ca0614a798882878563ffffffff6136c816565b6000806000846001600160a01b031663cc2ee1966040518163ffffffff1660e01b8152600401604080518083038186803b1580156152bb57600080fd5b505afa1580156152cf573d6000803e3d6000fd5b505050506040513d60408110156152e557600080fd5b508051602090910151909250905060008085600181111561530257fe5b1461537e5760408051634dcb776760e11b815230600482015290516001600160a01b03841691639b96eece916024808301926020929190829003018186803b15801561534d57600080fd5b505afa158015615361573d6000803e3d6000fd5b505050506040513d602081101561537757600080fd5b50516153f1565b60408051634dcb776760e11b815230600482015290516001600160a01b03851691639b96eece916024808301926020929190829003018186803b1580156153c457600080fd5b505afa1580156153d8573d6000803e3d6000fd5b505050506040513d60208110156153ee57600080fd5b50515b9695505050505050565b3b15159056fe4f6e6c792077686974656c697374656420616464726573736573206f72206f776e65722063616e206368616e676520495621596f75206d757374206265206e6f6d696e61746564206265666f726520796f752063616e20616363657074206f776e657273686970416c726561647920696e697469616c697a65642c20757365206e6f6d696e6174654e65774f776e65724f6e6c792074686520636f6e7472616374206f776e6572206d617920706572666f726d207468697320616374696f6e536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a65645468697320616374696f6e2063616e6e6f7420626520706572666f726d6564207768696c652074686520636f6e7472616374206973207061757365645361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a265627a7a7231582034e1e06e9bbdf270fd11894e3ce3fcf42b01429e9ff746b38a58496abf1592d564736f6c63430005100032

Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.