Contract 0xe15aaD5d6b7433e5988415274529311f6Bf6e8a3

 
 
Txn Hash
Method
Block
From
To
Value
0x0a85566aacfa312b1b8b5a2f1c017b45896f1f412127b8e138fef7000708b7430x60806040110012832020-10-06 9:02:00671 days 18 hrs agomStable: Deployer IN  Create: MStableHelper0 Ether0.1127037251.7
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MStableHelper

Compiler Version
v0.5.16+commit.9c3226ce

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, GNU AGPLv3 license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2020-10-06
*/

pragma solidity 0.5.16;
pragma experimental ABIEncoderV2;


interface MassetStructs {

    /** @dev Stores high level basket info */
    struct Basket {

        /** @dev Array of Bassets currently active */
        Basset[] bassets;

        /** @dev Max number of bAssets that can be present in any Basket */
        uint8 maxBassets;

        /** @dev Some bAsset is undergoing re-collateralisation */
        bool undergoingRecol;

        /**
         * @dev In the event that we do not raise enough funds from the auctioning of a failed Basset,
         * The Basket is deemed as failed, and is undercollateralised to a certain degree.
         * The collateralisation ratio is used to calc Masset burn rate.
         */
        bool failed;
        uint256 collateralisationRatio;

    }

    /** @dev Stores bAsset info. The struct takes 5 storage slots per Basset */
    struct Basset {

        /** @dev Address of the bAsset */
        address addr;

        /** @dev Status of the basset,  */
        BassetStatus status; // takes uint8 datatype (1 byte) in storage

        /** @dev An ERC20 can charge transfer fee, for example USDT, DGX tokens. */
        bool isTransferFeeCharged; // takes a byte in storage

        /**
         * @dev 1 Basset * ratio / ratioScale == x Masset (relative value)
         *      If ratio == 10e8 then 1 bAsset = 10 mAssets
         *      A ratio is divised as 10^(18-tokenDecimals) * measurementMultiple(relative value of 1 base unit)
         */
        uint256 ratio;

        /** @dev Target weights of the Basset (100% == 1e18) */
        uint256 maxWeight;

        /** @dev Amount of the Basset that is held in Collateral */
        uint256 vaultBalance;

    }

    /** @dev Status of the Basset - has it broken its peg? */
    enum BassetStatus {
        Default,
        Normal,
        BrokenBelowPeg,
        BrokenAbovePeg,
        Blacklisted,
        Liquidating,
        Liquidated,
        Failed
    }

    /** @dev Internal details on Basset */
    struct BassetDetails {
        Basset bAsset;
        address integrator;
        uint8 index;
    }

    /** @dev All details needed to Forge with multiple bAssets */
    struct ForgePropsMulti {
        bool isValid; // Flag to signify that forge bAssets have passed validity check
        Basset[] bAssets;
        address[] integrators;
        uint8[] indexes;
    }

    /** @dev All details needed for proportionate Redemption */
    struct RedeemPropsMulti {
        uint256 colRatio;
        Basset[] bAssets;
        address[] integrators;
        uint8[] indexes;
    }
}

contract IMasset is MassetStructs {

    /** @dev Calc interest */
    function collectInterest() external returns (uint256 massetMinted, uint256 newTotalSupply);

    /** @dev Minting */
    function mint(address _basset, uint256 _bassetQuantity)
        external returns (uint256 massetMinted);
    function mintTo(address _basset, uint256 _bassetQuantity, address _recipient)
        external returns (uint256 massetMinted);
    function mintMulti(address[] calldata _bAssets, uint256[] calldata _bassetQuantity, address _recipient)
        external returns (uint256 massetMinted);

    /** @dev Swapping */
    function swap( address _input, address _output, uint256 _quantity, address _recipient)
        external returns (uint256 output);
    function getSwapOutput( address _input, address _output, uint256 _quantity)
        external view returns (bool, string memory, uint256 output);

    /** @dev Redeeming */
    function redeem(address _basset, uint256 _bassetQuantity)
        external returns (uint256 massetRedeemed);
    function redeemTo(address _basset, uint256 _bassetQuantity, address _recipient)
        external returns (uint256 massetRedeemed);
    function redeemMulti(address[] calldata _bAssets, uint256[] calldata _bassetQuantities, address _recipient)
        external returns (uint256 massetRedeemed);
    function redeemMasset(uint256 _mAssetQuantity, address _recipient) external;

    /** @dev Setters for the Manager or Gov to update module info */
    function upgradeForgeValidator(address _newForgeValidator) external;

    /** @dev Setters for Gov to set system params */
    function setSwapFee(uint256 _swapFee) external;

    /** @dev Getters */
    function getBasketManager() external view returns(address);
    function forgeValidator() external view returns (address);
    function totalSupply() external view returns (uint256);
    function swapFee() external view returns (uint256);
}

contract IBasketManager is MassetStructs {

    /** @dev Setters for mAsset to update balances */
    function increaseVaultBalance(
        uint8 _bAsset,
        address _integrator,
        uint256 _increaseAmount) external;
    function increaseVaultBalances(
        uint8[] calldata _bAsset,
        address[] calldata _integrator,
        uint256[] calldata _increaseAmount) external;
    function decreaseVaultBalance(
        uint8 _bAsset,
        address _integrator,
        uint256 _decreaseAmount) external;
    function decreaseVaultBalances(
        uint8[] calldata _bAsset,
        address[] calldata _integrator,
        uint256[] calldata _decreaseAmount) external;
    function collectInterest() external
        returns (uint256 interestCollected, uint256[] memory gains);

    /** @dev Setters for Gov to update Basket composition */
    function addBasset(
        address _basset,
        address _integration,
        bool _isTransferFeeCharged) external returns (uint8 index);
    function setBasketWeights(address[] calldata _bassets, uint256[] calldata _weights) external;
    function setTransferFeesFlag(address _bAsset, bool _flag) external;

    /** @dev Getters to retrieve Basket information */
    function getBasket() external view returns (Basket memory b);
    function prepareForgeBasset(address _token, uint256 _amt, bool _mint) external
        returns (bool isValid, BassetDetails memory bInfo);
    function prepareSwapBassets(address _input, address _output, bool _isMint) external view
        returns (bool, string memory, BassetDetails memory, BassetDetails memory);
    function prepareForgeBassets(address[] calldata _bAssets, uint256[] calldata _amts, bool _mint) external
        returns (ForgePropsMulti memory props);
    function prepareRedeemMulti() external view
        returns (RedeemPropsMulti memory props);
    function getBasset(address _token) external view
        returns (Basset memory bAsset);
    function getBassets() external view
        returns (Basset[] memory bAssets, uint256 len);
    function paused() external view returns (bool);

    /** @dev Recollateralisation */
    function handlePegLoss(address _basset, bool _belowPeg) external returns (bool actioned);
    function negateIsolation(address _basset) external;
}

interface ISavingsContract {

    /** @dev Manager privs */
    function depositInterest(uint256 _amount) external;

    /** @dev Saver privs */
    function depositSavings(uint256 _amount) external returns (uint256 creditsIssued);
    function redeem(uint256 _amount) external returns (uint256 massetReturned);

    /** @dev Getters */
    function exchangeRate() external view returns (uint256);
    function creditBalances(address) external view returns (uint256);
}

interface IMStableHelper {

    /**
     * @dev Returns a valid bAsset with which to mint
     * @param _mAsset Masset addr
     * @return valid bool
     * @return string message
     * @return address of bAsset to mint
     */
    function suggestMintAsset(
        address _mAsset
    )
        external
        view
        returns (
            bool,
            string memory,
            address
        );

    /**
     * @dev Gets the maximum input for a valid swap pair
     * @param _mAsset mAsset address (e.g. mUSD)
     * @param _input Asset to input only bAssets accepted
     * @param _output Either a bAsset or the mAsset
     * @return valid
     * @return validity reason
     * @return max input units (in native decimals)
     * @return how much output this input would produce (in native decimals, after any fee)
     */
    function getMaxSwap(
        address _mAsset,
        address _input,
        address _output
    )
        external
        view
        returns (
            bool,
            string memory,
            uint256,
            uint256
        );


    /**
     * @dev Returns a valid bAsset to redeem
     * @param _mAsset Masset addr
     * @return valid bool
     * @return string message
     * @return address of bAsset to redeem
     */
    function suggestRedeemAsset(
        address _mAsset
    )
        external
        view
        returns (
            bool,
            string memory,
            address
        );

    /**
     * @dev Determines if a given Redemption is valid
     * @param _mAsset Address of the given mAsset (e.g. mUSD)
     * @param _mAssetQuantity Amount of mAsset to redeem (in mUSD units)
     * @param _outputBasset Desired output bAsset
     * @return valid
     * @return validity reason
     * @return output in bAsset units
     * @return bAssetQuantityArg - required input argument to the 'redeem' call
     */
    function getRedeemValidity(
        address _mAsset,
        uint256 _mAssetQuantity,
        address _outputBasset
    )
        external
        view
        returns (
            bool,
            string memory,
            uint256 output,
            uint256 bassetQuantityArg
        );

    /**
     * @dev Gets the users savings balance in Masset terms
     * @param _save SAVE contract address
     * @param _user Address of the user
     * @return balance in Masset units
     */
    function getSaveBalance(
        ISavingsContract _save,
        address _user
    )
        external
        view
        returns (
            uint256
        );

    /**
     * @dev Returns the 'credit' units required to withdraw a certain
     * amount of Masset from the SAVE contract
     * @param _save SAVE contract address
     * @param _amount Amount of mAsset to redeem from SAVE
     * @return input for the redeem function (ie. credit units to redeem)
     */
    function getSaveRedeemInput(
        ISavingsContract _save,
        uint256 _amount
    )
        external
        view
        returns (
            uint256
        );
}

contract IForgeValidator is MassetStructs {
    function validateMint(uint256 _totalVault, Basset calldata _basset, uint256 _bAssetQuantity)
        external pure returns (bool, string memory);
    function validateMintMulti(uint256 _totalVault, Basset[] calldata _bassets, uint256[] calldata _bAssetQuantities)
        external pure returns (bool, string memory);
    function validateSwap(uint256 _totalVault, Basset calldata _inputBasset, Basset calldata _outputBasset, uint256 _quantity)
        external pure returns (bool, string memory, uint256, bool);
    function validateRedemption(
        bool basketIsFailed,
        uint256 _totalVault,
        Basset[] calldata _allBassets,
        uint8[] calldata _indices,
        uint256[] calldata _bassetQuantities) external pure returns (bool, string memory, bool);
    function calculateRedemptionMulti(
        uint256 _mAssetQuantity,
        Basset[] calldata _allBassets) external pure returns (bool, string memory, uint256[] memory);
}

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) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * _Available since v2.4.0._
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        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-contracts/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) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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.
     *
     * _Available since v2.4.0._
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        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) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message 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.
     *
     * _Available since v2.4.0._
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

library StableMath {

    using SafeMath for uint256;

    /**
     * @dev Scaling unit for use in specific calculations,
     * where 1 * 10**18, or 1e18 represents a unit '1'
     */
    uint256 private constant FULL_SCALE = 1e18;

    /**
     * @notice Token Ratios are used when converting between units of bAsset, mAsset and MTA
     * Reasoning: Takes into account token decimals, and difference in base unit (i.e. grams to Troy oz for gold)
     * @dev bAsset ratio unit for use in exact calculations,
     * where (1 bAsset unit * bAsset.ratio) / ratioScale == x mAsset unit
     */
    uint256 private constant RATIO_SCALE = 1e8;

    /**
     * @dev Provides an interface to the scaling unit
     * @return Scaling unit (1e18 or 1 * 10**18)
     */
    function getFullScale() internal pure returns (uint256) {
        return FULL_SCALE;
    }

    /**
     * @dev Provides an interface to the ratio unit
     * @return Ratio scale unit (1e8 or 1 * 10**8)
     */
    function getRatioScale() internal pure returns (uint256) {
        return RATIO_SCALE;
    }

    /**
     * @dev Scales a given integer to the power of the full scale.
     * @param x   Simple uint256 to scale
     * @return    Scaled value a to an exact number
     */
    function scaleInteger(uint256 x)
        internal
        pure
        returns (uint256)
    {
        return x.mul(FULL_SCALE);
    }

    /***************************************
              PRECISE ARITHMETIC
    ****************************************/

    /**
     * @dev Multiplies two precise units, and then truncates by the full scale
     * @param x     Left hand input to multiplication
     * @param y     Right hand input to multiplication
     * @return      Result after multiplying the two inputs and then dividing by the shared
     *              scale unit
     */
    function mulTruncate(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        return mulTruncateScale(x, y, FULL_SCALE);
    }

    /**
     * @dev Multiplies two precise units, and then truncates by the given scale. For example,
     * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18
     * @param x     Left hand input to multiplication
     * @param y     Right hand input to multiplication
     * @param scale Scale unit
     * @return      Result after multiplying the two inputs and then dividing by the shared
     *              scale unit
     */
    function mulTruncateScale(uint256 x, uint256 y, uint256 scale)
        internal
        pure
        returns (uint256)
    {
        // e.g. assume scale = fullScale
        // z = 10e18 * 9e17 = 9e36
        uint256 z = x.mul(y);
        // return 9e38 / 1e18 = 9e18
        return z.div(scale);
    }

    /**
     * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result
     * @param x     Left hand input to multiplication
     * @param y     Right hand input to multiplication
     * @return      Result after multiplying the two inputs and then dividing by the shared
     *              scale unit, rounded up to the closest base unit.
     */
    function mulTruncateCeil(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        // e.g. 8e17 * 17268172638 = 138145381104e17
        uint256 scaled = x.mul(y);
        // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17
        uint256 ceil = scaled.add(FULL_SCALE.sub(1));
        // e.g. 13814538111.399...e18 / 1e18 = 13814538111
        return ceil.div(FULL_SCALE);
    }

    /**
     * @dev Precisely divides two units, by first scaling the left hand operand. Useful
     *      for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)
     * @param x     Left hand input to division
     * @param y     Right hand input to division
     * @return      Result after multiplying the left operand by the scale, and
     *              executing the division on the right hand input.
     */
    function divPrecisely(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        // e.g. 8e18 * 1e18 = 8e36
        uint256 z = x.mul(FULL_SCALE);
        // e.g. 8e36 / 10e18 = 8e17
        return z.div(y);
    }


    /***************************************
                  RATIO FUNCS
    ****************************************/

    /**
     * @dev Multiplies and truncates a token ratio, essentially flooring the result
     *      i.e. How much mAsset is this bAsset worth?
     * @param x     Left hand operand to multiplication (i.e Exact quantity)
     * @param ratio bAsset ratio
     * @return      Result after multiplying the two inputs and then dividing by the ratio scale
     */
    function mulRatioTruncate(uint256 x, uint256 ratio)
        internal
        pure
        returns (uint256 c)
    {
        return mulTruncateScale(x, ratio, RATIO_SCALE);
    }

    /**
     * @dev Multiplies and truncates a token ratio, rounding up the result
     *      i.e. How much mAsset is this bAsset worth?
     * @param x     Left hand input to multiplication (i.e Exact quantity)
     * @param ratio bAsset ratio
     * @return      Result after multiplying the two inputs and then dividing by the shared
     *              ratio scale, rounded up to the closest base unit.
     */
    function mulRatioTruncateCeil(uint256 x, uint256 ratio)
        internal
        pure
        returns (uint256)
    {
        // e.g. How much mAsset should I burn for this bAsset (x)?
        // 1e18 * 1e8 = 1e26
        uint256 scaled = x.mul(ratio);
        // 1e26 + 9.99e7 = 100..00.999e8
        uint256 ceil = scaled.add(RATIO_SCALE.sub(1));
        // return 100..00.999e8 / 1e8 = 1e18
        return ceil.div(RATIO_SCALE);
    }


    /**
     * @dev Precisely divides two ratioed units, by first scaling the left hand operand
     *      i.e. How much bAsset is this mAsset worth?
     * @param x     Left hand operand in division
     * @param ratio bAsset ratio
     * @return      Result after multiplying the left operand by the scale, and
     *              executing the division on the right hand input.
     */
    function divRatioPrecisely(uint256 x, uint256 ratio)
        internal
        pure
        returns (uint256 c)
    {
        // e.g. 1e14 * 1e8 = 1e22
        uint256 y = x.mul(RATIO_SCALE);
        // return 1e22 / 1e12 = 1e10
        return y.div(ratio);
    }

    /***************************************
                    HELPERS
    ****************************************/

    /**
     * @dev Calculates minimum of two numbers
     * @param x     Left hand input
     * @param y     Right hand input
     * @return      Minimum of the two inputs
     */
    function min(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        return x > y ? y : x;
    }

    /**
     * @dev Calculated maximum of two numbers
     * @param x     Left hand input
     * @param y     Right hand input
     * @return      Maximum of the two inputs
     */
    function max(uint256 x, uint256 y)
        internal
        pure
        returns (uint256)
    {
        return x > y ? x : y;
    }

    /**
     * @dev Clamps a value to an upper bound
     * @param x           Left hand input
     * @param upperBound  Maximum possible value to return
     * @return            Input x clamped to a maximum value, upperBound
     */
    function clamp(uint256 x, uint256 upperBound)
        internal
        pure
        returns (uint256)
    {
        return x > upperBound ? upperBound : x;
    }
}

/**
 * @title   MStableHelper
 * @author  Stability Labs Pty. Ltd.
 * @notice  Returns the validity and output of a given redemption
 * @dev     VERSION: 1.0
 *          DATE:    2020-06-18
 */
contract MStableHelper is IMStableHelper, MassetStructs {

    using StableMath for uint256;
    using SafeMath for uint256;


    /***************************************
                MINT/SWAP/REDEEM
    ****************************************/

    /**
     * @dev Returns a valid bAsset with which to mint
     * @param _mAsset Masset addr
     * @return valid bool
     * @return string message
     * @return address of bAsset to mint
     */
    function suggestMintAsset(
        address _mAsset
    )
        external
        view
        returns (
            bool,
            string memory,
            address
        )
    {
        require(_mAsset != address(0), "Invalid mAsset");
        // Get the data
        IBasketManager basketManager = IBasketManager(
            IMasset(_mAsset).getBasketManager()
        );
        Basket memory basket = basketManager.getBasket();
        uint256 totalSupply = IMasset(_mAsset).totalSupply();

        // Calc the max weight delta (i.e is X% away from Max weight)
        uint256 len = basket.bassets.length;
        uint256[] memory maxWeightDelta = new uint256[](len);
        for(uint256 i = 0; i < len; i++){
            Basset memory bAsset = basket.bassets[i];
            uint256 scaledBasset = bAsset.vaultBalance.mulRatioTruncate(bAsset.ratio);
            // e.g. (1e21 * 1e18) / 1e23 = 1e16 or 1%
            uint256 weight = scaledBasset.divPrecisely(totalSupply);
            maxWeightDelta[i] = weight > bAsset.maxWeight ? 0 : bAsset.maxWeight.sub(weight);
            if(bAsset.status != BassetStatus.Normal){
                return (false, "No assets available", address(0));
            }
        }
        // Ideal delta is the bAsset > 10 but closest
        uint256 idealMaxWeight = 0;
        address selected = address(0);
        for(uint256 j = 0; j < len; j++){
            uint256 bAssetDelta = maxWeightDelta[j];
            if(bAssetDelta >= 1e17){
                if(selected == address(0) || bAssetDelta < idealMaxWeight){
                    idealMaxWeight = bAssetDelta;
                    selected = basket.bassets[j].addr;
                }
            }
        }
        if(selected == address(0)){
            return (false, "No assets available", address(0));
        }
        return (true, "", selected);
    }


    /**
     * @dev Gets the maximum input for a valid swap pair
     * @param _mAsset mAsset address (e.g. mUSD)
     * @param _input Asset to input only bAssets accepted
     * @param _output Either a bAsset or the mAsset
     * @return valid
     * @return validity reason
     * @return max input units (in native decimals)
     * @return how much output this input would produce (in native decimals, after any fee)
     */
    function getMaxSwap(
        address _mAsset,
        address _input,
        address _output
    )
        external
        view
        returns (
            bool,
            string memory,
            uint256,
            uint256
        )
    {
        Data memory data = _getData(_mAsset, _input, _output);
        if(!data.isValid) {
          return (false, data.reason, 0, 0);
        }
        uint256 inputMaxWeightUnits = data.totalSupply.mulTruncate(data.input.maxWeight);
        uint256 inputVaultBalanceScaled = data.input.vaultBalance.mulRatioTruncate(
            data.input.ratio
        );
        if (data.isMint) {
            // M = ((t * maxW) - c)/(1-maxW)
            // M = max mint (scaled)
            // t = totalSupply before
            // maxW = max weight %
            // c = vault balance (scaled)
            // num = (t * maxW) - c
            // e.g. 1e22 - 1e21 = 9e21
            uint256 num = inputMaxWeightUnits.sub(inputVaultBalanceScaled);
            // den = 1e18 - maxW
            // e.g. 1e18 - 75e16 = 25e16
            uint256 den = StableMath.getFullScale().sub(data.input.maxWeight);
            uint256 maxMintScaled = den > 0 ? num.divPrecisely(den) : num;
            uint256 maxMint = maxMintScaled.divRatioPrecisely(data.input.ratio);
            maxMintScaled = maxMint.mulRatioTruncate(data.input.ratio);
            return (true, "", maxMint, maxMintScaled);
        } else {
            // get max input
            uint256 maxInputScaled = inputMaxWeightUnits.sub(inputVaultBalanceScaled);
            // get max output
            uint256 outputMaxWeight = data.totalSupply.mulTruncate(data.output.maxWeight);
            uint256 outputVaultBalanceScaled = data.output.vaultBalance.mulRatioTruncate(data.output.ratio);
            // If maxInput = 2, outputVaultBalance = 1, then clamp to 1
            uint256 clampedMax = maxInputScaled > outputVaultBalanceScaled ? outputVaultBalanceScaled : maxInputScaled;
            // if output is overweight, no fee, else fee
            bool applyFee = outputVaultBalanceScaled < outputMaxWeight;
            uint256 maxInputUnits = clampedMax.divRatioPrecisely(data.input.ratio);
            uint256 outputUnitsIncFee = maxInputUnits.mulRatioTruncate(data.input.ratio).divRatioPrecisely(data.output.ratio);

            uint256 fee = applyFee ? data.mAsset.swapFee() : 0;
            uint256 outputFee = outputUnitsIncFee.mulTruncate(fee);
            return (true, "", maxInputUnits, outputUnitsIncFee.sub(outputFee));
        }
    }

    /**
     * @dev Returns a valid bAsset to redeem
     * @param _mAsset Masset addr
     * @return valid bool
     * @return string message
     * @return address of bAsset to redeem
     */
    function suggestRedeemAsset(
        address _mAsset
    )
        external
        view
        returns (
            bool,
            string memory,
            address
        )
    {
        require(_mAsset != address(0), "Invalid mAsset");
        // Get the data
        IBasketManager basketManager = IBasketManager(
            IMasset(_mAsset).getBasketManager()
        );
        Basket memory basket = basketManager.getBasket();
        uint256 totalSupply = IMasset(_mAsset).totalSupply();

        // Calc the max weight delta (i.e is X% away from Max weight)
        uint256 len = basket.bassets.length;
        uint256 overweightCount = 0;
        uint256[] memory maxWeightDelta = new uint256[](len);
        
        for(uint256 i = 0; i < len; i++){
            Basset memory bAsset = basket.bassets[i];
            uint256 scaledBasset = bAsset.vaultBalance.mulRatioTruncate(bAsset.ratio);
            // e.g. (1e21 * 1e18) / 1e23 = 1e16 or 1%
            uint256 weight = scaledBasset.divPrecisely(totalSupply);
            if(weight > bAsset.maxWeight) {
                overweightCount++;
            }
            maxWeightDelta[i] = weight > bAsset.maxWeight ? uint256(-1) : bAsset.maxWeight.sub(weight);
            if(bAsset.status != BassetStatus.Normal){
                return (false, "No assets available", address(0));
            }
        }

        // if > 1 overweight, fail
        if(overweightCount > 1) {
            return (false, "No assets available", address(0));
        } else if(overweightCount == 1){
            // if 1 overweight, choose asset
            for(uint256 j = 0; j < len; j++){
                if(maxWeightDelta[j] == uint256(-1)){
                    return (true, "", basket.bassets[j].addr);
                }
            }
        }
        // else choose highest %
        uint256 lowestDelta = uint256(-1);
        address selected = address(0);
        for(uint256 k = 0; k < len; k++){
            if(maxWeightDelta[k] < lowestDelta) {
                selected = basket.bassets[k].addr;
                lowestDelta = maxWeightDelta[k];
            }
        }
        return (true, "", selected);
    }

    /**
     * @dev Determines if a given Redemption is valid
     * @param _mAsset Address of the given mAsset (e.g. mUSD)
     * @param _mAssetQuantity Amount of mAsset to redeem (in mUSD units)
     * @param _outputBasset Desired output bAsset
     * @return valid
     * @return validity reason
     * @return output in bAsset units
     * @return bAssetQuantityArg - required input argument to the 'redeem' call
     */
    function getRedeemValidity(
        address _mAsset,
        uint256 _mAssetQuantity,
        address _outputBasset
    )
        external
        view
        returns (
            bool,
            string memory,
            uint256 output,
            uint256 bassetQuantityArg
        )
    {
        // Convert the `mAssetQuantity` (input) into bAsset units
        IBasketManager basketManager = IBasketManager(
            IMasset(_mAsset).getBasketManager()
        );
        Basset memory bAsset = basketManager.getBasset(_outputBasset);
        uint256 bAssetQuantity = _mAssetQuantity.divRatioPrecisely(
            bAsset.ratio
        );

        // Prepare params for internal validity
        address[] memory bAssets = new address[](1);
        uint256[] memory quantities = new uint256[](1);
        bAssets[0] = _outputBasset;
        quantities[0] = bAssetQuantity;
        (
            bool valid,
            string memory reason,
            uint256 bAssetOutput
        ) = _getRedeemValidity(_mAsset, bAssets, quantities);
        return (valid, reason, bAssetOutput, bAssetQuantity);
    }


    /***************************************
                    SAVE
    ****************************************/

    /**
     * @dev Gets the users savings balance in Masset terms
     * @param _save SAVE contract address
     * @param _user Address of the user
     * @return balance in Masset units
     */
    function getSaveBalance(
        ISavingsContract _save,
        address _user
    )
        external
        view
        returns (
            uint256
        )
    {
        require(address(_save) != address(0), "Invalid contract");
        require(_user != address(0), "Invalid user");

        uint256 credits = _save.creditBalances(_user);
        uint256 rate = _save.exchangeRate();
        require(rate > 0, "Invalid rate");

        return credits.mulTruncate(rate);
    }

    /**
     * @dev Returns the 'credit' units required to withdraw a certain
     * amount of Masset from the SAVE contract
     * @param _save SAVE contract address
     * @param _mAssetUnits Amount of mAsset to redeem from SAVE
     * @return input for the redeem function (ie. credit units to redeem)
     */
    function getSaveRedeemInput(
        ISavingsContract _save,
        uint256 _mAssetUnits
    )
        external
        view
        returns (
            uint256
        )
    {
        require(address(_save) != address(0), "Invalid contract");

        uint256 rate = _save.exchangeRate();
        require(rate > 0, "Invalid rate");

        uint256 credits = _mAssetUnits.divPrecisely(rate);

        // Add 1 because the amounts always round down
        // e.g. i have 51 credits, e4 10 = 20.4
        // to withdraw 20 i need 20*10/4 = 50 + 1
        return credits + 1;
    }


    /***************************************
                    INTERNAL
    ****************************************/

    struct Data {
        bool isValid;
        string reason;
        IMasset mAsset;
        IBasketManager basketManager;
        bool isMint;
        uint256 totalSupply;
        Basset input;
        Basset output;
    }

    function _getData(address _mAsset, address _input, address _output) internal view returns (Data memory) {
        bool isMint = _output == _mAsset;
        IMasset mAsset = IMasset(_mAsset);
        IBasketManager basketManager = IBasketManager(
            mAsset.getBasketManager()
        );
        (bool isValid, string memory reason, ) = mAsset
            .getSwapOutput(_input, _output, 1);
        uint256 totalSupply = mAsset.totalSupply();
        Basset memory input = basketManager.getBasset(_input);
        Basset memory output = !isMint ? basketManager.getBasset(_output) : Basset({
            addr: _output,
            ratio: StableMath.getRatioScale(),
            maxWeight: 0,
            vaultBalance: 0,
            status: BassetStatus.Normal,
            isTransferFeeCharged: false
        });
        return Data({
            isValid: isValid,
            reason: reason,
            mAsset: mAsset,
            basketManager: basketManager,
            isMint: isMint,
            totalSupply: totalSupply,
            input: input,
            output: output
        });
    }


    function _getRedeemValidity(
        address _mAsset,
        address[] memory _bAssets,
        uint256[] memory _bAssetQuantities
    )
        internal
        view
        returns (
            bool,
            string memory,
            uint256 output
        )
    {
        uint256 bAssetCount = _bAssetQuantities.length;
        require(
            bAssetCount == 1 && bAssetCount == _bAssets.length,
            "Input array mismatch"
        );

        IMasset mAsset = IMasset(_mAsset);
        IBasketManager basketManager = IBasketManager(
            mAsset.getBasketManager()
        );

        Basket memory basket = basketManager.getBasket();

        if (basket.undergoingRecol || basketManager.paused()) {
            return (false, "Invalid basket state", 0);
        }

        (
            bool redemptionValid,
            string memory reason,
            bool applyFee
        ) = _validateRedeem(
            mAsset,
            _bAssetQuantities,
            _bAssets[0],
            basket.failed,
            mAsset.totalSupply(),
            basket.bassets
        );
        if (!redemptionValid) {
            return (false, reason, 0);
        }
        uint256 fee = applyFee ? mAsset.swapFee() : 0;
        uint256 feeAmount = _bAssetQuantities[0].mulTruncate(fee);
        uint256 outputMinusFee = _bAssetQuantities[0].sub(feeAmount);
        return (true, "", outputMinusFee);
    }


    function _validateRedeem(
        IMasset mAsset,
        uint256[] memory quantities,
        address bAsset,
        bool failed,
        uint256 supply,
        Basset[] memory allBassets
    )
        internal
        view
        returns (
            bool,
            string memory,
            bool
        )
    {
        IForgeValidator forgeValidator = IForgeValidator(
            mAsset.forgeValidator()
        );
        uint8[] memory bAssetIndexes = new uint8[](1);
        for (uint8 i = 0; i < uint8(allBassets.length); i++) {
            if (allBassets[i].addr == bAsset) {
                bAssetIndexes[0] = i;
                break;
            }
        }
        return
            forgeValidator.validateRedemption(
                failed,
                supply,
                allBassets,
                bAssetIndexes,
                quantities
            );
    }

}

Contract Security Audit

Contract ABI

[{"constant":true,"inputs":[{"internalType":"address","name":"_mAsset","type":"address"},{"internalType":"address","name":"_input","type":"address"},{"internalType":"address","name":"_output","type":"address"}],"name":"getMaxSwap","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_mAsset","type":"address"},{"internalType":"uint256","name":"_mAssetQuantity","type":"uint256"},{"internalType":"address","name":"_outputBasset","type":"address"}],"name":"getRedeemValidity","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"output","type":"uint256"},{"internalType":"uint256","name":"bassetQuantityArg","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract ISavingsContract","name":"_save","type":"address"},{"internalType":"address","name":"_user","type":"address"}],"name":"getSaveBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract ISavingsContract","name":"_save","type":"address"},{"internalType":"uint256","name":"_mAssetUnits","type":"uint256"}],"name":"getSaveRedeemInput","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_mAsset","type":"address"}],"name":"suggestMintAsset","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_mAsset","type":"address"}],"name":"suggestRedeemAsset","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"string","name":"","type":"string"},{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b5061267e806100206000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c8063466c07ce146100675780634ace2ba3146100935780637fcf5dfa146100b3578063960a27f8146100c6578063ecfee14c146100e8578063f943b283146100fb575b600080fd5b61007a610075366004611e01565b61010e565b60405161008a94939291906123c0565b60405180910390f35b6100a66100a1366004611f54565b6103f7565b60405161008a91906124cd565b6100a66100c1366004611f8e565b61057f565b6100d96100d4366004611dc5565b61065a565b60405161008a93929190612391565b61007a6100f6366004611e4e565b610a11565b6100d9610109366004611dc5565b610be8565b6000606060008061011d611a9e565b610128888888611018565b805190915061014657602001516000945092508391508190506103ee565b60006101678260c00151608001518360a0015161136690919063ffffffff16565b60c0830151606081015160a09091015191925060009161018c9163ffffffff61138216565b90508260800151156102515760006101aa838363ffffffff61139316565b905060006101ce8560c00151608001516101c26113d5565b9063ffffffff61139316565b905060008082116101df57826101ef565b6101ef838363ffffffff6113e116565b9050600061020e8760c00151606001518361141690919063ffffffff16565b905061022b8760c00151606001518261138290919063ffffffff16565b60408051602081019091526000815260019c509a5090985096506103ee95505050505050565b6000610263838363ffffffff61139316565b905060006102868560e00151608001518660a0015161136690919063ffffffff16565b60e0860151606081015160a0909101519192506000916102ab9163ffffffff61138216565b905060008184116102bc57836102be565b815b60c088015160600151909150838310906000906102e290849063ffffffff61141616565b905060006103198a60e001516060015161030d8c60c00151606001518561138290919063ffffffff16565b9063ffffffff61141616565b905060008361032957600061039e565b8a604001516001600160a01b03166354cf2aeb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561036657600080fd5b505afa15801561037a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061039e9190810190612011565b905060006103b2838363ffffffff61136616565b90506001846103c7858463ffffffff61139316565b6040518060200160405280600081525091909f509f509f509f505050505050505050505050505b93509350935093565b60006001600160a01b0383166104285760405162461bcd60e51b815260040161041f9061247d565b60405180910390fd5b6001600160a01b03821661044e5760405162461bcd60e51b815260040161041f906124bd565b604051630c01345b60e31b81526000906001600160a01b03851690636009a2d89061047d90869060040161235b565b60206040518083038186803b15801561049557600080fd5b505afa1580156104a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506104cd9190810190612011565b90506000846001600160a01b0316633ba0b9a96040518163ffffffff1660e01b815260040160206040518083038186803b15801561050a57600080fd5b505afa15801561051e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506105429190810190612011565b9050600081116105645760405162461bcd60e51b815260040161041f906124ad565b610574828263ffffffff61136616565b925050505b92915050565b60006001600160a01b0383166105a75760405162461bcd60e51b815260040161041f9061247d565b6000836001600160a01b0316633ba0b9a96040518163ffffffff1660e01b815260040160206040518083038186803b1580156105e257600080fd5b505afa1580156105f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061061a9190810190612011565b90506000811161063c5760405162461bcd60e51b815260040161041f906124ad565b600061064e848363ffffffff6113e116565b60010195945050505050565b60006060816001600160a01b0384166106855760405162461bcd60e51b815260040161041f9061248d565b6000846001600160a01b031663eeea5d366040518163ffffffff1660e01b815260040160206040518083038186803b1580156106c057600080fd5b505afa1580156106d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106f89190810190611de3565b9050610702611b04565b816001600160a01b031663cd6ef2b06040518163ffffffff1660e01b815260040160006040518083038186803b15801561073b57600080fd5b505afa15801561074f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107779190810190611fbe565b90506000866001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156107b457600080fd5b505afa1580156107c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506107ec9190810190612011565b8251516040805182815260208084028201019091529192509060609082801561081f578160200160208202803883390190505b50905060005b8281101561092357610835611b32565b855180518390811061084357fe5b60200260200101519050600061086a82606001518360a0015161138290919063ffffffff16565b9050600061087e828863ffffffff6113e116565b9050826080015181116108a55760808301516108a0908263ffffffff61139316565b6108a8565b60005b8585815181106108b457fe5b60209081029190910101526001836020015160078111156108d157fe5b146109185750506040805180820190915260138152724e6f2061737365747320617661696c61626c6560681b602082015260009a509850899750610a0a9650505050505050565b505050600101610825565b50600080805b8481101561099d57600084828151811061093f57fe5b6020026020010151905067016345785d8a00008110610994576001600160a01b038316158061096d57508381105b15610994578093508760000151828151811061098557fe5b60200260200101516000015192505b50600101610929565b506001600160a01b0381166109ec5750506040805180820190915260138152724e6f2061737365747320617661696c61626c6560681b6020820152600098509650879550610a0a945050505050565b60408051602081019091526000815260019a50985096505050505050505b9193909250565b600060606000806000876001600160a01b031663eeea5d366040518163ffffffff1660e01b815260040160206040518083038186803b158015610a5357600080fd5b505afa158015610a67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610a8b9190810190611de3565b9050610a95611b32565b604051630f8def2f60e21b81526001600160a01b03831690633e37bcbc90610ac1908a9060040161235b565b60c06040518083038186803b158015610ad957600080fd5b505afa158015610aed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b119190810190611ff3565b90506000610b2c82606001518a61141690919063ffffffff16565b604080516001808252818301909252919250606091906020808301908038833950506040805160018082528183019092529293506060929150602080830190803883390190505090508982600081518110610b8357fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508281600081518110610bb157fe5b602002602001018181525050600060606000610bce8f868661142d565b919d509b5099509497505050505050505093509350935093565b60006060816001600160a01b038416610c135760405162461bcd60e51b815260040161041f9061248d565b6000846001600160a01b031663eeea5d366040518163ffffffff1660e01b815260040160206040518083038186803b158015610c4e57600080fd5b505afa158015610c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c869190810190611de3565b9050610c90611b04565b816001600160a01b031663cd6ef2b06040518163ffffffff1660e01b815260040160006040518083038186803b158015610cc957600080fd5b505afa158015610cdd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610d059190810190611fbe565b90506000866001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015610d4257600080fd5b505afa158015610d56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d7a9190810190612011565b82515160408051828152602080840282010190915291925090600090606090838015610db0578160200160208202803883390190505b50905060005b83811015610ec957610dc6611b32565b8651805183908110610dd457fe5b602002602001015190506000610dfb82606001518360a0015161138290919063ffffffff16565b90506000610e0f828963ffffffff6113e116565b90508260800151811115610e24576001909501945b82608001518111610e49576080830151610e44908263ffffffff61139316565b610e4d565b6000195b858581518110610e5957fe5b6020908102919091010152600183602001516007811115610e7657fe5b14610ebe5750506040805180820190915260138152724e6f2061737365747320617661696c61626c6560681b602082015260009b5099508a9850610a0a975050505050505050565b505050600101610db6565b506001821115610f125750506040805180820190915260138152724e6f2061737365747320617661696c61626c6560681b6020820152600097509550869450610a0a9350505050565b8160011415610f8e5760005b83811015610f8c57600019828281518110610f3557fe5b60200260200101511415610f8457600186600001518281518110610f5557fe5b602002602001015160000151604051806020016040528060008152509099509950995050505050505050610a0a565b600101610f1e565b505b6000196000805b85811015610ff35782848281518110610faa57fe5b60200260200101511015610feb578751805182908110610fc657fe5b6020026020010151600001519150838181518110610fe057fe5b602002602001015192505b600101610f95565b5060408051602081019091526000815260019d909c50909a5098505050505050505050565b611020611a9e565b6000846001600160a01b0316836001600160a01b031614905060008590506000816001600160a01b031663eeea5d366040518163ffffffff1660e01b815260040160206040518083038186803b15801561107957600080fd5b505afa15801561108d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110b19190810190611de3565b905060006060836001600160a01b03166372ea9076898960016040518463ffffffff1660e01b81526004016110e893929190612369565b60006040518083038186803b15801561110057600080fd5b505afa158015611114573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261113c9190810190611ef9565b50915091506000846001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561117c57600080fd5b505afa158015611190573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506111b49190810190612011565b90506111be611b32565b604051630f8def2f60e21b81526001600160a01b03861690633e37bcbc906111ea908d9060040161235b565b60c06040518083038186803b15801561120257600080fd5b505afa158015611216573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061123a9190810190611ff3565b9050611244611b32565b8715611291576040805160c081019091526001600160a01b038b16815260208101600181526000602082015260400161127b6117d9565b815260200160008152602001600081525061130d565b604051630f8def2f60e21b81526001600160a01b03871690633e37bcbc906112bd908d9060040161235b565b60c06040518083038186803b1580156112d557600080fd5b505afa1580156112e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061130d9190810190611ff3565b6040805161010081018252961515875260208701959095526001600160a01b039788169486019490945250939094166060830152931515608082015260a081019290925260c082015260e0810191909152949350505050565b600061137b8383670de0b6b3a76400006117e1565b9392505050565b600061137b83836305f5e1006117e1565b600061137b83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061180f565b670de0b6b3a764000090565b6000806113fc84670de0b6b3a764000063ffffffff61183b16565b905061140e818463ffffffff61187516565b949350505050565b6000806113fc846305f5e10063ffffffff61183b16565b60006060600080845190508060011480156114485750855181145b6114645760405162461bcd60e51b815260040161041f9061246d565b60008790506000816001600160a01b031663eeea5d366040518163ffffffff1660e01b815260040160206040518083038186803b1580156114a457600080fd5b505afa1580156114b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114dc9190810190611de3565b90506114e6611b04565b816001600160a01b031663cd6ef2b06040518163ffffffff1660e01b815260040160006040518083038186803b15801561151f57600080fd5b505afa158015611533573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261155b9190810190611fbe565b90508060400151806115d95750816001600160a01b0316635c975abb6040518163ffffffff1660e01b815260040160206040518083038186803b1580156115a157600080fd5b505afa1580156115b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115d99190810190611e80565b1561161c575050604080518082019091526014815273496e76616c6964206261736b657420737461746560601b60208201526000955093508492506117d0915050565b6000606060006116b9868c8e60008151811061163457fe5b602002602001015187606001518a6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561167a57600080fd5b505afa15801561168e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116b29190810190612011565b89516118b7565b925092509250826116d957506000985096508795506117d0945050505050565b6000816116e7576000611758565b866001600160a01b03166354cf2aeb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561172057600080fd5b505afa158015611734573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117589190810190612011565b90506000611783828e60008151811061176d57fe5b602002602001015161136690919063ffffffff16565b905060006117ae828f60008151811061179857fe5b602002602001015161139390919063ffffffff16565b60408051602081019091526000815260019e509c509a50505050505050505050505b93509350939050565b6305f5e10090565b6000806117f4858563ffffffff61183b16565b9050611806818463ffffffff61187516565b95945050505050565b600081848411156118335760405162461bcd60e51b815260040161041f919061245c565b505050900390565b60008261184a57506000610579565b8282028284828161185757fe5b041461137b5760405162461bcd60e51b815260040161041f9061249d565b600061137b83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250611a67565b60006060600080896001600160a01b031663d7d522666040518163ffffffff1660e01b815260040160206040518083038186803b1580156118f757600080fd5b505afa15801561190b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061192f9190810190611de3565b604080516001808252818301909252919250606091906020808301908038833901905050905060005b865160ff168160ff1610156119ca57896001600160a01b0316878260ff168151811061198057fe5b6020026020010151600001516001600160a01b031614156119c25780826000815181106119a957fe5b602002602001019060ff16908160ff16815250506119ca565b600101611958565b50816001600160a01b0316638fb1860b898989858f6040518663ffffffff1660e01b81526004016119ff9594939291906123fc565b60006040518083038186803b158015611a1757600080fd5b505afa158015611a2b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a539190810190611e9e565b945094509450505096509650969350505050565b60008183611a885760405162461bcd60e51b815260040161041f919061245c565b506000838581611a9457fe5b0495945050505050565b6040518061010001604052806000151581526020016060815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160001515815260200160008152602001611af2611b32565b8152602001611aff611b32565b905290565b6040805160a08101825260608082526000602083018190529282018390528101829052608081019190915290565b6040805160c081019091526000808252602082019081526020016000151581526020016000815260200160008152602001600081525090565b8035610579816125f6565b8051610579816125f6565b600082601f830112611b9257600080fd5b8151611ba5611ba082612502565b6124db565b915081818352602084019350602081019050838560c0840282011115611bca57600080fd5b60005b83811015611bf85781611be08882611d0d565b84525060209092019160c09190910190600101611bcd565b5050505092915050565b80516105798161260a565b803561057981612613565b80516105798161261c565b600082601f830112611c3457600080fd5b8151611c42611ba082612523565b91508082526020830160208301858383011115611c5e57600080fd5b611c698382846125b3565b50505092915050565b600060a08284031215611c8457600080fd5b611c8e60a06124db565b825190915067ffffffffffffffff811115611ca857600080fd5b611cb484828501611b81565b8252506020611cc584848301611dba565b6020830152506040611cd984828501611c02565b6040830152506060611ced84828501611c02565b6060830152506080611d0184828501611daf565b60808301525092915050565b600060c08284031215611d1f57600080fd5b611d2960c06124db565b90506000611d378484611b76565b8252506020611d4884848301611c18565b6020830152506040611d5c84828501611c02565b6040830152506060611d7084828501611daf565b6060830152506080611d8484828501611daf565b60808301525060a0611d9884828501611daf565b60a08301525092915050565b803561057981612629565b805161057981612629565b805161057981612632565b600060208284031215611dd757600080fd5b600061140e8484611b6b565b600060208284031215611df557600080fd5b600061140e8484611b76565b600080600060608486031215611e1657600080fd5b6000611e228686611b6b565b9350506020611e3386828701611b6b565b9250506040611e4486828701611b6b565b9150509250925092565b600080600060608486031215611e6357600080fd5b6000611e6f8686611b6b565b9350506020611e3386828701611da4565b600060208284031215611e9257600080fd5b600061140e8484611c02565b600080600060608486031215611eb357600080fd5b6000611ebf8686611c02565b935050602084015167ffffffffffffffff811115611edc57600080fd5b611ee886828701611c23565b9250506040611e4486828701611c02565b600080600060608486031215611f0e57600080fd5b6000611f1a8686611c02565b935050602084015167ffffffffffffffff811115611f3757600080fd5b611f4386828701611c23565b9250506040611e4486828701611daf565b60008060408385031215611f6757600080fd5b6000611f738585611c0d565b9250506020611f8485828601611b6b565b9150509250929050565b60008060408385031215611fa157600080fd5b6000611fad8585611c0d565b9250506020611f8485828601611da4565b600060208284031215611fd057600080fd5b815167ffffffffffffffff811115611fe757600080fd5b61140e84828501611c72565b600060c0828403121561200557600080fd5b600061140e8484611d0d565b60006020828403121561202357600080fd5b600061140e8484611daf565b600061203b83836122d3565b505060c00190565b600061204f8383612349565b505060200190565b600061204f8383612352565b61206c8161255e565b82525050565b600061207d82612551565b6120878185612555565b93506120928361254b565b8060005b838110156120c05781516120aa888261202f565b97506120b58361254b565b925050600101612096565b509495945050505050565b60006120d682612551565b6120e08185612555565b93506120eb8361254b565b8060005b838110156120c05781516121038882612043565b975061210e8361254b565b9250506001016120ef565b600061212482612551565b61212e8185612555565b93506121398361254b565b8060005b838110156120c05781516121518882612057565b975061215c8361254b565b92505060010161213d565b61206c81612569565b61206c8161259d565b61206c816125a8565b600061218d82612551565b6121978185612555565b93506121a78185602086016125b3565b6121b0816125df565b9093019392505050565b60006121c7601483612555565b73092dce0eae840c2e4e4c2f240dad2e6dac2e8c6d60631b815260200192915050565b60006121f7601083612555565b6f125b9d985b1a590818dbdb9d1c9858dd60821b815260200192915050565b6000612223600e83612555565b6d125b9d985b1a59081b505cdcd95d60921b815260200192915050565b600061224d602183612555565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000612290600c83612555565b6b496e76616c6964207261746560a01b815260200192915050565b60006122b8600c83612555565b6b24b73b30b634b2103ab9b2b960a11b815260200192915050565b805160c08301906122e48482612063565b5060208201516122f76020850182612170565b50604082015161230a6040850182612167565b50606082015161231d6060850182612349565b5060808201516123306080850182612349565b5060a082015161234360a0850182612349565b50505050565b61206c81612594565b61206c81612597565b602081016105798284612063565b606081016123778286612063565b6123846020830185612063565b61140e6040830184612179565b6060810161239f8286612167565b81810360208301526123b18185612182565b905061140e6040830184612063565b608081016123ce8287612167565b81810360208301526123e08186612182565b90506123ef6040830185612349565b6118066060830184612349565b60a0810161240a8288612167565b6124176020830187612349565b81810360408301526124298186612072565b9050818103606083015261243d8185612119565b9050818103608083015261245181846120cb565b979650505050505050565b6020808252810161137b8184612182565b60208082528101610579816121ba565b60208082528101610579816121ea565b6020808252810161057981612216565b6020808252810161057981612240565b6020808252810161057981612283565b60208082528101610579816122ab565b602081016105798284612349565b60405181810167ffffffffffffffff811182821017156124fa57600080fd5b604052919050565b600067ffffffffffffffff82111561251957600080fd5b5060209081020190565b600067ffffffffffffffff82111561253a57600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b600061057982612588565b151590565b60006105798261255e565b80612583816125e9565b919050565b6001600160a01b031690565b90565b60ff1690565b600061057982612579565b600061057982612594565b60005b838110156125ce5781810151838201526020016125b6565b838111156123435750506000910152565b601f01601f191690565b600881106125f357fe5b50565b6125ff8161255e565b81146125f357600080fd5b6125ff81612569565b6125ff8161256e565b600881106125f357600080fd5b6125ff81612594565b6125ff8161259756fea365627a7a723158202ae504ac92d28aea501d3291c32fa6e8eb7d0097ae887d882fc2ee67a87368a16c6578706572696d656e74616cf564736f6c63430005100040

Deployed ByteCode Sourcemap

24594:15163:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;24594:15163:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27424:2599;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;34390:500;;;;;;;;;:::i;:::-;;;;;;;;35218:604;;;;;;;;;:::i;25068:1908::-;;;;;;;;;:::i;:::-;;;;;;;;;;32907:1150;;;;;;;;;:::i;30232:2232::-;;;;;;;;;:::i;27424:2599::-;27592:4;27611:13;27639:7;27661;27696:16;;:::i;:::-;27715:34;27724:7;27733:6;27741:7;27715:8;:34::i;:::-;27764:12;;27696:53;;-1:-1:-1;27760:76:0;;27806:11;;;27799:5;;-1:-1:-1;27806:11:0;-1:-1:-1;27799:5:0;;-1:-1:-1;27799:5:0;;-1:-1:-1;27791:33:0;;27760:76;27846:27;27876:50;27905:4;:10;;;:20;;;27876:4;:16;;;:28;;:50;;;;:::i;:::-;28026:10;;;;:16;;;;27971:23;;;;;27846:80;;-1:-1:-1;27937:31:0;;27971:82;;;:40;:82;:::i;:::-;27937:116;;28068:4;:11;;;28064:1952;;;28375:11;28389:48;:19;28413:23;28389:48;:23;:48;:::i;:::-;28375:62;;28528:11;28542:51;28572:4;:10;;;:20;;;28542:25;:23;:25::i;:::-;:29;:51;:29;:51;:::i;:::-;28528:65;;28608:21;28638:1;28632:3;:7;:37;;28666:3;28632:37;;;28642:21;:3;28659;28642:21;:16;:21;:::i;:::-;28608:61;;28684:15;28702:49;28734:4;:10;;;:16;;;28702:13;:31;;:49;;;;:::i;:::-;28684:67;;28782:42;28807:4;:10;;;:16;;;28782:7;:24;;:42;;;;:::i;:::-;28839:41;;;;;;;;;-1:-1:-1;28839:41:0;;28847:4;;-1:-1:-1;28839:41:0;-1:-1:-1;28857:7:0;;-1:-1:-1;28766:58:0;-1:-1:-1;28839:41:0;;-1:-1:-1;;;;;;28839:41:0;28064:1952;28943:22;28968:48;:19;28992:23;28968:48;:23;:48;:::i;:::-;28943:73;;29062:23;29088:51;29117:4;:11;;;:21;;;29088:4;:16;;;:28;;:51;;;;:::i;:::-;29231:11;;;;:17;;;;29189:24;;;;;29062:77;;-1:-1:-1;29154:32:0;;29189:60;;;:41;:60;:::i;:::-;29154:95;;29337:18;29375:24;29358:14;:41;:85;;29429:14;29358:85;;;29402:24;29358:85;29642:10;;;;:16;;;29337:106;;-1:-1:-1;29532:42:0;;;;29516:13;;29613:46;;29337:106;;29613:46;:28;:46;:::i;:::-;29589:70;;29674:25;29702:85;29769:4;:11;;;:17;;;29702:48;29733:4;:10;;;:16;;;29702:13;:30;;:48;;;;:::i;:::-;:66;:85;:66;:85;:::i;:::-;29674:113;;29804:11;29818:8;:36;;29853:1;29818:36;;;29829:4;:11;;;-1:-1:-1;;;;;29829:19:0;;:21;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;29829:21:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;29829:21:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;29829:21:0;;;;;;;;;29804:50;-1:-1:-1;29869:17:0;29889:34;:17;29804:50;29889:34;:29;:34;:::i;:::-;29869:54;-1:-1:-1;29946:4:0;29956:13;29971:32;:17;29869:54;29971:32;:21;:32;:::i;:::-;29938:66;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27424:2599;;;;;;;;:::o;34390:500::-;34542:7;-1:-1:-1;;;;;34585:28:0;;34577:57;;;;-1:-1:-1;;;34577:57:0;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;34653:19:0;;34645:44;;;;-1:-1:-1;;;34645:44:0;;;;;;;;;34720:27;;-1:-1:-1;;;34720:27:0;;34702:15;;-1:-1:-1;;;;;34720:20:0;;;;;:27;;34741:5;;34720:27;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;34720:27:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;34720:27:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;34720:27:0;;;;;;;;;34702:45;;34758:12;34773:5;-1:-1:-1;;;;;34773:18:0;;:20;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;34773:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;34773:20:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;34773:20:0;;;;;;;;;34758:35;;34819:1;34812:4;:8;34804:33;;;;-1:-1:-1;;;34804:33:0;;;;;;;;;34857:25;:7;34877:4;34857:25;:19;:25;:::i;:::-;34850:32;;;;34390:500;;;;;:::o;35218:604::-;35381:7;-1:-1:-1;;;;;35424:28:0;;35416:57;;;;-1:-1:-1;;;35416:57:0;;;;;;;;;35486:12;35501:5;-1:-1:-1;;;;;35501:18:0;;:20;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;35501:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;35501:20:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;35501:20:0;;;;;;;;;35486:35;;35547:1;35540:4;:8;35532:33;;;;-1:-1:-1;;;35532:33:0;;;;;;;;;35578:15;35596:31;:12;35622:4;35596:31;:25;:31;:::i;:::-;35813:1;35803:11;;35218:604;-1:-1:-1;;;;;35218:604:0:o;25068:1908::-;25191:4;25210:13;25191:4;-1:-1:-1;;;;;25281:21:0;;25273:48;;;;-1:-1:-1;;;25273:48:0;;;;;;;;;25357:28;25425:7;-1:-1:-1;;;;;25417:33:0;;:35;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;25417:35:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;25417:35:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;25417:35:0;;;;;;;;;25357:106;;25474:20;;:::i;:::-;25497:13;-1:-1:-1;;;;;25497:23:0;;:25;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;25497:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;25497:25:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;25497:25:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;25497:25:0;;;;;;;;;25474:48;;25533:19;25563:7;-1:-1:-1;;;;;25555:28:0;;:30;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;25555:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;25555:30:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;25555:30:0;;;;;;;;;25683:14;;:21;25749:18;;;;;;;;;;;;;;;;25533:52;;-1:-1:-1;25683:21:0;25715:31;;25683:21;25749:18;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;25749:18:0;-1:-1:-1;25715:52:0;-1:-1:-1;25782:9:0;25778:545;25801:3;25797:1;:7;25778:545;;;25825:20;;:::i;:::-;25848:14;;:17;;25863:1;;25848:17;;;;;;;;;;;;25825:40;;25880:20;25903:50;25940:6;:12;;;25903:6;:19;;;:36;;:50;;;;:::i;:::-;25880:73;-1:-1:-1;26023:14:0;26040:38;25880:73;26066:11;26040:38;:25;:38;:::i;:::-;26023:55;;26122:6;:16;;;26113:6;:25;:60;;26145:16;;;;:28;;26166:6;26145:28;:20;:28;:::i;:::-;26113:60;;;26141:1;26113:60;26093:14;26108:1;26093:17;;;;;;;;;;;;;;;;;:80;26208:19;26191:6;:13;;;:36;;;;;;;;;26188:124;;-1:-1:-1;;26247:49:0;;;;;;;;;;;;-1:-1:-1;;;26247:49:0;;;;26255:5;;-1:-1:-1;26247:49:0;-1:-1:-1;26255:5:0;;-1:-1:-1;26247:49:0;;-1:-1:-1;;;;;;;26247:49:0;26188:124;-1:-1:-1;;;25806:3:0;;25778:545;;;-1:-1:-1;26388:22:0;;;26465:354;26488:3;26484:1;:7;26465:354;;;26512:19;26534:14;26549:1;26534:17;;;;;;;;;;;;;;26512:39;;26584:4;26569:11;:19;26566:242;;-1:-1:-1;;;;;26611:22:0;;;;:54;;;26651:14;26637:11;:28;26611:54;26608:185;;;26706:11;26689:28;;26751:6;:14;;;26766:1;26751:17;;;;;;;;;;;;;;:22;;;26740:33;;26608:185;-1:-1:-1;26493:3:0;;26465:354;;;-1:-1:-1;;;;;;26832:22:0;;26829:102;;-1:-1:-1;;26870:49:0;;;;;;;;;;;;-1:-1:-1;;;26870:49:0;;;;26878:5;;-1:-1:-1;26870:49:0;-1:-1:-1;26878:5:0;;-1:-1:-1;26870:49:0;;-1:-1:-1;;;;;26870:49:0;26829:102;26941:27;;;;;;;;;-1:-1:-1;26941:27:0;;26949:4;;-1:-1:-1;26941:27:0;-1:-1:-1;26959:8:0;-1:-1:-1;;;;;;;25068:1908:0;;;;;;:::o;32907:1150::-;33097:4;33116:13;33144:14;33173:25;33293:28;33361:7;-1:-1:-1;;;;;33353:33:0;;:35;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;33353:35:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;33353:35:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;33353:35:0;;;;;;;;;33293:106;;33410:20;;:::i;:::-;33433:38;;-1:-1:-1;;;33433:38:0;;-1:-1:-1;;;;;33433:23:0;;;;;:38;;33457:13;;33433:38;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;33433:38:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;33433:38:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;33433:38:0;;;;;;;;;33410:61;;33482:22;33507:71;33555:6;:12;;;33507:15;:33;;:71;;;;:::i;:::-;33667:16;;;33681:1;33667:16;;;;;;;;;33482:96;;-1:-1:-1;33640:24:0;;33667:16;;;;;;;105:10:-1;33667:16:0;88:34:-1;-1:-1;;33724:16:0;;;33738:1;33724:16;;;;;;;;;33640:43;;-1:-1:-1;33694:27:0;;33724:16;-1:-1:-1;33724:16:0;;;;;;105:10:-1;33724:16:0;88:34:-1;136:17;;-1:-1;33724:16:0;33694:46;;33764:13;33751:7;33759:1;33751:10;;;;;;;;;;;;;:26;-1:-1:-1;;;;;33751:26:0;;;-1:-1:-1;;;;;33751:26:0;;;;;33804:14;33788:10;33799:1;33788:13;;;;;;;;;;;;;:30;;;;;33844:10;33869:20;33904;33938:48;33957:7;33966;33975:10;33938:18;:48::i;:::-;33829:157;;-1:-1:-1;33829:157:0;-1:-1:-1;33829:157:0;-1:-1:-1;34034:14:0;;-1:-1:-1;;;;;;;;32907:1150:0;;;;;;;:::o;30232:2232::-;30357:4;30376:13;30357:4;-1:-1:-1;;;;;30447:21:0;;30439:48;;;;-1:-1:-1;;;30439:48:0;;;;;;;;;30523:28;30591:7;-1:-1:-1;;;;;30583:33:0;;:35;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;30583:35:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;30583:35:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;30583:35:0;;;;;;;;;30523:106;;30640:20;;:::i;:::-;30663:13;-1:-1:-1;;;;;30663:23:0;;:25;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;30663:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;30663:25:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;30663:25:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;30663:25:0;;;;;;;;;30640:48;;30699:19;30729:7;-1:-1:-1;;;;;30721:28:0;;:30;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;30721:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;30721:30:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;30721:30:0;;;;;;;;;30849:14;;:21;30953:18;;;;;;;;;;;;;;;;30699:52;;-1:-1:-1;30849:21:0;30835:11;;30919:31;;30849:21;30953:18;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;30953:18:0;-1:-1:-1;30919:52:0;-1:-1:-1;30996:9:0;30992:651;31015:3;31011:1;:7;30992:651;;;31039:20;;:::i;:::-;31062:14;;:17;;31077:1;;31062:17;;;;;;;;;;;;31039:40;;31094:20;31117:50;31154:6;:12;;;31117:6;:19;;;:36;;:50;;;;:::i;:::-;31094:73;-1:-1:-1;31237:14:0;31254:38;31094:73;31280:11;31254:38;:25;:38;:::i;:::-;31237:55;;31319:6;:16;;;31310:6;:25;31307:82;;;31356:17;;;;;31307:82;31432:6;:16;;;31423:6;:25;:70;;31465:16;;;;:28;;31486:6;31465:28;:20;:28;:::i;:::-;31423:70;;;-1:-1:-1;;31423:70:0;31403:14;31418:1;31403:17;;;;;;;;;;;;;;;;;:90;31528:19;31511:6;:13;;;:36;;;;;;;;;31508:124;;-1:-1:-1;;31567:49:0;;;;;;;;;;;;-1:-1:-1;;;31567:49:0;;;;31575:5;;-1:-1:-1;31567:49:0;-1:-1:-1;31575:5:0;;-1:-1:-1;31567:49:0;;-1:-1:-1;;;;;;;;31567:49:0;31508:124;-1:-1:-1;;;31020:3:0;;30992:651;;;;31712:1;31694:15;:19;31691:388;;;-1:-1:-1;;31730:49:0;;;;;;;;;;;;-1:-1:-1;;;31730:49:0;;;;31738:5;;-1:-1:-1;31730:49:0;-1:-1:-1;31738:5:0;;-1:-1:-1;31730:49:0;;-1:-1:-1;;;;31730:49:0;31691:388;31800:15;31819:1;31800:20;31797:282;;;31886:9;31882:186;31905:3;31901:1;:7;31882:186;;;-1:-1:-1;;31936:14:0;31951:1;31936:17;;;;;;;;;;;;;;:32;31933:120;;;32000:4;32010:6;:14;;;32025:1;32010:17;;;;;;;;;;;;;;:22;;;31992:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;31933:120;31910:3;;31882:186;;;;31797:282;-1:-1:-1;;32123:19:0;;32207:212;32230:3;32226:1;:7;32207:212;;;32277:11;32257:14;32272:1;32257:17;;;;;;;;;;;;;;:31;32254:154;;;32320:14;;:17;;32335:1;;32320:17;;;;;;;;;;;;:22;;;32309:33;;32375:14;32390:1;32375:17;;;;;;;;;;;;;;32361:31;;32254:154;32235:3;;32207:212;;;-1:-1:-1;32429:27:0;;;;;;;;;-1:-1:-1;32429:27:0;;32437:4;;32429:27;;-1:-1:-1;32447:8:0;;-1:-1:-1;30232:2232:0;-1:-1:-1;;;;;;;;;30232:2232:0:o;36195:1135::-;36286:11;;:::i;:::-;36310;36335:7;-1:-1:-1;;;;;36324:18:0;:7;-1:-1:-1;;;;;36324:18:0;;36310:32;;36353:14;36378:7;36353:33;;36397:28;36457:6;-1:-1:-1;;;;;36457:23:0;;:25;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;36457:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;36457:25:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;36457:25:0;;;;;;;;;36397:96;;36505:12;36519:20;36545:6;-1:-1:-1;;;;;36545:34:0;;36580:6;36588:7;36597:1;36545:54;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;36545:54:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;36545:54:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;36545:54:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;36545:54:0;;;;;;;;;36504:95;;;;;36610:19;36632:6;-1:-1:-1;;;;;36632:18:0;;:20;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;36632:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;36632:20:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;36632:20:0;;;;;;;;;36610:42;;36663:19;;:::i;:::-;36685:31;;-1:-1:-1;;;36685:31:0;;-1:-1:-1;;;;;36685:23:0;;;;;:31;;36709:6;;36685:31;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;36685:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;36685:31:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;36685:31:0;;;;;;;;;36663:53;;36727:20;;:::i;:::-;36751:6;36750:7;:281;;36795:236;;;;;;;;;-1:-1:-1;;;;;36795:236:0;;;;;;;36958:19;36795:236;;37014:5;36795:236;;;;;;36852:26;:24;:26::i;:::-;36795:236;;;;36904:1;36795:236;;;;36934:1;36795:236;;;36750:281;;;36760:32;;-1:-1:-1;;;36760:32:0;;-1:-1:-1;;;;;36760:23:0;;;;;:32;;36784:7;;36760:32;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;36760:32:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;36760:32:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;36760:32:0;;;;;;;;;37049:273;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;37049:273:0;;;;;;;;;;-1:-1:-1;37049:273:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;36195:1135;-1:-1:-1;;;;36195:1135:0:o;18445:167::-;18538:7;18570:34;18587:1;18590;16776:4;18570:16;:34::i;:::-;18563:41;18445:167;-1:-1:-1;;;18445:167:0:o;21437:183::-;21539:9;21573:39;21590:1;21593:5;17192:3;21573:16;:39::i;12400:136::-;12458:7;12485:43;12489:1;12492;12485:43;;;;;;;;;;;;;;;;;:3;:43::i;17327:92::-;16776:4;17327:92;:::o;20677:255::-;20771:7;;20844:17;:1;16776:4;20844:17;:5;:17;:::i;:::-;20832:29;-1:-1:-1;20916:8:0;20832:29;20922:1;20916:8;:5;:8;:::i;:::-;20909:15;20677:255;-1:-1:-1;;;;20677:255:0:o;22909:271::-;23012:9;;23086:18;:1;17192:3;23086:18;:5;:18;:::i;37340:1473::-;37546:4;37565:13;37593:14;37635:19;37657:17;:24;37635:46;;37714:11;37729:1;37714:16;:50;;;;;37749:8;:15;37734:11;:30;37714:50;37692:120;;;;-1:-1:-1;;;37692:120:0;;;;;;;;;37825:14;37850:7;37825:33;;37869:28;37929:6;-1:-1:-1;;;;;37929:23:0;;:25;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;37929:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;37929:25:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;37929:25:0;;;;;;;;;37869:96;;37978:20;;:::i;:::-;38001:13;-1:-1:-1;;;;;38001:23:0;;:25;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;38001:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;38001:25:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;38001:25:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;38001:25:0;;;;;;;;;37978:48;;38043:6;:22;;;:48;;;;38069:13;-1:-1:-1;;;;;38069:20:0;;:22;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;38069:22:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;38069:22:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;38069:22:0;;;;;;;;;38039:122;;;-1:-1:-1;;38108:41:0;;;;;;;;;;;;-1:-1:-1;;;38108:41:0;;;;38116:5;;-1:-1:-1;38108:41:0;-1:-1:-1;38116:5:0;;-1:-1:-1;38108:41:0;;-1:-1:-1;;38108:41:0;38039:122;38188:20;38223;38258:13;38285:197;38315:6;38336:17;38368:8;38377:1;38368:11;;;;;;;;;;;;;;38394:6;:13;;;38422:6;-1:-1:-1;;;;;38422:18:0;;:20;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;38422:20:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;38422:20:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;38422:20:0;;;;;;;;;38457:14;;38285:15;:197::i;:::-;38173:309;;;;;;38498:15;38493:74;;-1:-1:-1;38538:5:0;;-1:-1:-1;38545:6:0;-1:-1:-1;38538:5:0;;-1:-1:-1;38530:25:0;;-1:-1:-1;;;;;38530:25:0;38493:74;38577:11;38591:8;:31;;38621:1;38591:31;;;38602:6;-1:-1:-1;;;;;38602:14:0;;:16;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;38602:16:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;38602:16:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;38602:16:0;;;;;;;;;38577:45;;38633:17;38653:37;38686:3;38653:17;38671:1;38653:20;;;;;;;;;;;;;;:32;;:37;;;;:::i;:::-;38633:57;;38701:22;38726:35;38751:9;38726:17;38744:1;38726:20;;;;;;;;;;;;;;:24;;:35;;;;:::i;:::-;38772:33;;;;;;;;;-1:-1:-1;38772:33:0;;38780:4;;-1:-1:-1;38772:33:0;-1:-1:-1;38701:60:0;-1:-1:-1;;;;;;;;;;;37340:1473:0;;;;;;;;:::o;17550:94::-;17192:3;17550:94;:::o;19085:312::-;19198:7;;19313:8;:1;19319;19313:8;:5;:8;:::i;:::-;19301:20;-1:-1:-1;19377:12:0;19301:20;19383:5;19377:12;:5;:12;:::i;:::-;19370:19;19085:312;-1:-1:-1;;;;;19085:312:0:o;12873:192::-;12959:7;12995:12;12987:6;;;;12979:29;;;;-1:-1:-1;;;12979:29:0;;;;;;;;;;-1:-1:-1;;;13031:5:0;;;12873:192::o;13316:471::-;13374:7;13619:6;13615:47;;-1:-1:-1;13649:1:0;13642:8;;13615:47;13686:5;;;13690:1;13686;:5;:1;13710:5;;;;;:10;13702:56;;;;-1:-1:-1;;;13702:56:0;;;;;;;;14255:132;14313:7;14340:39;14344:1;14347;14340:39;;;;;;;;;;;;;;;;;:3;:39::i;38823:929::-;39091:4;39110:13;39138:4;39170:30;39233:6;-1:-1:-1;;;;;39233:21:0;;:23;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;39233:23:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;39233:23:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;39233:23:0;;;;;;;;;39309:14;;;39321:1;39309:14;;;;;;;;;39170:97;;-1:-1:-1;39278:28:0;;39309:14;;;;;;;105:10:-1;39309:14:0;88:34:-1;136:17;;-1:-1;;39278:45:0;-1:-1:-1;39339:7:0;39334:192;39362:10;:17;39352:28;;:1;:28;;;39334:192;;;39428:6;-1:-1:-1;;;;;39406:28:0;:10;39417:1;39406:13;;;;;;;;;;;;;;;;:18;;;-1:-1:-1;;;;;39406:28:0;;39402:113;;;39474:1;39455:13;39469:1;39455:16;;;;;;;;;;;;;:20;;;;;;;;;;;39494:5;;39402:113;39382:3;;39334:192;;;;39556:14;-1:-1:-1;;;;;39556:33:0;;39608:6;39633;39658:10;39687:13;39719:10;39556:188;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;39556:188:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;39556:188:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;39556:188:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;39556:188:0;;;;;;;;;39536:208;;;;;;;;38823:929;;;;;;;;;;:::o;14917:345::-;15003:7;15105:12;15098:5;15090:28;;;;-1:-1:-1;;;15090:28:0;;;;;;;;;;;15129:9;15145:1;15141;:5;;;;;;;14917:345;-1:-1:-1;;;;;14917:345:0:o;24594:15163::-;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;24594:15163:0;;;;;;-1:-1:-1;;;;;24594:15163:0;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;24594:15163:0;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;-1:-1:-1;24594:15163:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;5:130:-1:-;72:20;;97:33;72:20;97:33;;142:134;220:13;;238:33;220:13;238:33;;321:768;;463:3;456:4;448:6;444:17;440:27;430:2;;481:1;478;471:12;430:2;511:6;505:13;533:94;548:78;619:6;548:78;;;533:94;;;524:103;;644:5;669:6;662:5;655:21;699:4;691:6;687:17;677:27;;721:4;716:3;712:14;705:21;;774:6;821:3;813:4;805:6;801:17;796:3;792:27;789:36;786:2;;;838:1;835;828:12;786:2;863:1;848:235;873:6;870:1;867:13;848:235;;;931:3;953:66;1015:3;1003:10;953:66;;;941:79;;-1:-1;1043:4;1034:14;;;;1071:4;1062:14;;;;;895:1;888:9;848:235;;;852:14;423:666;;;;;;;;1097:128;1172:13;;1190:30;1172:13;1190:30;;1232:178;1323:20;;1348:57;1323:20;1348:57;;1417:164;1510:13;;1528:48;1510:13;1528:48;;1589:444;;1702:3;1695:4;1687:6;1683:17;1679:27;1669:2;;1720:1;1717;1710:12;1669:2;1750:6;1744:13;1772:65;1787:49;1829:6;1787:49;;1772:65;1763:74;;1857:6;1850:5;1843:21;1893:4;1885:6;1881:17;1926:4;1919:5;1915:16;1961:3;1952:6;1947:3;1943:16;1940:25;1937:2;;;1978:1;1975;1968:12;1937:2;1988:39;2020:6;2015:3;2010;1988:39;;;1662:371;;;;;;;;2075:1113;;2197:4;2185:9;2180:3;2176:19;2172:30;2169:2;;;2215:1;2212;2205:12;2169:2;2233:20;2248:4;2233:20;;;2306:24;;2224:29;;-1:-1;2350:18;2339:30;;2336:2;;;2382:1;2379;2372:12;2336:2;2417:99;2512:3;2503:6;2492:9;2488:22;2417:99;;;2392:125;;-1:-1;2584:2;2617:58;2671:3;2647:22;;;2617:58;;;2610:4;2603:5;2599:16;2592:84;2538:149;2748:2;2781:57;2834:3;2825:6;2814:9;2810:22;2781:57;;;2774:4;2767:5;2763:16;2756:83;2697:153;2902:2;2935:57;2988:3;2979:6;2968:9;2964:22;2935:57;;;2928:4;2921:5;2917:16;2910:83;2860:144;3072:3;3106:60;3162:3;3153:6;3142:9;3138:22;3106:60;;;3099:4;3092:5;3088:16;3081:86;3014:164;2163:1025;;;;;3229:1159;;3347:4;3335:9;3330:3;3326:19;3322:30;3319:2;;;3365:1;3362;3355:12;3319:2;3383:20;3398:4;3383:20;;;3374:29;-1:-1;3453:1;3485:60;3541:3;3521:9;3485:60;;;3460:86;;-1:-1;3609:2;3642:75;3713:3;3689:22;;;3642:75;;;3635:4;3628:5;3624:16;3617:101;3567:162;3795:2;3828:57;3881:3;3872:6;3861:9;3857:22;3828:57;;;3821:4;3814:5;3810:16;3803:83;3739:158;3948:2;3981:60;4037:3;4028:6;4017:9;4013:22;3981:60;;;3974:4;3967:5;3963:16;3956:86;3907:146;4108:3;4142:60;4198:3;4189:6;4178:9;4174:22;4142:60;;;4135:4;4128:5;4124:16;4117:86;4063:151;4272:3;4306:60;4362:3;4353:6;4342:9;4338:22;4306:60;;;4299:4;4292:5;4288:16;4281:86;4224:154;3313:1075;;;;;5599:130;5666:20;;5691:33;5666:20;5691:33;;5736:134;5814:13;;5832:33;5814:13;5832:33;;5877:130;5953:13;;5971:31;5953:13;5971:31;;6014:241;;6118:2;6106:9;6097:7;6093:23;6089:32;6086:2;;;6134:1;6131;6124:12;6086:2;6169:1;6186:53;6231:7;6211:9;6186:53;;6262:263;;6377:2;6365:9;6356:7;6352:23;6348:32;6345:2;;;6393:1;6390;6383:12;6345:2;6428:1;6445:64;6501:7;6481:9;6445:64;;6532:491;;;;6670:2;6658:9;6649:7;6645:23;6641:32;6638:2;;;6686:1;6683;6676:12;6638:2;6721:1;6738:53;6783:7;6763:9;6738:53;;;6728:63;;6700:97;6828:2;6846:53;6891:7;6882:6;6871:9;6867:22;6846:53;;;6836:63;;6807:98;6936:2;6954:53;6999:7;6990:6;6979:9;6975:22;6954:53;;;6944:63;;6915:98;6632:391;;;;;;7030:491;;;;7168:2;7156:9;7147:7;7143:23;7139:32;7136:2;;;7184:1;7181;7174:12;7136:2;7219:1;7236:53;7281:7;7261:9;7236:53;;;7226:63;;7198:97;7326:2;7344:53;7389:7;7380:6;7369:9;7365:22;7344:53;;7528:257;;7640:2;7628:9;7619:7;7615:23;7611:32;7608:2;;;7656:1;7653;7646:12;7608:2;7691:1;7708:61;7761:7;7741:9;7708:61;;7792:622;;;;7945:2;7933:9;7924:7;7920:23;7916:32;7913:2;;;7961:1;7958;7951:12;7913:2;7996:1;8013:61;8066:7;8046:9;8013:61;;;8003:71;;7975:105;8132:2;8121:9;8117:18;8111:25;8156:18;8148:6;8145:30;8142:2;;;8188:1;8185;8178:12;8142:2;8208:74;8274:7;8265:6;8254:9;8250:22;8208:74;;;8198:84;;8090:198;8319:2;8337:61;8390:7;8381:6;8370:9;8366:22;8337:61;;8421:628;;;;8577:2;8565:9;8556:7;8552:23;8548:32;8545:2;;;8593:1;8590;8583:12;8545:2;8628:1;8645:61;8698:7;8678:9;8645:61;;;8635:71;;8607:105;8764:2;8753:9;8749:18;8743:25;8788:18;8780:6;8777:30;8774:2;;;8820:1;8817;8810:12;8774:2;8840:74;8906:7;8897:6;8886:9;8882:22;8840:74;;;8830:84;;8722:198;8951:2;8969:64;9025:7;9016:6;9005:9;9001:22;8969:64;;9056:414;;;9201:2;9189:9;9180:7;9176:23;9172:32;9169:2;;;9217:1;9214;9207:12;9169:2;9252:1;9269:77;9338:7;9318:9;9269:77;;;9259:87;;9231:121;9383:2;9401:53;9446:7;9437:6;9426:9;9422:22;9401:53;;;9391:63;;9362:98;9163:307;;;;;;9477:414;;;9622:2;9610:9;9601:7;9597:23;9593:32;9590:2;;;9638:1;9635;9628:12;9590:2;9673:1;9690:77;9759:7;9739:9;9690:77;;;9680:87;;9652:121;9804:2;9822:53;9867:7;9858:6;9847:9;9843:22;9822:53;;9898:386;;10035:2;10023:9;10014:7;10010:23;10006:32;10003:2;;;10051:1;10048;10041:12;10003:2;10086:24;;10130:18;10119:30;;10116:2;;;10162:1;10159;10152:12;10116:2;10182:86;10260:7;10251:6;10240:9;10236:22;10182:86;;10291:308;;10428:3;10416:9;10407:7;10403:23;10399:33;10396:2;;;10445:1;10442;10435:12;10396:2;10480:1;10497:86;10575:7;10555:9;10497:86;;10606:263;;10721:2;10709:9;10700:7;10696:23;10692:32;10689:2;;;10737:1;10734;10727:12;10689:2;10772:1;10789:64;10845:7;10825:9;10789:64;;10877:249;;11000:86;11082:3;11074:6;11000:86;;;-1:-1;;11115:4;11106:14;;10993:133;11135:173;;11222:46;11264:3;11256:6;11222:46;;;-1:-1;;11297:4;11288:14;;11215:93;11317:165;;11400:42;11438:3;11430:6;11400:42;;11490:103;11563:24;11581:5;11563:24;;;11558:3;11551:37;11545:48;;;11791:834;;11972:72;12038:5;11972:72;;;12057:104;12154:6;12149:3;12057:104;;;12050:111;;12182:74;12250:5;12182:74;;;12276:7;12304:1;12289:314;12314:6;12311:1;12308:13;12289:314;;;12381:6;12375:13;12402:99;12497:3;12482:13;12402:99;;;12395:106;;12518:78;12589:6;12518:78;;;12508:88;-1:-1;;12336:1;12329:9;12289:314;;;-1:-1;12616:3;;11951:674;-1:-1;;;;;11951:674;12664:690;;12809:54;12857:5;12809:54;;;12876:86;12955:6;12950:3;12876:86;;;12869:93;;12983:56;13033:5;12983:56;;;13059:7;13087:1;13072:260;13097:6;13094:1;13091:13;13072:260;;;13164:6;13158:13;13185:63;13244:3;13229:13;13185:63;;;13178:70;;13265:60;13318:6;13265:60;;;13255:70;-1:-1;;13119:1;13112:9;13072:260;;13389:674;;13530:52;13576:5;13530:52;;;13595:84;13672:6;13667:3;13595:84;;;13588:91;;13700:54;13748:5;13700:54;;;13774:7;13802:1;13787:254;13812:6;13809:1;13806:13;13787:254;;;13879:6;13873:13;13900:59;13955:3;13940:13;13900:59;;;13893:66;;13976:58;14027:6;13976:58;;;13966:68;-1:-1;;13834:1;13827:9;13787:254;;14071:94;14138:21;14153:5;14138:21;;14283:142;14369:50;14413:5;14369:50;;14432:142;14523:45;14562:5;14523:45;;14581:347;;14693:39;14726:5;14693:39;;;14744:71;14808:6;14803:3;14744:71;;;14737:78;;14820:52;14865:6;14860:3;14853:4;14846:5;14842:16;14820:52;;;14893:29;14915:6;14893:29;;;14884:39;;;;14673:255;-1:-1;;;14673:255;14936:320;;15096:67;15160:2;15155:3;15096:67;;;-1:-1;;;15176:43;;15247:2;15238:12;;15082:174;-1:-1;;15082:174;15265:316;;15425:67;15489:2;15484:3;15425:67;;;-1:-1;;;15505:39;;15572:2;15563:12;;15411:170;-1:-1;;15411:170;15590:314;;15750:67;15814:2;15809:3;15750:67;;;-1:-1;;;15830:37;;15895:2;15886:12;;15736:168;-1:-1;;15736:168;15913:370;;16073:67;16137:2;16132:3;16073:67;;;16173:34;16153:55;;-1:-1;;;16237:2;16228:12;;16221:25;16274:2;16265:12;;16059:224;-1:-1;;16059:224;16292:312;;16452:67;16516:2;16511:3;16452:67;;;-1:-1;;;16532:35;;16595:2;16586:12;;16438:166;-1:-1;;16438:166;16613:312;;16773:67;16837:2;16832:3;16773:67;;;-1:-1;;;16853:35;;16916:2;16907:12;;16759:166;-1:-1;;16759:166;16998:1111;17187:23;;17121:4;17112:14;;;17216:63;17116:3;17187:23;17216:63;;;17141:144;17360:4;17353:5;17349:16;17343:23;17372:76;17442:4;17437:3;17433:14;17419:12;17372:76;;;17295:159;17543:4;17536:5;17532:16;17526:23;17555:57;17606:4;17601:3;17597:14;17583:12;17555:57;;;17464:154;17692:4;17685:5;17681:16;17675:23;17704:63;17761:4;17756:3;17752:14;17738:12;17704:63;;;17628:145;17851:4;17844:5;17840:16;17834:23;17863:63;17920:4;17915:3;17911:14;17897:12;17863:63;;;17783:149;18013:4;18006:5;18002:16;17996:23;18025:63;18082:4;18077:3;18073:14;18059:12;18025:63;;;17942:152;17094:1015;;;;18116:103;18189:24;18207:5;18189:24;;18346:97;18415:22;18431:5;18415:22;;18450:213;18568:2;18553:18;;18582:71;18557:9;18626:6;18582:71;;18670:451;18852:2;18837:18;;18866:71;18841:9;18910:6;18866:71;;;18948:72;19016:2;19005:9;19001:18;18992:6;18948:72;;;19031:80;19107:2;19096:9;19092:18;19083:6;19031:80;;19128:511;19316:2;19301:18;;19330:65;19305:9;19368:6;19330:65;;;19443:9;19437:4;19433:20;19428:2;19417:9;19413:18;19406:48;19468:78;19541:4;19532:6;19468:78;;;19460:86;;19557:72;19625:2;19614:9;19610:18;19601:6;19557:72;;19646:623;19862:3;19847:19;;19877:65;19851:9;19915:6;19877:65;;;19990:9;19984:4;19980:20;19975:2;19964:9;19960:18;19953:48;20015:78;20088:4;20079:6;20015:78;;;20007:86;;20104:72;20172:2;20161:9;20157:18;20148:6;20104:72;;;20187;20255:2;20244:9;20240:18;20231:6;20187:72;;20276:1155;20682:3;20667:19;;20697:65;20671:9;20735:6;20697:65;;;20773:72;20841:2;20830:9;20826:18;20817:6;20773:72;;;20893:9;20887:4;20883:20;20878:2;20867:9;20863:18;20856:48;20918:144;21057:4;21048:6;20918:144;;;20910:152;;21110:9;21104:4;21100:20;21095:2;21084:9;21080:18;21073:48;21135:104;21234:4;21225:6;21135:104;;;21127:112;;21288:9;21282:4;21278:20;21272:3;21261:9;21257:19;21250:49;21313:108;21416:4;21407:6;21313:108;;;21305:116;20653:778;-1:-1;;;;;;;20653:778;21438:301;21576:2;21590:47;;;21561:18;;21651:78;21561:18;21715:6;21651:78;;21746:407;21937:2;21951:47;;;21922:18;;22012:131;21922:18;22012:131;;22160:407;22351:2;22365:47;;;22336:18;;22426:131;22336:18;22426:131;;22574:407;22765:2;22779:47;;;22750:18;;22840:131;22750:18;22840:131;;22988:407;23179:2;23193:47;;;23164:18;;23254:131;23164:18;23254:131;;23402:407;23593:2;23607:47;;;23578:18;;23668:131;23578:18;23668:131;;23816:407;24007:2;24021:47;;;23992:18;;24082:131;23992:18;24082:131;;24230:213;24348:2;24333:18;;24362:71;24337:9;24406:6;24362:71;;24450:256;24512:2;24506:9;24538:17;;;24613:18;24598:34;;24634:22;;;24595:62;24592:2;;;24670:1;24667;24660:12;24592:2;24686;24679:22;24490:216;;-1:-1;24490:216;24713:318;;24886:18;24878:6;24875:30;24872:2;;;24918:1;24915;24908:12;24872:2;-1:-1;24953:4;24941:17;;;25006:15;;24809:222;25038:322;;25182:18;25174:6;25171:30;25168:2;;;25214:1;25211;25204:12;25168:2;-1:-1;25345:4;25281;25258:17;;;;-1:-1;;25254:33;25335:15;;25105:255;25367:169;25509:4;25500:14;;25457:79;25857:155;25978:12;;25949:63;26796:196;26932:19;;;26981:4;26972:14;;26925:67;27544:91;;27606:24;27624:5;27606:24;;27642:85;27708:13;27701:21;;27684:43;27734:115;;27820:24;27838:5;27820:24;;27856:136;27933:5;27939:48;27933:5;27939:48;;;27916:76;;;;27999:121;-1:-1;;;;;28061:54;;28044:76;28127:72;28189:5;28172:27;28206:81;28277:4;28266:16;;28249:38;28294:136;;28386:39;28419:5;28386:39;;28437:116;;28524:24;28542:5;28524:24;;28561:268;28626:1;28633:101;28647:6;28644:1;28641:13;28633:101;;;28714:11;;;28708:18;28695:11;;;28688:39;28669:2;28662:10;28633:101;;;28749:6;28746:1;28743:13;28740:2;;;-1:-1;;28814:1;28796:16;;28789:27;28610:219;28837:97;28925:2;28905:14;-1:-1;;28901:28;;28885:49;28942:106;29026:1;29019:5;29016:12;29006:2;;29032:9;29006:2;29000:48;;29055:117;29124:24;29142:5;29124:24;;;29117:5;29114:35;29104:2;;29163:1;29160;29153:12;29179:111;29245:21;29260:5;29245:21;;29297:165;29390:48;29432:5;29390:48;;29469:109;29553:1;29546:5;29543:12;29533:2;;29569:1;29566;29559:12;29585:117;29654:24;29672:5;29654:24;;29709:113;29776:22;29792:5;29776:22;

Swarm Source

bzzr://2ae504ac92d28aea501d3291c32fa6e8eb7d0097ae887d882fc2ee67a87368a1
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.

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.