ETH Price: $3,264.84 (+4.84%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
StableMasterFront

Compiler Version
v0.8.7+commit.e28d00a7

Optimization Enabled:
Yes with 830 runs

Other Settings:
default evmVersion, GNU GPLv3 license
File 1 of 29 : StableMasterFront.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "./StableMaster.sol";

/// @title StableMasterFront
/// @author Angle Core Team
/// @notice `StableMaster` is the contract handling all the collateral types accepted for a given stablecoin
/// It does all the accounting and is the point of entry in the protocol for stable holders and seekers as well as SLPs
/// @dev This file contains the front end, that is all external functions associated to the given stablecoin
contract StableMasterFront is StableMaster {
    using SafeERC20 for IERC20;

    // ============================ CONSTRUCTORS AND DEPLOYERS =====================

    /// @notice Initializes the `StableMaster` contract
    /// @param core_ Address of the `Core` contract handling all the different `StableMaster` contracts
    function initialize(address core_) external zeroCheck(core_) initializer {
        __AccessControl_init();
        // Access control
        _core = ICore(core_);
        _setupRole(CORE_ROLE, core_);
        // `Core` is admin of all roles
        _setRoleAdmin(CORE_ROLE, CORE_ROLE);
        _setRoleAdmin(GOVERNOR_ROLE, CORE_ROLE);
        _setRoleAdmin(GUARDIAN_ROLE, CORE_ROLE);
        // All the roles that are specific to a given collateral can be changed by the governor
        // in the `deployCollateral`, `revokeCollateral` and `setFeeManager` functions by updating the `contractMap`
    }

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() initializer {}

    // ============================= USERS =========================================

    /// @notice Lets a user send collateral to the system to mint stablecoins
    /// @param amount Amount of collateral sent
    /// @param user Address of the contract or the person to give the minted tokens to
    /// @param poolManager Address of the `PoolManager` of the required collateral
    /// @param minStableAmount Minimum amount of stablecoins the user wants to get with this transaction
    /// @dev This function works as a swap from a user perspective from collateral to stablecoins
    /// @dev It is impossible to mint tokens and to have them sent to the zero address: there
    /// would be an issue with the `_mint` function called by the `AgToken` contract
    /// @dev The parameter `minStableAmount` serves as a slippage protection for users
    /// @dev From a user perspective, this function is equivalent to a swap between collateral and
    /// stablecoins
    function mint(
        uint256 amount,
        address user,
        IPoolManager poolManager,
        uint256 minStableAmount
    ) external {
        Collateral storage col = collateralMap[poolManager];
        _contractMapCheck(col);
        // Checking if the contract is paused for this agent
        _whenNotPaused(STABLE, address(poolManager));

        // No overflow check are needed for the amount since it's never casted to `int` and Solidity 0.8.0
        // automatically handles overflows
        col.token.safeTransferFrom(msg.sender, address(poolManager), amount);

        // Getting a quote for the amount of stablecoins to issue
        // We read the lowest oracle value we get for this collateral/stablecoin pair: it's the one
        // that is most at the advantage of the protocol
        // Decimals are handled directly in the oracle contract
        uint256 amountForUserInStable = col.oracle.readQuoteLower(amount);

        // Getting the fees paid for this transaction, expressed in `BASE_PARAMS`
        // Floor values are taken for fees computation, as what is earned by users is lost by SLP
        // when calling `_updateSanRate` and vice versa
        uint256 fees = _computeFeeMint(amountForUserInStable, col);

        // Computing the net amount that will be taken into account for this user by deducing fees
        amountForUserInStable = (amountForUserInStable * (BASE_PARAMS - fees)) / BASE_PARAMS;
        // Checking if the user got more stablecoins than the least amount specified in the parameters of the
        // function
        require(amountForUserInStable >= minStableAmount, "15");

        // Updating the `stocksUsers` for this collateral, that is the amount of collateral that was
        // brought by users
        col.stocksUsers += amountForUserInStable;
        // Checking if stablecoins can still be issued using this collateral type
        require(col.stocksUsers <= col.feeData.capOnStableMinted, "16");

        // Event needed to track `col.stocksUsers` off-chain
        emit MintedStablecoins(address(poolManager), amount, amountForUserInStable);

        // Distributing the fees taken to SLPs
        // The `fees` variable computed above is a proportion expressed in `BASE_PARAMS`.
        // To compute the amount of fees in collateral value, we can directly use the `amount` of collateral
        // entered by the user
        // Not all the fees are distributed to SLPs, a portion determined by `col.slpData.feesForSLPs` goes to surplus
        _updateSanRate((amount * fees * col.slpData.feesForSLPs) / (BASE_PARAMS**2), col);

        // Minting
        agToken.mint(user, amountForUserInStable);
    }

    /// @notice Lets a user burn agTokens (stablecoins) and receive the collateral specified by the `poolManager`
    /// in exchange
    /// @param amount Amount of stable asset burnt
    /// @param burner Address from which the agTokens will be burnt
    /// @param dest Address where collateral is going to be
    /// @param poolManager Collateral type requested by the user burning
    /// @param minCollatAmount Minimum amount of collateral that the user is willing to get for this transaction
    /// @dev The `msg.sender` should have approval to burn from the `burner` or the `msg.sender` should be the `burner`
    /// @dev If there are not enough reserves this transaction will revert and the user will have to come back to the
    /// protocol with a correct amount. Checking for the reserves currently available in the `PoolManager`
    /// is something that should be handled by the front interacting with this contract
    /// @dev In case there are not enough reserves, strategies should be harvested or their debt ratios should be adjusted
    /// by governance to make sure that users, HAs or SLPs withdrawing always have free collateral they can use
    /// @dev From a user perspective, this function is equivalent to a swap from stablecoins to collateral
    function burn(
        uint256 amount,
        address burner,
        address dest,
        IPoolManager poolManager,
        uint256 minCollatAmount
    ) external {
        // Searching collateral data
        Collateral storage col = collateralMap[poolManager];
        // Checking the collateral requested
        _contractMapCheck(col);
        _whenNotPaused(STABLE, address(poolManager));

        // Checking if the amount is not going to make the `stocksUsers` negative
        // A situation like that is likely to happen if users mint using one collateral type and in volume redeem
        // another collateral type
        // In this situation, governance should rapidly react to pause the pool and then rebalance the `stocksUsers`
        // between different collateral types, or at least rebalance what is stored in the reserves through
        // the `recoverERC20` function followed by a swap and then a transfer
        require(amount <= col.stocksUsers, "17");

        // Burning the tokens will revert if there are not enough tokens in balance or if the `msg.sender`
        // does not have approval from the burner
        // A reentrancy attack is potentially possible here as state variables are written after the burn,
        // but as the `AgToken` is a protocol deployed contract, it can be trusted. Still, `AgToken` is
        // upgradeable by governance, the following could become risky in case of a governance attack
        if (burner == msg.sender) {
            agToken.burnSelf(amount, burner);
        } else {
            agToken.burnFrom(amount, burner, msg.sender);
        }

        // Getting the highest possible oracle value
        uint256 oracleValue = col.oracle.readUpper();

        // Converting amount of agTokens in collateral and computing how much should be reimbursed to the user
        // Amount is in `BASE_TOKENS` and the outputted collateral amount should be in collateral base
        uint256 amountInC = (amount * col.collatBase) / oracleValue;

        // Computing how much of collateral can be redeemed by the user after taking fees
        // The value of the fees here is `_computeFeeBurn(amount,col)` (it is a proportion expressed in `BASE_PARAMS`)
        // The real value of what can be redeemed by the user is `amountInC * (BASE_PARAMS - fees) / BASE_PARAMS`,
        // but we prefer to avoid doing multiplications after divisions
        uint256 redeemInC = (amount * (BASE_PARAMS - _computeFeeBurn(amount, col)) * col.collatBase) /
            (oracleValue * BASE_PARAMS);
        require(redeemInC >= minCollatAmount, "15");

        // Updating the `stocksUsers` that is the amount of collateral that was brought by users
        col.stocksUsers -= amount;

        // Event needed to track `col.stocksUsers` off-chain
        emit BurntStablecoins(address(poolManager), amount, redeemInC);

        // Computing the exact amount of fees from this transaction and accumulating it for SLPs
        _updateSanRate(((amountInC - redeemInC) * col.slpData.feesForSLPs) / BASE_PARAMS, col);

        col.token.safeTransferFrom(address(poolManager), dest, redeemInC);
    }

    // ============================== SLPs =========================================

    /// @notice Lets a SLP enter the protocol by sending collateral to the system in exchange of sanTokens
    /// @param user Address of the SLP to send sanTokens to
    /// @param amount Amount of collateral sent
    /// @param poolManager Address of the `PoolManager` of the required collateral
    function deposit(
        uint256 amount,
        address user,
        IPoolManager poolManager
    ) external {
        // Searching collateral data
        Collateral storage col = collateralMap[poolManager];
        _contractMapCheck(col);
        _whenNotPaused(SLP, address(poolManager));
        _updateSanRate(0, col);

        // No overflow check needed for the amount since it's never casted to int and Solidity versions above 0.8.0
        // automatically handle overflows
        col.token.safeTransferFrom(msg.sender, address(poolManager), amount);
        col.sanToken.mint(user, (amount * BASE_TOKENS) / col.sanRate);
    }

    /// @notice Lets a SLP burn of sanTokens and receive the corresponding collateral back in exchange at the
    /// current exchange rate between sanTokens and collateral
    /// @param amount Amount of sanTokens burnt by the SLP
    /// @param burner Address that will burn its sanTokens
    /// @param dest Address that will receive the collateral
    /// @param poolManager Address of the `PoolManager` of the required collateral
    /// @dev The `msg.sender` should have approval to burn from the `burner` or the `msg.sender` should be the `burner`
    /// @dev This transaction will fail if the `PoolManager` does not have enough reserves, the front will however be here
    /// to notify them that they cannot withdraw
    /// @dev In case there are not enough reserves, strategies should be harvested or their debt ratios should be adjusted
    /// by governance to make sure that users, HAs or SLPs withdrawing always have free collateral they can use
    function withdraw(
        uint256 amount,
        address burner,
        address dest,
        IPoolManager poolManager
    ) external {
        Collateral storage col = collateralMap[poolManager];
        _contractMapCheck(col);
        _whenNotPaused(SLP, address(poolManager));
        _updateSanRate(0, col);

        if (burner == msg.sender) {
            col.sanToken.burnSelf(amount, burner);
        } else {
            col.sanToken.burnFrom(amount, burner, msg.sender);
        }
        // Computing the amount of collateral to give back to the SLP depending on slippage and on the `sanRate`
        uint256 redeemInC = (amount * (BASE_PARAMS - col.slpData.slippage) * col.sanRate) / (BASE_TOKENS * BASE_PARAMS);

        col.token.safeTransferFrom(address(poolManager), dest, redeemInC);
    }
}

File 2 of 29 : Initializable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

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

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }
}

File 3 of 29 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

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

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

File 4 of 29 : StringsUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

File 5 of 29 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

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

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

File 6 of 29 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

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

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

File 7 of 29 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

File 8 of 29 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

File 9 of 29 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

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

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

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 10 of 29 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

File 11 of 29 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 12 of 29 : AccessControlUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.7;

import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

import "../interfaces/IAccessControl.sol";

/**
 * @dev This contract is fully forked from OpenZeppelin `AccessControlUpgradeable`.
 * The only difference is the removal of the ERC165 implementation as it's not
 * needed in Angle.
 *
 * Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControlUpgradeable is Initializable, IAccessControl {
    function __AccessControl_init() internal initializer {
        __AccessControl_init_unchained();
    }

    function __AccessControl_init_unchained() internal initializer {}

    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, msg.sender);
        _;
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
     */
    function _checkRole(bytes32 role, address account) internal view {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        StringsUpgradeable.toHexString(uint160(account), 20),
                        " is missing role ",
                        StringsUpgradeable.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external override {
        require(account == msg.sender, "71");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     */
    function _setupRole(bytes32 role, address account) internal {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal {
        emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
        _roles[role].adminRole = adminRole;
    }

    function _grantRole(bytes32 role, address account) internal {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, msg.sender);
        }
    }

    function _revokeRole(bytes32 role, address account) internal {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, msg.sender);
        }
    }

    uint256[49] private __gap;
}

File 13 of 29 : IAccessControl.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

/// @title IAccessControl
/// @author Forked from OpenZeppelin
/// @notice Interface for `AccessControl` contracts
interface IAccessControl {
    function hasRole(bytes32 role, address account) external view returns (bool);

    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    function grantRole(bytes32 role, address account) external;

    function revokeRole(bytes32 role, address account) external;

    function renounceRole(bytes32 role, address account) external;
}

File 14 of 29 : IAgToken.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

/// @title IAgToken
/// @author Angle Core Team
/// @notice Interface for the stablecoins `AgToken` contracts
/// @dev The only functions that are left in the interface are the functions which are used
/// at another point in the protocol by a different contract
interface IAgToken is IERC20Upgradeable {
    // ======================= `StableMaster` functions ============================
    function mint(address account, uint256 amount) external;

    function burnFrom(
        uint256 amount,
        address burner,
        address sender
    ) external;

    function burnSelf(uint256 amount, address burner) external;

    // ========================= External function =================================

    function stableMaster() external view returns (address);
}

File 15 of 29 : ICollateralSettler.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

/// @title ICollateralSettler
/// @author Angle Core Team
/// @notice Interface for the collateral settlement contracts
interface ICollateralSettler {
    function triggerSettlement(
        uint256 _oracleValue,
        uint256 _sanRate,
        uint256 _stocksUsers
    ) external;
}

File 16 of 29 : ICore.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "./IStableMaster.sol";

/// @title ICore
/// @author Angle Core Team
/// @dev Interface for the functions of the `Core` contract
interface ICore {
    function revokeStableMaster(address stableMaster) external;

    function addGovernor(address _governor) external;

    function removeGovernor(address _governor) external;

    function setGuardian(address _guardian) external;

    function revokeGuardian() external;

    function governorList() external view returns (address[] memory);

    function stablecoinList() external view returns (address[] memory);

    function guardian() external view returns (address);
}

File 17 of 29 : IERC721.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface IERC721 is IERC165 {
    function balanceOf(address owner) external view returns (uint256 balance);

    function ownerOf(uint256 tokenId) external view returns (address owner);

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    function approve(address to, uint256 tokenId) external;

    function getApproved(uint256 tokenId) external view returns (address operator);

    function setApprovalForAll(address operator, bool _approved) external;

    function isApprovedForAll(address owner, address operator) external view returns (bool);

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

interface IERC721Metadata is IERC721 {
    function name() external view returns (string memory);

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

    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 18 of 29 : IFeeManager.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "./IAccessControl.sol";

/// @title IFeeManagerFunctions
/// @author Angle Core Team
/// @dev Interface for the `FeeManager` contract
interface IFeeManagerFunctions is IAccessControl {
    // ================================= Keepers ===================================

    function updateUsersSLP() external;

    function updateHA() external;

    // ================================= Governance ================================

    function deployCollateral(
        address[] memory governorList,
        address guardian,
        address _perpetualManager
    ) external;

    function setFees(
        uint256[] memory xArray,
        uint64[] memory yArray,
        uint8 typeChange
    ) external;

    function setHAFees(uint64 _haFeeDeposit, uint64 _haFeeWithdraw) external;
}

/// @title IFeeManager
/// @author Angle Core Team
/// @notice Previous interface with additionnal getters for public variables and mappings
/// @dev We need these getters as they are used in other contracts of the protocol
interface IFeeManager is IFeeManagerFunctions {
    function stableMaster() external view returns (address);

    function perpetualManager() external view returns (address);
}

File 19 of 29 : IOracle.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

/// @title IOracle
/// @author Angle Core Team
/// @notice Interface for Angle's oracle contracts reading oracle rates from both UniswapV3 and Chainlink
/// from just UniswapV3 or from just Chainlink
interface IOracle {
    function read() external view returns (uint256);

    function readAll() external view returns (uint256 lowerRate, uint256 upperRate);

    function readLower() external view returns (uint256);

    function readUpper() external view returns (uint256);

    function readQuote(uint256 baseAmount) external view returns (uint256);

    function readQuoteLower(uint256 baseAmount) external view returns (uint256);

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

File 20 of 29 : IPerpetualManager.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "./IERC721.sol";
import "./IFeeManager.sol";
import "./IOracle.sol";
import "./IAccessControl.sol";

/// @title Interface of the contract managing perpetuals
/// @author Angle Core Team
/// @dev Front interface, meaning only user-facing functions
interface IPerpetualManagerFront is IERC721Metadata {
    function openPerpetual(
        address owner,
        uint256 amountBrought,
        uint256 amountCommitted,
        uint256 maxOracleRate,
        uint256 minNetMargin
    ) external returns (uint256 perpetualID);

    function closePerpetual(
        uint256 perpetualID,
        address to,
        uint256 minCashOutAmount
    ) external;

    function addToPerpetual(uint256 perpetualID, uint256 amount) external;

    function removeFromPerpetual(
        uint256 perpetualID,
        uint256 amount,
        address to
    ) external;

    function liquidatePerpetuals(uint256[] memory perpetualIDs) external;

    function forceClosePerpetuals(uint256[] memory perpetualIDs) external;

    // ========================= External View Functions =============================

    function getCashOutAmount(uint256 perpetualID, uint256 rate) external view returns (uint256, uint256);

    function isApprovedOrOwner(address spender, uint256 perpetualID) external view returns (bool);
}

/// @title Interface of the contract managing perpetuals
/// @author Angle Core Team
/// @dev This interface does not contain user facing functions, it just has functions that are
/// interacted with in other parts of the protocol
interface IPerpetualManagerFunctions is IAccessControl {
    // ================================= Governance ================================

    function deployCollateral(
        address[] memory governorList,
        address guardian,
        IFeeManager feeManager,
        IOracle oracle_
    ) external;

    function setFeeManager(IFeeManager feeManager_) external;

    function setHAFees(
        uint64[] memory _xHAFees,
        uint64[] memory _yHAFees,
        uint8 deposit
    ) external;

    function setTargetAndLimitHAHedge(uint64 _targetHAHedge, uint64 _limitHAHedge) external;

    function setKeeperFeesLiquidationRatio(uint64 _keeperFeesLiquidationRatio) external;

    function setKeeperFeesCap(uint256 _keeperFeesLiquidationCap, uint256 _keeperFeesClosingCap) external;

    function setKeeperFeesClosing(uint64[] memory _xKeeperFeesClosing, uint64[] memory _yKeeperFeesClosing) external;

    function setLockTime(uint64 _lockTime) external;

    function setBoundsPerpetual(uint64 _maxLeverage, uint64 _maintenanceMargin) external;

    function pause() external;

    function unpause() external;

    // ==================================== Keepers ================================

    function setFeeKeeper(uint64 feeDeposit, uint64 feesWithdraw) external;

    // =============================== StableMaster ================================

    function setOracle(IOracle _oracle) external;
}

/// @title IPerpetualManager
/// @author Angle Core Team
/// @notice Previous interface with additionnal getters for public variables
interface IPerpetualManager is IPerpetualManagerFunctions {
    function poolManager() external view returns (address);

    function oracle() external view returns (address);

    function targetHAHedge() external view returns (uint64);

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

File 21 of 29 : IPoolManager.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "./IFeeManager.sol";
import "./IPerpetualManager.sol";
import "./IOracle.sol";

// Struct for the parameters associated to a strategy interacting with a collateral `PoolManager`
// contract
struct StrategyParams {
    // Timestamp of last report made by this strategy
    // It is also used to check if a strategy has been initialized
    uint256 lastReport;
    // Total amount the strategy is expected to have
    uint256 totalStrategyDebt;
    // The share of the total assets in the `PoolManager` contract that the `strategy` can access to.
    uint256 debtRatio;
}

/// @title IPoolManagerFunctions
/// @author Angle Core Team
/// @notice Interface for the collateral poolManager contracts handling each one type of collateral for
/// a given stablecoin
/// @dev Only the functions used in other contracts of the protocol are left here
interface IPoolManagerFunctions {
    // ============================ Constructor ====================================

    function deployCollateral(
        address[] memory governorList,
        address guardian,
        IPerpetualManager _perpetualManager,
        IFeeManager feeManager,
        IOracle oracle
    ) external;

    // ============================ Yield Farming ==================================

    function creditAvailable() external view returns (uint256);

    function debtOutstanding() external view returns (uint256);

    function report(
        uint256 _gain,
        uint256 _loss,
        uint256 _debtPayment
    ) external;

    // ============================ Governance =====================================

    function addGovernor(address _governor) external;

    function removeGovernor(address _governor) external;

    function setGuardian(address _guardian, address guardian) external;

    function revokeGuardian(address guardian) external;

    function setFeeManager(IFeeManager _feeManager) external;

    // ============================= Getters =======================================

    function getBalance() external view returns (uint256);

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

/// @title IPoolManager
/// @author Angle Core Team
/// @notice Previous interface with additionnal getters for public variables and mappings
/// @dev Used in other contracts of the protocol
interface IPoolManager is IPoolManagerFunctions {
    function stableMaster() external view returns (address);

    function perpetualManager() external view returns (address);

    function token() external view returns (address);

    function feeManager() external view returns (address);

    function totalDebt() external view returns (uint256);

    function strategies(address _strategy) external view returns (StrategyParams memory);
}

File 22 of 29 : ISanToken.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

/// @title ISanToken
/// @author Angle Core Team
/// @notice Interface for Angle's `SanToken` contract that handles sanTokens, tokens that are given to SLPs
/// contributing to a collateral for a given stablecoin
interface ISanToken is IERC20Upgradeable {
    // ================================== StableMaster =============================

    function mint(address account, uint256 amount) external;

    function burnFrom(
        uint256 amount,
        address burner,
        address sender
    ) external;

    function burnSelf(uint256 amount, address burner) external;

    function stableMaster() external view returns (address);

    function poolManager() external view returns (address);
}

File 23 of 29 : IStableMaster.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

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

// Normally just importing `IPoolManager` should be sufficient, but for clarity here
// we prefer to import all concerned interfaces
import "./IPoolManager.sol";
import "./IOracle.sol";
import "./IPerpetualManager.sol";
import "./ISanToken.sol";

// Struct to handle all the parameters to manage the fees
// related to a given collateral pool (associated to the stablecoin)
struct MintBurnData {
    // Values of the thresholds to compute the minting fees
    // depending on HA hedge (scaled by `BASE_PARAMS`)
    uint64[] xFeeMint;
    // Values of the fees at thresholds (scaled by `BASE_PARAMS`)
    uint64[] yFeeMint;
    // Values of the thresholds to compute the burning fees
    // depending on HA hedge (scaled by `BASE_PARAMS`)
    uint64[] xFeeBurn;
    // Values of the fees at thresholds (scaled by `BASE_PARAMS`)
    uint64[] yFeeBurn;
    // Max proportion of collateral from users that can be covered by HAs
    // It is exactly the same as the parameter of the same name in `PerpetualManager`, whenever one is updated
    // the other changes accordingly
    uint64 targetHAHedge;
    // Minting fees correction set by the `FeeManager` contract: they are going to be multiplied
    // to the value of the fees computed using the hedge curve
    // Scaled by `BASE_PARAMS`
    uint64 bonusMalusMint;
    // Burning fees correction set by the `FeeManager` contract: they are going to be multiplied
    // to the value of the fees computed using the hedge curve
    // Scaled by `BASE_PARAMS`
    uint64 bonusMalusBurn;
    // Parameter used to limit the number of stablecoins that can be issued using the concerned collateral
    uint256 capOnStableMinted;
}

// Struct to handle all the variables and parameters to handle SLPs in the protocol
// including the fraction of interests they receive or the fees to be distributed to
// them
struct SLPData {
    // Last timestamp at which the `sanRate` has been updated for SLPs
    uint256 lastBlockUpdated;
    // Fees accumulated from previous blocks and to be distributed to SLPs
    uint256 lockedInterests;
    // Max interests used to update the `sanRate` in a single block
    // Should be in collateral token base
    uint256 maxInterestsDistributed;
    // Amount of fees left aside for SLPs and that will be distributed
    // when the protocol is collateralized back again
    uint256 feesAside;
    // Part of the fees normally going to SLPs that is left aside
    // before the protocol is collateralized back again (depends on collateral ratio)
    // Updated by keepers and scaled by `BASE_PARAMS`
    uint64 slippageFee;
    // Portion of the fees from users minting and burning
    // that goes to SLPs (the rest goes to surplus)
    uint64 feesForSLPs;
    // Slippage factor that's applied to SLPs exiting (depends on collateral ratio)
    // If `slippage = BASE_PARAMS`, SLPs can get nothing, if `slippage = 0` they get their full claim
    // Updated by keepers and scaled by `BASE_PARAMS`
    uint64 slippage;
    // Portion of the interests from lending
    // that goes to SLPs (the rest goes to surplus)
    uint64 interestsForSLPs;
}

/// @title IStableMasterFunctions
/// @author Angle Core Team
/// @notice Interface for the `StableMaster` contract
interface IStableMasterFunctions {
    function deploy(
        address[] memory _governorList,
        address _guardian,
        address _agToken
    ) external;

    // ============================== Lending ======================================

    function accumulateInterest(uint256 gain) external;

    function signalLoss(uint256 loss) external;

    // ============================== HAs ==========================================

    function getStocksUsers() external view returns (uint256 maxCAmountInStable);

    function convertToSLP(uint256 amount, address user) external;

    // ============================== Keepers ======================================

    function getCollateralRatio() external returns (uint256);

    function setFeeKeeper(
        uint64 feeMint,
        uint64 feeBurn,
        uint64 _slippage,
        uint64 _slippageFee
    ) external;

    // ============================== AgToken ======================================

    function updateStocksUsers(uint256 amount, address poolManager) external;

    // ============================= Governance ====================================

    function setCore(address newCore) external;

    function addGovernor(address _governor) external;

    function removeGovernor(address _governor) external;

    function setGuardian(address newGuardian, address oldGuardian) external;

    function revokeGuardian(address oldGuardian) external;

    function setCapOnStableAndMaxInterests(
        uint256 _capOnStableMinted,
        uint256 _maxInterestsDistributed,
        IPoolManager poolManager
    ) external;

    function setIncentivesForSLPs(
        uint64 _feesForSLPs,
        uint64 _interestsForSLPs,
        IPoolManager poolManager
    ) external;

    function setUserFees(
        IPoolManager poolManager,
        uint64[] memory _xFee,
        uint64[] memory _yFee,
        uint8 _mint
    ) external;

    function setTargetHAHedge(uint64 _targetHAHedge) external;

    function pause(bytes32 agent, IPoolManager poolManager) external;

    function unpause(bytes32 agent, IPoolManager poolManager) external;
}

/// @title IStableMaster
/// @author Angle Core Team
/// @notice Previous interface with additionnal getters for public variables and mappings
interface IStableMaster is IStableMasterFunctions {
    function agToken() external view returns (address);

    function collateralMap(IPoolManager poolManager)
        external
        view
        returns (
            IERC20 token,
            ISanToken sanToken,
            IPerpetualManager perpetualManager,
            IOracle oracle,
            uint256 stocksUsers,
            uint256 sanRate,
            uint256 collatBase,
            SLPData memory slpData,
            MintBurnData memory feeData
        );
}

File 24 of 29 : StableMaster.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "./StableMasterInternal.sol";

/// @title StableMaster
/// @author Angle Core Team
/// @notice `StableMaster` is the contract handling all the collateral types accepted for a given stablecoin
/// It does all the accounting and is the point of entry in the protocol for stable holders and seekers as well as SLPs
/// @dev This file contains the core functions of the `StableMaster` contract
contract StableMaster is StableMasterInternal, IStableMasterFunctions, AccessControlUpgradeable {
    using SafeERC20 for IERC20;

    /// @notice Role for governors only
    bytes32 public constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE");
    /// @notice Role for guardians and governors
    bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
    /// @notice Role for `Core` only, used to propagate guardian and governors
    bytes32 public constant CORE_ROLE = keccak256("CORE_ROLE");

    bytes32 public constant STABLE = keccak256("STABLE");
    bytes32 public constant SLP = keccak256("SLP");

    // ============================ DEPLOYER =======================================

    /// @notice Creates the access control logic for the governor and guardian addresses
    /// @param governorList List of the governor addresses of the protocol
    /// @param guardian Guardian address of the protocol
    /// @param _agToken Reference to the `AgToken`, that is the ERC20 token handled by the `StableMaster`
    /// @dev This function is called by the `Core` when a stablecoin is deployed to maintain consistency
    /// across the governor and guardian roles
    /// @dev When this function is called by the `Core`, it has already been checked that the `stableMaster`
    /// corresponding to the `agToken` was this `stableMaster`
    function deploy(
        address[] memory governorList,
        address guardian,
        address _agToken
    ) external override onlyRole(CORE_ROLE) {
        for (uint256 i = 0; i < governorList.length; i++) {
            _grantRole(GOVERNOR_ROLE, governorList[i]);
            _grantRole(GUARDIAN_ROLE, governorList[i]);
        }
        _grantRole(GUARDIAN_ROLE, guardian);
        agToken = IAgToken(_agToken);
        // Since there is only one address that can be the `AgToken`, and since `AgToken`
        // is not to be admin of any role, we do not define any access control role for it
    }

    // ============================ STRATEGIES =====================================

    /// @notice Takes into account the gains made while lending and distributes it to SLPs by updating the `sanRate`
    /// @param gain Interests accumulated from lending
    /// @dev This function is called by a `PoolManager` contract having some yield farming strategies associated
    /// @dev To prevent flash loans, the `sanRate` is not directly updated, it is updated at the blocks that follow
    function accumulateInterest(uint256 gain) external override {
        // Searching collateral data
        Collateral storage col = collateralMap[IPoolManager(msg.sender)];
        _contractMapCheck(col);
        // A part of the gain goes to SLPs, the rest to the surplus of the protocol
        _updateSanRate((gain * col.slpData.interestsForSLPs) / BASE_PARAMS, col);
    }

    /// @notice Takes into account a loss made by a yield farming strategy
    /// @param loss Loss made by the yield farming strategy
    /// @dev This function is called by a `PoolManager` contract having some yield farming strategies associated
    /// @dev Fees are not accumulated for this function before being distributed: everything is directly used to
    /// update the `sanRate`
    function signalLoss(uint256 loss) external override {
        // Searching collateral data
        IPoolManager poolManager = IPoolManager(msg.sender);
        Collateral storage col = collateralMap[poolManager];
        _contractMapCheck(col);
        uint256 sanMint = col.sanToken.totalSupply();
        if (sanMint != 0) {
            // Updating the `sanRate` and the `lockedInterests` by taking into account a loss
            if (col.sanRate * sanMint + col.slpData.lockedInterests * BASE_TOKENS > loss * BASE_TOKENS) {
                // The loss is first taken from the `lockedInterests`
                uint256 withdrawFromLoss = col.slpData.lockedInterests;

                if (withdrawFromLoss >= loss) {
                    withdrawFromLoss = loss;
                }

                col.slpData.lockedInterests -= withdrawFromLoss;
                col.sanRate -= ((loss - withdrawFromLoss) * BASE_TOKENS) / sanMint;
            } else {
                // Normally it should be set to 0, but this would imply that no SLP can enter afterwards
                // we therefore set it to 1 (equivalent to 10**(-18))
                col.sanRate = 1;
                col.slpData.lockedInterests = 0;
                // As it is a critical time, governance pauses SLPs to solve the situation
                _pause(keccak256(abi.encodePacked(SLP, address(poolManager))));
            }
            emit SanRateUpdated(address(col.token), col.sanRate);
        }
    }

    // ============================== HAs ==========================================

    /// @notice Transforms a HA position into a SLP Position
    /// @param amount The amount to transform
    /// @param user Address to mint sanTokens to
    /// @dev Can only be called by a `PerpetualManager` contract
    /// @dev This is typically useful when a HA wishes to cash out but there is not enough collateral
    /// in reserves
    function convertToSLP(uint256 amount, address user) external override {
        // Data about the `PerpetualManager` calling the function is fetched using the `contractMap`
        IPoolManager poolManager = _contractMap[msg.sender];
        Collateral storage col = collateralMap[poolManager];
        _contractMapCheck(col);
        // If SLPs are paused, in this situation, then this transaction should revert
        // In this extremely rare case, governance should take action and also pause HAs
        _whenNotPaused(SLP, address(poolManager));
        _updateSanRate(0, col);
        col.sanToken.mint(user, (amount * BASE_TOKENS) / col.sanRate);
    }

    /// @notice Sets the proportion of `stocksUsers` available for perpetuals
    /// @param _targetHAHedge New value of the hedge ratio that the protocol wants to arrive to
    /// @dev Can only be called by the `PerpetualManager`
    function setTargetHAHedge(uint64 _targetHAHedge) external override {
        // Data about the `PerpetualManager` calling the function is fetched using the `contractMap`
        IPoolManager poolManager = _contractMap[msg.sender];
        Collateral storage col = collateralMap[poolManager];
        _contractMapCheck(col);
        col.feeData.targetHAHedge = _targetHAHedge;
        // No need to issue an event here, one has already been issued by the corresponding `PerpetualManager`
    }

    // ============================ VIEW FUNCTIONS =================================

    /// @notice Transmits to the `PerpetualManager` the max amount of collateral (in stablecoin value) HAs can hedge
    /// @return _stocksUsers All stablecoins currently assigned to the pool of the caller
    /// @dev This function will not return something relevant if it is not called by a `PerpetualManager`
    function getStocksUsers() external view override returns (uint256 _stocksUsers) {
        _stocksUsers = collateralMap[_contractMap[msg.sender]].stocksUsers;
    }

    /// @notice Returns the collateral ratio for this stablecoin
    /// @dev The ratio returned is scaled by `BASE_PARAMS` since the value is used to
    /// in the `FeeManager` contrat to be compared with the values in `xArrays` expressed in `BASE_PARAMS`
    function getCollateralRatio() external view override returns (uint256) {
        uint256 mints = agToken.totalSupply();
        if (mints == 0) {
            // If nothing has been minted, the collateral ratio is infinity
            return type(uint256).max;
        }
        uint256 val;
        for (uint256 i = 0; i < _managerList.length; i++) {
            // Oracle needs to be called for each collateral to compute the collateral ratio
            val += collateralMap[_managerList[i]].oracle.readQuote(_managerList[i].getTotalAsset());
        }
        return (val * BASE_PARAMS) / mints;
    }

    // ============================== KEEPERS ======================================

    /// @notice Updates all the fees not depending on personal agents inputs via a keeper calling the corresponding
    /// function in the `FeeManager` contract
    /// @param _bonusMalusMint New corrector of user mint fees for this collateral. These fees will correct
    /// the mint fees from users that just depend on the hedge curve by HAs by introducing other dependencies.
    /// In normal times they will be equal to `BASE_PARAMS` meaning fees will just depend on the hedge ratio
    /// @param _bonusMalusBurn New corrector of user burn fees, depending on collateral ratio
    /// @param _slippage New global slippage (the SLP fees from withdrawing) factor
    /// @param _slippageFee New global slippage fee (the non distributed accumulated fees) factor
    function setFeeKeeper(
        uint64 _bonusMalusMint,
        uint64 _bonusMalusBurn,
        uint64 _slippage,
        uint64 _slippageFee
    ) external override {
        // Fetching data about the `FeeManager` contract calling this function
        // It is stored in the `_contractMap`
        Collateral storage col = collateralMap[_contractMap[msg.sender]];
        _contractMapCheck(col);

        col.feeData.bonusMalusMint = _bonusMalusMint;
        col.feeData.bonusMalusBurn = _bonusMalusBurn;
        col.slpData.slippage = _slippage;
        col.slpData.slippageFee = _slippageFee;
        // An event is already emitted in the `FeeManager` contract
    }

    // ============================== AgToken ======================================

    /// @notice Allows the `agToken` contract to update the `stocksUsers` for a given collateral after a burn
    /// with no redeem
    /// @param amount Amount by which `stocksUsers` should decrease
    /// @param poolManager Reference to `PoolManager` for which `stocksUsers` needs to be updated
    /// @dev This function can be called by the `agToken` contract after a burn of agTokens for which no collateral has been
    /// redeemed
    function updateStocksUsers(uint256 amount, address poolManager) external override {
        require(msg.sender == address(agToken), "3");
        Collateral storage col = collateralMap[IPoolManager(poolManager)];
        _contractMapCheck(col);
        require(col.stocksUsers >= amount, "4");
        col.stocksUsers -= amount;
        emit StocksUsersUpdated(address(col.token), col.stocksUsers);
    }

    // ================================= GOVERNANCE ================================

    // =============================== Core Functions ==============================

    /// @notice Changes the `Core` contract
    /// @param newCore New core address
    /// @dev This function can only be called by the `Core` contract
    function setCore(address newCore) external override onlyRole(CORE_ROLE) {
        // Access control for this contract
        _revokeRole(CORE_ROLE, address(_core));
        _grantRole(CORE_ROLE, newCore);
        _core = ICore(newCore);
    }

    /// @notice Adds a new governor address
    /// @param governor New governor address
    /// @dev This function propagates changes from `Core` to other contracts
    /// @dev Propagating changes like that allows to maintain the protocol's integrity
    function addGovernor(address governor) external override onlyRole(CORE_ROLE) {
        // Access control for this contract
        _grantRole(GOVERNOR_ROLE, governor);
        _grantRole(GUARDIAN_ROLE, governor);

        for (uint256 i = 0; i < _managerList.length; i++) {
            // The `PoolManager` will echo the changes across all the corresponding contracts
            _managerList[i].addGovernor(governor);
        }
    }

    /// @notice Removes a governor address which loses its role
    /// @param governor Governor address to remove
    /// @dev This function propagates changes from `Core` to other contracts
    /// @dev Propagating changes like that allows to maintain the protocol's integrity
    /// @dev It has already been checked in the `Core` that this address could be removed
    /// and that it would not put the protocol in a situation with no governor at all
    function removeGovernor(address governor) external override onlyRole(CORE_ROLE) {
        // Access control for this contract
        _revokeRole(GOVERNOR_ROLE, governor);
        _revokeRole(GUARDIAN_ROLE, governor);

        for (uint256 i = 0; i < _managerList.length; i++) {
            // The `PoolManager` will echo the changes across all the corresponding contracts
            _managerList[i].removeGovernor(governor);
        }
    }

    /// @notice Changes the guardian address
    /// @param newGuardian New guardian address
    /// @param oldGuardian Old guardian address
    /// @dev This function propagates changes from `Core` to other contracts
    /// @dev The zero check for the guardian address has already been performed by the `Core`
    /// contract
    function setGuardian(address newGuardian, address oldGuardian) external override onlyRole(CORE_ROLE) {
        _revokeRole(GUARDIAN_ROLE, oldGuardian);
        _grantRole(GUARDIAN_ROLE, newGuardian);

        for (uint256 i = 0; i < _managerList.length; i++) {
            _managerList[i].setGuardian(newGuardian, oldGuardian);
        }
    }

    /// @notice Revokes the guardian address
    /// @param oldGuardian Guardian address to revoke
    /// @dev This function propagates changes from `Core` to other contracts
    function revokeGuardian(address oldGuardian) external override onlyRole(CORE_ROLE) {
        _revokeRole(GUARDIAN_ROLE, oldGuardian);
        for (uint256 i = 0; i < _managerList.length; i++) {
            _managerList[i].revokeGuardian(oldGuardian);
        }
    }

    // ============================= Governor Functions ============================

    /// @notice Deploys a new collateral by creating the correct references in the corresponding contracts
    /// @param poolManager Contract managing and storing this collateral for this stablecoin
    /// @param perpetualManager Contract managing HA perpetuals for this stablecoin
    /// @param oracle Reference to the oracle that will give the price of the collateral with respect to the stablecoin
    /// @param sanToken Reference to the sanTokens associated to the collateral
    /// @dev All the references in parameters should correspond to contracts that have already been deployed and
    /// initialized with appropriate references
    /// @dev After calling this function, governance should initialize all parameters corresponding to this new collateral
    function deployCollateral(
        IPoolManager poolManager,
        IPerpetualManager perpetualManager,
        IFeeManager feeManager,
        IOracle oracle,
        ISanToken sanToken
    ) external onlyRole(GOVERNOR_ROLE) {
        // If the `sanToken`, `poolManager`, `perpetualManager` and `feeManager` were zero
        // addresses, the following require would fail
        // The only elements that are checked here are those that are defined in the constructors/initializers
        // of the concerned contracts
        require(
            sanToken.stableMaster() == address(this) &&
                sanToken.poolManager() == address(poolManager) &&
                poolManager.stableMaster() == address(this) &&
                perpetualManager.poolManager() == address(poolManager) &&
                // If the `feeManager` is not initialized with the correct `poolManager` then this function
                // will revert when `poolManager.deployCollateral` will be executed
                feeManager.stableMaster() == address(this),
            "9"
        );
        // Checking if the base of the tokens and of the oracle are not similar with one another
        address token = poolManager.token();
        uint256 collatBase = 10**(IERC20Metadata(token).decimals());
        // If the address of the oracle was the zero address, the following would revert
        require(oracle.inBase() == collatBase, "11");
        // Checking if the collateral has not already been deployed
        Collateral storage col = collateralMap[poolManager];
        require(address(col.token) == address(0), "13");

        // Creating the correct references
        col.token = IERC20(token);
        col.sanToken = sanToken;
        col.perpetualManager = perpetualManager;
        col.oracle = oracle;
        // Initializing with the correct values
        col.sanRate = BASE_TOKENS;
        col.collatBase = collatBase;

        // Adding the correct references in the `contractMap` we use in order not to have to pass addresses when
        // calling the `StableMaster` from the `PerpetualManager` contract, or the `FeeManager` contract
        // This is equivalent to granting Access Control roles for these contracts
        _contractMap[address(perpetualManager)] = poolManager;
        _contractMap[address(feeManager)] = poolManager;
        _managerList.push(poolManager);

        // Pausing agents at deployment to leave governance time to set parameters
        // The `PerpetualManager` contract is automatically paused after being initialized, so HAs will not be able to
        // interact with the protocol
        _pause(keccak256(abi.encodePacked(SLP, address(poolManager))));
        _pause(keccak256(abi.encodePacked(STABLE, address(poolManager))));

        // Fetching the governor list and the guardian to initialize the `poolManager` correctly
        address[] memory governorList = _core.governorList();
        address guardian = _core.guardian();

        // Propagating the deployment and passing references to the corresponding contracts
        poolManager.deployCollateral(governorList, guardian, perpetualManager, feeManager, oracle);
        emit CollateralDeployed(address(poolManager), address(perpetualManager), address(sanToken), address(oracle));
    }

    /// @notice Removes a collateral from the list of accepted collateral types and pauses all actions associated
    /// to this collateral
    /// @param poolManager Reference to the contract managing this collateral for this stablecoin in the protocol
    /// @param settlementContract Settlement contract that will be used to close everyone's positions and to let
    /// users, SLPs and HAs redeem if not all a portion of their claim
    /// @dev Since this function has the ability to transfer the contract's funds to another contract, it should
    /// only be accessible to the governor
    /// @dev Before calling this function, governance should make sure that all the collateral lent to strategies
    /// has been withdrawn
    function revokeCollateral(IPoolManager poolManager, ICollateralSettler settlementContract)
        external
        onlyRole(GOVERNOR_ROLE)
    {
        // Checking if the `poolManager` given here is well in the list of managers and taking advantage of that to remove
        // the `poolManager` from the list
        uint256 indexMet;
        uint256 managerListLength = _managerList.length;
        require(managerListLength >= 1, "10");
        for (uint256 i = 0; i < managerListLength - 1; i++) {
            if (_managerList[i] == poolManager) {
                indexMet = 1;
                _managerList[i] = _managerList[managerListLength - 1];
                break;
            }
        }
        require(indexMet == 1 || _managerList[managerListLength - 1] == poolManager, "10");
        _managerList.pop();
        Collateral memory col = collateralMap[poolManager];

        // Deleting the references of the associated contracts: `perpetualManager` and `keeper` in the
        // `_contractMap` and `poolManager` from the `collateralMap`
        delete _contractMap[poolManager.feeManager()];
        delete _contractMap[address(col.perpetualManager)];
        delete collateralMap[poolManager];
        emit CollateralRevoked(address(poolManager));

        // Pausing entry (and exits for HAs)
        col.perpetualManager.pause();
        // No need to pause `SLP` and `STABLE_HOLDERS` as deleting the entry associated to the `poolManager`
        // in the `collateralMap` will make everything revert

        // Transferring the whole balance to global settlement
        uint256 balance = col.token.balanceOf(address(poolManager));
        col.token.safeTransferFrom(address(poolManager), address(settlementContract), balance);

        // Settlement works with a fixed oracle value for HAs, it needs to be computed here
        uint256 oracleValue = col.oracle.readLower();
        // Notifying the global settlement contract with the properties of the contract to settle
        // In case of global shutdown, there would be one settlement contract per collateral type
        // Not using the `lockedInterests` to update the value of the sanRate
        settlementContract.triggerSettlement(oracleValue, col.sanRate, col.stocksUsers);
    }

    // ============================= Guardian Functions ============================

    /// @notice Pauses an agent's actions within this contract for a given collateral type for this stablecoin
    /// @param agent Bytes representing the agent (`SLP` or `STABLE`) and the collateral type that is going to
    /// be paused. To get the `bytes32` from a string, we use in Solidity a `keccak256` function
    /// @param poolManager Reference to the contract managing this collateral for this stablecoin in the protocol and
    /// for which `agent` needs to be paused
    /// @dev If agent is `STABLE`, it is going to be impossible for users to mint stablecoins using collateral or to burn
    /// their stablecoins
    /// @dev If agent is `SLP`, it is going to be impossible for SLPs to deposit collateral and receive
    /// sanTokens in exchange, or to withdraw collateral from their sanTokens
    function pause(bytes32 agent, IPoolManager poolManager) external override onlyRole(GUARDIAN_ROLE) {
        Collateral storage col = collateralMap[poolManager];
        // Checking for the `poolManager`
        _contractMapCheck(col);
        _pause(keccak256(abi.encodePacked(agent, address(poolManager))));
    }

    /// @notice Unpauses an agent's action for a given collateral type for this stablecoin
    /// @param agent Agent (`SLP` or `STABLE`) to unpause the action of
    /// @param poolManager Reference to the associated `PoolManager`
    /// @dev Before calling this function, the agent should have been paused for this collateral
    function unpause(bytes32 agent, IPoolManager poolManager) external override onlyRole(GUARDIAN_ROLE) {
        Collateral storage col = collateralMap[poolManager];
        // Checking for the `poolManager`
        _contractMapCheck(col);
        _unpause(keccak256(abi.encodePacked(agent, address(poolManager))));
    }

    /// @notice Updates the `stocksUsers` for a given pair of collateral
    /// @param amount Amount of `stocksUsers` to transfer from a pool to another
    /// @param poolManagerUp Reference to `PoolManager` for which `stocksUsers` needs to increase
    /// @param poolManagerDown Reference to `PoolManager` for which `stocksUsers` needs to decrease
    /// @dev This function can be called in case where the reserves of the protocol for each collateral do not exactly
    /// match what is stored in the `stocksUsers` because of increases or decreases in collateral prices at times
    /// in which the protocol was not fully hedged by HAs
    /// @dev With this function, governance can allow/prevent more HAs coming in a pool while preventing/allowing HAs
    /// from other pools because the accounting variable of `stocksUsers` does not really match
    function rebalanceStocksUsers(
        uint256 amount,
        IPoolManager poolManagerUp,
        IPoolManager poolManagerDown
    ) external onlyRole(GUARDIAN_ROLE) {
        Collateral storage colUp = collateralMap[poolManagerUp];
        Collateral storage colDown = collateralMap[poolManagerDown];
        // Checking for the `poolManager`
        _contractMapCheck(colUp);
        _contractMapCheck(colDown);
        // The invariant `col.stocksUsers <= col.capOnStableMinted` should remain true even after a
        // governance update
        require(colUp.stocksUsers + amount <= colUp.feeData.capOnStableMinted, "8");
        colDown.stocksUsers -= amount;
        colUp.stocksUsers += amount;
        emit StocksUsersUpdated(address(colUp.token), colUp.stocksUsers);
        emit StocksUsersUpdated(address(colDown.token), colDown.stocksUsers);
    }

    /// @notice Propagates the change of oracle for one collateral to all the contracts which need to have
    /// the correct oracle reference
    /// @param _oracle New oracle contract for the pair collateral/stablecoin
    /// @param poolManager Reference to the `PoolManager` contract associated to the collateral
    function setOracle(IOracle _oracle, IPoolManager poolManager)
        external
        onlyRole(GOVERNOR_ROLE)
        zeroCheck(address(_oracle))
    {
        Collateral storage col = collateralMap[poolManager];
        // Checking for the `poolManager`
        _contractMapCheck(col);
        require(col.oracle != _oracle, "12");
        // The `inBase` of the new oracle should be the same as the `_collatBase` stored for this collateral
        require(col.collatBase == _oracle.inBase(), "11");
        col.oracle = _oracle;
        emit OracleUpdated(address(poolManager), address(_oracle));
        col.perpetualManager.setOracle(_oracle);
    }

    /// @notice Changes the parameters to cap the number of stablecoins you can issue using one
    /// collateral type and the maximum interests you can distribute to SLPs in a sanRate update
    /// in a block
    /// @param _capOnStableMinted New value of the cap
    /// @param _maxInterestsDistributed Maximum amount of interests distributed to SLPs in a block
    /// @param poolManager Reference to the `PoolManager` contract associated to the collateral
    function setCapOnStableAndMaxInterests(
        uint256 _capOnStableMinted,
        uint256 _maxInterestsDistributed,
        IPoolManager poolManager
    ) external override onlyRole(GUARDIAN_ROLE) {
        Collateral storage col = collateralMap[poolManager];
        // Checking for the `poolManager`
        _contractMapCheck(col);
        // The invariant `col.stocksUsers <= col.capOnStableMinted` should remain true even after a
        // governance update
        require(_capOnStableMinted >= col.stocksUsers, "8");
        col.feeData.capOnStableMinted = _capOnStableMinted;
        col.slpData.maxInterestsDistributed = _maxInterestsDistributed;
        emit CapOnStableAndMaxInterestsUpdated(address(poolManager), _capOnStableMinted, _maxInterestsDistributed);
    }

    /// @notice Sets a new `FeeManager` contract and removes the old one which becomes useless
    /// @param newFeeManager New `FeeManager` contract
    /// @param oldFeeManager Old `FeeManager` contract
    /// @param poolManager Reference to the contract managing this collateral for this stablecoin in the protocol
    /// and associated to the `FeeManager` to update
    function setFeeManager(
        address newFeeManager,
        address oldFeeManager,
        IPoolManager poolManager
    ) external onlyRole(GUARDIAN_ROLE) zeroCheck(newFeeManager) {
        Collateral storage col = collateralMap[poolManager];
        // Checking for the `poolManager`
        _contractMapCheck(col);
        require(_contractMap[oldFeeManager] == poolManager, "10");
        require(newFeeManager != oldFeeManager, "14");
        delete _contractMap[oldFeeManager];
        _contractMap[newFeeManager] = poolManager;
        emit FeeManagerUpdated(address(poolManager), newFeeManager);
        poolManager.setFeeManager(IFeeManager(newFeeManager));
    }

    /// @notice Sets the proportion of fees from burn/mint of users and the proportion
    /// of lending interests going to SLPs
    /// @param _feesForSLPs New proportion of mint/burn fees going to SLPs
    /// @param _interestsForSLPs New proportion of interests from lending going to SLPs
    /// @dev The higher these proportions the bigger the APY for SLPs
    /// @dev These proportions should be inferior to `BASE_PARAMS`
    function setIncentivesForSLPs(
        uint64 _feesForSLPs,
        uint64 _interestsForSLPs,
        IPoolManager poolManager
    ) external override onlyRole(GUARDIAN_ROLE) onlyCompatibleFees(_feesForSLPs) onlyCompatibleFees(_interestsForSLPs) {
        Collateral storage col = collateralMap[poolManager];
        _contractMapCheck(col);
        col.slpData.feesForSLPs = _feesForSLPs;
        col.slpData.interestsForSLPs = _interestsForSLPs;
        emit SLPsIncentivesUpdated(address(poolManager), _feesForSLPs, _interestsForSLPs);
    }

    /// @notice Sets the x array (ie ratios between amount hedged by HAs and amount to hedge)
    /// and the y array (ie values of fees at thresholds) used to compute mint and burn fees for users
    /// @param poolManager Reference to the `PoolManager` handling the collateral
    /// @param _xFee Thresholds of hedge ratios
    /// @param _yFee Values of the fees at thresholds
    /// @param _mint Whether mint fees or burn fees should be updated
    /// @dev The evolution of the fees between two thresholds is linear
    /// @dev The length of the two arrays should be the same
    /// @dev The values of `_xFee` should be in ascending order
    /// @dev For mint fees, values in the y-array below should normally be decreasing: the higher the `x` the cheaper
    /// it should be for stable seekers to come in as a high `x` corresponds to a high demand for volatility and hence
    /// to a situation where all the collateral can be hedged
    /// @dev For burn fees, values in the array below should normally be decreasing: the lower the `x` the cheaper it should
    /// be for stable seekers to go out, as a low `x` corresponds to low demand for volatility and hence
    /// to a situation where the protocol has a hard time covering its collateral
    function setUserFees(
        IPoolManager poolManager,
        uint64[] memory _xFee,
        uint64[] memory _yFee,
        uint8 _mint
    ) external override onlyRole(GUARDIAN_ROLE) onlyCompatibleInputArrays(_xFee, _yFee) {
        Collateral storage col = collateralMap[poolManager];
        _contractMapCheck(col);
        if (_mint > 0) {
            col.feeData.xFeeMint = _xFee;
            col.feeData.yFeeMint = _yFee;
        } else {
            col.feeData.xFeeBurn = _xFee;
            col.feeData.yFeeBurn = _yFee;
        }
        emit FeeArrayUpdated(address(poolManager), _xFee, _yFee, _mint);
    }
}

File 25 of 29 : StableMasterEvents.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "../external/AccessControlUpgradeable.sol";

import "../interfaces/IAgToken.sol";
import "../interfaces/ICollateralSettler.sol";
import "../interfaces/ICore.sol";
import "../interfaces/IFeeManager.sol";
import "../interfaces/IOracle.sol";
import "../interfaces/IPerpetualManager.sol";
import "../interfaces/IPoolManager.sol";
import "../interfaces/ISanToken.sol";
import "../interfaces/IStableMaster.sol";

import "../utils/FunctionUtils.sol";
import "../utils/PausableMapUpgradeable.sol";

/// @title StableMasterEvents
/// @author Angle Core Team
/// @notice `StableMaster` is the contract handling all the collateral types accepted for a given stablecoin
/// It does all the accounting and is the point of entry in the protocol for stable holders and seekers as well as SLPs
/// @dev This file contains all the events of the `StableMaster` contract
contract StableMasterEvents {
    event SanRateUpdated(address indexed _token, uint256 _newSanRate);

    event StocksUsersUpdated(address indexed _poolManager, uint256 _stocksUsers);

    event MintedStablecoins(address indexed _poolManager, uint256 amount, uint256 amountForUserInStable);

    event BurntStablecoins(address indexed _poolManager, uint256 amount, uint256 redeemInC);

    // ============================= Governors =====================================

    event CollateralDeployed(
        address indexed _poolManager,
        address indexed _perpetualManager,
        address indexed _sanToken,
        address _oracle
    );

    event CollateralRevoked(address indexed _poolManager);

    // ========================= Parameters update =================================

    event OracleUpdated(address indexed _poolManager, address indexed _oracle);

    event FeeManagerUpdated(address indexed _poolManager, address indexed newFeeManager);

    event CapOnStableAndMaxInterestsUpdated(
        address indexed _poolManager,
        uint256 _capOnStableMinted,
        uint256 _maxInterestsDistributed
    );

    event SLPsIncentivesUpdated(address indexed _poolManager, uint64 _feesForSLPs, uint64 _interestsForSLPs);

    event FeeArrayUpdated(address indexed _poolManager, uint64[] _xFee, uint64[] _yFee, uint8 _type);
}

File 26 of 29 : StableMasterInternal.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "./StableMasterStorage.sol";

/// @title StableMasterInternal
/// @author Angle Core Team
/// @notice `StableMaster` is the contract handling all the collateral types accepted for a given stablecoin
/// It does all the accounting and is the point of entry in the protocol for stable holders and seekers as well as SLPs
/// @dev This file contains all the internal function of the `StableMaster` contract
contract StableMasterInternal is StableMasterStorage, PausableMapUpgradeable {
    /// @notice Checks if the `msg.sender` calling the contract has the right to do it
    /// @param col Struct for the collateral associated to the caller address
    /// @dev Since the `StableMaster` contract uses a `contractMap` that stores addresses of some verified
    /// protocol's contracts in it, and since the roles corresponding to these addresses are never admin roles
    /// it is cheaper not to use for these contracts OpenZeppelin's access control logic
    /// @dev A non null associated token address is what is used to check if a `PoolManager` has well been initialized
    /// @dev We could set `PERPETUALMANAGER_ROLE`, `POOLMANAGER_ROLE` and `FEEMANAGER_ROLE` for this
    /// contract, but this would actually be inefficient
    function _contractMapCheck(Collateral storage col) internal view {
        require(address(col.token) != address(0), "3");
    }

    /// @notice Checks if the protocol has been paused for an agent and for a given collateral type for this
    /// stablecoin
    /// @param agent Name of the agent to check, it is either going to be `STABLE` or `SLP`
    /// @param poolManager `PoolManager` contract for which to check pauses
    function _whenNotPaused(bytes32 agent, address poolManager) internal view {
        require(!paused[keccak256(abi.encodePacked(agent, poolManager))], "18");
    }

    /// @notice Updates the `sanRate` that is the exchange rate between sanTokens given to SLPs and collateral or
    /// accumulates fees to be distributed to SLPs before doing it at next block
    /// @param toShare Amount of interests that needs to be redistributed to the SLPs through the `sanRate`
    /// @param col Struct for the collateral of interest here which values are going to be updated
    /// @dev This function can only increase the `sanRate` and is not used to take into account a loss made through
    /// lending or another yield farming strategy: this is done in the `signalLoss` function
    /// @dev The `sanRate` is only be updated from the fees accumulated from previous blocks and the fees to share to SLPs
    /// are just accumulated to be distributed at next block
    /// @dev A flashloan attack could consist in seeing fees to be distributed, deposit, increase the `sanRate` and then
    /// withdraw: what is done with the `lockedInterests` parameter is a way to mitigate that
    /// @dev Another solution against flash loans would be to have a non null `slippage` at all times: this is far from ideal
    /// for SLPs in the first place
    function _updateSanRate(uint256 toShare, Collateral storage col) internal {
        uint256 _lockedInterests = col.slpData.lockedInterests;
        // Checking if the `sanRate` has been updated in the current block using past block fees
        // This is a way to prevent flash loans attacks when an important amount of fees are going to be distributed
        // in a block: fees are stored but will just be distributed to SLPs who will be here during next blocks
        if (block.timestamp != col.slpData.lastBlockUpdated && _lockedInterests > 0) {
            uint256 sanMint = col.sanToken.totalSupply();
            if (sanMint != 0) {
                // Checking if the update is too important and should be made in multiple blocks
                if (_lockedInterests > col.slpData.maxInterestsDistributed) {
                    // `sanRate` is expressed in `BASE_TOKENS`
                    col.sanRate += (col.slpData.maxInterestsDistributed * BASE_TOKENS) / sanMint;
                    _lockedInterests -= col.slpData.maxInterestsDistributed;
                } else {
                    col.sanRate += (_lockedInterests * BASE_TOKENS) / sanMint;
                    _lockedInterests = 0;
                }
                emit SanRateUpdated(address(col.token), col.sanRate);
            } else {
                _lockedInterests = 0;
            }
        }
        // Adding the fees to be distributed at next block
        if (toShare != 0) {
            if ((col.slpData.slippageFee == 0) && (col.slpData.feesAside != 0)) {
                // If the collateral ratio is big enough, all the fees or gains will be used to update the `sanRate`
                // If there were fees or lending gains that had been put aside, they will be added in this case to the
                // update of the `sanRate`
                toShare += col.slpData.feesAside;
                col.slpData.feesAside = 0;
            } else if (col.slpData.slippageFee != 0) {
                // Computing the fraction of fees and gains that should be left aside if the collateral ratio is too small
                uint256 aside = (toShare * col.slpData.slippageFee) / BASE_PARAMS;
                toShare -= aside;
                // The amount of fees left aside should be rounded above
                col.slpData.feesAside += aside;
            }
            // Updating the amount of fees to be distributed next block
            _lockedInterests += toShare;
        }
        col.slpData.lockedInterests = _lockedInterests;
        col.slpData.lastBlockUpdated = block.timestamp;
    }

    /// @notice Computes the current fees to be taken when minting using `amount` of collateral
    /// @param amount Amount of collateral in the transaction to get stablecoins
    /// @param col Struct for the collateral of interest
    /// @return feeMint Mint Fees taken to users expressed in collateral
    /// @dev Fees depend on the hedge ratio that is the ratio between what is hedged by HAs and what should be hedged
    /// @dev The more is hedged by HAs, the smaller fees are expected to be
    /// @dev Fees are also corrected by the `bonusMalusMint` parameter which induces a dependence in collateral ratio
    function _computeFeeMint(uint256 amount, Collateral storage col) internal view returns (uint256 feeMint) {
        uint64 feeMint64;
        if (col.feeData.xFeeMint.length == 1) {
            // This is done to avoid an external call in the case where the fees are constant regardless of the collateral
            // ratio
            feeMint64 = col.feeData.yFeeMint[0];
        } else {
            uint64 hedgeRatio = _computeHedgeRatio(amount + col.stocksUsers, col);
            // Computing the fees based on the spread
            feeMint64 = _piecewiseLinear(hedgeRatio, col.feeData.xFeeMint, col.feeData.yFeeMint);
        }
        // Fees could in some occasions depend on other factors like collateral ratio
        // Keepers are the ones updating this part of the fees
        feeMint = (feeMint64 * col.feeData.bonusMalusMint) / BASE_PARAMS;
    }

    /// @notice Computes the current fees to be taken when burning stablecoins
    /// @param amount Amount of collateral corresponding to the stablecoins burnt in the transaction
    /// @param col Struct for the collateral of interest
    /// @return feeBurn Burn fees taken to users expressed in collateral
    /// @dev The amount is obtained after the amount of agTokens sent is converted in collateral
    /// @dev Fees depend on the hedge ratio that is the ratio between what is hedged by HAs and what should be hedged
    /// @dev The more is hedged by HAs, the higher fees are expected to be
    /// @dev Fees are also corrected by the `bonusMalusBurn` parameter which induces a dependence in collateral ratio
    function _computeFeeBurn(uint256 amount, Collateral storage col) internal view returns (uint256 feeBurn) {
        uint64 feeBurn64;
        if (col.feeData.xFeeBurn.length == 1) {
            // Avoiding an external call if fees are constant
            feeBurn64 = col.feeData.yFeeBurn[0];
        } else {
            uint64 hedgeRatio = _computeHedgeRatio(col.stocksUsers - amount, col);
            // Computing the fees based on the spread
            feeBurn64 = _piecewiseLinear(hedgeRatio, col.feeData.xFeeBurn, col.feeData.yFeeBurn);
        }
        // Fees could in some occasions depend on other factors like collateral ratio
        // Keepers are the ones updating this part of the fees
        feeBurn = (feeBurn64 * col.feeData.bonusMalusBurn) / BASE_PARAMS;
    }

    /// @notice Computes the hedge ratio that is the ratio between the amount of collateral hedged by HAs
    /// divided by the amount that should be hedged
    /// @param newStocksUsers Value of the collateral from users to hedge
    /// @param col Struct for the collateral of interest
    /// @return ratio Ratio between what's hedged divided what's to hedge
    /// @dev This function is typically called to compute mint or burn fees
    /// @dev It seeks from the `PerpetualManager` contract associated to the collateral the total amount
    /// already hedged by HAs and compares it to the amount to hedge
    function _computeHedgeRatio(uint256 newStocksUsers, Collateral storage col) internal view returns (uint64 ratio) {
        // Fetching the amount hedged by HAs from the corresponding `perpetualManager` contract
        uint256 totalHedgeAmount = col.perpetualManager.totalHedgeAmount();
        newStocksUsers = (col.feeData.targetHAHedge * newStocksUsers) / BASE_PARAMS;
        if (newStocksUsers > totalHedgeAmount) ratio = uint64((totalHedgeAmount * BASE_PARAMS) / newStocksUsers);
        else ratio = uint64(BASE_PARAMS);
    }
}

File 27 of 29 : StableMasterStorage.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

import "./StableMasterEvents.sol";

/// @title StableMasterStorage
/// @author Angle Core Team
/// @notice `StableMaster` is the contract handling all the collateral types accepted for a given stablecoin
/// It does all the accounting and is the point of entry in the protocol for stable holders and seekers as well as SLPs
/// @dev This file contains all the variables and parameters used in the `StableMaster` contract
contract StableMasterStorage is StableMasterEvents, FunctionUtils {
    // All the details about a collateral that are going to be stored in `StableMaster`
    struct Collateral {
        // Interface for the token accepted by the underlying `PoolManager` contract
        IERC20 token;
        // Reference to the `SanToken` for the pool
        ISanToken sanToken;
        // Reference to the `PerpetualManager` for the pool
        IPerpetualManager perpetualManager;
        // Adress of the oracle for the change rate between
        // collateral and the corresponding stablecoin
        IOracle oracle;
        // Amount of collateral in the reserves that comes from users
        // converted in stablecoin value. Updated at minting and burning.
        // A `stocksUsers` of 10 for a collateral type means that overall the balance of the collateral from users
        // that minted/burnt stablecoins using this collateral is worth 10 of stablecoins
        uint256 stocksUsers;
        // Exchange rate between sanToken and collateral
        uint256 sanRate;
        // Base used in the collateral implementation (ERC20 decimal)
        uint256 collatBase;
        // Parameters for SLPs and update of the `sanRate`
        SLPData slpData;
        // All the fees parameters
        MintBurnData feeData;
    }

    // ============================ Variables and References =====================================

    /// @notice Maps a `PoolManager` contract handling a collateral for this stablecoin to the properties of the struct above
    mapping(IPoolManager => Collateral) public collateralMap;

    /// @notice Reference to the `AgToken` used in this `StableMaster`
    /// This reference cannot be changed
    IAgToken public agToken;

    // Maps a contract to an address corresponding to the `IPoolManager` address
    // It is typically used to avoid passing in parameters the address of the `PerpetualManager` when `PerpetualManager`
    // is calling `StableMaster` to get information
    // It is the Access Control equivalent for the `SanToken`, `PoolManager`, `PerpetualManager` and `FeeManager`
    // contracts associated to this `StableMaster`
    mapping(address => IPoolManager) internal _contractMap;

    // List of all collateral managers
    IPoolManager[] internal _managerList;

    // Reference to the `Core` contract of the protocol
    ICore internal _core;
}

File 28 of 29 : FunctionUtils.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

/// @title FunctionUtils
/// @author Angle Core Team
/// @notice Contains all the utility functions that are needed in different places of the protocol
/// @dev Functions in this contract should typically be pure functions
/// @dev This contract is voluntarily a contract and not a library to save some gas cost every time it is used
contract FunctionUtils {
    /// @notice Base that is used to compute ratios and floating numbers
    uint256 public constant BASE_TOKENS = 10**18;
    /// @notice Base that is used to define parameters that need to have a floating value (for instance parameters
    /// that are defined as ratios)
    uint256 public constant BASE_PARAMS = 10**9;

    /// @notice Computes the value of a linear by part function at a given point
    /// @param x Point of the function we want to compute
    /// @param xArray List of breaking points (in ascending order) that define the linear by part function
    /// @param yArray List of values at breaking points (not necessarily in ascending order)
    /// @dev The evolution of the linear by part function between two breaking points is linear
    /// @dev Before the first breaking point and after the last one, the function is constant with a value
    /// equal to the first or last value of the yArray
    /// @dev This function is relevant if `x` is between O and `BASE_PARAMS`. If `x` is greater than that, then
    /// everything will be as if `x` is equal to the greater element of the `xArray`
    function _piecewiseLinear(
        uint64 x,
        uint64[] memory xArray,
        uint64[] memory yArray
    ) internal pure returns (uint64) {
        if (x >= xArray[xArray.length - 1]) {
            return yArray[xArray.length - 1];
        } else if (x <= xArray[0]) {
            return yArray[0];
        } else {
            uint256 lower;
            uint256 upper = xArray.length - 1;
            uint256 mid;
            while (upper - lower > 1) {
                mid = lower + (upper - lower) / 2;
                if (xArray[mid] <= x) {
                    lower = mid;
                } else {
                    upper = mid;
                }
            }
            if (yArray[upper] > yArray[lower]) {
                // There is no risk of overflow here as in the product of the difference of `y`
                // with the difference of `x`, the product is inferior to `BASE_PARAMS**2` which does not
                // overflow for `uint64`
                return
                    yArray[lower] +
                    ((yArray[upper] - yArray[lower]) * (x - xArray[lower])) /
                    (xArray[upper] - xArray[lower]);
            } else {
                return
                    yArray[lower] -
                    ((yArray[lower] - yArray[upper]) * (x - xArray[lower])) /
                    (xArray[upper] - xArray[lower]);
            }
        }
    }

    /// @notice Checks if the input arrays given by governance to update the fee structure is valid
    /// @param xArray List of breaking points (in ascending order) that define the linear by part function
    /// @param yArray List of values at breaking points (not necessarily in ascending order)
    /// @dev This function is a way to avoid some governance attacks or errors
    /// @dev The modifier checks if the arrays have a non null length, if their length is the same, if the values
    /// in the `xArray` are in ascending order and if the values in the `xArray` and in the `yArray` are not superior
    /// to `BASE_PARAMS`
    modifier onlyCompatibleInputArrays(uint64[] memory xArray, uint64[] memory yArray) {
        require(xArray.length == yArray.length && xArray.length > 0, "5");
        for (uint256 i = 0; i <= yArray.length - 1; i++) {
            require(yArray[i] <= uint64(BASE_PARAMS) && xArray[i] <= uint64(BASE_PARAMS), "6");
            if (i > 0) {
                require(xArray[i] > xArray[i - 1], "7");
            }
        }
        _;
    }

    /// @notice Checks if the new value given for the parameter is consistent (it should be inferior to 1
    /// if it corresponds to a ratio)
    /// @param fees Value of the new parameter to check
    modifier onlyCompatibleFees(uint64 fees) {
        require(fees <= BASE_PARAMS, "4");
        _;
    }

    /// @notice Checks if the new address given is not null
    /// @param newAddress Address to check
    /// @dev Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#missing-zero-address-validation
    modifier zeroCheck(address newAddress) {
        require(newAddress != address(0), "0");
        _;
    }
}

File 29 of 29 : PausableMapUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.7;

/// @title PausableMap
/// @author Angle Core Team after a fork from OpenZeppelin's similar Pausable Contracts
/// @notice Contract module which allows children to implement an emergency stop
/// mechanism that can be triggered by an authorized account.
/// @notice It generalizes Pausable from OpenZeppelin by allowing to specify a bytes32 that
/// should be stopped
/// @dev This module is used through inheritance
/// @dev In Angle's protocol, this contract is mainly used in `StableMasterFront`
/// to prevent SLPs and new stable holders from coming in
/// @dev The modifiers `whenNotPaused` and `whenPaused` from the original OpenZeppelin contracts were removed
/// to save some space and because they are not used in the `StableMaster` contract where this contract
/// is imported
contract PausableMapUpgradeable {
    /// @dev Emitted when the pause is triggered for `name`
    event Paused(bytes32 name);

    /// @dev Emitted when the pause is lifted for `name`
    event Unpaused(bytes32 name);

    /// @dev Mapping between a name and a boolean representing the paused state
    mapping(bytes32 => bool) public paused;

    /// @notice Triggers stopped state for `name`
    /// @param name Name for which to pause the contract
    /// @dev The contract must not be paused for `name`
    function _pause(bytes32 name) internal {
        require(!paused[name], "18");
        paused[name] = true;
        emit Paused(name);
    }

    /// @notice Returns to normal state for `name`
    /// @param name Name for which to unpause the contract
    /// @dev The contract must be paused for `name`
    function _unpause(bytes32 name) internal {
        require(paused[name], "19");
        paused[name] = false;
        emit Unpaused(name);
    }
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 830
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolManager","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemInC","type":"uint256"}],"name":"BurntStablecoins","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolManager","type":"address"},{"indexed":false,"internalType":"uint256","name":"_capOnStableMinted","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_maxInterestsDistributed","type":"uint256"}],"name":"CapOnStableAndMaxInterestsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolManager","type":"address"},{"indexed":true,"internalType":"address","name":"_perpetualManager","type":"address"},{"indexed":true,"internalType":"address","name":"_sanToken","type":"address"},{"indexed":false,"internalType":"address","name":"_oracle","type":"address"}],"name":"CollateralDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolManager","type":"address"}],"name":"CollateralRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolManager","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"_xFee","type":"uint64[]"},{"indexed":false,"internalType":"uint64[]","name":"_yFee","type":"uint64[]"},{"indexed":false,"internalType":"uint8","name":"_type","type":"uint8"}],"name":"FeeArrayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolManager","type":"address"},{"indexed":true,"internalType":"address","name":"newFeeManager","type":"address"}],"name":"FeeManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolManager","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountForUserInStable","type":"uint256"}],"name":"MintedStablecoins","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolManager","type":"address"},{"indexed":true,"internalType":"address","name":"_oracle","type":"address"}],"name":"OracleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"name","type":"bytes32"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolManager","type":"address"},{"indexed":false,"internalType":"uint64","name":"_feesForSLPs","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"_interestsForSLPs","type":"uint64"}],"name":"SLPsIncentivesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_newSanRate","type":"uint256"}],"name":"SanRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolManager","type":"address"},{"indexed":false,"internalType":"uint256","name":"_stocksUsers","type":"uint256"}],"name":"StocksUsersUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"name","type":"bytes32"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"BASE_PARAMS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BASE_TOKENS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CORE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GOVERNOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GUARDIAN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SLP","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STABLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gain","type":"uint256"}],"name":"accumulateInterest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"governor","type":"address"}],"name":"addGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"agToken","outputs":[{"internalType":"contract IAgToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"burner","type":"address"},{"internalType":"address","name":"dest","type":"address"},{"internalType":"contract IPoolManager","name":"poolManager","type":"address"},{"internalType":"uint256","name":"minCollatAmount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"name":"collateralMap","outputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"contract ISanToken","name":"sanToken","type":"address"},{"internalType":"contract IPerpetualManager","name":"perpetualManager","type":"address"},{"internalType":"contract IOracle","name":"oracle","type":"address"},{"internalType":"uint256","name":"stocksUsers","type":"uint256"},{"internalType":"uint256","name":"sanRate","type":"uint256"},{"internalType":"uint256","name":"collatBase","type":"uint256"},{"components":[{"internalType":"uint256","name":"lastBlockUpdated","type":"uint256"},{"internalType":"uint256","name":"lockedInterests","type":"uint256"},{"internalType":"uint256","name":"maxInterestsDistributed","type":"uint256"},{"internalType":"uint256","name":"feesAside","type":"uint256"},{"internalType":"uint64","name":"slippageFee","type":"uint64"},{"internalType":"uint64","name":"feesForSLPs","type":"uint64"},{"internalType":"uint64","name":"slippage","type":"uint64"},{"internalType":"uint64","name":"interestsForSLPs","type":"uint64"}],"internalType":"struct SLPData","name":"slpData","type":"tuple"},{"components":[{"internalType":"uint64[]","name":"xFeeMint","type":"uint64[]"},{"internalType":"uint64[]","name":"yFeeMint","type":"uint64[]"},{"internalType":"uint64[]","name":"xFeeBurn","type":"uint64[]"},{"internalType":"uint64[]","name":"yFeeBurn","type":"uint64[]"},{"internalType":"uint64","name":"targetHAHedge","type":"uint64"},{"internalType":"uint64","name":"bonusMalusMint","type":"uint64"},{"internalType":"uint64","name":"bonusMalusBurn","type":"uint64"},{"internalType":"uint256","name":"capOnStableMinted","type":"uint256"}],"internalType":"struct MintBurnData","name":"feeData","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"convertToSLP","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"governorList","type":"address[]"},{"internalType":"address","name":"guardian","type":"address"},{"internalType":"address","name":"_agToken","type":"address"}],"name":"deploy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPoolManager","name":"poolManager","type":"address"},{"internalType":"contract IPerpetualManager","name":"perpetualManager","type":"address"},{"internalType":"contract IFeeManager","name":"feeManager","type":"address"},{"internalType":"contract IOracle","name":"oracle","type":"address"},{"internalType":"contract ISanToken","name":"sanToken","type":"address"}],"name":"deployCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"user","type":"address"},{"internalType":"contract IPoolManager","name":"poolManager","type":"address"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getCollateralRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStocksUsers","outputs":[{"internalType":"uint256","name":"_stocksUsers","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"core_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"user","type":"address"},{"internalType":"contract IPoolManager","name":"poolManager","type":"address"},{"internalType":"uint256","name":"minStableAmount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"agent","type":"bytes32"},{"internalType":"contract IPoolManager","name":"poolManager","type":"address"}],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"contract IPoolManager","name":"poolManagerUp","type":"address"},{"internalType":"contract IPoolManager","name":"poolManagerDown","type":"address"}],"name":"rebalanceStocksUsers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"governor","type":"address"}],"name":"removeGovernor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPoolManager","name":"poolManager","type":"address"},{"internalType":"contract ICollateralSettler","name":"settlementContract","type":"address"}],"name":"revokeCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oldGuardian","type":"address"}],"name":"revokeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_capOnStableMinted","type":"uint256"},{"internalType":"uint256","name":"_maxInterestsDistributed","type":"uint256"},{"internalType":"contract IPoolManager","name":"poolManager","type":"address"}],"name":"setCapOnStableAndMaxInterests","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newCore","type":"address"}],"name":"setCore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_bonusMalusMint","type":"uint64"},{"internalType":"uint64","name":"_bonusMalusBurn","type":"uint64"},{"internalType":"uint64","name":"_slippage","type":"uint64"},{"internalType":"uint64","name":"_slippageFee","type":"uint64"}],"name":"setFeeKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeManager","type":"address"},{"internalType":"address","name":"oldFeeManager","type":"address"},{"internalType":"contract IPoolManager","name":"poolManager","type":"address"}],"name":"setFeeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newGuardian","type":"address"},{"internalType":"address","name":"oldGuardian","type":"address"}],"name":"setGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_feesForSLPs","type":"uint64"},{"internalType":"uint64","name":"_interestsForSLPs","type":"uint64"},{"internalType":"contract IPoolManager","name":"poolManager","type":"address"}],"name":"setIncentivesForSLPs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOracle","name":"_oracle","type":"address"},{"internalType":"contract IPoolManager","name":"poolManager","type":"address"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_targetHAHedge","type":"uint64"}],"name":"setTargetHAHedge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPoolManager","name":"poolManager","type":"address"},{"internalType":"uint64[]","name":"_xFee","type":"uint64[]"},{"internalType":"uint64[]","name":"_yFee","type":"uint64[]"},{"internalType":"uint8","name":"_mint","type":"uint8"}],"name":"setUserFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loss","type":"uint256"}],"name":"signalLoss","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"agent","type":"bytes32"},{"internalType":"contract IPoolManager","name":"poolManager","type":"address"}],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"poolManager","type":"address"}],"name":"updateStocksUsers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"burner","type":"address"},{"internalType":"address","name":"dest","type":"address"},{"internalType":"contract IPoolManager","name":"poolManager","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b50600654610100900460ff16806200002c575060065460ff16155b620000945760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b600654610100900460ff16158015620000b7576006805461ffff19166101011790555b8015620000ca576006805461ff00191690555b50615fdd80620000db6000396000f3fe608060405234801561001057600080fd5b50600436106102ff5760003560e01c80635c38eb3a1161019c578063ba8b7223116100ee578063d547741f11610097578063ee565a6311610071578063ee565a63146106b2578063eecdac88146106dd578063f36246d1146106f057600080fd5b8063d547741f1461067d578063df1b8bd314610690578063ec5b1bee1461069f57600080fd5b8063cbb3edd2116100c8578063cbb3edd21461063b578063ccc574901461064e578063cd377c531461067557600080fd5b8063ba8b7223146105ed578063be8c31ff14610615578063c4d66de81461062857600080fd5b806391d14854116101505780639f48118f1161012a5780639f48118f146105c7578063a217fddf146105d2578063af648c3d146105da57600080fd5b806391d148541461056e5780639af2dcae146105915780639e9e4666146105a457600080fd5b806380009630116101815780638000963014610535578063892cfc161461054857806391a9ca481461055b57600080fd5b80635c38eb3a1461050f5780636d1042161461052257600080fd5b8063252c94061161025557806336568abe1161020957806359de6866116101e357806359de6866146104d65780635a3c10f1146104e95780635b749f35146104fc57600080fd5b806336568abe146104895780633c4a25d01461049c57806354d9f653146104af57600080fd5b80632f2ff15d1161023a5780632f2ff15d1461043657806333c509d114610449578063344844db1461045c57600080fd5b8063252c9406146104105780632e2d29841461042357600080fd5b806322ac5c9a116102b757806324ea54f41161029157806324ea54f4146103c15780632509f1b9146103d6578063251ad873146103fd57600080fd5b806322ac5c9a1461036557806323e103a814610378578063248a9ca31461038b57600080fd5b8063074efa78116102e8578063074efa781461032c578063087264c91461033f5780631a115ff11461035257600080fd5b8063050f53fe14610304578063074ee44614610319575b600080fd5b610317610312366004615498565b610705565b005b610317610327366004615796565b6107e1565b61031761033a3660046153ad565b610a85565b61031761034d36600461557c565b610c5f565b610317610360366004615595565b610cb4565b610317610373366004615642565b610dce565b6103176103863660046156cc565b6116f7565b6103ae61039936600461557c565b60009081526007602052604090206001015490565b6040519081526020015b60405180910390f35b6103ae600080516020615f8883398151915281565b6103ae7fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d74581565b61031761040b366004615595565b6118ba565b61031761041e36600461557c565b6119b5565b61031761043136600461576f565b611bbe565b610317610444366004615595565b611c2b565b610317610457366004615374565b611c56565b336000908152600260209081526040808320546001600160a01b03168352908290529020600401546103ae565b610317610497366004615595565b611d42565b6103176104aa36600461533a565b611d89565b6103ae7ffb286912c6eadba541f23a3bb3e83373ab139b6e65d84e2a473c186efc2b464281565b6103176104e4366004615595565b611e7f565b6103176104f73660046157de565b611f05565b61031761050a36600461580c565b611fd0565b61031761051d366004615374565b612025565b610317610530366004615714565b612250565b61031761054336600461533a565b612596565b610317610556366004615374565b61260f565b610317610569366004615853565b612f29565b61058161057c366004615595565b613005565b60405190151581526020016103b8565b61031761059f36600461576f565b613032565b6105816105b236600461557c565b60056020526000908152604090205460ff1681565b6103ae633b9aca0081565b6103ae600081565b6103176105e836600461533a565b61318d565b6106006105fb36600461533a565b613259565b6040516103b899989796959493929190615b23565b6103176106233660046155ba565b613586565b61031761063636600461533a565b613809565b610317610649366004615595565b6139a9565b6103ae7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f5581565b6103ae613a14565b61031761068b366004615595565b613c4a565b6103ae670de0b6b3a764000081565b6103176106ad366004615827565b613c70565b6001546106c5906001600160a01b031681565b6040516001600160a01b0390911681526020016103b8565b6103176106eb36600461533a565b613ddc565b6103ae600080516020615f6883398151915281565b600080516020615f6883398151915261071e8133613ed2565b60005b84518110156107a35761076d7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f5586838151811061076057610760615f17565b6020026020010151613f36565b610791600080516020615f8883398151915286838151811061076057610760615f17565b8061079b81615eba565b915050610721565b506107bc600080516020615f8883398151915284613f36565b50600180546001600160a01b0319166001600160a01b03929092169190911790555050565b6001600160a01b038216600090815260208190526040902061080281613f9f565b61082c7ffb286912c6eadba541f23a3bb3e83373ab139b6e65d84e2a473c186efc2b464284613fdd565b8054610843906001600160a01b031633858861406a565b6003810154604051633593ba7b60e01b8152600481018790526000916001600160a01b031690633593ba7b9060240160206040518083038186803b15801561088a57600080fd5b505afa15801561089e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c291906156b3565b905060006108d082846140d9565b9050633b9aca006108e18282615e38565b6108eb9084615dea565b6108f59190615cc2565b9150838210156109315760405162461bcd60e51b8152602060048201526002602482015261313560f01b60448201526064015b60405180910390fd5b818360040160008282546109459190615c7f565b90915550506011830154600484015411156109875760405162461bcd60e51b8152602060048201526002602482015261189b60f11b6044820152606401610928565b60408051888152602081018490526001600160a01b038716917ff0543d9ab673fd2b6789938fb372d27b43b955c1a90eb8a982bc4aa50687962c910160405180910390a2610a166109dd6002633b9aca00615d3f565b600b850154600160401b90046001600160401b03166109fc848b615dea565b610a069190615dea565b610a109190615cc2565b846142a2565b6001546040516340c10f1960e01b81526001600160a01b03888116600483015260248201859052909116906340c10f1990604401600060405180830381600087803b158015610a6457600080fd5b505af1158015610a78573d6000803e3d6000fd5b5050505050505050505050565b600080516020615f88833981519152610a9e8133613ed2565b836001600160a01b038116610ad95760405162461bcd60e51b81526020600482015260016024820152600360fc1b6044820152606401610928565b6001600160a01b0383166000908152602081905260409020610afa81613f9f565b6001600160a01b03858116600090815260026020526040902054811690851614610b4b5760405162461bcd60e51b8152602060048201526002602482015261031360f41b6044820152606401610928565b846001600160a01b0316866001600160a01b03161415610b925760405162461bcd60e51b81526020600482015260026024820152610c4d60f21b6044820152606401610928565b6001600160a01b0380861660009081526002602052604080822080546001600160a01b03199081169091558984168084528284208054958a1695909216851790915590519092917fe139a19f7890c89603e0d013fb99aed7d286ef55ef5a092ddd2980a3afd2138d91a360405163472d35b960e01b81526001600160a01b03878116600483015285169063472d35b990602401600060405180830381600087803b158015610c3f57600080fd5b505af1158015610c53573d6000803e3d6000fd5b50505050505050505050565b336000908152602081905260409020610c7781613f9f565b600b810154610cb090633b9aca0090610ca090600160c01b90046001600160401b031685615dea565b610caa9190615cc2565b826142a2565b5050565b336000908152600260209081526040808320546001600160a01b031680845291839052909120610ce381613f9f565b610d0d7fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d74583613fdd565b610d186000826142a2565b600181015460058201546001600160a01b03909116906340c10f19908590610d48670de0b6b3a764000089615dea565b610d529190615cc2565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015610db057600080fd5b505af1158015610dc4573d6000803e3d6000fd5b5050505050505050565b7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f55610df98133613ed2565b306001600160a01b0316826001600160a01b0316636ac5dc466040518163ffffffff1660e01b815260040160206040518083038186803b158015610e3c57600080fd5b505afa158015610e50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e749190615357565b6001600160a01b0316148015610f0b5750856001600160a01b0316826001600160a01b031663dc4c90d36040518163ffffffff1660e01b815260040160206040518083038186803b158015610ec857600080fd5b505afa158015610edc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f009190615357565b6001600160a01b0316145b8015610f985750306001600160a01b0316866001600160a01b0316636ac5dc466040518163ffffffff1660e01b815260040160206040518083038186803b158015610f5557600080fd5b505afa158015610f69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f8d9190615357565b6001600160a01b0316145b80156110255750856001600160a01b0316856001600160a01b031663dc4c90d36040518163ffffffff1660e01b815260040160206040518083038186803b158015610fe257600080fd5b505afa158015610ff6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101a9190615357565b6001600160a01b0316145b80156110b25750306001600160a01b0316846001600160a01b0316636ac5dc466040518163ffffffff1660e01b815260040160206040518083038186803b15801561106f57600080fd5b505afa158015611083573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a79190615357565b6001600160a01b0316145b6110e25760405162461bcd60e51b81526020600482015260016024820152603960f81b6044820152606401610928565b6000866001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561111d57600080fd5b505afa158015611131573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111559190615357565b90506000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561119257600080fd5b505afa1580156111a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ca91906158a7565b6111d590600a615d3f565b905080856001600160a01b0316639aefb5c36040518163ffffffff1660e01b815260040160206040518083038186803b15801561121157600080fd5b505afa158015611225573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061124991906156b3565b1461127b5760405162461bcd60e51b8152602060048201526002602482015261313160f01b6044820152606401610928565b6001600160a01b0380891660009081526020819052604090208054909116156112cb5760405162461bcd60e51b8152602060048201526002602482015261313360f01b6044820152606401610928565b828160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550848160010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550878160020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550858160030160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550670de0b6b3a7640000816005018190555081816006018190555088600260008a6001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055508860026000896001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055506003899080600181540180825580915050600190039060005260206000200160009091909190916101000a8154816001600160a01b0302191690836001600160a01b031602179055506114d27fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d7458a6040516020016114b792919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b604051602081830303815290604052805190602001206144fd565b6115237ffb286912c6eadba541f23a3bb3e83373ab139b6e65d84e2a473c186efc2b46428a6040516020016114b792919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b6000600460009054906101000a90046001600160a01b03166001600160a01b031663a6d7fe2d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561157357600080fd5b505afa158015611587573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115af91908101906153f8565b90506000600460009054906101000a90046001600160a01b03166001600160a01b031663452a93206040518163ffffffff1660e01b815260040160206040518083038186803b15801561160157600080fd5b505afa158015611615573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116399190615357565b604051636cb2e74d60e11b81529091506001600160a01b038c169063d965ce9a9061167090859085908f908f908f90600401615a71565b600060405180830381600087803b15801561168a57600080fd5b505af115801561169e573d6000803e3d6000fd5b50506040516001600160a01b038b81168252808b1693508d811692508e16907f129e7033df7c6cf5b088a92e04f596efded950692229dda217a8054d40bb02929060200160405180910390a45050505050505050505050565b6001600160a01b038116600090815260208190526040902061171881613f9f565b6117427fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d74583613fdd565b61174d6000826142a2565b6001600160a01b0384163314156117cb5760018101546040516315a38ec760e11b8152600481018790526001600160a01b03868116602483015290911690632b471d8e90604401600060405180830381600087803b1580156117ae57600080fd5b505af11580156117c2573d6000803e3d6000fd5b5050505061183a565b6001810154604051630d43af8160e21b8152600481018790526001600160a01b0386811660248301523360448301529091169063350ebe0490606401600060405180830381600087803b15801561182157600080fd5b505af1158015611835573d6000803e3d6000fd5b505050505b6000611852633b9aca00670de0b6b3a7640000615dea565b6005830154600b84015461187a90600160801b90046001600160401b0316633b9aca00615e38565b6118849089615dea565b61188e9190615dea565b6118989190615cc2565b82549091506118b2906001600160a01b031684868461406a565b505050505050565b6001546001600160a01b031633146118f85760405162461bcd60e51b81526020600482015260016024820152603360f81b6044820152606401610928565b6001600160a01b038116600090815260208190526040902061191981613f9f565b82816004015410156119515760405162461bcd60e51b81526020600482015260016024820152600d60fa1b6044820152606401610928565b828160040160008282546119659190615e38565b9091555050805460048201546040519081526001600160a01b03909116907f72e0daee492d85727eb3f32c69303a6dd57af5bd2b127a04641de5d70e1494de9060200160405180910390a2505050565b3360008181526020819052604090206119cd81613f9f565b6001810154604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b158015611a1457600080fd5b505afa158015611a28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a4c91906156b3565b90508015611bb857611a66670de0b6b3a764000085615dea565b6008830154611a7e90670de0b6b3a764000090615dea565b828460050154611a8e9190615dea565b611a989190615c7f565b1115611b11576008820154848110611aad5750835b80836007016001016000828254611ac49190615e38565b90915550829050670de0b6b3a7640000611ade8388615e38565b611ae89190615dea565b611af29190615cc2565b836005016000828254611b059190615e38565b90915550611b71915050565b6001600583015560006008830155604051611b71906114b7907fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d74590869060200191825260601b6bffffffffffffffffffffffff1916602082015260340190565b815460058301546040519081526001600160a01b03909116907faa959327c96abe1dbec8186dbe4bb630ffa7f1a65ad98780481f14757004498e9060200160405180910390a25b50505050565b6001600160a01b0381166000908152602081905260409020611bdf81613f9f565b611c097fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d74583613fdd565b611c146000826142a2565b8054610d18906001600160a01b031633848761406a565b600082815260076020526040902060010154611c478133613ed2565b611c518383613f36565b505050565b600080516020615f68833981519152611c6f8133613ed2565b611c87600080516020615f8883398151915283614597565b611c9f600080516020615f8883398151915284613f36565b60005b600354811015611bb85760038181548110611cbf57611cbf615f17565b6000918252602090912001546040516333c509d160e01b81526001600160a01b0386811660048301528581166024830152909116906333c509d190604401600060405180830381600087803b158015611d1757600080fd5b505af1158015611d2b573d6000803e3d6000fd5b505050508080611d3a90615eba565b915050611ca2565b6001600160a01b0381163314611d7f5760405162461bcd60e51b8152602060048201526002602482015261373160f01b6044820152606401610928565b610cb08282614597565b600080516020615f68833981519152611da28133613ed2565b611dcc7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f5583613f36565b611de4600080516020615f8883398151915283613f36565b60005b600354811015611c515760038181548110611e0457611e04615f17565b6000918252602090912001546040516303c4a25d60e41b81526001600160a01b03858116600483015290911690633c4a25d090602401600060405180830381600087803b158015611e5457600080fd5b505af1158015611e68573d6000803e3d6000fd5b505050508080611e7790615eba565b915050611de7565b600080516020615f88833981519152611e988133613ed2565b6001600160a01b0382166000908152602081905260409020611eb981613f9f565b611bb88484604051602001611eea92919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b604051602081830303815290604052805190602001206145fe565b600080516020615f88833981519152611f1e8133613ed2565b6001600160a01b0382166000908152602081905260409020611f3f81613f9f565b8060040154851015611f775760405162461bcd60e51b81526020600482015260016024820152600760fb1b6044820152606401610928565b601181018590556009810184905560408051868152602081018690526001600160a01b038516917faa2226ff9a1d81d234d2d96d4a86cc03d1b9165cda2f2591c1df913002201f22910160405180910390a25050505050565b336000908152600260209081526040808320546001600160a01b031680845291839052909120611fff81613f9f565b601001805467ffffffffffffffff19166001600160401b03939093169290921790915550565b7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f556120508133613ed2565b826001600160a01b03811661208b5760405162461bcd60e51b81526020600482015260016024820152600360fc1b6044820152606401610928565b6001600160a01b03831660009081526020819052604090206120ac81613f9f565b60038101546001600160a01b03868116911614156120f15760405162461bcd60e51b8152602060048201526002602482015261189960f11b6044820152606401610928565b846001600160a01b0316639aefb5c36040518163ffffffff1660e01b815260040160206040518083038186803b15801561212a57600080fd5b505afa15801561213e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216291906156b3565b8160060154146121995760405162461bcd60e51b8152602060048201526002602482015261313160f01b6044820152606401610928565b6003810180546001600160a01b0319166001600160a01b0387811691821790925560405190918616907f078c3b417dadf69374a59793b829c52001247130433427049317bde56607b1b790600090a36002810154604051637adbf97360e01b81526001600160a01b03878116600483015290911690637adbf97390602401600060405180830381600087803b15801561223157600080fd5b505af1158015612245573d6000803e3d6000fd5b505050505050505050565b6001600160a01b038216600090815260208190526040902061227181613f9f565b61229b7ffb286912c6eadba541f23a3bb3e83373ab139b6e65d84e2a473c186efc2b464284613fdd565b80600401548611156122d45760405162461bcd60e51b8152602060048201526002602482015261313760f01b6044820152606401610928565b6001600160a01b038516331415612350576001546040516315a38ec760e11b8152600481018890526001600160a01b03878116602483015290911690632b471d8e90604401600060405180830381600087803b15801561233357600080fd5b505af1158015612347573d6000803e3d6000fd5b505050506123bd565b600154604051630d43af8160e21b8152600481018890526001600160a01b0387811660248301523360448301529091169063350ebe0490606401600060405180830381600087803b1580156123a457600080fd5b505af11580156123b8573d6000803e3d6000fd5b505050505b60038101546040805163442133bd60e01b815290516000926001600160a01b03169163442133bd916004808301926020929190829003018186803b15801561240457600080fd5b505afa158015612418573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061243c91906156b3565b90506000818360060154896124519190615dea565b61245b9190615cc2565b9050600061246d633b9aca0084615dea565b846006015461247c8b87614689565b61248a90633b9aca00615e38565b612494908c615dea565b61249e9190615dea565b6124a89190615cc2565b9050848110156124df5760405162461bcd60e51b8152602060048201526002602482015261313560f01b6044820152606401610928565b888460040160008282546124f39190615e38565b9091555050604080518a8152602081018390526001600160a01b038816917f6acf1c7b1f88ba23f81a749aacb5bcaabf5095176f3eee66e251bbabd59544ed910160405180910390a2600b84015461257f90633b9aca0090600160401b90046001600160401b03166125658486615e38565b61256f9190615dea565b6125799190615cc2565b856142a2565b8354612245906001600160a01b031687898461406a565b600080516020615f688339815191526125af8133613ed2565b6004546125d490600080516020615f68833981519152906001600160a01b0316614597565b6125ec600080516020615f6883398151915283613f36565b50600480546001600160a01b0319166001600160a01b0392909216919091179055565b7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f5561263a8133613ed2565b60035460009060018110156126765760405162461bcd60e51b8152602060048201526002602482015261031360f41b6044820152606401610928565b60005b612684600183615e38565b81101561275457856001600160a01b0316600382815481106126a8576126a8615f17565b6000918252602090912001546001600160a01b03161415612742576001925060036126d38484615e38565b815481106126e3576126e3615f17565b600091825260209091200154600380546001600160a01b03909216918390811061270f5761270f615f17565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550612754565b8061274c81615eba565b915050612679565b50816001148061279d57506001600160a01b0385166003612776600184615e38565b8154811061278657612786615f17565b6000918252602090912001546001600160a01b0316145b6127ce5760405162461bcd60e51b8152602060048201526002602482015261031360f41b6044820152606401610928565b60038054806127df576127df615f01565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0387811683528282526040808420815161012080820184528254851682526001830154851682870152600283015485168285015260038301549094166060808301919091526004830154608080840191909152600584015460a080850191909152600685015460c08086019190915286516101008181018952600788015482526008880154828c01526009880154828a0152600a88015495820195909552600b8701546001600160401b0380821695830195909552600160401b8104851693820193909352600160801b8304841691810191909152600160c01b90910490911660e0828101919091528301528351600c840180549788028201870190955280820187815292969395918701949093909284929091849184018282801561298357602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116129405790505b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015612a0d57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116129ca5790505b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015612a9757602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b031681526020019060080190602082600701049283019260010382029150808411612a545790505b5050505050815260200160038201805480602002602001604051908101604052809291908181526020018280548015612b2157602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b031681526020019060080190602082600701049283019260010382029150808411612ade5790505b50505091835250506004828101546001600160401b03808216602080860191909152600160401b83048216604080870191909152600160801b909304909116606085015260059094015460809093019290925292909352825163d0fb020360e01b81529251949550600294600094506001600160a01b038c169363d0fb020393818101939291829003018186803b158015612bbb57600080fd5b505afa158015612bcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf39190615357565b6001600160a01b0390811682526020808301939093526040918201600090812080546001600160a01b031990811690915585840151831682526002808652848320805483169055928b1682529381905291822080548416815560018101805485169055908101805484169055600381018054909316909255600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c820181612cb282826151a8565b612cc06001830160006151a8565b612cce6002830160006151a8565b612cdc6003830160006151a8565b506004810180547fffffffffffffffff000000000000000000000000000000000000000000000000169055600060059091018190556040516001600160a01b038a1693507f10d8ed8cba916885e339cb21deefb3842b3f417eac81a7f34b2978b4ecc2040f9250a280604001516001600160a01b0316638456cb596040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612d8357600080fd5b505af1158015612d97573d6000803e3d6000fd5b505082516040516370a0823160e01b81526001600160a01b038a811660048301526000945090911691506370a082319060240160206040518083038186803b158015612de257600080fd5b505afa158015612df6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1a91906156b3565b8251909150612e34906001600160a01b031688888461406a565b600082606001516001600160a01b03166392611e336040518163ffffffff1660e01b815260040160206040518083038186803b158015612e7357600080fd5b505afa158015612e87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eab91906156b3565b60a08401516080850151604051630462bc3f60e51b815260048101849052602481019290925260448201529091506001600160a01b03881690638c5787e090606401600060405180830381600087803b158015612f0757600080fd5b505af1158015612f1b573d6000803e3d6000fd5b505050505050505050505050565b336000908152600260209081526040808320546001600160a01b03168352908290529020612f5681613f9f565b6010810180547fffffffffffffffff00000000000000000000000000000000ffffffffffffffff16600160401b6001600160401b03978816027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1617600160801b9587168602179055600b0180547fffffffffffffffff0000000000000000ffffffffffffffff00000000000000001692851690930267ffffffffffffffff1916919091179216919091179055565b60008281526007602090815260408083206001600160a01b038516845290915290205460ff165b92915050565b600080516020615f8883398151915261304b8133613ed2565b6001600160a01b038084166000908152602081905260408082209285168252902061307582613f9f565b61307e81613f9f565b60118201546004830154613093908890615c7f565b11156130c55760405162461bcd60e51b81526020600482015260016024820152600760fb1b6044820152606401610928565b858160040160008282546130d99190615e38565b92505081905550858260040160008282546130f49190615c7f565b9091555050815460048301546040519081526001600160a01b03909116907f72e0daee492d85727eb3f32c69303a6dd57af5bd2b127a04641de5d70e1494de9060200160405180910390a2805460048201546040519081526001600160a01b03909116907f72e0daee492d85727eb3f32c69303a6dd57af5bd2b127a04641de5d70e1494de9060200160405180910390a2505050505050565b600080516020615f688339815191526131a68133613ed2565b6131be600080516020615f8883398151915283614597565b60005b600354811015611c5157600381815481106131de576131de615f17565b60009182526020909120015460405163af648c3d60e01b81526001600160a01b0385811660048301529091169063af648c3d90602401600060405180830381600087803b15801561322e57600080fd5b505af1158015613242573d6000803e3d6000fd5b50505050808061325190615eba565b9150506131c1565b60006020818152918152604090819020805460018201546002830154600384015460048501546005860154600687015488516101008082018b5260078a0154825260088a0154828d015260098a0154828c0152600a8a01546060830152600b8a01546001600160401b038082166080850152600160401b8204811660a0850152600160801b8204811660c0850152600160c01b9091041660e08301528a51600c8b0180549d8e028201610120908101909d529181018d81526001600160a01b039a8b169d998b169c988b169b9a909716999598949793969295949093919284929091849184018282801561339e57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161335b5790505b505050505081526020016001820180548060200260200160405190810160405280929190818152602001828054801561342857602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116133e55790505b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156134b257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161346f5790505b505050505081526020016003820180548060200260200160405190810160405280929190818152602001828054801561353c57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116134f95790505b505050918352505060048201546001600160401b038082166020840152600160401b820481166040840152600160801b909104166060820152600590910154608090910152905089565b600080516020615f8883398151915261359f8133613ed2565b8383805182511480156135b3575060008251115b6135e35760405162461bcd60e51b81526020600482015260016024820152603560f81b6044820152606401610928565b60005b600182516135f49190615e38565b811161373157633b9aca006001600160401b031682828151811061361a5761361a615f17565b60200260200101516001600160401b0316111580156136685750633b9aca006001600160401b031683828151811061365457613654615f17565b60200260200101516001600160401b031611155b6136985760405162461bcd60e51b81526020600482015260016024820152601b60f91b6044820152606401610928565b801561371f57826136aa600183615e38565b815181106136ba576136ba615f17565b60200260200101516001600160401b03168382815181106136dd576136dd615f17565b60200260200101516001600160401b03161161371f5760405162461bcd60e51b81526020600482015260016024820152603760f81b6044820152606401610928565b8061372981615eba565b9150506135e6565b506001600160a01b038716600090815260208190526040902061375381613f9f565b60ff85161561378d57865161377190600c83019060208a01906151cd565b50855161378790600d83019060208901906151cd565b506137ba565b86516137a290600e83019060208a01906151cd565b5085516137b890600f83019060208901906151cd565b505b876001600160a01b03167f81d92f91000563c9b4868d5b791e00eb2c2e9b194b09df5608c862bda78c0b9d8888886040516137f793929190615aea565b60405180910390a25050505050505050565b806001600160a01b0381166138445760405162461bcd60e51b81526020600482015260016024820152600360fc1b6044820152606401610928565b600654610100900460ff168061385d575060065460ff16155b6138c05760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610928565b600654610100900460ff161580156138e2576006805461ffff19166101011790555b6138ea614807565b600480546001600160a01b0319166001600160a01b03851617905561391d600080516020615f68833981519152846148c1565b613935600080516020615f68833981519152806148cb565b61396d7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f55600080516020615f688339815191526148cb565b613993600080516020615f88833981519152600080516020615f688339815191526148cb565b8015611c51576006805461ff0019169055505050565b600080516020615f888339815191526139c28133613ed2565b6001600160a01b03821660009081526020819052604090206139e381613f9f565b611bb884846040516020016114b792919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b600080600160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015613a6557600080fd5b505afa158015613a79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a9d91906156b3565b905080613aad5760001991505090565b6000805b600354811015613c295760008060038381548110613ad157613ad1615f17565b60009182526020808320909101546001600160a01b0390811684529083019390935260409091019020600390810154815492169163240fd5ab919084908110613b1c57613b1c615f17565b6000918252602091829020015460408051632768385d60e01b815290516001600160a01b0390921692632768385d92600480840193829003018186803b158015613b6557600080fd5b505afa158015613b79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b9d91906156b3565b6040518263ffffffff1660e01b8152600401613bbb91815260200190565b60206040518083038186803b158015613bd357600080fd5b505afa158015613be7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c0b91906156b3565b613c159083615c7f565b915080613c2181615eba565b915050613ab1565b5081613c39633b9aca0083615dea565b613c439190615cc2565b9250505090565b600082815260076020526040902060010154613c668133613ed2565b611c518383614597565b600080516020615f88833981519152613c898133613ed2565b83633b9aca00816001600160401b03161115613ccb5760405162461bcd60e51b81526020600482015260016024820152600d60fa1b6044820152606401610928565b83633b9aca00816001600160401b03161115613d0d5760405162461bcd60e51b81526020600482015260016024820152600d60fa1b6044820152606401610928565b6001600160a01b0384166000908152602081905260409020613d2e81613f9f565b600b8101805477ffffffffffffffff0000000000000000ffffffffffffffff16600160401b6001600160401b038a811691820277ffffffffffffffffffffffffffffffffffffffffffffffff1692909217600160c01b928a16928302179092556040805192835260208301919091526001600160a01b038716917f1131c4b35ab7476b5e63eeda716dfe137c676f01395ab743a39ac34b099be9e2910160405180910390a250505050505050565b600080516020615f68833981519152613df58133613ed2565b613e1f7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f5583614597565b613e37600080516020615f8883398151915283614597565b60005b600354811015611c515760038181548110613e5757613e57615f17565b600091825260209091200154604051631dd9b59160e31b81526001600160a01b0385811660048301529091169063eecdac8890602401600060405180830381600087803b158015613ea757600080fd5b505af1158015613ebb573d6000803e3d6000fd5b505050508080613eca90615eba565b915050613e3a565b613edc8282613005565b610cb057613ef4816001600160a01b0316601461491f565b613eff83602061491f565b604051602001613f109291906159f0565b60408051601f198184030181529082905262461bcd60e51b825261092891600401615bf9565b613f408282613005565b610cb05760008281526007602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b80546001600160a01b0316613fda5760405162461bcd60e51b81526020600482015260016024820152603360f81b6044820152606401610928565b50565b60056000838360405160200161400f92919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b60408051601f198184030181529181528151602092830120835290820192909252016000205460ff1615610cb05760405162461bcd60e51b8152602060048201526002602482015261062760f31b6044820152606401610928565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166323b872dd60e01b179052611bb8908590614ace565b600c81015460009081906001141561413057600d8301805460009061410057614100615f17565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03169050614262565b600061414b8460040154866141459190615c7f565b85614bb3565b905061425e8185600c016000018054806020026020016040519081016040528092919081815260200182805480156141d457602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116141915790505b50505050600d8801805460408051602080840282018101909252828152935083018282801561425457602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116142115790505b5050505050614ca3565b9150505b6010830154633b9aca009061428790600160401b90046001600160401b031683615e09565b6001600160401b031661429a9190615cc2565b949350505050565b6008810154600782015442148015906142bb5750600081115b15614431576001820154604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b15801561430757600080fd5b505afa15801561431b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061433f91906156b3565b9050801561442a5760098301548211156143a5576009830154819061436d90670de0b6b3a764000090615dea565b6143779190615cc2565b83600501600082825461438a9190615c7f565b9091555050600983015461439e9083615e38565b91506143df565b806143b8670de0b6b3a764000084615dea565b6143c29190615cc2565b8360050160008282546143d59190615c7f565b9091555060009250505b825460058401546040519081526001600160a01b03909116907faa959327c96abe1dbec8186dbe4bb630ffa7f1a65ad98780481f14757004498e9060200160405180910390a261442f565b600091505b505b82156144ee57600b8201546001600160401b03161580156144555750600a82015415155b1561447757600a8201546144699084615c7f565b6000600a84015592506144e1565b600b8201546001600160401b0316156144e157600b820154600090633b9aca00906144ab906001600160401b031686615dea565b6144b59190615cc2565b90506144c18185615e38565b9350808360070160030160008282546144da9190615c7f565b9091555050505b6144eb8382615c7f565b90505b60088201554260079091015550565b60008181526005602052604090205460ff16156145415760405162461bcd60e51b8152602060048201526002602482015261062760f31b6044820152606401610928565b60008181526005602052604090819020805460ff19166001179055517f0cb09dc71d57eeec2046f6854976717e4874a3cf2d6ddeddde337e5b6de6ba319061458c9083815260200190565b60405180910390a150565b6145a18282613005565b15610cb05760008281526007602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60008181526005602052604090205460ff166146415760405162461bcd60e51b8152602060048201526002602482015261313960f01b6044820152606401610928565b60008181526005602052604090819020805460ff19169055517fd05bfc2250abb0f8fd265a54c53a24359c5484af63cad2e4ce87c78ab751395a9061458c9083815260200190565b600e8101546000908190600114156146e057600f830180546000906146b0576146b0615f17565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b031690506147e2565b60006146f58585600401546141459190615e38565b90506147de8185600c0160020180548060200260200160405190810160405280929190818152602001828054801561477e57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161473b5790505b50505050600f8801805460408051602080840282018101909252828152935083018282801561425457600091825260209182902080546001600160401b031684529082028301929091600891018084116142115790505050505050614ca3565b9150505b6010830154633b9aca009061428790600160801b90046001600160401b031683615e09565b600654610100900460ff1680614820575060065460ff16155b6148835760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610928565b600654610100900460ff161580156148a5576006805461ffff19166101011790555b6148ad614fec565b8015613fda576006805461ff001916905550565b610cb08282613f36565b600082815260076020526040902060010154819060405184907fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff90600090a460009182526007602052604090912060010155565b6060600061492e836002615dea565b614939906002615c7f565b6001600160401b0381111561495057614950615f2d565b6040519080825280601f01601f19166020018201604052801561497a576020820181803683370190505b509050600360fc1b8160008151811061499557614995615f17565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106149c4576149c4615f17565b60200101906001600160f81b031916908160001a90535060006149e8846002615dea565b6149f3906001615c7f565b90505b6001811115614a78577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110614a3457614a34615f17565b1a60f81b828281518110614a4a57614a4a615f17565b60200101906001600160f81b031916908160001a90535060049490941c93614a7181615ea3565b90506149f6565b508315614ac75760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610928565b9392505050565b6000614b23826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661509d9092919063ffffffff16565b805190915015611c515780806020019051810190614b41919061555a565b611c515760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610928565b6000808260020160009054906101000a90046001600160a01b03166001600160a01b031663b8f368956040518163ffffffff1660e01b815260040160206040518083038186803b158015614c0657600080fd5b505afa158015614c1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c3e91906156b3565b6010840154909150633b9aca0090614c609086906001600160401b0316615dea565b614c6a9190615cc2565b935080841115614c945783614c83633b9aca0083615dea565b614c8d9190615cc2565b9150614c9c565b633b9aca0091505b5092915050565b60008260018451614cb49190615e38565b81518110614cc457614cc4615f17565b60200260200101516001600160401b0316846001600160401b031610614d12578160018451614cf39190615e38565b81518110614d0357614d03615f17565b60200260200101519050614ac7565b82600081518110614d2557614d25615f17565b60200260200101516001600160401b0316846001600160401b031611614d585781600081518110614d0357614d03615f17565b60008060018551614d699190615e38565b905060005b6001614d7a8484615e38565b1115614de4576002614d8c8484615e38565b614d969190615cc2565b614da09084615c7f565b9050866001600160401b0316868281518110614dbe57614dbe615f17565b60200260200101516001600160401b031611614ddc57809250614d6e565b809150614d6e565b848381518110614df657614df6615f17565b60200260200101516001600160401b0316858381518110614e1957614e19615f17565b60200260200101516001600160401b03161115614f1357858381518110614e4257614e42615f17565b6020026020010151868381518110614e5c57614e5c615f17565b6020026020010151614e6e9190615e4f565b868481518110614e8057614e80615f17565b602002602001015188614e939190615e4f565b868581518110614ea557614ea5615f17565b6020026020010151878581518110614ebf57614ebf615f17565b6020026020010151614ed19190615e4f565b614edb9190615e09565b614ee59190615cd6565b858481518110614ef757614ef7615f17565b6020026020010151614f099190615c97565b9350505050614ac7565b858381518110614f2557614f25615f17565b6020026020010151868381518110614f3f57614f3f615f17565b6020026020010151614f519190615e4f565b868481518110614f6357614f63615f17565b602002602001015188614f769190615e4f565b868481518110614f8857614f88615f17565b6020026020010151878681518110614fa257614fa2615f17565b6020026020010151614fb49190615e4f565b614fbe9190615e09565b614fc89190615cd6565b858481518110614fda57614fda615f17565b6020026020010151614f099190615e4f565b600654610100900460ff1680615005575060065460ff16155b6150685760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610928565b600654610100900460ff161580156148ad576006805461ffff19166101011790558015613fda576006805461ff001916905550565b606061429a848460008585843b6150f65760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610928565b600080866001600160a01b0316858760405161511291906159d4565b60006040518083038185875af1925050503d806000811461514f576040519150601f19603f3d011682016040523d82523d6000602084013e615154565b606091505b509150915061516482828661516f565b979650505050505050565b6060831561517e575081614ac7565b82511561518e5782518084602001fd5b8160405162461bcd60e51b81526004016109289190615bf9565b508054600082556003016004900490600052602060002090810190613fda9190615285565b828054828255906000526020600020906003016004900481019282156152755791602002820160005b8382111561524057835183826101000a8154816001600160401b0302191690836001600160401b0316021790555092602001926008016020816007010492830192600103026151f6565b80156152735782816101000a8154906001600160401b030219169055600801602081600701049283019260010302615240565b505b50615281929150615285565b5090565b5b808211156152815760008155600101615286565b80356152a581615f43565b919050565b600082601f8301126152bb57600080fd5b813560206152d06152cb83615c5c565b615c2c565b80838252828201915082860187848660051b89010111156152f057600080fd5b60005b858110156153165761530482615323565b845292840192908401906001016152f3565b5090979650505050505050565b80356001600160401b03811681146152a557600080fd5b60006020828403121561534c57600080fd5b8135614ac781615f43565b60006020828403121561536957600080fd5b8151614ac781615f43565b6000806040838503121561538757600080fd5b823561539281615f43565b915060208301356153a281615f43565b809150509250929050565b6000806000606084860312156153c257600080fd5b83356153cd81615f43565b925060208401356153dd81615f43565b915060408401356153ed81615f43565b809150509250925092565b6000602080838503121561540b57600080fd5b82516001600160401b0381111561542157600080fd5b8301601f8101851361543257600080fd5b80516154406152cb82615c5c565b80828252848201915084840188868560051b870101111561546057600080fd5b600094505b8385101561548c57805161547881615f43565b835260019490940193918501918501615465565b50979650505050505050565b6000806000606084860312156154ad57600080fd5b83356001600160401b038111156154c357600080fd5b8401601f810186136154d457600080fd5b803560206154e46152cb83615c5c565b8083825282820191508285018a848660051b880101111561550457600080fd5b600095505b8486101561553057803561551c81615f43565b835260019590950194918301918301615509565b509650615540905087820161529a565b94505050506155516040850161529a565b90509250925092565b60006020828403121561556c57600080fd5b81518015158114614ac757600080fd5b60006020828403121561558e57600080fd5b5035919050565b600080604083850312156155a857600080fd5b8235915060208301356153a281615f43565b600080600080608085870312156155d057600080fd5b84356155db81615f43565b935060208501356001600160401b03808211156155f757600080fd5b615603888389016152aa565b9450604087013591508082111561561957600080fd5b50615626878288016152aa565b925050606085013561563781615f58565b939692955090935050565b600080600080600060a0868803121561565a57600080fd5b853561566581615f43565b9450602086013561567581615f43565b9350604086013561568581615f43565b9250606086013561569581615f43565b915060808601356156a581615f43565b809150509295509295909350565b6000602082840312156156c557600080fd5b5051919050565b600080600080608085870312156156e257600080fd5b8435935060208501356156f481615f43565b9250604085013561570481615f43565b9150606085013561563781615f43565b600080600080600060a0868803121561572c57600080fd5b85359450602086013561573e81615f43565b9350604086013561574e81615f43565b9250606086013561575e81615f43565b949793965091946080013592915050565b60008060006060848603121561578457600080fd5b8335925060208401356153dd81615f43565b600080600080608085870312156157ac57600080fd5b8435935060208501356157be81615f43565b925060408501356157ce81615f43565b9396929550929360600135925050565b6000806000606084860312156157f357600080fd5b833592506020840135915060408401356153ed81615f43565b60006020828403121561581e57600080fd5b614ac782615323565b60008060006060848603121561583c57600080fd5b61584584615323565b92506153dd60208501615323565b6000806000806080858703121561586957600080fd5b61587285615323565b935061588060208601615323565b925061588e60408601615323565b915061589c60608601615323565b905092959194509250565b6000602082840312156158b957600080fd5b8151614ac781615f58565b600081518084526020808501945080840160005b838110156158fd5781516001600160401b0316875295820195908201906001016158d8565b509495945050505050565b6000610100825181855261591e828601826158c4565b9150506020830151848203602086015261593882826158c4565b9150506040830151848203604086015261595282826158c4565b9150506060830151848203606086015261596c82826158c4565b915050608083015161598960808601826001600160401b03169052565b5060a08301516159a460a08601826001600160401b03169052565b5060c08301516159bf60c08601826001600160401b03169052565b5060e083015160e08501528091505092915050565b600082516159e6818460208701615e77565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615a28816017850160208801615e77565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351615a65816028840160208801615e77565b01602801949350505050565b60a0808252865190820181905260009060209060c0840190828a01845b82811015615ab35781516001600160a01b031684529284019290840190600101615a8e565b5050506001600160a01b03978816918401919091529486166040830152509184166060830152909216608090920191909152919050565b606081526000615afd60608301866158c4565b8281036020840152615b0f81866158c4565b91505060ff83166040830152949350505050565b60006102006001600160a01b03808d168452808c166020850152808b166040850152808a166060850152508760808401528660a08401528560c0840152845160e084015260208501516101008401526040850151610120840152606085015161014084015260808501516001600160401b038082166101608601528060a0880151166101808601528060c0880151166101a0860152505060e0850151615bd56101c08501826001600160401b03169052565b50806101e0840152615be981840185615908565b9c9b505050505050505050505050565b6020815260008251806020840152615c18816040850160208701615e77565b601f01601f19169190910160400192915050565b604051601f8201601f191681016001600160401b0381118282101715615c5457615c54615f2d565b604052919050565b60006001600160401b03821115615c7557615c75615f2d565b5060051b60200190565b60008219821115615c9257615c92615ed5565b500190565b60006001600160401b03808316818516808303821115615cb957615cb9615ed5565b01949350505050565b600082615cd157615cd1615eeb565b500490565b60006001600160401b0380841680615cf057615cf0615eeb565b92169190910492915050565b600181815b80851115615d37578160001904821115615d1d57615d1d615ed5565b80851615615d2a57918102915b93841c9390800290615d01565b509250929050565b6000614ac760ff841683600082615d585750600161302c565b81615d655750600061302c565b8160018114615d7b5760028114615d8557615da1565b600191505061302c565b60ff841115615d9657615d96615ed5565b50506001821b61302c565b5060208310610133831016604e8410600b8410161715615dc4575081810a61302c565b615dce8383615cfc565b8060001904821115615de257615de2615ed5565b029392505050565b6000816000190483118215151615615e0457615e04615ed5565b500290565b60006001600160401b0380831681851681830481118215151615615e2f57615e2f615ed5565b02949350505050565b600082821015615e4a57615e4a615ed5565b500390565b60006001600160401b0383811690831681811015615e6f57615e6f615ed5565b039392505050565b60005b83811015615e92578181015183820152602001615e7a565b83811115611bb85750506000910152565b600081615eb257615eb2615ed5565b506000190190565b6000600019821415615ece57615ece615ed5565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114613fda57600080fd5b60ff81168114613fda57600080fdfe502d3d275257923b2bea6ea25d9631f12369fb532871f13eb85eb09dc0fb484255435dd261a4b9b3364963f7738a7a662ad9c84396d64be3365284bb7f0a5041a2646970667358221220fc0954c09bd004c0cf6f4c5b7926ec39df1969875ddca840ece9b5a7217e371c64736f6c63430008070033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102ff5760003560e01c80635c38eb3a1161019c578063ba8b7223116100ee578063d547741f11610097578063ee565a6311610071578063ee565a63146106b2578063eecdac88146106dd578063f36246d1146106f057600080fd5b8063d547741f1461067d578063df1b8bd314610690578063ec5b1bee1461069f57600080fd5b8063cbb3edd2116100c8578063cbb3edd21461063b578063ccc574901461064e578063cd377c531461067557600080fd5b8063ba8b7223146105ed578063be8c31ff14610615578063c4d66de81461062857600080fd5b806391d14854116101505780639f48118f1161012a5780639f48118f146105c7578063a217fddf146105d2578063af648c3d146105da57600080fd5b806391d148541461056e5780639af2dcae146105915780639e9e4666146105a457600080fd5b806380009630116101815780638000963014610535578063892cfc161461054857806391a9ca481461055b57600080fd5b80635c38eb3a1461050f5780636d1042161461052257600080fd5b8063252c94061161025557806336568abe1161020957806359de6866116101e357806359de6866146104d65780635a3c10f1146104e95780635b749f35146104fc57600080fd5b806336568abe146104895780633c4a25d01461049c57806354d9f653146104af57600080fd5b80632f2ff15d1161023a5780632f2ff15d1461043657806333c509d114610449578063344844db1461045c57600080fd5b8063252c9406146104105780632e2d29841461042357600080fd5b806322ac5c9a116102b757806324ea54f41161029157806324ea54f4146103c15780632509f1b9146103d6578063251ad873146103fd57600080fd5b806322ac5c9a1461036557806323e103a814610378578063248a9ca31461038b57600080fd5b8063074efa78116102e8578063074efa781461032c578063087264c91461033f5780631a115ff11461035257600080fd5b8063050f53fe14610304578063074ee44614610319575b600080fd5b610317610312366004615498565b610705565b005b610317610327366004615796565b6107e1565b61031761033a3660046153ad565b610a85565b61031761034d36600461557c565b610c5f565b610317610360366004615595565b610cb4565b610317610373366004615642565b610dce565b6103176103863660046156cc565b6116f7565b6103ae61039936600461557c565b60009081526007602052604090206001015490565b6040519081526020015b60405180910390f35b6103ae600080516020615f8883398151915281565b6103ae7fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d74581565b61031761040b366004615595565b6118ba565b61031761041e36600461557c565b6119b5565b61031761043136600461576f565b611bbe565b610317610444366004615595565b611c2b565b610317610457366004615374565b611c56565b336000908152600260209081526040808320546001600160a01b03168352908290529020600401546103ae565b610317610497366004615595565b611d42565b6103176104aa36600461533a565b611d89565b6103ae7ffb286912c6eadba541f23a3bb3e83373ab139b6e65d84e2a473c186efc2b464281565b6103176104e4366004615595565b611e7f565b6103176104f73660046157de565b611f05565b61031761050a36600461580c565b611fd0565b61031761051d366004615374565b612025565b610317610530366004615714565b612250565b61031761054336600461533a565b612596565b610317610556366004615374565b61260f565b610317610569366004615853565b612f29565b61058161057c366004615595565b613005565b60405190151581526020016103b8565b61031761059f36600461576f565b613032565b6105816105b236600461557c565b60056020526000908152604090205460ff1681565b6103ae633b9aca0081565b6103ae600081565b6103176105e836600461533a565b61318d565b6106006105fb36600461533a565b613259565b6040516103b899989796959493929190615b23565b6103176106233660046155ba565b613586565b61031761063636600461533a565b613809565b610317610649366004615595565b6139a9565b6103ae7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f5581565b6103ae613a14565b61031761068b366004615595565b613c4a565b6103ae670de0b6b3a764000081565b6103176106ad366004615827565b613c70565b6001546106c5906001600160a01b031681565b6040516001600160a01b0390911681526020016103b8565b6103176106eb36600461533a565b613ddc565b6103ae600080516020615f6883398151915281565b600080516020615f6883398151915261071e8133613ed2565b60005b84518110156107a35761076d7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f5586838151811061076057610760615f17565b6020026020010151613f36565b610791600080516020615f8883398151915286838151811061076057610760615f17565b8061079b81615eba565b915050610721565b506107bc600080516020615f8883398151915284613f36565b50600180546001600160a01b0319166001600160a01b03929092169190911790555050565b6001600160a01b038216600090815260208190526040902061080281613f9f565b61082c7ffb286912c6eadba541f23a3bb3e83373ab139b6e65d84e2a473c186efc2b464284613fdd565b8054610843906001600160a01b031633858861406a565b6003810154604051633593ba7b60e01b8152600481018790526000916001600160a01b031690633593ba7b9060240160206040518083038186803b15801561088a57600080fd5b505afa15801561089e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c291906156b3565b905060006108d082846140d9565b9050633b9aca006108e18282615e38565b6108eb9084615dea565b6108f59190615cc2565b9150838210156109315760405162461bcd60e51b8152602060048201526002602482015261313560f01b60448201526064015b60405180910390fd5b818360040160008282546109459190615c7f565b90915550506011830154600484015411156109875760405162461bcd60e51b8152602060048201526002602482015261189b60f11b6044820152606401610928565b60408051888152602081018490526001600160a01b038716917ff0543d9ab673fd2b6789938fb372d27b43b955c1a90eb8a982bc4aa50687962c910160405180910390a2610a166109dd6002633b9aca00615d3f565b600b850154600160401b90046001600160401b03166109fc848b615dea565b610a069190615dea565b610a109190615cc2565b846142a2565b6001546040516340c10f1960e01b81526001600160a01b03888116600483015260248201859052909116906340c10f1990604401600060405180830381600087803b158015610a6457600080fd5b505af1158015610a78573d6000803e3d6000fd5b5050505050505050505050565b600080516020615f88833981519152610a9e8133613ed2565b836001600160a01b038116610ad95760405162461bcd60e51b81526020600482015260016024820152600360fc1b6044820152606401610928565b6001600160a01b0383166000908152602081905260409020610afa81613f9f565b6001600160a01b03858116600090815260026020526040902054811690851614610b4b5760405162461bcd60e51b8152602060048201526002602482015261031360f41b6044820152606401610928565b846001600160a01b0316866001600160a01b03161415610b925760405162461bcd60e51b81526020600482015260026024820152610c4d60f21b6044820152606401610928565b6001600160a01b0380861660009081526002602052604080822080546001600160a01b03199081169091558984168084528284208054958a1695909216851790915590519092917fe139a19f7890c89603e0d013fb99aed7d286ef55ef5a092ddd2980a3afd2138d91a360405163472d35b960e01b81526001600160a01b03878116600483015285169063472d35b990602401600060405180830381600087803b158015610c3f57600080fd5b505af1158015610c53573d6000803e3d6000fd5b50505050505050505050565b336000908152602081905260409020610c7781613f9f565b600b810154610cb090633b9aca0090610ca090600160c01b90046001600160401b031685615dea565b610caa9190615cc2565b826142a2565b5050565b336000908152600260209081526040808320546001600160a01b031680845291839052909120610ce381613f9f565b610d0d7fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d74583613fdd565b610d186000826142a2565b600181015460058201546001600160a01b03909116906340c10f19908590610d48670de0b6b3a764000089615dea565b610d529190615cc2565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015610db057600080fd5b505af1158015610dc4573d6000803e3d6000fd5b5050505050505050565b7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f55610df98133613ed2565b306001600160a01b0316826001600160a01b0316636ac5dc466040518163ffffffff1660e01b815260040160206040518083038186803b158015610e3c57600080fd5b505afa158015610e50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e749190615357565b6001600160a01b0316148015610f0b5750856001600160a01b0316826001600160a01b031663dc4c90d36040518163ffffffff1660e01b815260040160206040518083038186803b158015610ec857600080fd5b505afa158015610edc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f009190615357565b6001600160a01b0316145b8015610f985750306001600160a01b0316866001600160a01b0316636ac5dc466040518163ffffffff1660e01b815260040160206040518083038186803b158015610f5557600080fd5b505afa158015610f69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f8d9190615357565b6001600160a01b0316145b80156110255750856001600160a01b0316856001600160a01b031663dc4c90d36040518163ffffffff1660e01b815260040160206040518083038186803b158015610fe257600080fd5b505afa158015610ff6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061101a9190615357565b6001600160a01b0316145b80156110b25750306001600160a01b0316846001600160a01b0316636ac5dc466040518163ffffffff1660e01b815260040160206040518083038186803b15801561106f57600080fd5b505afa158015611083573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a79190615357565b6001600160a01b0316145b6110e25760405162461bcd60e51b81526020600482015260016024820152603960f81b6044820152606401610928565b6000866001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561111d57600080fd5b505afa158015611131573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111559190615357565b90506000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561119257600080fd5b505afa1580156111a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ca91906158a7565b6111d590600a615d3f565b905080856001600160a01b0316639aefb5c36040518163ffffffff1660e01b815260040160206040518083038186803b15801561121157600080fd5b505afa158015611225573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061124991906156b3565b1461127b5760405162461bcd60e51b8152602060048201526002602482015261313160f01b6044820152606401610928565b6001600160a01b0380891660009081526020819052604090208054909116156112cb5760405162461bcd60e51b8152602060048201526002602482015261313360f01b6044820152606401610928565b828160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550848160010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550878160020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550858160030160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550670de0b6b3a7640000816005018190555081816006018190555088600260008a6001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055508860026000896001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055506003899080600181540180825580915050600190039060005260206000200160009091909190916101000a8154816001600160a01b0302191690836001600160a01b031602179055506114d27fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d7458a6040516020016114b792919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b604051602081830303815290604052805190602001206144fd565b6115237ffb286912c6eadba541f23a3bb3e83373ab139b6e65d84e2a473c186efc2b46428a6040516020016114b792919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b6000600460009054906101000a90046001600160a01b03166001600160a01b031663a6d7fe2d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561157357600080fd5b505afa158015611587573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115af91908101906153f8565b90506000600460009054906101000a90046001600160a01b03166001600160a01b031663452a93206040518163ffffffff1660e01b815260040160206040518083038186803b15801561160157600080fd5b505afa158015611615573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116399190615357565b604051636cb2e74d60e11b81529091506001600160a01b038c169063d965ce9a9061167090859085908f908f908f90600401615a71565b600060405180830381600087803b15801561168a57600080fd5b505af115801561169e573d6000803e3d6000fd5b50506040516001600160a01b038b81168252808b1693508d811692508e16907f129e7033df7c6cf5b088a92e04f596efded950692229dda217a8054d40bb02929060200160405180910390a45050505050505050505050565b6001600160a01b038116600090815260208190526040902061171881613f9f565b6117427fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d74583613fdd565b61174d6000826142a2565b6001600160a01b0384163314156117cb5760018101546040516315a38ec760e11b8152600481018790526001600160a01b03868116602483015290911690632b471d8e90604401600060405180830381600087803b1580156117ae57600080fd5b505af11580156117c2573d6000803e3d6000fd5b5050505061183a565b6001810154604051630d43af8160e21b8152600481018790526001600160a01b0386811660248301523360448301529091169063350ebe0490606401600060405180830381600087803b15801561182157600080fd5b505af1158015611835573d6000803e3d6000fd5b505050505b6000611852633b9aca00670de0b6b3a7640000615dea565b6005830154600b84015461187a90600160801b90046001600160401b0316633b9aca00615e38565b6118849089615dea565b61188e9190615dea565b6118989190615cc2565b82549091506118b2906001600160a01b031684868461406a565b505050505050565b6001546001600160a01b031633146118f85760405162461bcd60e51b81526020600482015260016024820152603360f81b6044820152606401610928565b6001600160a01b038116600090815260208190526040902061191981613f9f565b82816004015410156119515760405162461bcd60e51b81526020600482015260016024820152600d60fa1b6044820152606401610928565b828160040160008282546119659190615e38565b9091555050805460048201546040519081526001600160a01b03909116907f72e0daee492d85727eb3f32c69303a6dd57af5bd2b127a04641de5d70e1494de9060200160405180910390a2505050565b3360008181526020819052604090206119cd81613f9f565b6001810154604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b158015611a1457600080fd5b505afa158015611a28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a4c91906156b3565b90508015611bb857611a66670de0b6b3a764000085615dea565b6008830154611a7e90670de0b6b3a764000090615dea565b828460050154611a8e9190615dea565b611a989190615c7f565b1115611b11576008820154848110611aad5750835b80836007016001016000828254611ac49190615e38565b90915550829050670de0b6b3a7640000611ade8388615e38565b611ae89190615dea565b611af29190615cc2565b836005016000828254611b059190615e38565b90915550611b71915050565b6001600583015560006008830155604051611b71906114b7907fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d74590869060200191825260601b6bffffffffffffffffffffffff1916602082015260340190565b815460058301546040519081526001600160a01b03909116907faa959327c96abe1dbec8186dbe4bb630ffa7f1a65ad98780481f14757004498e9060200160405180910390a25b50505050565b6001600160a01b0381166000908152602081905260409020611bdf81613f9f565b611c097fe0136b3661826a483734248681e4f59ae66bc6065ceb43fdd469ecb22c21d74583613fdd565b611c146000826142a2565b8054610d18906001600160a01b031633848761406a565b600082815260076020526040902060010154611c478133613ed2565b611c518383613f36565b505050565b600080516020615f68833981519152611c6f8133613ed2565b611c87600080516020615f8883398151915283614597565b611c9f600080516020615f8883398151915284613f36565b60005b600354811015611bb85760038181548110611cbf57611cbf615f17565b6000918252602090912001546040516333c509d160e01b81526001600160a01b0386811660048301528581166024830152909116906333c509d190604401600060405180830381600087803b158015611d1757600080fd5b505af1158015611d2b573d6000803e3d6000fd5b505050508080611d3a90615eba565b915050611ca2565b6001600160a01b0381163314611d7f5760405162461bcd60e51b8152602060048201526002602482015261373160f01b6044820152606401610928565b610cb08282614597565b600080516020615f68833981519152611da28133613ed2565b611dcc7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f5583613f36565b611de4600080516020615f8883398151915283613f36565b60005b600354811015611c515760038181548110611e0457611e04615f17565b6000918252602090912001546040516303c4a25d60e41b81526001600160a01b03858116600483015290911690633c4a25d090602401600060405180830381600087803b158015611e5457600080fd5b505af1158015611e68573d6000803e3d6000fd5b505050508080611e7790615eba565b915050611de7565b600080516020615f88833981519152611e988133613ed2565b6001600160a01b0382166000908152602081905260409020611eb981613f9f565b611bb88484604051602001611eea92919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b604051602081830303815290604052805190602001206145fe565b600080516020615f88833981519152611f1e8133613ed2565b6001600160a01b0382166000908152602081905260409020611f3f81613f9f565b8060040154851015611f775760405162461bcd60e51b81526020600482015260016024820152600760fb1b6044820152606401610928565b601181018590556009810184905560408051868152602081018690526001600160a01b038516917faa2226ff9a1d81d234d2d96d4a86cc03d1b9165cda2f2591c1df913002201f22910160405180910390a25050505050565b336000908152600260209081526040808320546001600160a01b031680845291839052909120611fff81613f9f565b601001805467ffffffffffffffff19166001600160401b03939093169290921790915550565b7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f556120508133613ed2565b826001600160a01b03811661208b5760405162461bcd60e51b81526020600482015260016024820152600360fc1b6044820152606401610928565b6001600160a01b03831660009081526020819052604090206120ac81613f9f565b60038101546001600160a01b03868116911614156120f15760405162461bcd60e51b8152602060048201526002602482015261189960f11b6044820152606401610928565b846001600160a01b0316639aefb5c36040518163ffffffff1660e01b815260040160206040518083038186803b15801561212a57600080fd5b505afa15801561213e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216291906156b3565b8160060154146121995760405162461bcd60e51b8152602060048201526002602482015261313160f01b6044820152606401610928565b6003810180546001600160a01b0319166001600160a01b0387811691821790925560405190918616907f078c3b417dadf69374a59793b829c52001247130433427049317bde56607b1b790600090a36002810154604051637adbf97360e01b81526001600160a01b03878116600483015290911690637adbf97390602401600060405180830381600087803b15801561223157600080fd5b505af1158015612245573d6000803e3d6000fd5b505050505050505050565b6001600160a01b038216600090815260208190526040902061227181613f9f565b61229b7ffb286912c6eadba541f23a3bb3e83373ab139b6e65d84e2a473c186efc2b464284613fdd565b80600401548611156122d45760405162461bcd60e51b8152602060048201526002602482015261313760f01b6044820152606401610928565b6001600160a01b038516331415612350576001546040516315a38ec760e11b8152600481018890526001600160a01b03878116602483015290911690632b471d8e90604401600060405180830381600087803b15801561233357600080fd5b505af1158015612347573d6000803e3d6000fd5b505050506123bd565b600154604051630d43af8160e21b8152600481018890526001600160a01b0387811660248301523360448301529091169063350ebe0490606401600060405180830381600087803b1580156123a457600080fd5b505af11580156123b8573d6000803e3d6000fd5b505050505b60038101546040805163442133bd60e01b815290516000926001600160a01b03169163442133bd916004808301926020929190829003018186803b15801561240457600080fd5b505afa158015612418573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061243c91906156b3565b90506000818360060154896124519190615dea565b61245b9190615cc2565b9050600061246d633b9aca0084615dea565b846006015461247c8b87614689565b61248a90633b9aca00615e38565b612494908c615dea565b61249e9190615dea565b6124a89190615cc2565b9050848110156124df5760405162461bcd60e51b8152602060048201526002602482015261313560f01b6044820152606401610928565b888460040160008282546124f39190615e38565b9091555050604080518a8152602081018390526001600160a01b038816917f6acf1c7b1f88ba23f81a749aacb5bcaabf5095176f3eee66e251bbabd59544ed910160405180910390a2600b84015461257f90633b9aca0090600160401b90046001600160401b03166125658486615e38565b61256f9190615dea565b6125799190615cc2565b856142a2565b8354612245906001600160a01b031687898461406a565b600080516020615f688339815191526125af8133613ed2565b6004546125d490600080516020615f68833981519152906001600160a01b0316614597565b6125ec600080516020615f6883398151915283613f36565b50600480546001600160a01b0319166001600160a01b0392909216919091179055565b7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f5561263a8133613ed2565b60035460009060018110156126765760405162461bcd60e51b8152602060048201526002602482015261031360f41b6044820152606401610928565b60005b612684600183615e38565b81101561275457856001600160a01b0316600382815481106126a8576126a8615f17565b6000918252602090912001546001600160a01b03161415612742576001925060036126d38484615e38565b815481106126e3576126e3615f17565b600091825260209091200154600380546001600160a01b03909216918390811061270f5761270f615f17565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550612754565b8061274c81615eba565b915050612679565b50816001148061279d57506001600160a01b0385166003612776600184615e38565b8154811061278657612786615f17565b6000918252602090912001546001600160a01b0316145b6127ce5760405162461bcd60e51b8152602060048201526002602482015261031360f41b6044820152606401610928565b60038054806127df576127df615f01565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0387811683528282526040808420815161012080820184528254851682526001830154851682870152600283015485168285015260038301549094166060808301919091526004830154608080840191909152600584015460a080850191909152600685015460c08086019190915286516101008181018952600788015482526008880154828c01526009880154828a0152600a88015495820195909552600b8701546001600160401b0380821695830195909552600160401b8104851693820193909352600160801b8304841691810191909152600160c01b90910490911660e0828101919091528301528351600c840180549788028201870190955280820187815292969395918701949093909284929091849184018282801561298357602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116129405790505b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015612a0d57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116129ca5790505b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015612a9757602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b031681526020019060080190602082600701049283019260010382029150808411612a545790505b5050505050815260200160038201805480602002602001604051908101604052809291908181526020018280548015612b2157602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b031681526020019060080190602082600701049283019260010382029150808411612ade5790505b50505091835250506004828101546001600160401b03808216602080860191909152600160401b83048216604080870191909152600160801b909304909116606085015260059094015460809093019290925292909352825163d0fb020360e01b81529251949550600294600094506001600160a01b038c169363d0fb020393818101939291829003018186803b158015612bbb57600080fd5b505afa158015612bcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf39190615357565b6001600160a01b0390811682526020808301939093526040918201600090812080546001600160a01b031990811690915585840151831682526002808652848320805483169055928b1682529381905291822080548416815560018101805485169055908101805484169055600381018054909316909255600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c820181612cb282826151a8565b612cc06001830160006151a8565b612cce6002830160006151a8565b612cdc6003830160006151a8565b506004810180547fffffffffffffffff000000000000000000000000000000000000000000000000169055600060059091018190556040516001600160a01b038a1693507f10d8ed8cba916885e339cb21deefb3842b3f417eac81a7f34b2978b4ecc2040f9250a280604001516001600160a01b0316638456cb596040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612d8357600080fd5b505af1158015612d97573d6000803e3d6000fd5b505082516040516370a0823160e01b81526001600160a01b038a811660048301526000945090911691506370a082319060240160206040518083038186803b158015612de257600080fd5b505afa158015612df6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1a91906156b3565b8251909150612e34906001600160a01b031688888461406a565b600082606001516001600160a01b03166392611e336040518163ffffffff1660e01b815260040160206040518083038186803b158015612e7357600080fd5b505afa158015612e87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eab91906156b3565b60a08401516080850151604051630462bc3f60e51b815260048101849052602481019290925260448201529091506001600160a01b03881690638c5787e090606401600060405180830381600087803b158015612f0757600080fd5b505af1158015612f1b573d6000803e3d6000fd5b505050505050505050505050565b336000908152600260209081526040808320546001600160a01b03168352908290529020612f5681613f9f565b6010810180547fffffffffffffffff00000000000000000000000000000000ffffffffffffffff16600160401b6001600160401b03978816027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1617600160801b9587168602179055600b0180547fffffffffffffffff0000000000000000ffffffffffffffff00000000000000001692851690930267ffffffffffffffff1916919091179216919091179055565b60008281526007602090815260408083206001600160a01b038516845290915290205460ff165b92915050565b600080516020615f8883398151915261304b8133613ed2565b6001600160a01b038084166000908152602081905260408082209285168252902061307582613f9f565b61307e81613f9f565b60118201546004830154613093908890615c7f565b11156130c55760405162461bcd60e51b81526020600482015260016024820152600760fb1b6044820152606401610928565b858160040160008282546130d99190615e38565b92505081905550858260040160008282546130f49190615c7f565b9091555050815460048301546040519081526001600160a01b03909116907f72e0daee492d85727eb3f32c69303a6dd57af5bd2b127a04641de5d70e1494de9060200160405180910390a2805460048201546040519081526001600160a01b03909116907f72e0daee492d85727eb3f32c69303a6dd57af5bd2b127a04641de5d70e1494de9060200160405180910390a2505050505050565b600080516020615f688339815191526131a68133613ed2565b6131be600080516020615f8883398151915283614597565b60005b600354811015611c5157600381815481106131de576131de615f17565b60009182526020909120015460405163af648c3d60e01b81526001600160a01b0385811660048301529091169063af648c3d90602401600060405180830381600087803b15801561322e57600080fd5b505af1158015613242573d6000803e3d6000fd5b50505050808061325190615eba565b9150506131c1565b60006020818152918152604090819020805460018201546002830154600384015460048501546005860154600687015488516101008082018b5260078a0154825260088a0154828d015260098a0154828c0152600a8a01546060830152600b8a01546001600160401b038082166080850152600160401b8204811660a0850152600160801b8204811660c0850152600160c01b9091041660e08301528a51600c8b0180549d8e028201610120908101909d529181018d81526001600160a01b039a8b169d998b169c988b169b9a909716999598949793969295949093919284929091849184018282801561339e57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161335b5790505b505050505081526020016001820180548060200260200160405190810160405280929190818152602001828054801561342857602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116133e55790505b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156134b257602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161346f5790505b505050505081526020016003820180548060200260200160405190810160405280929190818152602001828054801561353c57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116134f95790505b505050918352505060048201546001600160401b038082166020840152600160401b820481166040840152600160801b909104166060820152600590910154608090910152905089565b600080516020615f8883398151915261359f8133613ed2565b8383805182511480156135b3575060008251115b6135e35760405162461bcd60e51b81526020600482015260016024820152603560f81b6044820152606401610928565b60005b600182516135f49190615e38565b811161373157633b9aca006001600160401b031682828151811061361a5761361a615f17565b60200260200101516001600160401b0316111580156136685750633b9aca006001600160401b031683828151811061365457613654615f17565b60200260200101516001600160401b031611155b6136985760405162461bcd60e51b81526020600482015260016024820152601b60f91b6044820152606401610928565b801561371f57826136aa600183615e38565b815181106136ba576136ba615f17565b60200260200101516001600160401b03168382815181106136dd576136dd615f17565b60200260200101516001600160401b03161161371f5760405162461bcd60e51b81526020600482015260016024820152603760f81b6044820152606401610928565b8061372981615eba565b9150506135e6565b506001600160a01b038716600090815260208190526040902061375381613f9f565b60ff85161561378d57865161377190600c83019060208a01906151cd565b50855161378790600d83019060208901906151cd565b506137ba565b86516137a290600e83019060208a01906151cd565b5085516137b890600f83019060208901906151cd565b505b876001600160a01b03167f81d92f91000563c9b4868d5b791e00eb2c2e9b194b09df5608c862bda78c0b9d8888886040516137f793929190615aea565b60405180910390a25050505050505050565b806001600160a01b0381166138445760405162461bcd60e51b81526020600482015260016024820152600360fc1b6044820152606401610928565b600654610100900460ff168061385d575060065460ff16155b6138c05760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610928565b600654610100900460ff161580156138e2576006805461ffff19166101011790555b6138ea614807565b600480546001600160a01b0319166001600160a01b03851617905561391d600080516020615f68833981519152846148c1565b613935600080516020615f68833981519152806148cb565b61396d7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f55600080516020615f688339815191526148cb565b613993600080516020615f88833981519152600080516020615f688339815191526148cb565b8015611c51576006805461ff0019169055505050565b600080516020615f888339815191526139c28133613ed2565b6001600160a01b03821660009081526020819052604090206139e381613f9f565b611bb884846040516020016114b792919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b600080600160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015613a6557600080fd5b505afa158015613a79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a9d91906156b3565b905080613aad5760001991505090565b6000805b600354811015613c295760008060038381548110613ad157613ad1615f17565b60009182526020808320909101546001600160a01b0390811684529083019390935260409091019020600390810154815492169163240fd5ab919084908110613b1c57613b1c615f17565b6000918252602091829020015460408051632768385d60e01b815290516001600160a01b0390921692632768385d92600480840193829003018186803b158015613b6557600080fd5b505afa158015613b79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b9d91906156b3565b6040518263ffffffff1660e01b8152600401613bbb91815260200190565b60206040518083038186803b158015613bd357600080fd5b505afa158015613be7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c0b91906156b3565b613c159083615c7f565b915080613c2181615eba565b915050613ab1565b5081613c39633b9aca0083615dea565b613c439190615cc2565b9250505090565b600082815260076020526040902060010154613c668133613ed2565b611c518383614597565b600080516020615f88833981519152613c898133613ed2565b83633b9aca00816001600160401b03161115613ccb5760405162461bcd60e51b81526020600482015260016024820152600d60fa1b6044820152606401610928565b83633b9aca00816001600160401b03161115613d0d5760405162461bcd60e51b81526020600482015260016024820152600d60fa1b6044820152606401610928565b6001600160a01b0384166000908152602081905260409020613d2e81613f9f565b600b8101805477ffffffffffffffff0000000000000000ffffffffffffffff16600160401b6001600160401b038a811691820277ffffffffffffffffffffffffffffffffffffffffffffffff1692909217600160c01b928a16928302179092556040805192835260208301919091526001600160a01b038716917f1131c4b35ab7476b5e63eeda716dfe137c676f01395ab743a39ac34b099be9e2910160405180910390a250505050505050565b600080516020615f68833981519152613df58133613ed2565b613e1f7f7935bd0ae54bc31f548c14dba4d37c5c64b3f8ca900cb468fb8abd54d5894f5583614597565b613e37600080516020615f8883398151915283614597565b60005b600354811015611c515760038181548110613e5757613e57615f17565b600091825260209091200154604051631dd9b59160e31b81526001600160a01b0385811660048301529091169063eecdac8890602401600060405180830381600087803b158015613ea757600080fd5b505af1158015613ebb573d6000803e3d6000fd5b505050508080613eca90615eba565b915050613e3a565b613edc8282613005565b610cb057613ef4816001600160a01b0316601461491f565b613eff83602061491f565b604051602001613f109291906159f0565b60408051601f198184030181529082905262461bcd60e51b825261092891600401615bf9565b613f408282613005565b610cb05760008281526007602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b80546001600160a01b0316613fda5760405162461bcd60e51b81526020600482015260016024820152603360f81b6044820152606401610928565b50565b60056000838360405160200161400f92919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b60408051601f198184030181529181528151602092830120835290820192909252016000205460ff1615610cb05760405162461bcd60e51b8152602060048201526002602482015261062760f31b6044820152606401610928565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166323b872dd60e01b179052611bb8908590614ace565b600c81015460009081906001141561413057600d8301805460009061410057614100615f17565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b03169050614262565b600061414b8460040154866141459190615c7f565b85614bb3565b905061425e8185600c016000018054806020026020016040519081016040528092919081815260200182805480156141d457602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116141915790505b50505050600d8801805460408051602080840282018101909252828152935083018282801561425457602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116142115790505b5050505050614ca3565b9150505b6010830154633b9aca009061428790600160401b90046001600160401b031683615e09565b6001600160401b031661429a9190615cc2565b949350505050565b6008810154600782015442148015906142bb5750600081115b15614431576001820154604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd916004808301926020929190829003018186803b15801561430757600080fd5b505afa15801561431b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061433f91906156b3565b9050801561442a5760098301548211156143a5576009830154819061436d90670de0b6b3a764000090615dea565b6143779190615cc2565b83600501600082825461438a9190615c7f565b9091555050600983015461439e9083615e38565b91506143df565b806143b8670de0b6b3a764000084615dea565b6143c29190615cc2565b8360050160008282546143d59190615c7f565b9091555060009250505b825460058401546040519081526001600160a01b03909116907faa959327c96abe1dbec8186dbe4bb630ffa7f1a65ad98780481f14757004498e9060200160405180910390a261442f565b600091505b505b82156144ee57600b8201546001600160401b03161580156144555750600a82015415155b1561447757600a8201546144699084615c7f565b6000600a84015592506144e1565b600b8201546001600160401b0316156144e157600b820154600090633b9aca00906144ab906001600160401b031686615dea565b6144b59190615cc2565b90506144c18185615e38565b9350808360070160030160008282546144da9190615c7f565b9091555050505b6144eb8382615c7f565b90505b60088201554260079091015550565b60008181526005602052604090205460ff16156145415760405162461bcd60e51b8152602060048201526002602482015261062760f31b6044820152606401610928565b60008181526005602052604090819020805460ff19166001179055517f0cb09dc71d57eeec2046f6854976717e4874a3cf2d6ddeddde337e5b6de6ba319061458c9083815260200190565b60405180910390a150565b6145a18282613005565b15610cb05760008281526007602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60008181526005602052604090205460ff166146415760405162461bcd60e51b8152602060048201526002602482015261313960f01b6044820152606401610928565b60008181526005602052604090819020805460ff19169055517fd05bfc2250abb0f8fd265a54c53a24359c5484af63cad2e4ce87c78ab751395a9061458c9083815260200190565b600e8101546000908190600114156146e057600f830180546000906146b0576146b0615f17565b90600052602060002090600491828204019190066008029054906101000a90046001600160401b031690506147e2565b60006146f58585600401546141459190615e38565b90506147de8185600c0160020180548060200260200160405190810160405280929190818152602001828054801561477e57602002820191906000526020600020906000905b82829054906101000a90046001600160401b03166001600160401b03168152602001906008019060208260070104928301926001038202915080841161473b5790505b50505050600f8801805460408051602080840282018101909252828152935083018282801561425457600091825260209182902080546001600160401b031684529082028301929091600891018084116142115790505050505050614ca3565b9150505b6010830154633b9aca009061428790600160801b90046001600160401b031683615e09565b600654610100900460ff1680614820575060065460ff16155b6148835760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610928565b600654610100900460ff161580156148a5576006805461ffff19166101011790555b6148ad614fec565b8015613fda576006805461ff001916905550565b610cb08282613f36565b600082815260076020526040902060010154819060405184907fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff90600090a460009182526007602052604090912060010155565b6060600061492e836002615dea565b614939906002615c7f565b6001600160401b0381111561495057614950615f2d565b6040519080825280601f01601f19166020018201604052801561497a576020820181803683370190505b509050600360fc1b8160008151811061499557614995615f17565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106149c4576149c4615f17565b60200101906001600160f81b031916908160001a90535060006149e8846002615dea565b6149f3906001615c7f565b90505b6001811115614a78577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110614a3457614a34615f17565b1a60f81b828281518110614a4a57614a4a615f17565b60200101906001600160f81b031916908160001a90535060049490941c93614a7181615ea3565b90506149f6565b508315614ac75760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610928565b9392505050565b6000614b23826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661509d9092919063ffffffff16565b805190915015611c515780806020019051810190614b41919061555a565b611c515760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610928565b6000808260020160009054906101000a90046001600160a01b03166001600160a01b031663b8f368956040518163ffffffff1660e01b815260040160206040518083038186803b158015614c0657600080fd5b505afa158015614c1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c3e91906156b3565b6010840154909150633b9aca0090614c609086906001600160401b0316615dea565b614c6a9190615cc2565b935080841115614c945783614c83633b9aca0083615dea565b614c8d9190615cc2565b9150614c9c565b633b9aca0091505b5092915050565b60008260018451614cb49190615e38565b81518110614cc457614cc4615f17565b60200260200101516001600160401b0316846001600160401b031610614d12578160018451614cf39190615e38565b81518110614d0357614d03615f17565b60200260200101519050614ac7565b82600081518110614d2557614d25615f17565b60200260200101516001600160401b0316846001600160401b031611614d585781600081518110614d0357614d03615f17565b60008060018551614d699190615e38565b905060005b6001614d7a8484615e38565b1115614de4576002614d8c8484615e38565b614d969190615cc2565b614da09084615c7f565b9050866001600160401b0316868281518110614dbe57614dbe615f17565b60200260200101516001600160401b031611614ddc57809250614d6e565b809150614d6e565b848381518110614df657614df6615f17565b60200260200101516001600160401b0316858381518110614e1957614e19615f17565b60200260200101516001600160401b03161115614f1357858381518110614e4257614e42615f17565b6020026020010151868381518110614e5c57614e5c615f17565b6020026020010151614e6e9190615e4f565b868481518110614e8057614e80615f17565b602002602001015188614e939190615e4f565b868581518110614ea557614ea5615f17565b6020026020010151878581518110614ebf57614ebf615f17565b6020026020010151614ed19190615e4f565b614edb9190615e09565b614ee59190615cd6565b858481518110614ef757614ef7615f17565b6020026020010151614f099190615c97565b9350505050614ac7565b858381518110614f2557614f25615f17565b6020026020010151868381518110614f3f57614f3f615f17565b6020026020010151614f519190615e4f565b868481518110614f6357614f63615f17565b602002602001015188614f769190615e4f565b868481518110614f8857614f88615f17565b6020026020010151878681518110614fa257614fa2615f17565b6020026020010151614fb49190615e4f565b614fbe9190615e09565b614fc89190615cd6565b858481518110614fda57614fda615f17565b6020026020010151614f099190615e4f565b600654610100900460ff1680615005575060065460ff16155b6150685760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610928565b600654610100900460ff161580156148ad576006805461ffff19166101011790558015613fda576006805461ff001916905550565b606061429a848460008585843b6150f65760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610928565b600080866001600160a01b0316858760405161511291906159d4565b60006040518083038185875af1925050503d806000811461514f576040519150601f19603f3d011682016040523d82523d6000602084013e615154565b606091505b509150915061516482828661516f565b979650505050505050565b6060831561517e575081614ac7565b82511561518e5782518084602001fd5b8160405162461bcd60e51b81526004016109289190615bf9565b508054600082556003016004900490600052602060002090810190613fda9190615285565b828054828255906000526020600020906003016004900481019282156152755791602002820160005b8382111561524057835183826101000a8154816001600160401b0302191690836001600160401b0316021790555092602001926008016020816007010492830192600103026151f6565b80156152735782816101000a8154906001600160401b030219169055600801602081600701049283019260010302615240565b505b50615281929150615285565b5090565b5b808211156152815760008155600101615286565b80356152a581615f43565b919050565b600082601f8301126152bb57600080fd5b813560206152d06152cb83615c5c565b615c2c565b80838252828201915082860187848660051b89010111156152f057600080fd5b60005b858110156153165761530482615323565b845292840192908401906001016152f3565b5090979650505050505050565b80356001600160401b03811681146152a557600080fd5b60006020828403121561534c57600080fd5b8135614ac781615f43565b60006020828403121561536957600080fd5b8151614ac781615f43565b6000806040838503121561538757600080fd5b823561539281615f43565b915060208301356153a281615f43565b809150509250929050565b6000806000606084860312156153c257600080fd5b83356153cd81615f43565b925060208401356153dd81615f43565b915060408401356153ed81615f43565b809150509250925092565b6000602080838503121561540b57600080fd5b82516001600160401b0381111561542157600080fd5b8301601f8101851361543257600080fd5b80516154406152cb82615c5c565b80828252848201915084840188868560051b870101111561546057600080fd5b600094505b8385101561548c57805161547881615f43565b835260019490940193918501918501615465565b50979650505050505050565b6000806000606084860312156154ad57600080fd5b83356001600160401b038111156154c357600080fd5b8401601f810186136154d457600080fd5b803560206154e46152cb83615c5c565b8083825282820191508285018a848660051b880101111561550457600080fd5b600095505b8486101561553057803561551c81615f43565b835260019590950194918301918301615509565b509650615540905087820161529a565b94505050506155516040850161529a565b90509250925092565b60006020828403121561556c57600080fd5b81518015158114614ac757600080fd5b60006020828403121561558e57600080fd5b5035919050565b600080604083850312156155a857600080fd5b8235915060208301356153a281615f43565b600080600080608085870312156155d057600080fd5b84356155db81615f43565b935060208501356001600160401b03808211156155f757600080fd5b615603888389016152aa565b9450604087013591508082111561561957600080fd5b50615626878288016152aa565b925050606085013561563781615f58565b939692955090935050565b600080600080600060a0868803121561565a57600080fd5b853561566581615f43565b9450602086013561567581615f43565b9350604086013561568581615f43565b9250606086013561569581615f43565b915060808601356156a581615f43565b809150509295509295909350565b6000602082840312156156c557600080fd5b5051919050565b600080600080608085870312156156e257600080fd5b8435935060208501356156f481615f43565b9250604085013561570481615f43565b9150606085013561563781615f43565b600080600080600060a0868803121561572c57600080fd5b85359450602086013561573e81615f43565b9350604086013561574e81615f43565b9250606086013561575e81615f43565b949793965091946080013592915050565b60008060006060848603121561578457600080fd5b8335925060208401356153dd81615f43565b600080600080608085870312156157ac57600080fd5b8435935060208501356157be81615f43565b925060408501356157ce81615f43565b9396929550929360600135925050565b6000806000606084860312156157f357600080fd5b833592506020840135915060408401356153ed81615f43565b60006020828403121561581e57600080fd5b614ac782615323565b60008060006060848603121561583c57600080fd5b61584584615323565b92506153dd60208501615323565b6000806000806080858703121561586957600080fd5b61587285615323565b935061588060208601615323565b925061588e60408601615323565b915061589c60608601615323565b905092959194509250565b6000602082840312156158b957600080fd5b8151614ac781615f58565b600081518084526020808501945080840160005b838110156158fd5781516001600160401b0316875295820195908201906001016158d8565b509495945050505050565b6000610100825181855261591e828601826158c4565b9150506020830151848203602086015261593882826158c4565b9150506040830151848203604086015261595282826158c4565b9150506060830151848203606086015261596c82826158c4565b915050608083015161598960808601826001600160401b03169052565b5060a08301516159a460a08601826001600160401b03169052565b5060c08301516159bf60c08601826001600160401b03169052565b5060e083015160e08501528091505092915050565b600082516159e6818460208701615e77565b9190910192915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351615a28816017850160208801615e77565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351615a65816028840160208801615e77565b01602801949350505050565b60a0808252865190820181905260009060209060c0840190828a01845b82811015615ab35781516001600160a01b031684529284019290840190600101615a8e565b5050506001600160a01b03978816918401919091529486166040830152509184166060830152909216608090920191909152919050565b606081526000615afd60608301866158c4565b8281036020840152615b0f81866158c4565b91505060ff83166040830152949350505050565b60006102006001600160a01b03808d168452808c166020850152808b166040850152808a166060850152508760808401528660a08401528560c0840152845160e084015260208501516101008401526040850151610120840152606085015161014084015260808501516001600160401b038082166101608601528060a0880151166101808601528060c0880151166101a0860152505060e0850151615bd56101c08501826001600160401b03169052565b50806101e0840152615be981840185615908565b9c9b505050505050505050505050565b6020815260008251806020840152615c18816040850160208701615e77565b601f01601f19169190910160400192915050565b604051601f8201601f191681016001600160401b0381118282101715615c5457615c54615f2d565b604052919050565b60006001600160401b03821115615c7557615c75615f2d565b5060051b60200190565b60008219821115615c9257615c92615ed5565b500190565b60006001600160401b03808316818516808303821115615cb957615cb9615ed5565b01949350505050565b600082615cd157615cd1615eeb565b500490565b60006001600160401b0380841680615cf057615cf0615eeb565b92169190910492915050565b600181815b80851115615d37578160001904821115615d1d57615d1d615ed5565b80851615615d2a57918102915b93841c9390800290615d01565b509250929050565b6000614ac760ff841683600082615d585750600161302c565b81615d655750600061302c565b8160018114615d7b5760028114615d8557615da1565b600191505061302c565b60ff841115615d9657615d96615ed5565b50506001821b61302c565b5060208310610133831016604e8410600b8410161715615dc4575081810a61302c565b615dce8383615cfc565b8060001904821115615de257615de2615ed5565b029392505050565b6000816000190483118215151615615e0457615e04615ed5565b500290565b60006001600160401b0380831681851681830481118215151615615e2f57615e2f615ed5565b02949350505050565b600082821015615e4a57615e4a615ed5565b500390565b60006001600160401b0383811690831681811015615e6f57615e6f615ed5565b039392505050565b60005b83811015615e92578181015183820152602001615e7a565b83811115611bb85750506000910152565b600081615eb257615eb2615ed5565b506000190190565b6000600019821415615ece57615ece615ed5565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114613fda57600080fd5b60ff81168114613fda57600080fdfe502d3d275257923b2bea6ea25d9631f12369fb532871f13eb85eb09dc0fb484255435dd261a4b9b3364963f7738a7a662ad9c84396d64be3365284bb7f0a5041a2646970667358221220fc0954c09bd004c0cf6f4c5b7926ec39df1969875ddca840ece9b5a7217e371c64736f6c63430008070033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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.