More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x60806040 | 19666142 | 151 days ago | IN | 0 ETH | 0.02835468 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
LlamaLendSwapper
Compiler Version
v0.8.10+commit.fc410830
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
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
6080604052600080546001600160a01b03199081167333fdb79afb4456b604f376a45a546e7ae700e88017909155600180549091167376720ac2574631530ec8163e4085d6f98513fb2717905534801561005857600080fd5b50613751806100686000396000f3fe608060405234801561001057600080fd5b50600436106101f05760003560e01c806381650aad1161010f578063c2dfea78116100a2578063ef67dc7411610071578063ef67dc7414610474578063f3be054b14610487578063fa09e6301461049a578063fc0626a8146104ad57600080fd5b8063c2dfea781461042f578063c579d49014610442578063e62214fe14610455578063e7940a6a1461046857600080fd5b806390238c39116100de57806390238c3914610387578063935b5233146103f6578063b548036014610409578063b97830c61461041c57600080fd5b806381650aad146103a257806383c21367146103b55780638cedca71146103c85780638da41b0f146103e357600080fd5b80633b6bc3a6116101875780634ea696bb116101565780634ea696bb1461031e57806353a6e47c1461034c57806353c92f46146103745780637d9f77121461038757600080fd5b80633b6bc3a6146102dd57806341c0e1b5146102e657806346904840146102f057806349a691571461030b57600080fd5b806325ad0b1e116101c357806325ad0b1e1461026457806328773b1e1461027757806329dae02b1461028a5780633a357a27146102b557600080fd5b806302266147146101f55780630527599f1461021b5780631982e00d1461022e5780632452878614610251575b600080fd5b610208610203366004612b53565b6104c0565b6040519081526020015b60405180910390f35b610208610229366004612b70565b610588565b61024161023c366004612b89565b61065d565b6040519015158152602001610212565b61020861025f366004612b53565b61070b565b610208610272366004612b53565b610810565b610208610285366004612b53565b6108ba565b61029d610298366004612b53565b6108fd565b6040516001600160a01b039091168152602001610212565b6102c86102c3366004612bb5565b6109c3565b60408051928352901515602083015201610212565b6102086107d081565b6102ee610c98565b005b61029d7339c4a92dc506300c3ea4c67ca4ca611102ee6f2a81565b610208610319366004612bfc565b610d5b565b61033161032c366004612c9b565b610e3b565b60408051825181526020928301519281019290925201610212565b61035f61035a366004612d8b565b611018565b60408051928352602083019190915201610212565b610208610382366004612b53565b61116e565b61029d7347fb2585d2c56fe188d0e6ec628a38b74fceeedf81565b61035f6103b0366004612bb5565b6112b1565b6102086103c3366004612dd6565b6113b5565b61029d73ccf3d848e08b94478ed8f46ffead3008faf581fd81565b6102086103f1366004612bb5565b6114d4565b610208610404366004612b53565b611600565b610208610417366004612b53565b611655565b61020861042a366004612b70565b611690565b61020861043d366004612b53565b611711565b6102ee610450366004612e0e565b611766565b610331610463366004612c9b565b6118a0565b61020864e8d4a5100081565b610331610482366004612c9b565b6119fb565b610208610495366004612b53565b611b8a565b6102ee6104a8366004612b53565b611b96565b61035f6104bb366004612e3e565b611c8f565b6000806104cc836108fd565b905060006104db826001610d5b565b9050806104ee576104eb846108ba565b90505b806104ff576104fc84611711565b90505b806105105761050d8461070b565b90505b8061051f575060009392505050565b6001600160a01b038416737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156105505761054d81611690565b90505b6001600160a01b038416732260fac5e5542a773aa44fbcfedf7c193bc2c59914156105815761057e81610588565b90505b9392505050565b60405163bcfd032d60e01b8152732260fac5e5542a773aa44fbcfedf7c193bc2c599600482015273bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb602482015260009081907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa15801561060a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061062e9190612e6c565b5050509150506305f5e10081846106459190612eda565b610653906302faf080612f7a565b6105819190612fe8565b6040517fe94b0dd2000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b0384169073ea6876dde9e3467564acbee1ed5bac88783205e09063e94b0dd290602401602060405180830381865afa1580156106d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f89190613021565b6001600160a01b03161490505b92915050565b6000807302c3ea4e34c0cbd694d2adfa2c690eecbc1793ee6001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107849190613021565b6040517fb3596f070000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301529192509082169063b3596f0790602401602060405180830381865afa925050508015610803575060408051601f3d908101601f191682019092526108009181019061303e565b60015b6105815750600092915050565b60405163bcfd032d60e01b81526001600160a01b038216600482015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248201526000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa9250505080156108a1575060408051601f3d908101601f1916820190925261089e91810190612e6c565b60015b6108ad57506000919050565b509193505050505b919050565b6000806108c683611600565b905060006108e973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b90506108f58282611d5a565b949350505050565b60006001600160a01b03821673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2141561093f575073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee919050565b6001600160a01b038216737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0141561097f575073ae7ab96520de3a18e5e111b5eaab095312d7fe84919050565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c59914156109bf575073bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb919050565b5090565b6040517f9b6c56ec0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301526000918291829190851690639b6c56ec90602401602060405180830381865afa158015610a2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4e919061303e565b905080610a62576000809250925050610c91565b600080610a6f86886112b1565b90925090508115610a7f57600193505b6000866001600160a01b0316632a9439456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610abf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae39190613021565b90506000816001600160a01b03166386fc88d36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b49919061303e565b90506000886001600160a01b0316632621db2f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610baf9190613021565b90506000816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bf1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c15919061303e565b9050600060128211610c4657610c2c826012613057565b610c3790600a613152565b610c41908761315e565b610c66565b610c51601283613057565b610c5c90600a613152565b610c66908761317d565b9050610c8687610c768387611d5a565b610c809190613191565b89611d94565b995050505050505050505b9250929050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d189190613021565b6001600160a01b031614610d58576040517fa6c827a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33ff5b60405163bcfd032d60e01b81526001600160a01b038316600482015261034860248201526000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa925050508015610dda575060408051601f3d908101601f19168201909252610dd791810190612e6c565b60015b610e2f578115610e27576000610def84610810565b90506000610e1273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b9050610e1e8282611d5a565b92505050610705565b506000610705565b50919695505050505050565b6040805180820190915260008082526020820152600082600081518110610e6457610e646131a9565b60200260200101519050610e923384600181518110610e8557610e856131a9565b602002602001015161065d565b610eaf5760405163dff5248f60e01b815260040160405180910390fd5b60008084600281518110610ec557610ec56131a9565b6020026020010151119050600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af1158015610f26573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f4e919081019061324f565b806020019051810190610f61919061333e565b80516020820151919250908315610f8b57610f856001600160a01b03831630611db7565b60408401525b600080610f98858e611e79565b9093509150508615610fbd57610fb082848984611fc3565b610fba9083613057565b91505b818852610fd36001600160a01b03851630611db7565b60208901819052610ff0906001600160a01b0386169033906120b4565b8751611008906001600160a01b0385169033906120b4565b5050505050505095945050505050565b6000808069ffffffffffffffffffff851661109d57836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801561106b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108f9190612e6c565b509450909250611133915050565b6040517f9a6fc8f500000000000000000000000000000000000000000000000000000000815269ffffffffffffffffffff861660048201526001600160a01b03851690639a6fc8f59060240160a060405180830381865afa158015611106573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112a9190612e6c565b50945090925050505b6001600160a01b038616737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156111645761116181611690565b90505b9150935093915050565b60008061117a836108fd565b90506000611189826000610d5b565b905080156112315760006111b273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b905060006111c08383611d94565b90506001600160a01b038616737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156111fa576111f081611690565b9695505050505050565b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c5991415611228576111f081610588565b95945050505050565b600061123c83610810565b9050801561124c57949350505050565b600061125786611600565b905080156112685795945050505050565b600061127387611b8a565b90508015611285579695505050505050565b600061129088611655565b905080156112a357979650505050505050565b506000979650505050505050565b6000806000846001600160a01b0316632a9439456040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113189190613021565b6040517f544fb5c10000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015291925060009183169063544fb5c1906024016040805180830381865afa15801561137c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a09190613442565b80516020909101519097909650945050505050565b60003a64e8d4a510008111156113cd575064e8d4a510005b458511156113d9574594505b826113e4828761315e565b6113ee9190613191565b91506001600160a01b03841673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2146114cc57600061141f8561116e565b90506000611435866001600160a01b03166121b8565b9050601281111561148d5760405162461bcd60e51b815260206004820152601560248201527f546f6b656e20646563696d616c20746f6f20626967000000000000000000000060448201526064015b60405180910390fd5b81156114c45761149e816012613057565b6114a990600a613152565b6114b38584611d94565b6114bd919061317d565b93506114c9565b600093505b50505b509392505050565b6040517fec74d0a80000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152600091829185169063ec74d0a890602401608060405180830381865afa158015611538573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061155c91906134c2565b604080820151606083015191517fa7573206000000000000000000000000000000000000000000000000000000008152600481019190915260248101919091529091506001600160a01b0385169063a757320690604401602060405180830381865afa1580156115d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f4919061303e565b81516108f59190613057565b60008073b53c1a33016b2dc2ff3653530bff1848a515c8c56001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b6000806116618361070b565b9050600061168473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b90506108f58282611d94565b600061070582737f39c581f595b53c5cb19bd0b3f8da6c935e2ca06001600160a01b031663035faf826040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170c919061303e565b611d5a565b600080732f39d218133afab8f2b819b1066c7e434ad94e9e6001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e69190613021565b6001600160a01b031614611826576040517f19494c8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6001600160a01b0384161415611887576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015611881573d6000803e3d6000fd5b50505050565b61189b6001600160a01b0384168383612249565b505050565b60408051808201909152600080825260208201526000826000815181106118c9576118c96131a9565b602002602001015190506118ea3384600181518110610e8557610e856131a9565b6119075760405163dff5248f60e01b815260040160405180910390fd5b600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af115801561195d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611985919081019061324f565b806020019051810190611998919061333e565b60208101519091506000806119ad848c611e79565b90935091505084156119d2576119c582848784611fc3565b6119cf9083613057565b91505b602086018290526119ed6001600160a01b03841633846120b4565b505050505095945050505050565b6040805180820190915260008082526020820152600082600081518110611a2457611a246131a9565b60200260200101519050611a453384600181518110610e8557610e856131a9565b611a625760405163dff5248f60e01b815260040160405180910390fd5b600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af1158015611ab8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611ae0919081019061324f565b806020019051810190611af3919061333e565b8051602082015191925090600080611b0b858d611e79565b9093509150508515611b3057611b2382848884611fc3565b611b2d9083613057565b91505b818752611b466001600160a01b03851630611db7565b60208801819052611b63906001600160a01b0386169033906120b4565b8651611b7b906001600160a01b0385169033906120b4565b50505050505095945050505050565b60008061166183611711565b6000816001600160a01b0316632621db2f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfa9190613021565b90506000826001600160a01b031663765337b66040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c609190613021565b9050611c786001600160a01b03821633600019612292565b506118816001600160a01b03831633600019612292565b6000806000611c9d856108fd565b6040517fd2edb6dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015261034860248201529091506000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063d2edb6dd90604401602060405180830381865afa158015611d1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d409190613021565b9050611d4d868683611018565b9350935050509250929050565b6000670de0b6b3a7640000611d8a611d7285856123c5565b611d856002670de0b6b3a764000061317d565b6123d1565b610581919061317d565b600081611d8a611dac85670de0b6b3a76400006123c5565b611d8560028661317d565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415611def57506001600160a01b03811631610705565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa158015611e4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e72919061303e565b9050610705565b60408201516101208301515160208401516000928392839283908190611ea8906001600160a01b031630611db7565b90508860a00151600014611ee157611edb8960400151611ed68b604001518b8d600001518e60a001516123dd565b61252f565b60408a01525b6101208901516060015115611efd57611ef98961253b565b5091505b81611f1457611f0b8961273d565b508860e0015192505b6020890151600090611f2f906001600160a01b031630611db7565b90506000611f3d8383613057565b9050611f518b608001518c60400151611d5a565b811015611fa65780611f6b8c608001518d60400151611d5a565b6040517f16437be000000000000000000000000000000000000000000000000000000000815260048101929092526024820152604401611484565b60408b018051908790529498509650505050141590509250925092565b6000611fd1838560006113b5565b611fdb9082613191565b9050611fe860058661317d565b811115611ffd57611ffa60058661317d565b90505b811561201c5761200f6107d08661317d565b6120199082613191565b90505b60007339c4a92dc506300c3ea4c67ca4ca611102ee6f2a6001600160a01b031663b38779eb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612070573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120949190613021565b90506120aa6001600160a01b0386168284612292565b5050949350505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663095ea7b360e01b17905261211a8482612890565b611881576040516001600160a01b0384166024820152600060448201526121ae90859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612933565b6118818482612933565b60006001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14156121e757506012919050565b816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612225573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610705919061303e565b6040516001600160a01b03831660248201526044810182905261189b9084907fa9059cbb000000000000000000000000000000000000000000000000000000009060640161214a565b60006000198214156122ab576122a88430611db7565b91505b6001600160a01b038316158015906122cc57506001600160a01b0383163014155b80156122d757508115155b156123be576001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14612319576123146001600160a01b0385168484612249565b6123be565b6000836001600160a01b03168360405160006040518083038185875af1925050503d8060008114612366576040519150601f19603f3d011682016040523d82523d6000602084013e61236b565b606091505b50509050806123bc5760405162461bcd60e51b815260206004820152600d60248201527f4574682073656e64206661696c000000000000000000000000000000000000006044820152606401611484565b505b5092915050565b6000610581828461315e565b60006105818284613191565b6000811580159061247e57506040517f66ab66940000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201527384fe6d4aad0ca1ce3af7153eecd11729fa7a74f0906366ab669490602401602060405180830381865afa15801561245a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247e919061351b565b1561248857600091505b81612495575060006108f5565b61249f828661317d565b905060007339c4a92dc506300c3ea4c67ca4ca611102ee6f2a6001600160a01b031663b38779eb6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125199190613021565b90506120aa6001600160a01b0385168284612292565b60006105818284613057565b610120810151602001516040517f27f99a5d0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526000908190737b67d9d7993a258c4b2c31cdd9e6cbd5fb674985906327f99a5d90602401602060405180830381865afa1580156125bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e0919061351b565b6125ef57506000928392509050565b6101208301515160405163e0aa279760e01b81526001600160a01b03909116600482015273653893375dd1d942d2c429cab51641f2bf14d4269063e0aa279790602401602060405180830381865afa15801561264f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612673919061351b565b61268257506000928392509050565b61012083015151604084015184516126a5926001600160a01b0390911691612249565b610120830151516040517f16d2a88c0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116906316d2a88c906126f29086906004016135ba565b60408051808303816000875af1158015612710573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612734919061368c565b91509150915091565b60e081015160405163e0aa279760e01b81526001600160a01b03909116600482015260009073653893375dd1d942d2c429cab51641f2bf14d4269063e0aa279790602401602060405180830381865afa15801561279e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127c2919061351b565b6128095760e08201516040517f4113a62c0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401611484565b60e08201516040830151835161282a926001600160a01b0390911691612249565b8160e001516001600160a01b0316635b6f36fc8360000151846020015185604001518661010001516040518563ffffffff1660e01b815260040161287194939291906136ba565b6020604051808303816000875af1158015612225573d6000803e3d6000fd5b6000806000846001600160a01b0316846040516128ad91906136ec565b6000604051808303816000865af19150503d80600081146128ea576040519150601f19603f3d011682016040523d82523d6000602084013e6128ef565b606091505b5091509150818015612919575080511580612919575080806020019051810190612919919061351b565b80156112285750505050506001600160a01b03163b151590565b6000612988826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612a1b9092919063ffffffff16565b90508051600014806129a95750808060200190518101906129a9919061351b565b61189b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401611484565b606061057e84846000856060612a3085612b02565b612a66576040517f304619b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080866001600160a01b03168587604051612a8291906136ec565b60006040518083038185875af1925050503d8060008114612abf576040519150601f19603f3d011682016040523d82523d6000602084013e612ac4565b606091505b50915091508115612ad85791506108f59050565b805115612ae85780518082602001fd5b8360405162461bcd60e51b81526004016114849190613708565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906108f5575050151592915050565b6001600160a01b0381168114612b5057600080fd5b50565b600060208284031215612b6557600080fd5b813561058181612b3b565b600060208284031215612b8257600080fd5b5035919050565b60008060408385031215612b9c57600080fd5b8235612ba781612b3b565b946020939093013593505050565b60008060408385031215612bc857600080fd5b8235612bd381612b3b565b91506020830135612be381612b3b565b809150509250929050565b8015158114612b5057600080fd5b60008060408385031215612c0f57600080fd5b8235612c1a81612b3b565b91506020830135612be381612bee565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff81118282101715612c6457612c64612c2a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715612c9357612c93612c2a565b604052919050565b600080600080600060a08688031215612cb357600080fd5b8535612cbe81612b3b565b945060208681013594506040870135935060608701359250608087013567ffffffffffffffff80821115612cf157600080fd5b818901915089601f830112612d0557600080fd5b813581811115612d1757612d17612c2a565b8060051b9150612d28848301612c6a565b818152918301840191848101908c841115612d4257600080fd5b938501935b83851015612d6057843582529385019390850190612d47565b8096505050505050509295509295909350565b69ffffffffffffffffffff81168114612b5057600080fd5b600080600060608486031215612da057600080fd5b8335612dab81612b3b565b92506020840135612dbb81612d73565b91506040840135612dcb81612b3b565b809150509250925092565b600080600060608486031215612deb57600080fd5b833592506020840135612dfd81612b3b565b929592945050506040919091013590565b600080600060608486031215612e2357600080fd5b8335612e2e81612b3b565b92506020840135612dfd81612b3b565b60008060408385031215612e5157600080fd5b8235612e5c81612b3b565b91506020830135612be381612d73565b600080600080600060a08688031215612e8457600080fd5b8551612e8f81612d73565b809550506020860151935060408601519250606086015191506080860151612eb681612d73565b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600084136000841385830485118282161615612f1b57612f1b612ec4565b600160ff1b6000871286820588128184161615612f3a57612f3a612ec4565b60008712925087820587128484161615612f5657612f56612ec4565b87850587128184161615612f6c57612f6c612ec4565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615612fb457612fb4612ec4565b82600160ff1b038412811615612fcc57612fcc612ec4565b50500190565b634e487b7160e01b600052601260045260246000fd5b600082612ff757612ff7612fd2565b600160ff1b82146000198414161561301157613011612ec4565b500590565b80516108b581612b3b565b60006020828403121561303357600080fd5b815161058181612b3b565b60006020828403121561305057600080fd5b5051919050565b60008282101561306957613069612ec4565b500390565b600181815b808511156130a957816000190482111561308f5761308f612ec4565b8085161561309c57918102915b93841c9390800290613073565b509250929050565b6000826130c057506001610705565b816130cd57506000610705565b81600181146130e357600281146130ed57613109565b6001915050610705565b60ff8411156130fe576130fe612ec4565b50506001821b610705565b5060208310610133831016604e8410600b841016171561312c575081810a610705565b613136838361306e565b806000190482111561314a5761314a612ec4565b029392505050565b600061058183836130b1565b600081600019048311821515161561317857613178612ec4565b500290565b60008261318c5761318c612fd2565b500490565b600082198211156131a4576131a4612ec4565b500190565b634e487b7160e01b600052603260045260246000fd5b60005b838110156131da5781810151838201526020016131c2565b838111156118815750506000910152565b600082601f8301126131fc57600080fd5b815167ffffffffffffffff81111561321657613216612c2a565b613229601f8201601f1916602001612c6a565b81815284602083860101111561323e57600080fd5b6108f58260208301602087016131bf565b60006020828403121561326157600080fd5b815167ffffffffffffffff81111561327857600080fd5b6108f5848285016131eb565b600060c0828403121561329657600080fd5b60405160c0810167ffffffffffffffff82821081831117156132ba576132ba612c2a565b81604052829350845191506132ce82612b3b565b9082526020840151906132e082612b3b565b816020840152604085015191506132f682612b3b565b816040840152606085015160608401526080850151608084015260a085015191508082111561332457600080fd5b50613331858286016131eb565b60a0830152505092915050565b60006020828403121561335057600080fd5b815167ffffffffffffffff8082111561336857600080fd5b90830190610140828603121561337d57600080fd5b613385612c40565b61338e83613016565b815261339c60208401613016565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a08201526133d560c08401613016565b60c08201526133e660e08401613016565b60e082015261010080840151838111156133ff57600080fd5b61340b888287016131eb565b828401525050610120808401518381111561342557600080fd5b61343188828701613284565b918301919091525095945050505050565b60006040828403121561345457600080fd5b82601f83011261346357600080fd5b6040516040810181811067ffffffffffffffff8211171561348657613486612c2a565b806040525080604084018581111561349d57600080fd5b845b818110156134b757805183526020928301920161349f565b509195945050505050565b6000608082840312156134d457600080fd5b82601f8301126134e357600080fd5b6040516080810181811067ffffffffffffffff8211171561350657613506612c2a565b60405280608084018581111561349d57600080fd5b60006020828403121561352d57600080fd5b815161058181612bee565b600081518084526135508160208601602086016131bf565b601f01601f19169290920160200192915050565b60006001600160a01b0380835116845280602084015116602085015280604084015116604085015250606082015160608401526080820151608084015260a082015160c060a08501526108f560c0850182613538565b602081526135d46020820183516001600160a01b03169052565b600060208301516135f060408401826001600160a01b03169052565b506040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015161363360e08401826001600160a01b03169052565b5060e0830151610100613650818501836001600160a01b03169052565b808501519150506101406101208181860152613670610160860184613538565b90860151858203601f1901838701529092506111f08382613564565b6000806040838503121561369f57600080fd5b82516136aa81612bee565b6020939093015192949293505050565b60006001600160a01b038087168352808616602084015250836040830152608060608301526111f06080830184613538565b600082516136fe8184602087016131bf565b9190910192915050565b602081526000610581602083018461353856fea2646970667358221220b82eef716c16025329447027f497849dfd5b117acf4e27bdd79bfbc77887f7e964736f6c634300080a0033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101f05760003560e01c806381650aad1161010f578063c2dfea78116100a2578063ef67dc7411610071578063ef67dc7414610474578063f3be054b14610487578063fa09e6301461049a578063fc0626a8146104ad57600080fd5b8063c2dfea781461042f578063c579d49014610442578063e62214fe14610455578063e7940a6a1461046857600080fd5b806390238c39116100de57806390238c3914610387578063935b5233146103f6578063b548036014610409578063b97830c61461041c57600080fd5b806381650aad146103a257806383c21367146103b55780638cedca71146103c85780638da41b0f146103e357600080fd5b80633b6bc3a6116101875780634ea696bb116101565780634ea696bb1461031e57806353a6e47c1461034c57806353c92f46146103745780637d9f77121461038757600080fd5b80633b6bc3a6146102dd57806341c0e1b5146102e657806346904840146102f057806349a691571461030b57600080fd5b806325ad0b1e116101c357806325ad0b1e1461026457806328773b1e1461027757806329dae02b1461028a5780633a357a27146102b557600080fd5b806302266147146101f55780630527599f1461021b5780631982e00d1461022e5780632452878614610251575b600080fd5b610208610203366004612b53565b6104c0565b6040519081526020015b60405180910390f35b610208610229366004612b70565b610588565b61024161023c366004612b89565b61065d565b6040519015158152602001610212565b61020861025f366004612b53565b61070b565b610208610272366004612b53565b610810565b610208610285366004612b53565b6108ba565b61029d610298366004612b53565b6108fd565b6040516001600160a01b039091168152602001610212565b6102c86102c3366004612bb5565b6109c3565b60408051928352901515602083015201610212565b6102086107d081565b6102ee610c98565b005b61029d7339c4a92dc506300c3ea4c67ca4ca611102ee6f2a81565b610208610319366004612bfc565b610d5b565b61033161032c366004612c9b565b610e3b565b60408051825181526020928301519281019290925201610212565b61035f61035a366004612d8b565b611018565b60408051928352602083019190915201610212565b610208610382366004612b53565b61116e565b61029d7347fb2585d2c56fe188d0e6ec628a38b74fceeedf81565b61035f6103b0366004612bb5565b6112b1565b6102086103c3366004612dd6565b6113b5565b61029d73ccf3d848e08b94478ed8f46ffead3008faf581fd81565b6102086103f1366004612bb5565b6114d4565b610208610404366004612b53565b611600565b610208610417366004612b53565b611655565b61020861042a366004612b70565b611690565b61020861043d366004612b53565b611711565b6102ee610450366004612e0e565b611766565b610331610463366004612c9b565b6118a0565b61020864e8d4a5100081565b610331610482366004612c9b565b6119fb565b610208610495366004612b53565b611b8a565b6102ee6104a8366004612b53565b611b96565b61035f6104bb366004612e3e565b611c8f565b6000806104cc836108fd565b905060006104db826001610d5b565b9050806104ee576104eb846108ba565b90505b806104ff576104fc84611711565b90505b806105105761050d8461070b565b90505b8061051f575060009392505050565b6001600160a01b038416737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156105505761054d81611690565b90505b6001600160a01b038416732260fac5e5542a773aa44fbcfedf7c193bc2c59914156105815761057e81610588565b90505b9392505050565b60405163bcfd032d60e01b8152732260fac5e5542a773aa44fbcfedf7c193bc2c599600482015273bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb602482015260009081907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa15801561060a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061062e9190612e6c565b5050509150506305f5e10081846106459190612eda565b610653906302faf080612f7a565b6105819190612fe8565b6040517fe94b0dd2000000000000000000000000000000000000000000000000000000008152600481018290526000906001600160a01b0384169073ea6876dde9e3467564acbee1ed5bac88783205e09063e94b0dd290602401602060405180830381865afa1580156106d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f89190613021565b6001600160a01b03161490505b92915050565b6000807302c3ea4e34c0cbd694d2adfa2c690eecbc1793ee6001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107849190613021565b6040517fb3596f070000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301529192509082169063b3596f0790602401602060405180830381865afa925050508015610803575060408051601f3d908101601f191682019092526108009181019061303e565b60015b6105815750600092915050565b60405163bcfd032d60e01b81526001600160a01b038216600482015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248201526000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa9250505080156108a1575060408051601f3d908101601f1916820190925261089e91810190612e6c565b60015b6108ad57506000919050565b509193505050505b919050565b6000806108c683611600565b905060006108e973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b90506108f58282611d5a565b949350505050565b60006001600160a01b03821673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2141561093f575073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee919050565b6001600160a01b038216737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0141561097f575073ae7ab96520de3a18e5e111b5eaab095312d7fe84919050565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c59914156109bf575073bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb919050565b5090565b6040517f9b6c56ec0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301526000918291829190851690639b6c56ec90602401602060405180830381865afa158015610a2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4e919061303e565b905080610a62576000809250925050610c91565b600080610a6f86886112b1565b90925090508115610a7f57600193505b6000866001600160a01b0316632a9439456040518163ffffffff1660e01b8152600401602060405180830381865afa158015610abf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae39190613021565b90506000816001600160a01b03166386fc88d36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b49919061303e565b90506000886001600160a01b0316632621db2f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610baf9190613021565b90506000816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bf1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c15919061303e565b9050600060128211610c4657610c2c826012613057565b610c3790600a613152565b610c41908761315e565b610c66565b610c51601283613057565b610c5c90600a613152565b610c66908761317d565b9050610c8687610c768387611d5a565b610c809190613191565b89611d94565b995050505050505050505b9250929050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d189190613021565b6001600160a01b031614610d58576040517fa6c827a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33ff5b60405163bcfd032d60e01b81526001600160a01b038316600482015261034860248201526000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063bcfd032d9060440160a060405180830381865afa925050508015610dda575060408051601f3d908101601f19168201909252610dd791810190612e6c565b60015b610e2f578115610e27576000610def84610810565b90506000610e1273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b9050610e1e8282611d5a565b92505050610705565b506000610705565b50919695505050505050565b6040805180820190915260008082526020820152600082600081518110610e6457610e646131a9565b60200260200101519050610e923384600181518110610e8557610e856131a9565b602002602001015161065d565b610eaf5760405163dff5248f60e01b815260040160405180910390fd5b60008084600281518110610ec557610ec56131a9565b6020026020010151119050600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af1158015610f26573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f4e919081019061324f565b806020019051810190610f61919061333e565b80516020820151919250908315610f8b57610f856001600160a01b03831630611db7565b60408401525b600080610f98858e611e79565b9093509150508615610fbd57610fb082848984611fc3565b610fba9083613057565b91505b818852610fd36001600160a01b03851630611db7565b60208901819052610ff0906001600160a01b0386169033906120b4565b8751611008906001600160a01b0385169033906120b4565b5050505050505095945050505050565b6000808069ffffffffffffffffffff851661109d57836001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801561106b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108f9190612e6c565b509450909250611133915050565b6040517f9a6fc8f500000000000000000000000000000000000000000000000000000000815269ffffffffffffffffffff861660048201526001600160a01b03851690639a6fc8f59060240160a060405180830381865afa158015611106573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112a9190612e6c565b50945090925050505b6001600160a01b038616737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156111645761116181611690565b90505b9150935093915050565b60008061117a836108fd565b90506000611189826000610d5b565b905080156112315760006111b273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b905060006111c08383611d94565b90506001600160a01b038616737f39c581f595b53c5cb19bd0b3f8da6c935e2ca014156111fa576111f081611690565b9695505050505050565b6001600160a01b038616732260fac5e5542a773aa44fbcfedf7c193bc2c5991415611228576111f081610588565b95945050505050565b600061123c83610810565b9050801561124c57949350505050565b600061125786611600565b905080156112685795945050505050565b600061127387611b8a565b90508015611285579695505050505050565b600061129088611655565b905080156112a357979650505050505050565b506000979650505050505050565b6000806000846001600160a01b0316632a9439456040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113189190613021565b6040517f544fb5c10000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015291925060009183169063544fb5c1906024016040805180830381865afa15801561137c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a09190613442565b80516020909101519097909650945050505050565b60003a64e8d4a510008111156113cd575064e8d4a510005b458511156113d9574594505b826113e4828761315e565b6113ee9190613191565b91506001600160a01b03841673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2146114cc57600061141f8561116e565b90506000611435866001600160a01b03166121b8565b9050601281111561148d5760405162461bcd60e51b815260206004820152601560248201527f546f6b656e20646563696d616c20746f6f20626967000000000000000000000060448201526064015b60405180910390fd5b81156114c45761149e816012613057565b6114a990600a613152565b6114b38584611d94565b6114bd919061317d565b93506114c9565b600093505b50505b509392505050565b6040517fec74d0a80000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152600091829185169063ec74d0a890602401608060405180830381865afa158015611538573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061155c91906134c2565b604080820151606083015191517fa7573206000000000000000000000000000000000000000000000000000000008152600481019190915260248101919091529091506001600160a01b0385169063a757320690604401602060405180830381865afa1580156115d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f4919061303e565b81516108f59190613057565b60008073b53c1a33016b2dc2ff3653530bff1848a515c8c56001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b6000806116618361070b565b9050600061168473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6000610d5b565b90506108f58282611d94565b600061070582737f39c581f595b53c5cb19bd0b3f8da6c935e2ca06001600160a01b031663035faf826040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170c919061303e565b611d5a565b600080732f39d218133afab8f2b819b1066c7e434ad94e9e6001600160a01b031663fca513a86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610760573d6000803e3d6000fd5b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e69190613021565b6001600160a01b031614611826576040517f19494c8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6001600160a01b0384161415611887576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015611881573d6000803e3d6000fd5b50505050565b61189b6001600160a01b0384168383612249565b505050565b60408051808201909152600080825260208201526000826000815181106118c9576118c96131a9565b602002602001015190506118ea3384600181518110610e8557610e856131a9565b6119075760405163dff5248f60e01b815260040160405180910390fd5b600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af115801561195d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611985919081019061324f565b806020019051810190611998919061333e565b60208101519091506000806119ad848c611e79565b90935091505084156119d2576119c582848784611fc3565b6119cf9083613057565b91505b602086018290526119ed6001600160a01b03841633846120b4565b505050505095945050505050565b6040805180820190915260008082526020820152600082600081518110611a2457611a246131a9565b60200260200101519050611a453384600181518110610e8557610e856131a9565b611a625760405163dff5248f60e01b815260040160405180910390fd5b600073b3fe6f712c8b8c64cd2780ce714a36e7640ddf0f6001600160a01b031663b0ade5d26040518163ffffffff1660e01b81526004016000604051808303816000875af1158015611ab8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611ae0919081019061324f565b806020019051810190611af3919061333e565b8051602082015191925090600080611b0b858d611e79565b9093509150508515611b3057611b2382848884611fc3565b611b2d9083613057565b91505b818752611b466001600160a01b03851630611db7565b60208801819052611b63906001600160a01b0386169033906120b4565b8651611b7b906001600160a01b0385169033906120b4565b50505050505095945050505050565b60008061166183611711565b6000816001600160a01b0316632621db2f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfa9190613021565b90506000826001600160a01b031663765337b66040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c609190613021565b9050611c786001600160a01b03821633600019612292565b506118816001600160a01b03831633600019612292565b6000806000611c9d856108fd565b6040517fd2edb6dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015261034860248201529091506000907347fb2585d2c56fe188d0e6ec628a38b74fceeedf9063d2edb6dd90604401602060405180830381865afa158015611d1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d409190613021565b9050611d4d868683611018565b9350935050509250929050565b6000670de0b6b3a7640000611d8a611d7285856123c5565b611d856002670de0b6b3a764000061317d565b6123d1565b610581919061317d565b600081611d8a611dac85670de0b6b3a76400006123c5565b611d8560028661317d565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415611def57506001600160a01b03811631610705565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa158015611e4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e72919061303e565b9050610705565b60408201516101208301515160208401516000928392839283908190611ea8906001600160a01b031630611db7565b90508860a00151600014611ee157611edb8960400151611ed68b604001518b8d600001518e60a001516123dd565b61252f565b60408a01525b6101208901516060015115611efd57611ef98961253b565b5091505b81611f1457611f0b8961273d565b508860e0015192505b6020890151600090611f2f906001600160a01b031630611db7565b90506000611f3d8383613057565b9050611f518b608001518c60400151611d5a565b811015611fa65780611f6b8c608001518d60400151611d5a565b6040517f16437be000000000000000000000000000000000000000000000000000000000815260048101929092526024820152604401611484565b60408b018051908790529498509650505050141590509250925092565b6000611fd1838560006113b5565b611fdb9082613191565b9050611fe860058661317d565b811115611ffd57611ffa60058661317d565b90505b811561201c5761200f6107d08661317d565b6120199082613191565b90505b60007339c4a92dc506300c3ea4c67ca4ca611102ee6f2a6001600160a01b031663b38779eb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612070573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120949190613021565b90506120aa6001600160a01b0386168284612292565b5050949350505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1663095ea7b360e01b17905261211a8482612890565b611881576040516001600160a01b0384166024820152600060448201526121ae90859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612933565b6118818482612933565b60006001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14156121e757506012919050565b816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612225573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610705919061303e565b6040516001600160a01b03831660248201526044810182905261189b9084907fa9059cbb000000000000000000000000000000000000000000000000000000009060640161214a565b60006000198214156122ab576122a88430611db7565b91505b6001600160a01b038316158015906122cc57506001600160a01b0383163014155b80156122d757508115155b156123be576001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14612319576123146001600160a01b0385168484612249565b6123be565b6000836001600160a01b03168360405160006040518083038185875af1925050503d8060008114612366576040519150601f19603f3d011682016040523d82523d6000602084013e61236b565b606091505b50509050806123bc5760405162461bcd60e51b815260206004820152600d60248201527f4574682073656e64206661696c000000000000000000000000000000000000006044820152606401611484565b505b5092915050565b6000610581828461315e565b60006105818284613191565b6000811580159061247e57506040517f66ab66940000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201527384fe6d4aad0ca1ce3af7153eecd11729fa7a74f0906366ab669490602401602060405180830381865afa15801561245a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247e919061351b565b1561248857600091505b81612495575060006108f5565b61249f828661317d565b905060007339c4a92dc506300c3ea4c67ca4ca611102ee6f2a6001600160a01b031663b38779eb6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125199190613021565b90506120aa6001600160a01b0385168284612292565b60006105818284613057565b610120810151602001516040517f27f99a5d0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911660048201526000908190737b67d9d7993a258c4b2c31cdd9e6cbd5fb674985906327f99a5d90602401602060405180830381865afa1580156125bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e0919061351b565b6125ef57506000928392509050565b6101208301515160405163e0aa279760e01b81526001600160a01b03909116600482015273653893375dd1d942d2c429cab51641f2bf14d4269063e0aa279790602401602060405180830381865afa15801561264f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612673919061351b565b61268257506000928392509050565b61012083015151604084015184516126a5926001600160a01b0390911691612249565b610120830151516040517f16d2a88c0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116906316d2a88c906126f29086906004016135ba565b60408051808303816000875af1158015612710573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612734919061368c565b91509150915091565b60e081015160405163e0aa279760e01b81526001600160a01b03909116600482015260009073653893375dd1d942d2c429cab51641f2bf14d4269063e0aa279790602401602060405180830381865afa15801561279e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127c2919061351b565b6128095760e08201516040517f4113a62c0000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401611484565b60e08201516040830151835161282a926001600160a01b0390911691612249565b8160e001516001600160a01b0316635b6f36fc8360000151846020015185604001518661010001516040518563ffffffff1660e01b815260040161287194939291906136ba565b6020604051808303816000875af1158015612225573d6000803e3d6000fd5b6000806000846001600160a01b0316846040516128ad91906136ec565b6000604051808303816000865af19150503d80600081146128ea576040519150601f19603f3d011682016040523d82523d6000602084013e6128ef565b606091505b5091509150818015612919575080511580612919575080806020019051810190612919919061351b565b80156112285750505050506001600160a01b03163b151590565b6000612988826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612a1b9092919063ffffffff16565b90508051600014806129a95750808060200190518101906129a9919061351b565b61189b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401611484565b606061057e84846000856060612a3085612b02565b612a66576040517f304619b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080866001600160a01b03168587604051612a8291906136ec565b60006040518083038185875af1925050503d8060008114612abf576040519150601f19603f3d011682016040523d82523d6000602084013e612ac4565b606091505b50915091508115612ad85791506108f59050565b805115612ae85780518082602001fd5b8360405162461bcd60e51b81526004016114849190613708565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906108f5575050151592915050565b6001600160a01b0381168114612b5057600080fd5b50565b600060208284031215612b6557600080fd5b813561058181612b3b565b600060208284031215612b8257600080fd5b5035919050565b60008060408385031215612b9c57600080fd5b8235612ba781612b3b565b946020939093013593505050565b60008060408385031215612bc857600080fd5b8235612bd381612b3b565b91506020830135612be381612b3b565b809150509250929050565b8015158114612b5057600080fd5b60008060408385031215612c0f57600080fd5b8235612c1a81612b3b565b91506020830135612be381612bee565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff81118282101715612c6457612c64612c2a565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715612c9357612c93612c2a565b604052919050565b600080600080600060a08688031215612cb357600080fd5b8535612cbe81612b3b565b945060208681013594506040870135935060608701359250608087013567ffffffffffffffff80821115612cf157600080fd5b818901915089601f830112612d0557600080fd5b813581811115612d1757612d17612c2a565b8060051b9150612d28848301612c6a565b818152918301840191848101908c841115612d4257600080fd5b938501935b83851015612d6057843582529385019390850190612d47565b8096505050505050509295509295909350565b69ffffffffffffffffffff81168114612b5057600080fd5b600080600060608486031215612da057600080fd5b8335612dab81612b3b565b92506020840135612dbb81612d73565b91506040840135612dcb81612b3b565b809150509250925092565b600080600060608486031215612deb57600080fd5b833592506020840135612dfd81612b3b565b929592945050506040919091013590565b600080600060608486031215612e2357600080fd5b8335612e2e81612b3b565b92506020840135612dfd81612b3b565b60008060408385031215612e5157600080fd5b8235612e5c81612b3b565b91506020830135612be381612d73565b600080600080600060a08688031215612e8457600080fd5b8551612e8f81612d73565b809550506020860151935060408601519250606086015191506080860151612eb681612d73565b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600084136000841385830485118282161615612f1b57612f1b612ec4565b600160ff1b6000871286820588128184161615612f3a57612f3a612ec4565b60008712925087820587128484161615612f5657612f56612ec4565b87850587128184161615612f6c57612f6c612ec4565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615612fb457612fb4612ec4565b82600160ff1b038412811615612fcc57612fcc612ec4565b50500190565b634e487b7160e01b600052601260045260246000fd5b600082612ff757612ff7612fd2565b600160ff1b82146000198414161561301157613011612ec4565b500590565b80516108b581612b3b565b60006020828403121561303357600080fd5b815161058181612b3b565b60006020828403121561305057600080fd5b5051919050565b60008282101561306957613069612ec4565b500390565b600181815b808511156130a957816000190482111561308f5761308f612ec4565b8085161561309c57918102915b93841c9390800290613073565b509250929050565b6000826130c057506001610705565b816130cd57506000610705565b81600181146130e357600281146130ed57613109565b6001915050610705565b60ff8411156130fe576130fe612ec4565b50506001821b610705565b5060208310610133831016604e8410600b841016171561312c575081810a610705565b613136838361306e565b806000190482111561314a5761314a612ec4565b029392505050565b600061058183836130b1565b600081600019048311821515161561317857613178612ec4565b500290565b60008261318c5761318c612fd2565b500490565b600082198211156131a4576131a4612ec4565b500190565b634e487b7160e01b600052603260045260246000fd5b60005b838110156131da5781810151838201526020016131c2565b838111156118815750506000910152565b600082601f8301126131fc57600080fd5b815167ffffffffffffffff81111561321657613216612c2a565b613229601f8201601f1916602001612c6a565b81815284602083860101111561323e57600080fd5b6108f58260208301602087016131bf565b60006020828403121561326157600080fd5b815167ffffffffffffffff81111561327857600080fd5b6108f5848285016131eb565b600060c0828403121561329657600080fd5b60405160c0810167ffffffffffffffff82821081831117156132ba576132ba612c2a565b81604052829350845191506132ce82612b3b565b9082526020840151906132e082612b3b565b816020840152604085015191506132f682612b3b565b816040840152606085015160608401526080850151608084015260a085015191508082111561332457600080fd5b50613331858286016131eb565b60a0830152505092915050565b60006020828403121561335057600080fd5b815167ffffffffffffffff8082111561336857600080fd5b90830190610140828603121561337d57600080fd5b613385612c40565b61338e83613016565b815261339c60208401613016565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a08201526133d560c08401613016565b60c08201526133e660e08401613016565b60e082015261010080840151838111156133ff57600080fd5b61340b888287016131eb565b828401525050610120808401518381111561342557600080fd5b61343188828701613284565b918301919091525095945050505050565b60006040828403121561345457600080fd5b82601f83011261346357600080fd5b6040516040810181811067ffffffffffffffff8211171561348657613486612c2a565b806040525080604084018581111561349d57600080fd5b845b818110156134b757805183526020928301920161349f565b509195945050505050565b6000608082840312156134d457600080fd5b82601f8301126134e357600080fd5b6040516080810181811067ffffffffffffffff8211171561350657613506612c2a565b60405280608084018581111561349d57600080fd5b60006020828403121561352d57600080fd5b815161058181612bee565b600081518084526135508160208601602086016131bf565b601f01601f19169290920160200192915050565b60006001600160a01b0380835116845280602084015116602085015280604084015116604085015250606082015160608401526080820151608084015260a082015160c060a08501526108f560c0850182613538565b602081526135d46020820183516001600160a01b03169052565b600060208301516135f060408401826001600160a01b03169052565b506040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015161363360e08401826001600160a01b03169052565b5060e0830151610100613650818501836001600160a01b03169052565b808501519150506101406101208181860152613670610160860184613538565b90860151858203601f1901838701529092506111f08382613564565b6000806040838503121561369f57600080fd5b82516136aa81612bee565b6020939093015192949293505050565b60006001600160a01b038087168352808616602084015250836040830152608060608301526111f06080830184613538565b600082516136fe8184602087016131bf565b9190910192915050565b602081526000610581602083018461353856fea2646970667358221220b82eef716c16025329447027f497849dfd5b117acf4e27bdd79bfbc77887f7e964736f6c634300080a0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.