ETH Price: $2,422.53 (+2.98%)

Contract

0xACd1179C667e27f485798dcA11d417d8603dDbaf
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040196661422024-04-16 6:04:35151 days ago1713247475IN
 Create: LlamaLendSwapper
0 ETH0.028354689

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LlamaLendSwapper

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2024-04-16
*/

// SPDX-License-Identifier: MIT
pragma solidity =0.8.10;









contract DSMath {
    function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x + y;
    }

    function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x - y;
    }

    function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x * y;
    }

    function div(uint256 x, uint256 y) internal pure returns (uint256 z) {
        return x / y;
    }

    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        return x <= y ? x : y;
    }

    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        return x >= y ? x : y;
    }

    function imin(int256 x, int256 y) internal pure returns (int256 z) {
        return x <= y ? x : y;
    }

    function imax(int256 x, int256 y) internal pure returns (int256 z) {
        return x >= y ? x : y;
    }

    uint256 constant WAD = 10**18;
    uint256 constant RAY = 10**27;

    function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, y), WAD / 2) / WAD;
    }

    function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, y), RAY / 2) / RAY;
    }

    function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, WAD), y / 2) / y;
    }

    function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, RAY), y / 2) / y;
    }

    // This famous algorithm is called "exponentiation by squaring"
    // and calculates x^n with x as fixed-point and n as regular unsigned.
    //
    // It's O(log n), instead of O(n) for naive repeated multiplication.
    //
    // These facts are why it works:
    //
    //  If n is even, then x^n = (x^2)^(n/2).
    //  If n is odd,  then x^n = x * x^(n-1),
    //   and applying the equation for even x gives
    //    x^n = x * (x^2)^((n-1) / 2).
    //
    //  Also, EVM division is flooring and
    //    floor[(n-1) / 2] = floor[n / 2].
    //
    function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {
        z = n % 2 != 0 ? x : RAY;

        for (n /= 2; n != 0; n /= 2) {
            x = rmul(x, x);

            if (n % 2 != 0) {
                z = rmul(z, x);
            }
        }
    }
}







contract MainnetAuthAddresses {
    address internal constant ADMIN_VAULT_ADDR = 0xCCf3d848e08b94478Ed8f46fFead3008faF581fD;
    address internal constant DSGUARD_FACTORY_ADDRESS = 0x5a15566417e6C1c9546523066500bDDBc53F88C7;
    address internal constant ADMIN_ADDR = 0x25eFA336886C74eA8E282ac466BdCd0199f85BB9; // USED IN ADMIN VAULT CONSTRUCTOR
    address internal constant PROXY_AUTH_ADDRESS = 0x149667b6FAe2c63D1B4317C716b0D0e4d3E2bD70;
    address internal constant MODULE_AUTH_ADDRESS = 0x7407974DDBF539e552F1d051e44573090912CC3D;
}







contract AuthHelper is MainnetAuthAddresses {
}








contract AdminVault is AuthHelper {
    address public owner;
    address public admin;

    error SenderNotAdmin();

    constructor() {
        owner = msg.sender;
        admin = ADMIN_ADDR;
    }

    /// @notice Admin is able to change owner
    /// @param _owner Address of new owner
    function changeOwner(address _owner) public {
        if (admin != msg.sender){
            revert SenderNotAdmin();
        }
        owner = _owner;
    }

    /// @notice Admin is able to set new admin
    /// @param _admin Address of multisig that becomes new admin
    function changeAdmin(address _admin) public {
        if (admin != msg.sender){
            revert SenderNotAdmin();
        }
        admin = _admin;
    }

}







interface IERC20 {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint256 digits);
    function totalSupply() external view returns (uint256 supply);

    function balanceOf(address _owner) external view returns (uint256 balance);

    function transfer(address _to, uint256 _value) external returns (bool success);

    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    ) external returns (bool success);

    function approve(address _spender, uint256 _value) external returns (bool success);

    function allowance(address _owner, address _spender) external view returns (uint256 remaining);

    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}







library Address {
    //insufficient balance
    error InsufficientBalance(uint256 available, uint256 required);
    //unable to send value, recipient may have reverted
    error SendingValueFail();
    //insufficient balance for call
    error InsufficientBalanceForCall(uint256 available, uint256 required);
    //call to non-contract
    error NonContractCall();
    
    function isContract(address account) internal view returns (bool) {
        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            codehash := extcodehash(account)
        }
        return (codehash != accountHash && codehash != 0x0);
    }

    function sendValue(address payable recipient, uint256 amount) internal {
        uint256 balance = address(this).balance;
        if (balance < amount){
            revert InsufficientBalance(balance, amount);
        }

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{value: amount}("");
        if (!(success)){
            revert SendingValueFail();
        }
    }

    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return _functionCallWithValue(target, data, 0, errorMessage);
    }

    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");
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        uint256 balance = address(this).balance;
        if (balance < value){
            revert InsufficientBalanceForCall(balance, value);
        }
        return _functionCallWithValue(target, data, value, errorMessage);
    }

    function _functionCallWithValue(
        address target,
        bytes memory data,
        uint256 weiValue,
        string memory errorMessage
    ) private returns (bytes memory) {
        if (!(isContract(target))){
            revert NonContractCall();
        }

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{value: weiValue}(data);
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}











library SafeERC20 {
    using Address for address;

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

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

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

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

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

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

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










contract AdminAuth is AuthHelper {
    using SafeERC20 for IERC20;

    AdminVault public constant adminVault = AdminVault(ADMIN_VAULT_ADDR);

    error SenderNotOwner();
    error SenderNotAdmin();

    modifier onlyOwner() {
        if (adminVault.owner() != msg.sender){
            revert SenderNotOwner();
        }
        _;
    }

    modifier onlyAdmin() {
        if (adminVault.admin() != msg.sender){
            revert SenderNotAdmin();
        }
        _;
    }

    /// @notice withdraw stuck funds
    function withdrawStuckFunds(address _token, address _receiver, uint256 _amount) public onlyOwner {
        if (_token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
            payable(_receiver).transfer(_amount);
        } else {
            IERC20(_token).safeTransfer(_receiver, _amount);
        }
    }

    /// @notice Destroy the contract
    /// @dev Deprecated method, selfdestruct will soon just send eth
    function kill() public onlyAdmin {
        selfdestruct(payable(msg.sender));
    }
}








contract FeeRecipient is AdminAuth {

    address public wallet;

    constructor(address _newWallet) {
        wallet = _newWallet;
    }

    function getFeeAddr() public view returns (address) {
        return wallet;
    }

    function changeWalletAddr(address _newWallet) public onlyOwner {
        wallet = _newWallet;
    }
}







interface ILendingPoolAddressesProviderV2 {
  event LendingPoolUpdated(address indexed newAddress);
  event ConfigurationAdminUpdated(address indexed newAddress);
  event EmergencyAdminUpdated(address indexed newAddress);
  event LendingPoolConfiguratorUpdated(address indexed newAddress);
  event LendingPoolCollateralManagerUpdated(address indexed newAddress);
  event PriceOracleUpdated(address indexed newAddress);
  event LendingRateOracleUpdated(address indexed newAddress);
  event ProxyCreated(bytes32 id, address indexed newAddress);
  event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy);

  function setAddress(bytes32 id, address newAddress) external;

  function setAddressAsProxy(bytes32 id, address impl) external;

  function getAddress(bytes32 id) external view returns (address);

  function getLendingPool() external view returns (address);

  function setLendingPoolImpl(address pool) external;

  function getLendingPoolConfigurator() external view returns (address);

  function setLendingPoolConfiguratorImpl(address configurator) external;

  function getLendingPoolCollateralManager() external view returns (address);

  function setLendingPoolCollateralManager(address manager) external;

  function getPoolAdmin() external view returns (address);

  function setPoolAdmin(address admin) external;

  function getEmergencyAdmin() external view returns (address);

  function setEmergencyAdmin(address admin) external;

  function getPriceOracle() external view returns (address);

  function setPriceOracle(address priceOracle) external;

  function getLendingRateOracle() external view returns (address);

  function setLendingRateOracle(address lendingRateOracle) external;
}







abstract contract IPriceOracleGetterAave {
    function getAssetPrice(address _asset) external virtual view returns (uint256);
    function getAssetsPrices(address[] calldata _assets) external virtual view returns(uint256[] memory);
    function getSourceOfAsset(address _asset) external virtual view returns(address);
    function getFallbackOracle() external virtual view returns(address);
}






interface IAggregatorV3 {
    function decimals() external view returns (uint8);

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

    function version() external view returns (uint256);

    // getRoundData and latestRoundData should both raise "No data present"
    // if they do not have data to report, instead of returning unset values
    // which could be misinterpreted as actual reported values.
    function getRoundData(uint80 _roundId)
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );

    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );

    function latestAnswer() external view returns (uint256);

    function getTimestamp(uint256 _roundId) external view returns (uint256);

    function phaseId() external view returns (uint16);

    function phaseAggregators(uint16 _phaseId) external view returns (address);
}






interface IFeedRegistry {
  struct Phase {
    uint16 phaseId;
    uint80 startingAggregatorRoundId;
    uint80 endingAggregatorRoundId;
  }

  event FeedProposed(
    address indexed asset,
    address indexed denomination,
    address indexed proposedAggregator,
    address currentAggregator,
    address sender
  );
  event FeedConfirmed(
    address indexed asset,
    address indexed denomination,
    address indexed latestAggregator,
    address previousAggregator,
    uint16 nextPhaseId,
    address sender
  );

  // V3 AggregatorV3Interface

  function decimals(
    address base,
    address quote
  )
    external
    view
    returns (
      uint8
    );

  function description(
    address base,
    address quote
  )
    external
    view
    returns (
      string memory
    );

  function version(
    address base,
    address quote
  )
    external
    view
    returns (
      uint256
    );

  function latestRoundData(
    address base,
    address quote
  )
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function getRoundData(
    address base,
    address quote,
    uint80 _roundId
  )
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  // V2 AggregatorInterface

  function latestAnswer(
    address base,
    address quote
  )
    external
    view
    returns (
      int256 answer
    );

  function latestTimestamp(
    address base,
    address quote
  )
    external
    view
    returns (
      uint256 timestamp
    );

  function latestRound(
    address base,
    address quote
  )
    external
    view
    returns (
      uint256 roundId
    );

  function getAnswer(
    address base,
    address quote,
    uint256 roundId
  )
    external
    view
    returns (
      int256 answer
    );

  function getTimestamp(
    address base,
    address quote,
    uint256 roundId
  )
    external
    view
    returns (
      uint256 timestamp
    );


  function isFeedEnabled(
    address aggregator
  )
    external
    view
    returns (
      bool
    );

  function getPhase(
    address base,
    address quote,
    uint16 phaseId
  )
    external
    view
    returns (
      Phase memory phase
    );

  // Round helpers


  function getPhaseRange(
    address base,
    address quote,
    uint16 phaseId
  )
    external
    view
    returns (
      uint80 startingRoundId,
      uint80 endingRoundId
    );

  function getPreviousRoundId(
    address base,
    address quote,
    uint80 roundId
  ) external
    view
    returns (
      uint80 previousRoundId
    );

  function getNextRoundId(
    address base,
    address quote,
    uint80 roundId
  ) external
    view
    returns (
      uint80 nextRoundId
    );

  // Feed management

  function proposeFeed(
    address base,
    address quote,
    address aggregator
  ) external;

  function confirmFeed(
    address base,
    address quote,
    address aggregator
  ) external;

  // Proposed aggregator

  function proposedGetRoundData(
    address base,
    address quote,
    uint80 roundId
  )
    external
    view
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function proposedLatestRoundData(
    address base,
    address quote
  )
    external
    view
    returns (
      uint80 id,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  // Phases
  function getCurrentPhaseId(
    address base,
    address quote
  )
    external
    view
    returns (
      uint16 currentPhaseId
    );

    function getFeed(address base, address quote) external view returns (address);
}








interface IWStEth {
    function wrap(uint256 _stETHAmount) external returns (uint256);
    function unwrap(uint256 _wstETHAmount) external returns (uint256);
    function stEthPerToken() external view returns (uint256);
    function tokensPerStEth() external view returns (uint256);
}







library Denominations {
  address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
  address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB;

  // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217
  address public constant USD = address(840);
  address public constant GBP = address(826);
  address public constant EUR = address(978);
  address public constant JPY = address(392);
  address public constant KRW = address(410);
  address public constant CNY = address(156);
  address public constant AUD = address(36);
  address public constant CAD = address(124);
  address public constant CHF = address(756);
  address public constant ARS = address(32);
  address public constant PHP = address(608);
  address public constant NZD = address(554);
  address public constant SGD = address(702);
  address public constant NGN = address(566);
  address public constant ZAR = address(710);
  address public constant RUB = address(643);
  address public constant INR = address(356);
  address public constant BRL = address(986);
}







contract MainnetUtilAddresses {
    address internal refillCaller = 0x33fDb79aFB4456B604f376A45A546e7ae700e880;
    address internal feeAddr = 0x76720aC2574631530eC8163e4085d6F98513fb27;

    address internal constant BOT_REGISTRY_ADDRESS = 0x637726f8b08a7ABE3aE3aCaB01A80E2d8ddeF77B;
    address internal constant UNI_V2_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
    address internal constant MKR_PROXY_REGISTRY = 0x4678f0a6958e4D2Bc4F1BAF7Bc52E8F3564f3fE4;
    address internal constant AAVE_MARKET = 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5;
    address internal constant AAVE_V3_MARKET = 0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e;
    address internal constant SPARK_MARKET = 0x02C3eA4e34C0cBd694D2adFa2c690EECbC1793eE;

    address internal constant DFS_PROXY_REGISTRY_ADDR = 0x29474FdaC7142f9aB7773B8e38264FA15E3805ed;

    address internal constant WETH_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address internal constant ETH_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address internal constant WSTETH_ADDR = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;
    address internal constant STETH_ADDR = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
    address internal constant WBTC_ADDR = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
    address internal constant CHAINLINK_WBTC_ADDR = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB;
    address internal constant DAI_ADDR = 0x6B175474E89094C44Da98b954EedeAC495271d0F;

    address internal constant FEE_RECEIVER_ADMIN_ADDR = 0xA74e9791D7D66c6a14B2C571BdA0F2A1f6D64E06;

    address internal constant UNI_V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
    address internal constant UNI_V3_QUOTER = 0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6;

    address internal constant FEE_RECIPIENT = 0x39C4a92Dc506300c3Ea4c67ca4CA611102ee6F2A;

    // not needed on mainnet
    address internal constant DEFAULT_BOT = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    address public constant CHAINLINK_FEED_REGISTRY = 0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf;
}







contract UtilHelper is MainnetUtilAddresses{
}















contract TokenPriceHelper is DSMath, UtilHelper {
    IFeedRegistry public constant feedRegistry = IFeedRegistry(CHAINLINK_FEED_REGISTRY);

    /// @dev Helper function that returns chainlink price data
    /// @param _inputTokenAddr Token address we are looking the usd price for
    /// @param _roundId Chainlink roundId, if 0 uses the latest
    function getRoundInfo(address _inputTokenAddr, uint80 _roundId, IAggregatorV3 aggregator)
        public
        view
        returns (uint256, uint256 updateTimestamp)
    {
        int256 price;

        /// @dev Price staleness not checked, the risk has been deemed acceptable
        if (_roundId == 0) {
            (, price, , updateTimestamp, ) = aggregator.latestRoundData();
        } else {
            (, price, , updateTimestamp, ) = aggregator.getRoundData(_roundId);
        }

        // no price for wsteth, can calculate from steth
        if (_inputTokenAddr == WSTETH_ADDR) price = getWStEthPrice(price);

        return (uint256(price), updateTimestamp);
    }

    /// @dev Helper function that returns chainlink price data
    /// @param _inputTokenAddr Token address we are looking the usd price for
    /// @param _roundId Chainlink roundId, if 0 uses the latest
    function getRoundInfo(address _inputTokenAddr, uint80 _roundId)
        public
        view
        returns (uint256, uint256 updateTimestamp)
    {
        address tokenAddr = getAddrForChainlinkOracle(_inputTokenAddr);
        IAggregatorV3 aggregator = IAggregatorV3(feedRegistry.getFeed(tokenAddr, Denominations.USD));

        return getRoundInfo(_inputTokenAddr, _roundId, aggregator);
    }

    /// @dev helper function that returns latest token price in USD
    /// @dev 1. Chainlink USD feed
    /// @dev 2. Chainlink ETH feed
    /// @dev 3. Aave feed
    /// @dev if no price found return 0
    function getPriceInUSD(address _inputTokenAddr) public view returns (uint256) {
        address chainlinkTokenAddr = getAddrForChainlinkOracle(_inputTokenAddr);

        int256 price;
        price = getChainlinkPriceInUSD(chainlinkTokenAddr, true);
        if (price == 0){
            price = int256(getAaveTokenPriceInUSD(_inputTokenAddr));
        }
        if (price == 0){
            price = int256(getAaveV3TokenPriceInUSD(_inputTokenAddr));
        }
        if (price == 0){
            price = int256(getSparkTokenPriceInUSD(_inputTokenAddr));
        }
        if (price == 0){
            return 0;
        }

        if (_inputTokenAddr == WSTETH_ADDR) price = getWStEthPrice(price);
        if (_inputTokenAddr == WBTC_ADDR) price = getWBtcPrice(price);
        return uint256(price);
    }

    /// @dev helper function that returns latest token price in USD
    /// @dev 1. Chainlink USD feed
    /// @dev 2. Chainlink ETH feed
    /// @dev 3. Aave feed
    /// @dev if no price found return 0
    /// @dev expect WBTC and WSTETH to have chainlink USD price
    function getPriceInETH(address _inputTokenAddr) public view returns (uint256) {
        address chainlinkTokenAddr = getAddrForChainlinkOracle(_inputTokenAddr);

        uint256 chainlinkPriceInUSD = uint256(getChainlinkPriceInUSD(chainlinkTokenAddr, false));
        if (chainlinkPriceInUSD != 0){
            uint256 chainlinkETHPriceInUSD = uint256(getChainlinkPriceInUSD(ETH_ADDR, false));
            uint256 priceInEth = wdiv(chainlinkPriceInUSD, chainlinkETHPriceInUSD);
            if (_inputTokenAddr == WSTETH_ADDR) return uint256(getWStEthPrice(int256(priceInEth)));
            if (_inputTokenAddr == WBTC_ADDR) return uint256(getWBtcPrice(int256(priceInEth)));
            return priceInEth;
        }

        uint256 chainlinkPriceInETH = uint256(getChainlinkPriceInETH(chainlinkTokenAddr));
        if (chainlinkPriceInETH != 0) return chainlinkPriceInETH;

        uint256 aavePriceInETH = getAaveTokenPriceInETH(_inputTokenAddr);
        if (aavePriceInETH != 0) return aavePriceInETH;

        uint256 aaveV3PriceInETH = getAaveV3TokenPriceInETH(_inputTokenAddr);
        if (aaveV3PriceInETH != 0) return aaveV3PriceInETH;

        uint256 sparkPriceInETH = getSparkTokenPriceInETH(_inputTokenAddr);
        if (sparkPriceInETH != 0) return sparkPriceInETH;
        
        return 0;
    }

    /// @dev If there's no USD price feed can fallback to ETH price feed, if there's no USD or ETH price feed return 0
    function getChainlinkPriceInUSD(address _inputTokenAddr, bool _useFallback) public view returns (int256 chainlinkPriceInUSD) {
        try feedRegistry.latestRoundData(_inputTokenAddr, Denominations.USD) returns (uint80, int256 answer, uint256, uint256, uint80){
            chainlinkPriceInUSD = answer;
        } catch {
            if (_useFallback){
                uint256 chainlinkPriceInETH = uint256(getChainlinkPriceInETH(_inputTokenAddr));
                uint256 chainlinkETHPriceInUSD = uint256(getChainlinkPriceInUSD(ETH_ADDR, false));
                chainlinkPriceInUSD = int256(wmul(chainlinkPriceInETH, chainlinkETHPriceInUSD));
            } else {
                chainlinkPriceInUSD = 0;
            }
        }
    }

    /// @dev If there's no ETH price feed returns 0
    function getChainlinkPriceInETH(address _inputTokenAddr) public view returns (int256 chainlinkPriceInETH) {
        try feedRegistry.latestRoundData(_inputTokenAddr, Denominations.ETH) returns (uint80, int256 answer, uint256, uint256, uint80){
            chainlinkPriceInETH = answer;
        } catch {
            chainlinkPriceInETH = 0;
        }
    }
    
    /// @dev chainlink uses different addresses for WBTC and ETH
    /// @dev there is only STETH price feed so we use that for WSTETH and handle later 
    function getAddrForChainlinkOracle(address _inputTokenAddr)
        public
        pure
        returns (address tokenAddrForChainlinkUsage)
    {
        if (_inputTokenAddr == WETH_ADDR) {
            tokenAddrForChainlinkUsage = ETH_ADDR;
        } else if (_inputTokenAddr == WSTETH_ADDR) {
            tokenAddrForChainlinkUsage = STETH_ADDR;
        } else if (_inputTokenAddr == WBTC_ADDR) {
            tokenAddrForChainlinkUsage = CHAINLINK_WBTC_ADDR;
        } else {
            tokenAddrForChainlinkUsage = _inputTokenAddr;
        }
    }

    function getWStEthPrice(int256 _stEthPrice) public view returns (int256 wStEthPrice) {
        wStEthPrice = int256(wmul(uint256(_stEthPrice), IWStEth(WSTETH_ADDR).stEthPerToken()));
    }

    function getWBtcPrice(int256 _btcPrice) public view returns (int256 wBtcPrice) {
        (, int256 wBtcPriceToPeg, , , ) = feedRegistry.latestRoundData(WBTC_ADDR, CHAINLINK_WBTC_ADDR);
        wBtcPrice = (_btcPrice * wBtcPriceToPeg + 1e8 / 2) / 1e8;
    }

    /// @dev if price isn't found this returns 0
    function getAaveTokenPriceInETH(address _tokenAddr) public view returns (uint256 price) {
        address priceOracleAddress = ILendingPoolAddressesProviderV2(AAVE_MARKET).getPriceOracle();

        try IPriceOracleGetterAave(priceOracleAddress).getAssetPrice(_tokenAddr) returns (uint256 tokenPrice){
            price = tokenPrice;
        } catch {
            price = 0;
        }
    }

    /// @dev if price isn't found this returns 0
    function getAaveTokenPriceInUSD(address _tokenAddr) public view returns (uint256) {
        uint256 tokenAavePriceInETH = getAaveTokenPriceInETH(_tokenAddr);
        uint256 ethPriceInUSD = uint256(getChainlinkPriceInUSD(ETH_ADDR, false));

        return wmul(tokenAavePriceInETH, ethPriceInUSD);
    }

    function getAaveV3TokenPriceInUSD(address _tokenAddr) public view returns (uint256 price) {
        address priceOracleAddress = ILendingPoolAddressesProviderV2(AAVE_V3_MARKET).getPriceOracle();

        try IPriceOracleGetterAave(priceOracleAddress).getAssetPrice(_tokenAddr) returns (uint256 tokenPrice) {
            price = tokenPrice;
        } catch {
            price = 0;
        }
    }

    /// @dev if price isn't found this returns 0
    function getAaveV3TokenPriceInETH(address _tokenAddr) public view returns (uint256) {
        uint256 tokenAavePriceInUSD = getAaveV3TokenPriceInUSD(_tokenAddr);
        uint256 ethPriceInUSD = uint256(getChainlinkPriceInUSD(ETH_ADDR, false));

        return wdiv(tokenAavePriceInUSD, ethPriceInUSD);
    }

    function getSparkTokenPriceInUSD(address _tokenAddr) public view returns (uint256 price) {
        address priceOracleAddress = ILendingPoolAddressesProviderV2(SPARK_MARKET).getPriceOracle();

        try IPriceOracleGetterAave(priceOracleAddress).getAssetPrice(_tokenAddr) returns (uint256 tokenPrice) {
            price = tokenPrice;
        } catch {
            price = 0;
        }
    }

    /// @dev if price isn't found this returns 0
    function getSparkTokenPriceInETH(address _tokenAddr) public view returns (uint256) {
        uint256 tokenSparkPriceInUSD = getSparkTokenPriceInUSD(_tokenAddr);
        uint256 ethPriceInUSD = uint256(getChainlinkPriceInUSD(ETH_ADDR, false));

        return wdiv(tokenSparkPriceInUSD, ethPriceInUSD);
    }
}







abstract contract IWETH {
    function allowance(address, address) public virtual view returns (uint256);

    function balanceOf(address) public virtual view returns (uint256);

    function approve(address, uint256) public virtual;

    function transfer(address, uint256) public virtual returns (bool);

    function transferFrom(
        address,
        address,
        uint256
    ) public virtual returns (bool);

    function deposit() public payable virtual;

    function withdraw(uint256) public virtual;
}








library TokenUtils {
    using SafeERC20 for IERC20;

    address public constant WETH_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address public constant ETH_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /// @dev Only approves the amount if allowance is lower than amount, does not decrease allowance
    function approveToken(
        address _tokenAddr,
        address _to,
        uint256 _amount
    ) internal {
        if (_tokenAddr == ETH_ADDR) return;

        if (IERC20(_tokenAddr).allowance(address(this), _to) < _amount) {
            IERC20(_tokenAddr).safeApprove(_to, _amount);
        }
    }

    function pullTokensIfNeeded(
        address _token,
        address _from,
        uint256 _amount
    ) internal returns (uint256) {
        // handle max uint amount
        if (_amount == type(uint256).max) {
            _amount = getBalance(_token, _from);
        }

        if (_from != address(0) && _from != address(this) && _token != ETH_ADDR && _amount != 0) {
            IERC20(_token).safeTransferFrom(_from, address(this), _amount);
        }

        return _amount;
    }

    function withdrawTokens(
        address _token,
        address _to,
        uint256 _amount
    ) internal returns (uint256) {
        if (_amount == type(uint256).max) {
            _amount = getBalance(_token, address(this));
        }

        if (_to != address(0) && _to != address(this) && _amount != 0) {
            if (_token != ETH_ADDR) {
                IERC20(_token).safeTransfer(_to, _amount);
            } else {
                (bool success, ) = _to.call{value: _amount}("");
                require(success, "Eth send fail");
            }
        }

        return _amount;
    }

    function depositWeth(uint256 _amount) internal {
        IWETH(WETH_ADDR).deposit{value: _amount}();
    }

    function withdrawWeth(uint256 _amount) internal {
        IWETH(WETH_ADDR).withdraw(_amount);
    }

    function getBalance(address _tokenAddr, address _acc) internal view returns (uint256) {
        if (_tokenAddr == ETH_ADDR) {
            return _acc.balance;
        } else {
            return IERC20(_tokenAddr).balanceOf(_acc);
        }
    }

    function getTokenDecimals(address _token) internal view returns (uint256) {
        if (_token == ETH_ADDR) return 18;

        return IERC20(_token).decimals();
    }
}










contract GasFeeHelper is DSMath, TokenPriceHelper {
    using TokenUtils for address;

    FeeRecipient public constant feeRecipient = FeeRecipient(FEE_RECIPIENT);

    uint256 public constant SANITY_GAS_PRICE = 1000 gwei;

    /// @dev Divider for input amount, 5 bps
    uint256 public constant MAX_DFS_FEE = 2000;

    function calcGasCost(uint256 _gasUsed, address _feeToken, uint256 _l1GasCostInEth) public view returns (uint256 txCost) {
        uint256 gasPrice = tx.gasprice;

        // gas price must be in a reasonable range
        if (tx.gasprice > SANITY_GAS_PRICE) {
            gasPrice = SANITY_GAS_PRICE;
        }

        // can't use more gas than the block gas limit
        if (_gasUsed > block.gaslimit) {
            _gasUsed = block.gaslimit;
        }

        // calc gas used
        txCost = (_gasUsed * gasPrice) + _l1GasCostInEth;

        // convert to token amount
        if (_feeToken != TokenUtils.WETH_ADDR) {
            uint256 price = getPriceInETH(_feeToken);
            uint256 tokenDecimals = _feeToken.getTokenDecimals();

            require(tokenDecimals <= 18, "Token decimal too big");

            if (price > 0) {
                txCost = wdiv(txCost, uint256(price)) / (10**(18 - tokenDecimals));
            } else {
                txCost = 0;
            }
        }
    }
}







contract MainnetLlamaLendAddresses {
    address internal constant BYTES_TRANSIENT_STORAGE = 0xB3FE6f712c8B8c64CD2780ce714A36e7640DDf0f;
    address internal constant LLAMALEND_FACTORY = 0xeA6876DDE9e3467564acBeE1Ed5bac88783205E0;
}






abstract contract IBytesTransientStorage {
    function setBytesTransiently(bytes calldata) public virtual;
    function getBytesTransiently() public virtual returns (bytes memory);
}







interface IAGG {
    function rate() external view returns (uint256);
    function rate(address) external view returns (uint256);
    function rate0() external view returns (uint256);
    function target_debt_fraction() external view returns (uint256);
    function sigma() external view returns (int256);
    function peg_keepers(uint256) external view returns (address); 
}






interface ILLAMMA {
    function active_band_with_skip() external view returns (int256);
    function get_sum_xy(address) external view returns (uint256[2] memory);
    function get_xy(address) external view returns (uint256[][2] memory);
    function get_p() external view returns (uint256);
    function read_user_tick_numbers(address) external view returns (int256[2] memory);
    function p_oracle_up(int256) external view returns (uint256);
    function p_oracle_down(int256) external view returns (uint256);
    function p_current_up(int256) external view returns (uint256);
    function p_current_down(int256) external view returns (uint256);
    function bands_x(int256) external view returns (uint256);
    function bands_y(int256) external view returns (uint256);
    function get_base_price() external view returns (uint256);
    function price_oracle() external view returns (uint256);
    function active_band() external view returns (int256);
    function A() external view returns (uint256);
    function min_band() external view returns (int256);
    function max_band() external view returns (int256);
    function rate() external view returns (uint256);
    function exchange(uint256 i, uint256 j, uint256 in_amount, uint256 min_amount) external returns (uint256[2] memory);
    function coins(uint256 i) external view returns (address);
    function user_state(address _user) external view returns (uint256[4] memory);
}






interface ILlamaLendController {
    function create_loan(uint256 _collateralAmount, uint256 _debtAmount, uint256 _nBands) external payable;
    function create_loan_extended(uint256 _collateralAmount, uint256 _debtAmount, uint256 _nBands, address _callbacker, uint256[] memory _callbackArgs) external payable;

    /// @dev all functions below: if _collateralAmount is 0 will just return
    function add_collateral(uint256 _collateralAmount) external payable;
    function add_collateral(uint256 _collateralAmount, address _for) external payable;

    function remove_collateral(uint256 _collateralAmount) external;
    /// @param _useEth relevant only for ETH collateral pools (currently not deployed)
    function remove_collateral(uint256 _collateralAmount, bool _useEth) external;

    /// @dev all functions below: if _debtAmount is 0 will just return
    function borrow_more(uint256 _collateralAmount, uint256 _debtAmount) external payable;
    function borrow_more_extended(uint256 _collateralAmount, uint256 _debt, address _callbacker, uint256[] memory _callbackArgs) external payable;

    /// @dev if _debtAmount > debt will do full repay
    function repay(uint256 _debtAmount) external payable;
    function repay(uint256 _debtAmount, address _for) external payable;
    /// @param _maxActiveBand Don't allow active band to be higher than this (to prevent front-running the repay)
    function repay(uint256 _debtAmount, address _for, int256 _maxActiveBand) external payable;
    function repay(uint256 _debtAmount, address _for, int256 _maxActiveBand, bool _useEth) external payable;
    function repay_extended(address _callbacker, uint256[] memory _callbackArgs) external;

    function liquidate(address user, uint256 min_x) external;
    function liquidate(address user, uint256 min_x, bool _useEth) external;
    function liquidate_extended(address user, uint256 min_x, uint256 frac, bool use_eth, address callbacker, uint256[] memory _callbackArgs) external;


    /// GETTERS
    function amm() external view returns (address);
    function monetary_policy() external view returns (address);
    function collateral_token() external view returns (address);
    function borrowed_token() external view returns (address);
    function debt(address) external view returns (uint256);
    function total_debt() external view returns (uint256);
    function health_calculator(address, int256, int256, bool, uint256) external view returns (int256);
    function health_calculator(address, int256, int256, bool) external view returns (int256);
    function health(address) external view returns (int256);
    function health(address, bool) external view returns (int256);
    function max_borrowable(uint256 collateralAmount, uint256 nBands) external view returns (uint256);
    function min_collateral(uint256 debtAmount, uint256 nBands) external view returns (uint256);
    function calculate_debt_n1(uint256, uint256, uint256) external view returns (int256);
    function minted() external view returns (uint256);
    function redeemed() external view returns (uint256);
    function amm_price() external view returns (uint256);
    function user_state(address) external view returns (uint256[4] memory);
    function user_prices(address) external view returns (uint256[2] memory);
    function loan_exists(address) external view returns (bool);
    function liquidation_discount() external view returns (uint256);
    function factory() external view returns (address);
    function loan_discount() external view returns (uint256);
}







interface ILlamaLendFactory {
    function controllers(uint256) external view returns (address);
}














contract LlamaLendHelper is MainnetLlamaLendAddresses, DSMath {
    using TokenUtils for address;

    error InvalidLlamaLendController();

    IBytesTransientStorage constant transientStorage = IBytesTransientStorage(BYTES_TRANSIENT_STORAGE);
    ILlamaLendFactory constant factory = ILlamaLendFactory(LLAMALEND_FACTORY);

    bytes4 constant LLAMALEND_SWAPPER_ID = bytes4(keccak256("LlamaLendSwapper"));

    function isControllerValid(address _controllerAddr, uint256 _controllerId) public view returns (bool) {
        return (factory.controllers(_controllerId) == _controllerAddr);
    }

    function getCollateralRatio(address _user, address _controllerAddr) public view returns (uint256 collRatio, bool isInSoftLiquidation) {
        // fetch users debt
        uint256 debt = ILlamaLendController(_controllerAddr).debt(_user);
        // no position can exist without debt
        if (debt == 0) return (0, false);
        (uint256 debtAssetCollAmount, uint256 collAmount) = getCollAmountsFromAMM(_controllerAddr, _user);
        // if user has debt asset as coll he is currently underwater
        if (debtAssetCollAmount > 0) isInSoftLiquidation = true;

        // fetch collToken oracle price
        address amm = ILlamaLendController(_controllerAddr).amm();
        uint256 oraclePrice = ILLAMMA(amm).price_oracle();
        // calculate collAmount as WAD (18 decimals)
        address collToken = ILlamaLendController(_controllerAddr).collateral_token();
        uint256 assetDec = IERC20(collToken).decimals();
        uint256 collAmountWAD = assetDec > 18 ? (collAmount / 10 ** (assetDec - 18)) : (collAmount * 10 ** (18 - assetDec));
        
        collRatio = wdiv(wmul(collAmountWAD, oraclePrice) + debtAssetCollAmount, debt);
    }

    function _sendLeftoverFunds(
        address _collToken,
        address _debtToken,
        uint256 _collStartingBalance,
        uint256 _debtStartingBalance,
        address _to
    ) internal returns (uint256 collTokenReceived, uint256 debtTokenReceived) {
        collTokenReceived = _collToken.getBalance(address(this)) - _collStartingBalance;
        debtTokenReceived = _debtToken.getBalance(address(this)) - _debtStartingBalance;
        _collToken.withdrawTokens(_to, collTokenReceived);
        _debtToken.withdrawTokens(_to, debtTokenReceived);
    }

    function userMaxWithdraw(
        address _controllerAddress,
        address _user
    ) public view returns (uint256 maxWithdraw) {
        uint256[4] memory userState = ILlamaLendController(_controllerAddress).user_state(_user);
        return
            userState[0] -
            ILlamaLendController(_controllerAddress).min_collateral(userState[2], userState[3]);
    }

    function getCollAmountsFromAMM(
        address _controllerAddress,
        address _user
    ) public view returns (uint256 debtAssetCollAmount, uint256 collAssetCollAmount) {
        address llammaAddress = ILlamaLendController(_controllerAddress).amm();
        uint256[2] memory xy = ILLAMMA(llammaAddress).get_sum_xy(_user);
        debtAssetCollAmount = xy[0];
        collAssetCollAmount = xy[1];
    }
}






contract DFSExchangeData {

    struct OffchainData {
        address wrapper; // dfs wrapper address for the aggregator (must be in WrapperExchangeRegistry)
        address exchangeAddr; // exchange address we are calling to execute the order (must be in ExchangeAggregatorRegistry)
        address allowanceTarget; // exchange aggregator contract we give allowance to
        uint256 price; // expected price that the aggregator sent us
        uint256 protocolFee; // deprecated (used as a separate fee amount for 0x v1)
        bytes callData; // 0ff-chain calldata the aggregator gives to perform the swap
    }

    struct ExchangeData {
        address srcAddr; // source token address (which we're selling)
        address destAddr; // destination token address (which we're buying)
        uint256 srcAmount; // amount of source token in token decimals
        uint256 destAmount; // amount of bought token in token decimals
        uint256 minPrice; // minPrice we are expecting (checked in DFSExchangeCore)
        uint256 dfsFeeDivider; // service fee divider
        address user; // currently deprecated (used to check custom fees for the user)
        address wrapper; // on-chain wrapper address (must be in WrapperExchangeRegistry)
        bytes wrapperData; // on-chain additional data for on-chain (uniswap route for example)
        OffchainData offchainData; // offchain aggregator order
    }
}







contract Discount is AdminAuth{
    mapping(address => bool) public serviceFeesDisabled;

    function reenableServiceFee(address _wallet) public onlyOwner{
        serviceFeesDisabled[_wallet] = false;
    }

    function disableServiceFee(address _wallet) public onlyOwner{
        serviceFeesDisabled[_wallet] = true;
    }
}








contract DFSExchangeHelper {
    
    using TokenUtils for address;
    
    error InvalidOffchainData();
    error OutOfRangeSlicingError();
    //Order success but amount 0
    error ZeroTokensSwapped();

    using SafeERC20 for IERC20;

    function sendLeftover(
        address _srcAddr,
        address _destAddr,
        address payable _to
    ) internal {
        // clean out any eth leftover
        TokenUtils.ETH_ADDR.withdrawTokens(_to, type(uint256).max);

        _srcAddr.withdrawTokens(_to, type(uint256).max);
        _destAddr.withdrawTokens(_to, type(uint256).max);
    }

    function sliceUint(bytes memory bs, uint256 start) internal pure returns (uint256) {
        if (bs.length < start + 32){
            revert OutOfRangeSlicingError();
        }

        uint256 x;
        assembly {
            x := mload(add(bs, add(0x20, start)))
        }

        return x;
    }

    function writeUint256(
        bytes memory _b,
        uint256 _index,
        uint256 _input
    ) internal pure {
        if (_b.length < _index + 32) {
            revert InvalidOffchainData();
        }

        bytes32 input = bytes32(_input);

        _index += 32;

        // Read the bytes32 from array memory
        assembly {
            mstore(add(_b, _index), input)
        }
    }
}







contract MainnetExchangeAddresses {

    address internal constant FEE_RECIPIENT_ADDRESS = 0x39C4a92Dc506300c3Ea4c67ca4CA611102ee6F2A;
    address internal constant DISCOUNT_ADDRESS = 0x84fE6D4aaD0CA1ce3af7153eecd11729fa7a74f0;
    address internal constant WRAPPER_EXCHANGE_REGISTRY = 0x653893375dD1D942D2C429caB51641F2bf14d426;
    address internal constant EXCHANGE_AGGREGATOR_REGISTRY_ADDR = 0x7b67D9D7993A258C4b2C31CDD9E6cbD5Fb674985;
    address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address internal constant TOKEN_GROUP_REGISTRY = 0xcA49e64FE1FE8be40ED30F682edA1b27a6c8611c;
}







contract ExchangeHelper is MainnetExchangeAddresses {
}







contract ExchangeAggregatorRegistry is AdminAuth {
    mapping(address => bool) public exchangeTargetAddresses;

    error EmptyAddrError();

    function setExchangeTargetAddr(address _exchangeAddr, bool _state) public onlyOwner {
        if(_exchangeAddr == address(0)) {
			revert EmptyAddrError();
		}

        exchangeTargetAddresses[_exchangeAddr] = _state;
    }

    function isExchangeAggregatorAddr(address _exchangeAddr) public view returns (bool) {
        return exchangeTargetAddresses[_exchangeAddr];
    }
}






contract WrapperExchangeRegistry is AdminAuth {
	mapping(address => bool) private wrappers;

	error EmptyAddrError();

	function addWrapper(address _wrapper) public onlyOwner {
		if(_wrapper == address(0)) {
			revert EmptyAddrError();
		}

		wrappers[_wrapper] = true;
	}

	function removeWrapper(address _wrapper) public onlyOwner {
		wrappers[_wrapper] = false;
	}

	function isWrapper(address _wrapper) public view returns(bool) {
		return wrappers[_wrapper];
	}
}







interface IExchangeV3 {
    function sell(address _srcAddr, address _destAddr, uint _srcAmount, bytes memory _additionalData) external returns (uint);
    function getSellRate(address _srcAddr, address _destAddr, uint _srcAmount, bytes memory _additionalData) external returns (uint);
}







abstract contract IOffchainWrapper is DFSExchangeData {
    function takeOrder(
        ExchangeData memory _exData
    ) virtual public payable returns (bool success, uint256);
}















contract DFSExchangeCore is DFSExchangeHelper, DSMath, DFSExchangeData, ExchangeHelper {
    using SafeERC20 for IERC20;
    using TokenUtils for address;

    error SlippageHitError(uint256 amountBought, uint256 amountExpected);
    error InvalidWrapperError(address wrapperAddr);

    ExchangeAggregatorRegistry internal constant exchangeAggRegistry = ExchangeAggregatorRegistry(EXCHANGE_AGGREGATOR_REGISTRY_ADDR);
    WrapperExchangeRegistry internal constant wrapperRegistry = WrapperExchangeRegistry(WRAPPER_EXCHANGE_REGISTRY);

    /// @notice Internal method that performs a sell on offchain aggregator/on-chain
    /// @dev Useful for other DFS contract to integrate for exchanging
    /// @param exData Exchange data struct
    function _sell(ExchangeData memory exData) internal returns (address wrapperAddress, uint256 destAmount) {
        (wrapperAddress, destAmount, ) = _sell(exData, address(this));
    }

    /// @notice Internal method that performs a sell on offchain aggregator/on-chain
    /// @dev Useful for other DFS contract to integrate for exchanging
    /// @param exData Exchange data struct
    /// @return (address, uint, bool) Address of the wrapper used and destAmount and if there was fee
    function _sell(ExchangeData memory exData, address smartWallet) internal returns (address, uint256, bool) {
        uint256 amountWithoutFee = exData.srcAmount;
        address wrapperAddr = exData.offchainData.wrapper;
        bool offChainSwapSuccess;

        uint256 destBalanceBefore = exData.destAddr.getBalance(address(this));

        // Takes DFS exchange fee
        if (exData.dfsFeeDivider != 0) {
            exData.srcAmount = sub(exData.srcAmount, getFee(
                exData.srcAmount,
                smartWallet,
                exData.srcAddr,
                exData.dfsFeeDivider
            ));
        }

        // Try offchain aggregator first and then fallback on specific wrapper
        if (exData.offchainData.price > 0) {
            (offChainSwapSuccess, ) = offChainSwap(exData);
        }

        // fallback to desired wrapper if offchain aggregator failed
        if (!offChainSwapSuccess) {
            onChainSwap(exData);
            wrapperAddr = exData.wrapper;
        }

        uint256 destBalanceAfter = exData.destAddr.getBalance(address(this));
        uint256 amountBought = destBalanceAfter - destBalanceBefore;

        // check slippage
        if (amountBought < wmul(exData.minPrice, exData.srcAmount)){
            revert SlippageHitError(amountBought, wmul(exData.minPrice, exData.srcAmount));
        }

        bool hasFee = exData.srcAmount != amountWithoutFee;
        // revert back exData changes to keep it consistent
        exData.srcAmount = amountWithoutFee;

        return (wrapperAddr, amountBought, hasFee);
    }

    /// @notice Takes order from exchange aggregator and returns bool indicating if it is successful
    /// @param _exData Exchange data
    function offChainSwap(ExchangeData memory _exData)
        internal
        returns (bool success, uint256)
    {
        /// @dev Check if exchange address is in our registry to not call an untrusted contract
        if (!exchangeAggRegistry.isExchangeAggregatorAddr(_exData.offchainData.exchangeAddr)) {
            return (false, 0);
        }

        /// @dev Check if we have the address is a registered wrapper
        if (!wrapperRegistry.isWrapper(_exData.offchainData.wrapper)) {
            return (false, 0);
        }

        // send src amount
        IERC20(_exData.srcAddr).safeTransfer(_exData.offchainData.wrapper, _exData.srcAmount);

        return IOffchainWrapper(_exData.offchainData.wrapper).takeOrder(_exData);
    }

    /// @notice Calls wrapper contract for exchange to preform an on-chain swap
    /// @param _exData Exchange data struct
    /// @return swappedTokens Dest amount of tokens we get after sell
    function onChainSwap(ExchangeData memory _exData)
        internal
        returns (uint256 swappedTokens)
    {
        if (!(WrapperExchangeRegistry(WRAPPER_EXCHANGE_REGISTRY).isWrapper(_exData.wrapper))){
            revert InvalidWrapperError(_exData.wrapper);
        }

        IERC20(_exData.srcAddr).safeTransfer(_exData.wrapper, _exData.srcAmount);

        swappedTokens = IExchangeV3(_exData.wrapper).sell(
            _exData.srcAddr,
            _exData.destAddr,
            _exData.srcAmount,
            _exData.wrapperData
        );
    }

    /// @notice Takes a feePercentage and sends it to wallet
    /// @param _amount Amount of the whole trade
    /// @param _wallet Address of the users wallet (safe or dsproxy)
    /// @param _token Address of the token
    /// @param _dfsFeeDivider Dfs fee divider
    /// @return feeAmount Amount owner earned on the fee
    function getFee(
        uint256 _amount,
        address _wallet,
        address _token,
        uint256 _dfsFeeDivider
    ) internal returns (uint256 feeAmount) {
        if (_dfsFeeDivider != 0 && Discount(DISCOUNT_ADDRESS).serviceFeesDisabled(_wallet)) {
            _dfsFeeDivider = 0;
        }

        if (_dfsFeeDivider == 0) {
            feeAmount = 0;
        } else {
            feeAmount = _amount / _dfsFeeDivider;
            address walletAddr = FeeRecipient(FEE_RECIPIENT_ADDRESS).getFeeAddr();
            _token.withdrawTokens(walletAddr, feeAmount);
        }
    }
}










contract LlamaLendSwapper is LlamaLendHelper, DFSExchangeCore, GasFeeHelper, AdminAuth {
    using SafeERC20 for IERC20;
    using TokenUtils for address;

    /// @dev Divider for automation fee, 5 bps
    uint256 internal constant AUTOMATION_DFS_FEE = 2000;

    struct CallbackData {
        uint256 stablecoins;
        uint256 collateral;
    }


    ///@dev called by llamalend controller after repay_extended
    ///@dev sends all collateral the user has to this contract, we swap a part or all of it
    ///@dev after swapping, llamalend will either recreate the position or close it fully
    function callback_repay(
        address _user,
        uint256,
        uint256,
        uint256,
        uint256[] memory info
    ) external returns (CallbackData memory cb) {
        uint256 gasUsed = info[0];
        if (!isControllerValid(msg.sender, info[1])) revert InvalidLlamaLendController();

        ExchangeData memory exData = abi.decode(transientStorage.getBytesTransiently(), (DFSExchangeData.ExchangeData));
        address collToken = exData.srcAddr;
        address debtToken = exData.destAddr;

        (, uint256 receivedAmount, bool hasFee) = _sell(exData, _user);
        
        if (gasUsed > 0){
            receivedAmount -= _takeAutomationFee(receivedAmount, debtToken, gasUsed, hasFee);
        }

        // if receivedAmount > current debt, leftover coll will be returned and receivedAmount-currentDebt will be returned
        // if receivedAmount < current debt, new position will be created with leftover coll and currentDebt-receivedAmount
        cb.stablecoins = receivedAmount;
        cb.collateral = collToken.getBalance(address(this));

        // approve the controller to create new position
        IERC20(collToken).safeApprove(msg.sender, cb.collateral);
        IERC20(debtToken).safeApprove(msg.sender, cb.stablecoins);
    }

    ///@dev called by llamalend controller after create_loan_extended and borrow_more_extended
    ///@dev sends exData.srcAmount of debt token to this contract for us to sell then pulls received coll token
    function callback_deposit(
        address _user,
        uint256,
        uint256,
        uint256,
        uint256[] memory info
    ) external returns (CallbackData memory cb) {
        uint256 gasUsed = info[0];
        if (!isControllerValid(msg.sender, info[1])) revert InvalidLlamaLendController();
        ExchangeData memory exData = abi.decode(transientStorage.getBytesTransiently(), (DFSExchangeData.ExchangeData));

        address collToken = exData.destAddr;

        (, uint256 receivedAmount, bool hasFee) = _sell(exData, _user);

        if (gasUsed > 0){
            receivedAmount -= _takeAutomationFee(receivedAmount, collToken, gasUsed, hasFee);
        }

        cb.collateral = receivedAmount;

        // approve the controller to create new position
        IERC20(collToken).safeApprove(msg.sender, cb.collateral);
    }

    ///@dev called by llamalend controller after liquidate_extended
    ///@dev if users debtTokenCollateralAmount is higher than debt, this won'te be called at all
    ///@dev this will send all marketCollateralAmount from users position to this contract, which we can sell all or a part of it
    function callback_liquidate(
        address _user,
        uint256,
        uint256,
        uint256,
        uint256[] memory info
    ) external returns (CallbackData memory cb) {
        uint256 gasUsed = info[0];
        if (!isControllerValid(msg.sender, info[1])) revert InvalidLlamaLendController();
        bool sellMax = info[2] > 0;
        ExchangeData memory exData = abi.decode(transientStorage.getBytesTransiently(), (DFSExchangeData.ExchangeData));
        
        address collToken = exData.srcAddr;
        address debtToken = exData.destAddr;
        if (sellMax) {
            exData.srcAmount = collToken.getBalance(address(this));
        }
        (, uint256 receivedAmount, bool hasFee) = _sell(exData, _user);

        if (gasUsed > 0){
            receivedAmount -= _takeAutomationFee(receivedAmount, debtToken, gasUsed, hasFee);
        }
        cb.stablecoins = receivedAmount;
        cb.collateral = collToken.getBalance(address(this));

        IERC20(collToken).safeApprove(msg.sender, cb.collateral);
        IERC20(debtToken).safeApprove(msg.sender, cb.stablecoins);

    }

    /// @dev No funds should be stored on this contract, but if anything is left send back to the user
    function withdrawAll(address _controllerAddress) external {
        address collToken = ILlamaLendController(_controllerAddress).collateral_token();
        address debtToken = ILlamaLendController(_controllerAddress).borrowed_token();

        debtToken.withdrawTokens(msg.sender, type(uint256).max);
        collToken.withdrawTokens(msg.sender, type(uint256).max);
    }

    function _takeAutomationFee(
        uint256 _destTokenAmount,
        address _token,
        uint256 _gasUsed,
        bool hasFee
    ) internal returns (uint256 feeAmount) {
        // we need to take the fee for tx cost as well, as it's in a strategy
        feeAmount += calcGasCost(_gasUsed, _token, 0);
        
        // gas fee can't go over 20% of the whole amount
        if (feeAmount > (_destTokenAmount / 5)) {
            feeAmount = _destTokenAmount / 5;
        }
        // if user has been whitelisted we don't take 0.05% fee
        if (hasFee) {
            feeAmount += _destTokenAmount / AUTOMATION_DFS_FEE;
        }

        address walletAddr = FeeRecipient(FEE_RECIPIENT_ADDRESS).getFeeAddr();
        _token.withdrawTokens(walletAddr, feeAmount);
    }

}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"InvalidLlamaLendController","type":"error"},{"inputs":[],"name":"InvalidOffchainData","type":"error"},{"inputs":[{"internalType":"address","name":"wrapperAddr","type":"address"}],"name":"InvalidWrapperError","type":"error"},{"inputs":[],"name":"NonContractCall","type":"error"},{"inputs":[],"name":"OutOfRangeSlicingError","type":"error"},{"inputs":[],"name":"SenderNotAdmin","type":"error"},{"inputs":[],"name":"SenderNotOwner","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountBought","type":"uint256"},{"internalType":"uint256","name":"amountExpected","type":"uint256"}],"name":"SlippageHitError","type":"error"},{"inputs":[],"name":"ZeroTokensSwapped","type":"error"},{"inputs":[],"name":"CHAINLINK_FEED_REGISTRY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DFS_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SANITY_GAS_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adminVault","outputs":[{"internalType":"contract AdminVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasUsed","type":"uint256"},{"internalType":"address","name":"_feeToken","type":"address"},{"internalType":"uint256","name":"_l1GasCostInEth","type":"uint256"}],"name":"calcGasCost","outputs":[{"internalType":"uint256","name":"txCost","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"info","type":"uint256[]"}],"name":"callback_deposit","outputs":[{"components":[{"internalType":"uint256","name":"stablecoins","type":"uint256"},{"internalType":"uint256","name":"collateral","type":"uint256"}],"internalType":"struct LlamaLendSwapper.CallbackData","name":"cb","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"info","type":"uint256[]"}],"name":"callback_liquidate","outputs":[{"components":[{"internalType":"uint256","name":"stablecoins","type":"uint256"},{"internalType":"uint256","name":"collateral","type":"uint256"}],"internalType":"struct LlamaLendSwapper.CallbackData","name":"cb","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"info","type":"uint256[]"}],"name":"callback_repay","outputs":[{"components":[{"internalType":"uint256","name":"stablecoins","type":"uint256"},{"internalType":"uint256","name":"collateral","type":"uint256"}],"internalType":"struct LlamaLendSwapper.CallbackData","name":"cb","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"contract FeeRecipient","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feedRegistry","outputs":[{"internalType":"contract IFeedRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddr","type":"address"}],"name":"getAaveTokenPriceInETH","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddr","type":"address"}],"name":"getAaveTokenPriceInUSD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddr","type":"address"}],"name":"getAaveV3TokenPriceInETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddr","type":"address"}],"name":"getAaveV3TokenPriceInUSD","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_inputTokenAddr","type":"address"}],"name":"getAddrForChainlinkOracle","outputs":[{"internalType":"address","name":"tokenAddrForChainlinkUsage","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_inputTokenAddr","type":"address"}],"name":"getChainlinkPriceInETH","outputs":[{"internalType":"int256","name":"chainlinkPriceInETH","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_inputTokenAddr","type":"address"},{"internalType":"bool","name":"_useFallback","type":"bool"}],"name":"getChainlinkPriceInUSD","outputs":[{"internalType":"int256","name":"chainlinkPriceInUSD","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_controllerAddress","type":"address"},{"internalType":"address","name":"_user","type":"address"}],"name":"getCollAmountsFromAMM","outputs":[{"internalType":"uint256","name":"debtAssetCollAmount","type":"uint256"},{"internalType":"uint256","name":"collAssetCollAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_controllerAddr","type":"address"}],"name":"getCollateralRatio","outputs":[{"internalType":"uint256","name":"collRatio","type":"uint256"},{"internalType":"bool","name":"isInSoftLiquidation","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_inputTokenAddr","type":"address"}],"name":"getPriceInETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_inputTokenAddr","type":"address"}],"name":"getPriceInUSD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_inputTokenAddr","type":"address"},{"internalType":"uint80","name":"_roundId","type":"uint80"},{"internalType":"contract IAggregatorV3","name":"aggregator","type":"address"}],"name":"getRoundInfo","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"updateTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_inputTokenAddr","type":"address"},{"internalType":"uint80","name":"_roundId","type":"uint80"}],"name":"getRoundInfo","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"updateTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddr","type":"address"}],"name":"getSparkTokenPriceInETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddr","type":"address"}],"name":"getSparkTokenPriceInUSD","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"_btcPrice","type":"int256"}],"name":"getWBtcPrice","outputs":[{"internalType":"int256","name":"wBtcPrice","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"_stEthPrice","type":"int256"}],"name":"getWStEthPrice","outputs":[{"internalType":"int256","name":"wStEthPrice","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_controllerAddr","type":"address"},{"internalType":"uint256","name":"_controllerId","type":"uint256"}],"name":"isControllerValid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_controllerAddress","type":"address"},{"internalType":"address","name":"_user","type":"address"}],"name":"userMaxWithdraw","outputs":[{"internalType":"uint256","name":"maxWithdraw","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_controllerAddress","type":"address"}],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawStuckFunds","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6080604052600080546001600160a01b03199081167333fdb79afb4456b604f376a45a546e7ae700e88017909155600180549091167376720ac2574631530ec8163e4085d6f98513fb2717905534801561005857600080fd5b50613751806100686000396000f3fe608060405234801561001057600080fd5b50600436106101f05760003560e01c806381650aad1161010f578063c2dfea78116100a2578063ef67dc7411610071578063ef67dc7414610474578063f3be054b14610487578063fa09e6301461049a578063fc0626a8146104ad57600080fd5b8063c2dfea781461042f578063c579d49014610442578063e62214fe14610455578063e7940a6a1461046857600080fd5b806390238c39116100de57806390238c3914610387578063935b5233146103f6578063b548036014610409578063b97830c61461041c57600080fd5b806381650aad146103a257806383c21367146103b55780638cedca71146103c85780638da41b0f146103e357600080fd5b80633b6bc3a6116101875780634ea696bb116101565780634ea696bb1461031e57806353a6e47c1461034c57806353c92f46146103745780637d9f77121461038757600080fd5b80633b6bc3a6146102dd57806341c0e1b5146102e657806346904840146102f057806349a691571461030b57600080fd5b806325ad0b1e116101c357806325ad0b1e1461026457806328773b1e1461027757806329dae02b1461028a5780633a357a27146102b557600080fd5b806302266147146101f55780630527599f1461021b5780631982e00d1461022e5780632452878614610251575b600080fd5b610208610203366004612b53565b6104c0565b6040519081526020015b60405180910390f35b610208610229366004612b70565b610588565b61024161023c366004612b89565b61065d565b6040519015158152602001610212565b61020861025f366004612b53565b61070b565b610208610272366004612b53565b610810565b610208610285366004612b53565b6108ba565b61029d610298366004612b53565b6108fd565b6040516001600160a01b039091168152602001610212565b6102c86102c3366004612bb5565b6109c3565b60408051928352901515602083015201610212565b6102086107d081565b6102ee610c98565b005b61029d7339c4a92dc506300c3ea4c67ca4ca611102ee6f2a81565b610208610319366004612bfc565b610d5b565b61033161032c366004612c9b565b610e3b565b60408051825181526020928301519281019290925201610212565b61035f61035a366004612d8b565b611018565b60408051928352602083019190915201610212565b610208610382366004612b53565b61116e565b61029d7347fb2585d2c56fe188d0e6ec628a38b74fceeedf81565b61035f6103b0366004612bb5565b6112b1565b6102086103c3366004612dd6565b6113b5565b61029d73ccf3d848e08b94478ed8f46ffead3008faf581fd81565b6102086103f1366004612bb5565b6114d4565b610208610404366004612b53565b611600565b610208610417366004612b53565b611655565b61020861042a366004612b70565b611690565b61020861043d366004612b53565b611711565b6102ee610450366004612e0e565b611766565b610331610463366004612c9b565b6118a0565b61020864e8d4a5100081565b610331610482366004612c9b565b6119fb565b610208610495366004612b53565b611b8a565b6102ee6104a8366004612b53565b611b96565b61035f6104bb366004612e3e565b611c8f565b6000806104cc836108fd565b905060006104db826001610d5b565b9050806104ee576104eb846108ba565b90505b806104ff576104fc84611711565b90505b806105105761050d8461070b565b90505b8061051f575060009392505050565b6001600160a01b038416737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156105505761054d81611690565b90505b6001600160a01b038416732260fac5e5542a773aa44fbcfedf7c193bc2c59914156105815761057e81610588565b90505b9392505050565b60405163bcfd032d60e01b8152732260fac5e5542a773aa44fbcfedf7c193bc2c599600482015273bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb602482015260009081907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa15801561060a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061062e9190612e6c565b5050509150506305f5e10081846106459190612eda565b610653906302faf080612f7a565b6105819190612fe8565b6040517fe94b0dd2000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b0384169073ea6876dde9e3467564acbee1ed5bac88783205e09063e94b0dd290602401602060405180830381865afa1580156106d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f89190613021565b6001600160a01b03161490505b92915050565b6000807302c3ea4e34c0cbd694d2adfa2c690eecbc1793ee6001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107849190613021565b6040517fb3596f070000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301529192509082169063b3596f0790602401602060405180830381865afa925050508015610803575060408051601f3d908101601f191682019092526108009181019061303e565b60015b6105815750600092915050565b60405163bcfd032d60e01b81526001600160a01b038216600482015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248201526000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa9250505080156108a1575060408051601f3d908101601f1916820190925261089e91810190612e6c565b60015b6108ad57506000919050565b509193505050505b919050565b6000806108c683611600565b905060006108e973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b90506108f58282611d5a565b949350505050565b60006001600160a01b03821673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2141561093f575073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee919050565b6001600160a01b038216737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0141561097f575073ae7ab96520de3a18e5e111b5eaab095312d7fe84919050565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c59914156109bf575073bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb919050565b5090565b6040517f9b6c56ec0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301526000918291829190851690639b6c56ec90602401602060405180830381865afa158015610a2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4e919061303e565b905080610a62576000809250925050610c91565b600080610a6f86886112b1565b90925090508115610a7f57600193505b6000866001600160a01b0316632a9439456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610abf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae39190613021565b90506000816001600160a01b03166386fc88d36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b49919061303e565b90506000886001600160a01b0316632621db2f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610baf9190613021565b90506000816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bf1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c15919061303e565b9050600060128211610c4657610c2c826012613057565b610c3790600a613152565b610c41908761315e565b610c66565b610c51601283613057565b610c5c90600a613152565b610c66908761317d565b9050610c8687610c768387611d5a565b610c809190613191565b89611d94565b995050505050505050505b9250929050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d189190613021565b6001600160a01b031614610d58576040517fa6c827a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33ff5b60405163bcfd032d60e01b81526001600160a01b038316600482015261034860248201526000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa925050508015610dda575060408051601f3d908101601f19168201909252610dd791810190612e6c565b60015b610e2f578115610e27576000610def84610810565b90506000610e1273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b9050610e1e8282611d5a565b92505050610705565b506000610705565b50919695505050505050565b6040805180820190915260008082526020820152600082600081518110610e6457610e646131a9565b60200260200101519050610e923384600181518110610e8557610e856131a9565b602002602001015161065d565b610eaf5760405163dff5248f60e01b815260040160405180910390fd5b60008084600281518110610ec557610ec56131a9565b6020026020010151119050600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af1158015610f26573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f4e919081019061324f565b806020019051810190610f61919061333e565b80516020820151919250908315610f8b57610f856001600160a01b03831630611db7565b60408401525b600080610f98858e611e79565b9093509150508615610fbd57610fb082848984611fc3565b610fba9083613057565b91505b818852610fd36001600160a01b03851630611db7565b60208901819052610ff0906001600160a01b0386169033906120b4565b8751611008906001600160a01b0385169033906120b4565b5050505050505095945050505050565b6000808069ffffffffffffffffffff851661109d57836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801561106b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108f9190612e6c565b509450909250611133915050565b6040517f9a6fc8f500000000000000000000000000000000000000000000000000000000815269ffffffffffffffffffff861660048201526001600160a01b03851690639a6fc8f59060240160a060405180830381865afa158015611106573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112a9190612e6c565b50945090925050505b6001600160a01b038616737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156111645761116181611690565b90505b9150935093915050565b60008061117a836108fd565b90506000611189826000610d5b565b905080156112315760006111b273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b905060006111c08383611d94565b90506001600160a01b038616737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156111fa576111f081611690565b9695505050505050565b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c5991415611228576111f081610588565b95945050505050565b600061123c83610810565b9050801561124c57949350505050565b600061125786611600565b905080156112685795945050505050565b600061127387611b8a565b90508015611285579695505050505050565b600061129088611655565b905080156112a357979650505050505050565b506000979650505050505050565b6000806000846001600160a01b0316632a9439456040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113189190613021565b6040517f544fb5c10000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015291925060009183169063544fb5c1906024016040805180830381865afa15801561137c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a09190613442565b80516020909101519097909650945050505050565b60003a64e8d4a510008111156113cd575064e8d4a510005b458511156113d9574594505b826113e4828761315e565b6113ee9190613191565b91506001600160a01b03841673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2146114cc57600061141f8561116e565b90506000611435866001600160a01b03166121b8565b9050601281111561148d5760405162461bcd60e51b815260206004820152601560248201527f546f6b656e20646563696d616c20746f6f20626967000000000000000000000060448201526064015b60405180910390fd5b81156114c45761149e816012613057565b6114a990600a613152565b6114b38584611d94565b6114bd919061317d565b93506114c9565b600093505b50505b509392505050565b6040517fec74d0a80000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152600091829185169063ec74d0a890602401608060405180830381865afa158015611538573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061155c91906134c2565b604080820151606083015191517fa7573206000000000000000000000000000000000000000000000000000000008152600481019190915260248101919091529091506001600160a01b0385169063a757320690604401602060405180830381865afa1580156115d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f4919061303e565b81516108f59190613057565b60008073b53c1a33016b2dc2ff3653530bff1848a515c8c56001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b6000806116618361070b565b9050600061168473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b90506108f58282611d94565b600061070582737f39c581f595b53c5cb19bd0b3f8da6c935e2ca06001600160a01b031663035faf826040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170c919061303e565b611d5a565b600080732f39d218133afab8f2b819b1066c7e434ad94e9e6001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e69190613021565b6001600160a01b031614611826576040517f19494c8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6001600160a01b0384161415611887576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015611881573d6000803e3d6000fd5b50505050565b61189b6001600160a01b0384168383612249565b505050565b60408051808201909152600080825260208201526000826000815181106118c9576118c96131a9565b602002602001015190506118ea3384600181518110610e8557610e856131a9565b6119075760405163dff5248f60e01b815260040160405180910390fd5b600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af115801561195d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611985919081019061324f565b806020019051810190611998919061333e565b60208101519091506000806119ad848c611e79565b90935091505084156119d2576119c582848784611fc3565b6119cf9083613057565b91505b602086018290526119ed6001600160a01b03841633846120b4565b505050505095945050505050565b6040805180820190915260008082526020820152600082600081518110611a2457611a246131a9565b60200260200101519050611a453384600181518110610e8557610e856131a9565b611a625760405163dff5248f60e01b815260040160405180910390fd5b600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af1158015611ab8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611ae0919081019061324f565b806020019051810190611af3919061333e565b8051602082015191925090600080611b0b858d611e79565b9093509150508515611b3057611b2382848884611fc3565b611b2d9083613057565b91505b818752611b466001600160a01b03851630611db7565b60208801819052611b63906001600160a01b0386169033906120b4565b8651611b7b906001600160a01b0385169033906120b4565b50505050505095945050505050565b60008061166183611711565b6000816001600160a01b0316632621db2f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfa9190613021565b90506000826001600160a01b031663765337b66040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c609190613021565b9050611c786001600160a01b03821633600019612292565b506118816001600160a01b03831633600019612292565b6000806000611c9d856108fd565b6040517fd2edb6dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015261034860248201529091506000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063d2edb6dd90604401602060405180830381865afa158015611d1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d409190613021565b9050611d4d868683611018565b9350935050509250929050565b6000670de0b6b3a7640000611d8a611d7285856123c5565b611d856002670de0b6b3a764000061317d565b6123d1565b610581919061317d565b600081611d8a611dac85670de0b6b3a76400006123c5565b611d8560028661317d565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415611def57506001600160a01b03811631610705565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa158015611e4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e72919061303e565b9050610705565b60408201516101208301515160208401516000928392839283908190611ea8906001600160a01b031630611db7565b90508860a00151600014611ee157611edb8960400151611ed68b604001518b8d600001518e60a001516123dd565b61252f565b60408a01525b6101208901516060015115611efd57611ef98961253b565b5091505b81611f1457611f0b8961273d565b508860e0015192505b6020890151600090611f2f906001600160a01b031630611db7565b90506000611f3d8383613057565b9050611f518b608001518c60400151611d5a565b811015611fa65780611f6b8c608001518d60400151611d5a565b6040517f16437be000000000000000000000000000000000000000000000000000000000815260048101929092526024820152604401611484565b60408b018051908790529498509650505050141590509250925092565b6000611fd1838560006113b5565b611fdb9082613191565b9050611fe860058661317d565b811115611ffd57611ffa60058661317d565b90505b811561201c5761200f6107d08661317d565b6120199082613191565b90505b60007339c4a92dc506300c3ea4c67ca4ca611102ee6f2a6001600160a01b031663b38779eb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612070573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120949190613021565b90506120aa6001600160a01b0386168284612292565b5050949350505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663095ea7b360e01b17905261211a8482612890565b611881576040516001600160a01b0384166024820152600060448201526121ae90859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612933565b6118818482612933565b60006001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14156121e757506012919050565b816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612225573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610705919061303e565b6040516001600160a01b03831660248201526044810182905261189b9084907fa9059cbb000000000000000000000000000000000000000000000000000000009060640161214a565b60006000198214156122ab576122a88430611db7565b91505b6001600160a01b038316158015906122cc57506001600160a01b0383163014155b80156122d757508115155b156123be576001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14612319576123146001600160a01b0385168484612249565b6123be565b6000836001600160a01b03168360405160006040518083038185875af1925050503d8060008114612366576040519150601f19603f3d011682016040523d82523d6000602084013e61236b565b606091505b50509050806123bc5760405162461bcd60e51b815260206004820152600d60248201527f4574682073656e64206661696c000000000000000000000000000000000000006044820152606401611484565b505b5092915050565b6000610581828461315e565b60006105818284613191565b6000811580159061247e57506040517f66ab66940000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201527384fe6d4aad0ca1ce3af7153eecd11729fa7a74f0906366ab669490602401602060405180830381865afa15801561245a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247e919061351b565b1561248857600091505b81612495575060006108f5565b61249f828661317d565b905060007339c4a92dc506300c3ea4c67ca4ca611102ee6f2a6001600160a01b031663b38779eb6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125199190613021565b90506120aa6001600160a01b0385168284612292565b60006105818284613057565b610120810151602001516040517f27f99a5d0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526000908190737b67d9d7993a258c4b2c31cdd9e6cbd5fb674985906327f99a5d90602401602060405180830381865afa1580156125bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e0919061351b565b6125ef57506000928392509050565b6101208301515160405163e0aa279760e01b81526001600160a01b03909116600482015273653893375dd1d942d2c429cab51641f2bf14d4269063e0aa279790602401602060405180830381865afa15801561264f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612673919061351b565b61268257506000928392509050565b61012083015151604084015184516126a5926001600160a01b0390911691612249565b610120830151516040517f16d2a88c0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116906316d2a88c906126f29086906004016135ba565b60408051808303816000875af1158015612710573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612734919061368c565b91509150915091565b60e081015160405163e0aa279760e01b81526001600160a01b03909116600482015260009073653893375dd1d942d2c429cab51641f2bf14d4269063e0aa279790602401602060405180830381865afa15801561279e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127c2919061351b565b6128095760e08201516040517f4113a62c0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401611484565b60e08201516040830151835161282a926001600160a01b0390911691612249565b8160e001516001600160a01b0316635b6f36fc8360000151846020015185604001518661010001516040518563ffffffff1660e01b815260040161287194939291906136ba565b6020604051808303816000875af1158015612225573d6000803e3d6000fd5b6000806000846001600160a01b0316846040516128ad91906136ec565b6000604051808303816000865af19150503d80600081146128ea576040519150601f19603f3d011682016040523d82523d6000602084013e6128ef565b606091505b5091509150818015612919575080511580612919575080806020019051810190612919919061351b565b80156112285750505050506001600160a01b03163b151590565b6000612988826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612a1b9092919063ffffffff16565b90508051600014806129a95750808060200190518101906129a9919061351b565b61189b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401611484565b606061057e84846000856060612a3085612b02565b612a66576040517f304619b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080866001600160a01b03168587604051612a8291906136ec565b60006040518083038185875af1925050503d8060008114612abf576040519150601f19603f3d011682016040523d82523d6000602084013e612ac4565b606091505b50915091508115612ad85791506108f59050565b805115612ae85780518082602001fd5b8360405162461bcd60e51b81526004016114849190613708565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906108f5575050151592915050565b6001600160a01b0381168114612b5057600080fd5b50565b600060208284031215612b6557600080fd5b813561058181612b3b565b600060208284031215612b8257600080fd5b5035919050565b60008060408385031215612b9c57600080fd5b8235612ba781612b3b565b946020939093013593505050565b60008060408385031215612bc857600080fd5b8235612bd381612b3b565b91506020830135612be381612b3b565b809150509250929050565b8015158114612b5057600080fd5b60008060408385031215612c0f57600080fd5b8235612c1a81612b3b565b91506020830135612be381612bee565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff81118282101715612c6457612c64612c2a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715612c9357612c93612c2a565b604052919050565b600080600080600060a08688031215612cb357600080fd5b8535612cbe81612b3b565b945060208681013594506040870135935060608701359250608087013567ffffffffffffffff80821115612cf157600080fd5b818901915089601f830112612d0557600080fd5b813581811115612d1757612d17612c2a565b8060051b9150612d28848301612c6a565b818152918301840191848101908c841115612d4257600080fd5b938501935b83851015612d6057843582529385019390850190612d47565b8096505050505050509295509295909350565b69ffffffffffffffffffff81168114612b5057600080fd5b600080600060608486031215612da057600080fd5b8335612dab81612b3b565b92506020840135612dbb81612d73565b91506040840135612dcb81612b3b565b809150509250925092565b600080600060608486031215612deb57600080fd5b833592506020840135612dfd81612b3b565b929592945050506040919091013590565b600080600060608486031215612e2357600080fd5b8335612e2e81612b3b565b92506020840135612dfd81612b3b565b60008060408385031215612e5157600080fd5b8235612e5c81612b3b565b91506020830135612be381612d73565b600080600080600060a08688031215612e8457600080fd5b8551612e8f81612d73565b809550506020860151935060408601519250606086015191506080860151612eb681612d73565b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600084136000841385830485118282161615612f1b57612f1b612ec4565b600160ff1b6000871286820588128184161615612f3a57612f3a612ec4565b60008712925087820587128484161615612f5657612f56612ec4565b87850587128184161615612f6c57612f6c612ec4565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615612fb457612fb4612ec4565b82600160ff1b038412811615612fcc57612fcc612ec4565b50500190565b634e487b7160e01b600052601260045260246000fd5b600082612ff757612ff7612fd2565b600160ff1b82146000198414161561301157613011612ec4565b500590565b80516108b581612b3b565b60006020828403121561303357600080fd5b815161058181612b3b565b60006020828403121561305057600080fd5b5051919050565b60008282101561306957613069612ec4565b500390565b600181815b808511156130a957816000190482111561308f5761308f612ec4565b8085161561309c57918102915b93841c9390800290613073565b509250929050565b6000826130c057506001610705565b816130cd57506000610705565b81600181146130e357600281146130ed57613109565b6001915050610705565b60ff8411156130fe576130fe612ec4565b50506001821b610705565b5060208310610133831016604e8410600b841016171561312c575081810a610705565b613136838361306e565b806000190482111561314a5761314a612ec4565b029392505050565b600061058183836130b1565b600081600019048311821515161561317857613178612ec4565b500290565b60008261318c5761318c612fd2565b500490565b600082198211156131a4576131a4612ec4565b500190565b634e487b7160e01b600052603260045260246000fd5b60005b838110156131da5781810151838201526020016131c2565b838111156118815750506000910152565b600082601f8301126131fc57600080fd5b815167ffffffffffffffff81111561321657613216612c2a565b613229601f8201601f1916602001612c6a565b81815284602083860101111561323e57600080fd5b6108f58260208301602087016131bf565b60006020828403121561326157600080fd5b815167ffffffffffffffff81111561327857600080fd5b6108f5848285016131eb565b600060c0828403121561329657600080fd5b60405160c0810167ffffffffffffffff82821081831117156132ba576132ba612c2a565b81604052829350845191506132ce82612b3b565b9082526020840151906132e082612b3b565b816020840152604085015191506132f682612b3b565b816040840152606085015160608401526080850151608084015260a085015191508082111561332457600080fd5b50613331858286016131eb565b60a0830152505092915050565b60006020828403121561335057600080fd5b815167ffffffffffffffff8082111561336857600080fd5b90830190610140828603121561337d57600080fd5b613385612c40565b61338e83613016565b815261339c60208401613016565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a08201526133d560c08401613016565b60c08201526133e660e08401613016565b60e082015261010080840151838111156133ff57600080fd5b61340b888287016131eb565b828401525050610120808401518381111561342557600080fd5b61343188828701613284565b918301919091525095945050505050565b60006040828403121561345457600080fd5b82601f83011261346357600080fd5b6040516040810181811067ffffffffffffffff8211171561348657613486612c2a565b806040525080604084018581111561349d57600080fd5b845b818110156134b757805183526020928301920161349f565b509195945050505050565b6000608082840312156134d457600080fd5b82601f8301126134e357600080fd5b6040516080810181811067ffffffffffffffff8211171561350657613506612c2a565b60405280608084018581111561349d57600080fd5b60006020828403121561352d57600080fd5b815161058181612bee565b600081518084526135508160208601602086016131bf565b601f01601f19169290920160200192915050565b60006001600160a01b0380835116845280602084015116602085015280604084015116604085015250606082015160608401526080820151608084015260a082015160c060a08501526108f560c0850182613538565b602081526135d46020820183516001600160a01b03169052565b600060208301516135f060408401826001600160a01b03169052565b506040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015161363360e08401826001600160a01b03169052565b5060e0830151610100613650818501836001600160a01b03169052565b808501519150506101406101208181860152613670610160860184613538565b90860151858203601f1901838701529092506111f08382613564565b6000806040838503121561369f57600080fd5b82516136aa81612bee565b6020939093015192949293505050565b60006001600160a01b038087168352808616602084015250836040830152608060608301526111f06080830184613538565b600082516136fe8184602087016131bf565b9190910192915050565b602081526000610581602083018461353856fea2646970667358221220b82eef716c16025329447027f497849dfd5b117acf4e27bdd79bfbc77887f7e964736f6c634300080a0033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101f05760003560e01c806381650aad1161010f578063c2dfea78116100a2578063ef67dc7411610071578063ef67dc7414610474578063f3be054b14610487578063fa09e6301461049a578063fc0626a8146104ad57600080fd5b8063c2dfea781461042f578063c579d49014610442578063e62214fe14610455578063e7940a6a1461046857600080fd5b806390238c39116100de57806390238c3914610387578063935b5233146103f6578063b548036014610409578063b97830c61461041c57600080fd5b806381650aad146103a257806383c21367146103b55780638cedca71146103c85780638da41b0f146103e357600080fd5b80633b6bc3a6116101875780634ea696bb116101565780634ea696bb1461031e57806353a6e47c1461034c57806353c92f46146103745780637d9f77121461038757600080fd5b80633b6bc3a6146102dd57806341c0e1b5146102e657806346904840146102f057806349a691571461030b57600080fd5b806325ad0b1e116101c357806325ad0b1e1461026457806328773b1e1461027757806329dae02b1461028a5780633a357a27146102b557600080fd5b806302266147146101f55780630527599f1461021b5780631982e00d1461022e5780632452878614610251575b600080fd5b610208610203366004612b53565b6104c0565b6040519081526020015b60405180910390f35b610208610229366004612b70565b610588565b61024161023c366004612b89565b61065d565b6040519015158152602001610212565b61020861025f366004612b53565b61070b565b610208610272366004612b53565b610810565b610208610285366004612b53565b6108ba565b61029d610298366004612b53565b6108fd565b6040516001600160a01b039091168152602001610212565b6102c86102c3366004612bb5565b6109c3565b60408051928352901515602083015201610212565b6102086107d081565b6102ee610c98565b005b61029d7339c4a92dc506300c3ea4c67ca4ca611102ee6f2a81565b610208610319366004612bfc565b610d5b565b61033161032c366004612c9b565b610e3b565b60408051825181526020928301519281019290925201610212565b61035f61035a366004612d8b565b611018565b60408051928352602083019190915201610212565b610208610382366004612b53565b61116e565b61029d7347fb2585d2c56fe188d0e6ec628a38b74fceeedf81565b61035f6103b0366004612bb5565b6112b1565b6102086103c3366004612dd6565b6113b5565b61029d73ccf3d848e08b94478ed8f46ffead3008faf581fd81565b6102086103f1366004612bb5565b6114d4565b610208610404366004612b53565b611600565b610208610417366004612b53565b611655565b61020861042a366004612b70565b611690565b61020861043d366004612b53565b611711565b6102ee610450366004612e0e565b611766565b610331610463366004612c9b565b6118a0565b61020864e8d4a5100081565b610331610482366004612c9b565b6119fb565b610208610495366004612b53565b611b8a565b6102ee6104a8366004612b53565b611b96565b61035f6104bb366004612e3e565b611c8f565b6000806104cc836108fd565b905060006104db826001610d5b565b9050806104ee576104eb846108ba565b90505b806104ff576104fc84611711565b90505b806105105761050d8461070b565b90505b8061051f575060009392505050565b6001600160a01b038416737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156105505761054d81611690565b90505b6001600160a01b038416732260fac5e5542a773aa44fbcfedf7c193bc2c59914156105815761057e81610588565b90505b9392505050565b60405163bcfd032d60e01b8152732260fac5e5542a773aa44fbcfedf7c193bc2c599600482015273bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb602482015260009081907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa15801561060a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061062e9190612e6c565b5050509150506305f5e10081846106459190612eda565b610653906302faf080612f7a565b6105819190612fe8565b6040517fe94b0dd2000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b0384169073ea6876dde9e3467564acbee1ed5bac88783205e09063e94b0dd290602401602060405180830381865afa1580156106d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f89190613021565b6001600160a01b03161490505b92915050565b6000807302c3ea4e34c0cbd694d2adfa2c690eecbc1793ee6001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107849190613021565b6040517fb3596f070000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301529192509082169063b3596f0790602401602060405180830381865afa925050508015610803575060408051601f3d908101601f191682019092526108009181019061303e565b60015b6105815750600092915050565b60405163bcfd032d60e01b81526001600160a01b038216600482015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248201526000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa9250505080156108a1575060408051601f3d908101601f1916820190925261089e91810190612e6c565b60015b6108ad57506000919050565b509193505050505b919050565b6000806108c683611600565b905060006108e973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b90506108f58282611d5a565b949350505050565b60006001600160a01b03821673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2141561093f575073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee919050565b6001600160a01b038216737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0141561097f575073ae7ab96520de3a18e5e111b5eaab095312d7fe84919050565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c59914156109bf575073bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb919050565b5090565b6040517f9b6c56ec0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301526000918291829190851690639b6c56ec90602401602060405180830381865afa158015610a2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4e919061303e565b905080610a62576000809250925050610c91565b600080610a6f86886112b1565b90925090508115610a7f57600193505b6000866001600160a01b0316632a9439456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610abf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae39190613021565b90506000816001600160a01b03166386fc88d36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b49919061303e565b90506000886001600160a01b0316632621db2f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610baf9190613021565b90506000816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bf1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c15919061303e565b9050600060128211610c4657610c2c826012613057565b610c3790600a613152565b610c41908761315e565b610c66565b610c51601283613057565b610c5c90600a613152565b610c66908761317d565b9050610c8687610c768387611d5a565b610c809190613191565b89611d94565b995050505050505050505b9250929050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d189190613021565b6001600160a01b031614610d58576040517fa6c827a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33ff5b60405163bcfd032d60e01b81526001600160a01b038316600482015261034860248201526000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa925050508015610dda575060408051601f3d908101601f19168201909252610dd791810190612e6c565b60015b610e2f578115610e27576000610def84610810565b90506000610e1273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b9050610e1e8282611d5a565b92505050610705565b506000610705565b50919695505050505050565b6040805180820190915260008082526020820152600082600081518110610e6457610e646131a9565b60200260200101519050610e923384600181518110610e8557610e856131a9565b602002602001015161065d565b610eaf5760405163dff5248f60e01b815260040160405180910390fd5b60008084600281518110610ec557610ec56131a9565b6020026020010151119050600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af1158015610f26573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f4e919081019061324f565b806020019051810190610f61919061333e565b80516020820151919250908315610f8b57610f856001600160a01b03831630611db7565b60408401525b600080610f98858e611e79565b9093509150508615610fbd57610fb082848984611fc3565b610fba9083613057565b91505b818852610fd36001600160a01b03851630611db7565b60208901819052610ff0906001600160a01b0386169033906120b4565b8751611008906001600160a01b0385169033906120b4565b5050505050505095945050505050565b6000808069ffffffffffffffffffff851661109d57836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801561106b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108f9190612e6c565b509450909250611133915050565b6040517f9a6fc8f500000000000000000000000000000000000000000000000000000000815269ffffffffffffffffffff861660048201526001600160a01b03851690639a6fc8f59060240160a060405180830381865afa158015611106573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112a9190612e6c565b50945090925050505b6001600160a01b038616737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156111645761116181611690565b90505b9150935093915050565b60008061117a836108fd565b90506000611189826000610d5b565b905080156112315760006111b273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b905060006111c08383611d94565b90506001600160a01b038616737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156111fa576111f081611690565b9695505050505050565b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c5991415611228576111f081610588565b95945050505050565b600061123c83610810565b9050801561124c57949350505050565b600061125786611600565b905080156112685795945050505050565b600061127387611b8a565b90508015611285579695505050505050565b600061129088611655565b905080156112a357979650505050505050565b506000979650505050505050565b6000806000846001600160a01b0316632a9439456040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113189190613021565b6040517f544fb5c10000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015291925060009183169063544fb5c1906024016040805180830381865afa15801561137c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a09190613442565b80516020909101519097909650945050505050565b60003a64e8d4a510008111156113cd575064e8d4a510005b458511156113d9574594505b826113e4828761315e565b6113ee9190613191565b91506001600160a01b03841673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2146114cc57600061141f8561116e565b90506000611435866001600160a01b03166121b8565b9050601281111561148d5760405162461bcd60e51b815260206004820152601560248201527f546f6b656e20646563696d616c20746f6f20626967000000000000000000000060448201526064015b60405180910390fd5b81156114c45761149e816012613057565b6114a990600a613152565b6114b38584611d94565b6114bd919061317d565b93506114c9565b600093505b50505b509392505050565b6040517fec74d0a80000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152600091829185169063ec74d0a890602401608060405180830381865afa158015611538573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061155c91906134c2565b604080820151606083015191517fa7573206000000000000000000000000000000000000000000000000000000008152600481019190915260248101919091529091506001600160a01b0385169063a757320690604401602060405180830381865afa1580156115d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f4919061303e565b81516108f59190613057565b60008073b53c1a33016b2dc2ff3653530bff1848a515c8c56001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b6000806116618361070b565b9050600061168473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b90506108f58282611d94565b600061070582737f39c581f595b53c5cb19bd0b3f8da6c935e2ca06001600160a01b031663035faf826040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170c919061303e565b611d5a565b600080732f39d218133afab8f2b819b1066c7e434ad94e9e6001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e69190613021565b6001600160a01b031614611826576040517f19494c8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6001600160a01b0384161415611887576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015611881573d6000803e3d6000fd5b50505050565b61189b6001600160a01b0384168383612249565b505050565b60408051808201909152600080825260208201526000826000815181106118c9576118c96131a9565b602002602001015190506118ea3384600181518110610e8557610e856131a9565b6119075760405163dff5248f60e01b815260040160405180910390fd5b600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af115801561195d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611985919081019061324f565b806020019051810190611998919061333e565b60208101519091506000806119ad848c611e79565b90935091505084156119d2576119c582848784611fc3565b6119cf9083613057565b91505b602086018290526119ed6001600160a01b03841633846120b4565b505050505095945050505050565b6040805180820190915260008082526020820152600082600081518110611a2457611a246131a9565b60200260200101519050611a453384600181518110610e8557610e856131a9565b611a625760405163dff5248f60e01b815260040160405180910390fd5b600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af1158015611ab8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611ae0919081019061324f565b806020019051810190611af3919061333e565b8051602082015191925090600080611b0b858d611e79565b9093509150508515611b3057611b2382848884611fc3565b611b2d9083613057565b91505b818752611b466001600160a01b03851630611db7565b60208801819052611b63906001600160a01b0386169033906120b4565b8651611b7b906001600160a01b0385169033906120b4565b50505050505095945050505050565b60008061166183611711565b6000816001600160a01b0316632621db2f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfa9190613021565b90506000826001600160a01b031663765337b66040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c609190613021565b9050611c786001600160a01b03821633600019612292565b506118816001600160a01b03831633600019612292565b6000806000611c9d856108fd565b6040517fd2edb6dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015261034860248201529091506000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063d2edb6dd90604401602060405180830381865afa158015611d1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d409190613021565b9050611d4d868683611018565b9350935050509250929050565b6000670de0b6b3a7640000611d8a611d7285856123c5565b611d856002670de0b6b3a764000061317d565b6123d1565b610581919061317d565b600081611d8a611dac85670de0b6b3a76400006123c5565b611d8560028661317d565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415611def57506001600160a01b03811631610705565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa158015611e4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e72919061303e565b9050610705565b60408201516101208301515160208401516000928392839283908190611ea8906001600160a01b031630611db7565b90508860a00151600014611ee157611edb8960400151611ed68b604001518b8d600001518e60a001516123dd565b61252f565b60408a01525b6101208901516060015115611efd57611ef98961253b565b5091505b81611f1457611f0b8961273d565b508860e0015192505b6020890151600090611f2f906001600160a01b031630611db7565b90506000611f3d8383613057565b9050611f518b608001518c60400151611d5a565b811015611fa65780611f6b8c608001518d60400151611d5a565b6040517f16437be000000000000000000000000000000000000000000000000000000000815260048101929092526024820152604401611484565b60408b018051908790529498509650505050141590509250925092565b6000611fd1838560006113b5565b611fdb9082613191565b9050611fe860058661317d565b811115611ffd57611ffa60058661317d565b90505b811561201c5761200f6107d08661317d565b6120199082613191565b90505b60007339c4a92dc506300c3ea4c67ca4ca611102ee6f2a6001600160a01b031663b38779eb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612070573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120949190613021565b90506120aa6001600160a01b0386168284612292565b5050949350505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663095ea7b360e01b17905261211a8482612890565b611881576040516001600160a01b0384166024820152600060448201526121ae90859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612933565b6118818482612933565b60006001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14156121e757506012919050565b816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612225573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610705919061303e565b6040516001600160a01b03831660248201526044810182905261189b9084907fa9059cbb000000000000000000000000000000000000000000000000000000009060640161214a565b60006000198214156122ab576122a88430611db7565b91505b6001600160a01b038316158015906122cc57506001600160a01b0383163014155b80156122d757508115155b156123be576001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14612319576123146001600160a01b0385168484612249565b6123be565b6000836001600160a01b03168360405160006040518083038185875af1925050503d8060008114612366576040519150601f19603f3d011682016040523d82523d6000602084013e61236b565b606091505b50509050806123bc5760405162461bcd60e51b815260206004820152600d60248201527f4574682073656e64206661696c000000000000000000000000000000000000006044820152606401611484565b505b5092915050565b6000610581828461315e565b60006105818284613191565b6000811580159061247e57506040517f66ab66940000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201527384fe6d4aad0ca1ce3af7153eecd11729fa7a74f0906366ab669490602401602060405180830381865afa15801561245a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247e919061351b565b1561248857600091505b81612495575060006108f5565b61249f828661317d565b905060007339c4a92dc506300c3ea4c67ca4ca611102ee6f2a6001600160a01b031663b38779eb6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125199190613021565b90506120aa6001600160a01b0385168284612292565b60006105818284613057565b610120810151602001516040517f27f99a5d0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526000908190737b67d9d7993a258c4b2c31cdd9e6cbd5fb674985906327f99a5d90602401602060405180830381865afa1580156125bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e0919061351b565b6125ef57506000928392509050565b6101208301515160405163e0aa279760e01b81526001600160a01b03909116600482015273653893375dd1d942d2c429cab51641f2bf14d4269063e0aa279790602401602060405180830381865afa15801561264f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612673919061351b565b61268257506000928392509050565b61012083015151604084015184516126a5926001600160a01b0390911691612249565b610120830151516040517f16d2a88c0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116906316d2a88c906126f29086906004016135ba565b60408051808303816000875af1158015612710573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612734919061368c565b91509150915091565b60e081015160405163e0aa279760e01b81526001600160a01b03909116600482015260009073653893375dd1d942d2c429cab51641f2bf14d4269063e0aa279790602401602060405180830381865afa15801561279e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127c2919061351b565b6128095760e08201516040517f4113a62c0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401611484565b60e08201516040830151835161282a926001600160a01b0390911691612249565b8160e001516001600160a01b0316635b6f36fc8360000151846020015185604001518661010001516040518563ffffffff1660e01b815260040161287194939291906136ba565b6020604051808303816000875af1158015612225573d6000803e3d6000fd5b6000806000846001600160a01b0316846040516128ad91906136ec565b6000604051808303816000865af19150503d80600081146128ea576040519150601f19603f3d011682016040523d82523d6000602084013e6128ef565b606091505b5091509150818015612919575080511580612919575080806020019051810190612919919061351b565b80156112285750505050506001600160a01b03163b151590565b6000612988826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612a1b9092919063ffffffff16565b90508051600014806129a95750808060200190518101906129a9919061351b565b61189b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401611484565b606061057e84846000856060612a3085612b02565b612a66576040517f304619b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080866001600160a01b03168587604051612a8291906136ec565b60006040518083038185875af1925050503d8060008114612abf576040519150601f19603f3d011682016040523d82523d6000602084013e612ac4565b606091505b50915091508115612ad85791506108f59050565b805115612ae85780518082602001fd5b8360405162461bcd60e51b81526004016114849190613708565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906108f5575050151592915050565b6001600160a01b0381168114612b5057600080fd5b50565b600060208284031215612b6557600080fd5b813561058181612b3b565b600060208284031215612b8257600080fd5b5035919050565b60008060408385031215612b9c57600080fd5b8235612ba781612b3b565b946020939093013593505050565b60008060408385031215612bc857600080fd5b8235612bd381612b3b565b91506020830135612be381612b3b565b809150509250929050565b8015158114612b5057600080fd5b60008060408385031215612c0f57600080fd5b8235612c1a81612b3b565b91506020830135612be381612bee565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff81118282101715612c6457612c64612c2a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715612c9357612c93612c2a565b604052919050565b600080600080600060a08688031215612cb357600080fd5b8535612cbe81612b3b565b945060208681013594506040870135935060608701359250608087013567ffffffffffffffff80821115612cf157600080fd5b818901915089601f830112612d0557600080fd5b813581811115612d1757612d17612c2a565b8060051b9150612d28848301612c6a565b818152918301840191848101908c841115612d4257600080fd5b938501935b83851015612d6057843582529385019390850190612d47565b8096505050505050509295509295909350565b69ffffffffffffffffffff81168114612b5057600080fd5b600080600060608486031215612da057600080fd5b8335612dab81612b3b565b92506020840135612dbb81612d73565b91506040840135612dcb81612b3b565b809150509250925092565b600080600060608486031215612deb57600080fd5b833592506020840135612dfd81612b3b565b929592945050506040919091013590565b600080600060608486031215612e2357600080fd5b8335612e2e81612b3b565b92506020840135612dfd81612b3b565b60008060408385031215612e5157600080fd5b8235612e5c81612b3b565b91506020830135612be381612d73565b600080600080600060a08688031215612e8457600080fd5b8551612e8f81612d73565b809550506020860151935060408601519250606086015191506080860151612eb681612d73565b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600084136000841385830485118282161615612f1b57612f1b612ec4565b600160ff1b6000871286820588128184161615612f3a57612f3a612ec4565b60008712925087820587128484161615612f5657612f56612ec4565b87850587128184161615612f6c57612f6c612ec4565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615612fb457612fb4612ec4565b82600160ff1b038412811615612fcc57612fcc612ec4565b50500190565b634e487b7160e01b600052601260045260246000fd5b600082612ff757612ff7612fd2565b600160ff1b82146000198414161561301157613011612ec4565b500590565b80516108b581612b3b565b60006020828403121561303357600080fd5b815161058181612b3b565b60006020828403121561305057600080fd5b5051919050565b60008282101561306957613069612ec4565b500390565b600181815b808511156130a957816000190482111561308f5761308f612ec4565b8085161561309c57918102915b93841c9390800290613073565b509250929050565b6000826130c057506001610705565b816130cd57506000610705565b81600181146130e357600281146130ed57613109565b6001915050610705565b60ff8411156130fe576130fe612ec4565b50506001821b610705565b5060208310610133831016604e8410600b841016171561312c575081810a610705565b613136838361306e565b806000190482111561314a5761314a612ec4565b029392505050565b600061058183836130b1565b600081600019048311821515161561317857613178612ec4565b500290565b60008261318c5761318c612fd2565b500490565b600082198211156131a4576131a4612ec4565b500190565b634e487b7160e01b600052603260045260246000fd5b60005b838110156131da5781810151838201526020016131c2565b838111156118815750506000910152565b600082601f8301126131fc57600080fd5b815167ffffffffffffffff81111561321657613216612c2a565b613229601f8201601f1916602001612c6a565b81815284602083860101111561323e57600080fd5b6108f58260208301602087016131bf565b60006020828403121561326157600080fd5b815167ffffffffffffffff81111561327857600080fd5b6108f5848285016131eb565b600060c0828403121561329657600080fd5b60405160c0810167ffffffffffffffff82821081831117156132ba576132ba612c2a565b81604052829350845191506132ce82612b3b565b9082526020840151906132e082612b3b565b816020840152604085015191506132f682612b3b565b816040840152606085015160608401526080850151608084015260a085015191508082111561332457600080fd5b50613331858286016131eb565b60a0830152505092915050565b60006020828403121561335057600080fd5b815167ffffffffffffffff8082111561336857600080fd5b90830190610140828603121561337d57600080fd5b613385612c40565b61338e83613016565b815261339c60208401613016565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a08201526133d560c08401613016565b60c08201526133e660e08401613016565b60e082015261010080840151838111156133ff57600080fd5b61340b888287016131eb565b828401525050610120808401518381111561342557600080fd5b61343188828701613284565b918301919091525095945050505050565b60006040828403121561345457600080fd5b82601f83011261346357600080fd5b6040516040810181811067ffffffffffffffff8211171561348657613486612c2a565b806040525080604084018581111561349d57600080fd5b845b818110156134b757805183526020928301920161349f565b509195945050505050565b6000608082840312156134d457600080fd5b82601f8301126134e357600080fd5b6040516080810181811067ffffffffffffffff8211171561350657613506612c2a565b60405280608084018581111561349d57600080fd5b60006020828403121561352d57600080fd5b815161058181612bee565b600081518084526135508160208601602086016131bf565b601f01601f19169290920160200192915050565b60006001600160a01b0380835116845280602084015116602085015280604084015116604085015250606082015160608401526080820151608084015260a082015160c060a08501526108f560c0850182613538565b602081526135d46020820183516001600160a01b03169052565b600060208301516135f060408401826001600160a01b03169052565b506040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015161363360e08401826001600160a01b03169052565b5060e0830151610100613650818501836001600160a01b03169052565b808501519150506101406101208181860152613670610160860184613538565b90860151858203601f1901838701529092506111f08382613564565b6000806040838503121561369f57600080fd5b82516136aa81612bee565b6020939093015192949293505050565b60006001600160a01b038087168352808616602084015250836040830152608060608301526111f06080830184613538565b600082516136fe8184602087016131bf565b9190910192915050565b602081526000610581602083018461353856fea2646970667358221220b82eef716c16025329447027f497849dfd5b117acf4e27bdd79bfbc77887f7e964736f6c634300080a0033

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
[ Download: CSV Export  ]

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.