Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
FLAction
Compiler Version
v0.8.24+commit.e11b9ed9
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2024-12-03 */ // SPDX-License-Identifier: MIT pragma solidity =0.8.24; contract MainnetActionsUtilAddresses { address internal constant DFS_REG_CONTROLLER_ADDR = 0xF8f8B3C98Cf2E63Df3041b73f80F362a4cf3A576; address internal constant REGISTRY_ADDR = 0x287778F121F134C66212FB16c9b53eC991D32f5b; address internal constant DFS_LOGGER_ADDR = 0xcE7a977Cac4a481bc84AC06b2Da0df614e621cf3; address internal constant SUB_STORAGE_ADDR = 0x1612fc28Ee0AB882eC99842Cde0Fc77ff0691e90; address internal constant PROXY_AUTH_ADDR = 0x149667b6FAe2c63D1B4317C716b0D0e4d3E2bD70; address internal constant LSV_PROXY_REGISTRY_ADDRESS = 0xa8a3c86c4A2DcCf350E84D2b3c46BDeBc711C16e; address internal constant TRANSIENT_STORAGE = 0x2F7Ef2ea5E8c97B8687CA703A0e50Aa5a49B7eb2; } contract ActionsUtilHelper is MainnetActionsUtilAddresses { } 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 DFSRegistry is AdminAuth { error EntryAlreadyExistsError(bytes4); error EntryNonExistentError(bytes4); error EntryNotInChangeError(bytes4); error ChangeNotReadyError(uint256,uint256); error EmptyPrevAddrError(bytes4); error AlreadyInContractChangeError(bytes4); error AlreadyInWaitPeriodChangeError(bytes4); event AddNewContract(address,bytes4,address,uint256); event RevertToPreviousAddress(address,bytes4,address,address); event StartContractChange(address,bytes4,address,address); event ApproveContractChange(address,bytes4,address,address); event CancelContractChange(address,bytes4,address,address); event StartWaitPeriodChange(address,bytes4,uint256); event ApproveWaitPeriodChange(address,bytes4,uint256,uint256); event CancelWaitPeriodChange(address,bytes4,uint256,uint256); struct Entry { address contractAddr; uint256 waitPeriod; uint256 changeStartTime; bool inContractChange; bool inWaitPeriodChange; bool exists; } mapping(bytes4 => Entry) public entries; mapping(bytes4 => address) public previousAddresses; mapping(bytes4 => address) public pendingAddresses; mapping(bytes4 => uint256) public pendingWaitTimes; /// @notice Given an contract id returns the registered address /// @dev Id is keccak256 of the contract name /// @param _id Id of contract function getAddr(bytes4 _id) public view returns (address) { return entries[_id].contractAddr; } /// @notice Helper function to easily query if id is registered /// @param _id Id of contract function isRegistered(bytes4 _id) public view returns (bool) { return entries[_id].exists; } /////////////////////////// OWNER ONLY FUNCTIONS /////////////////////////// /// @notice Adds a new contract to the registry /// @param _id Id of contract /// @param _contractAddr Address of the contract /// @param _waitPeriod Amount of time to wait before a contract address can be changed function addNewContract( bytes4 _id, address _contractAddr, uint256 _waitPeriod ) public onlyOwner { if (entries[_id].exists){ revert EntryAlreadyExistsError(_id); } entries[_id] = Entry({ contractAddr: _contractAddr, waitPeriod: _waitPeriod, changeStartTime: 0, inContractChange: false, inWaitPeriodChange: false, exists: true }); emit AddNewContract(msg.sender, _id, _contractAddr, _waitPeriod); } /// @notice Reverts to the previous address immediately /// @dev In case the new version has a fault, a quick way to fallback to the old contract /// @param _id Id of contract function revertToPreviousAddress(bytes4 _id) public onlyOwner { if (!(entries[_id].exists)){ revert EntryNonExistentError(_id); } if (previousAddresses[_id] == address(0)){ revert EmptyPrevAddrError(_id); } address currentAddr = entries[_id].contractAddr; entries[_id].contractAddr = previousAddresses[_id]; emit RevertToPreviousAddress(msg.sender, _id, currentAddr, previousAddresses[_id]); } /// @notice Starts an address change for an existing entry /// @dev Can override a change that is currently in progress /// @param _id Id of contract /// @param _newContractAddr Address of the new contract function startContractChange(bytes4 _id, address _newContractAddr) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (entries[_id].inWaitPeriodChange){ revert AlreadyInWaitPeriodChangeError(_id); } entries[_id].changeStartTime = block.timestamp; // solhint-disable-line entries[_id].inContractChange = true; pendingAddresses[_id] = _newContractAddr; emit StartContractChange(msg.sender, _id, entries[_id].contractAddr, _newContractAddr); } /// @notice Changes new contract address, correct time must have passed /// @param _id Id of contract function approveContractChange(bytes4 _id) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (!entries[_id].inContractChange){ revert EntryNotInChangeError(_id); } if (block.timestamp < (entries[_id].changeStartTime + entries[_id].waitPeriod)){// solhint-disable-line revert ChangeNotReadyError(block.timestamp, (entries[_id].changeStartTime + entries[_id].waitPeriod)); } address oldContractAddr = entries[_id].contractAddr; entries[_id].contractAddr = pendingAddresses[_id]; entries[_id].inContractChange = false; entries[_id].changeStartTime = 0; pendingAddresses[_id] = address(0); previousAddresses[_id] = oldContractAddr; emit ApproveContractChange(msg.sender, _id, oldContractAddr, entries[_id].contractAddr); } /// @notice Cancel pending change /// @param _id Id of contract function cancelContractChange(bytes4 _id) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (!entries[_id].inContractChange){ revert EntryNotInChangeError(_id); } address oldContractAddr = pendingAddresses[_id]; pendingAddresses[_id] = address(0); entries[_id].inContractChange = false; entries[_id].changeStartTime = 0; emit CancelContractChange(msg.sender, _id, oldContractAddr, entries[_id].contractAddr); } /// @notice Starts the change for waitPeriod /// @param _id Id of contract /// @param _newWaitPeriod New wait time function startWaitPeriodChange(bytes4 _id, uint256 _newWaitPeriod) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (entries[_id].inContractChange){ revert AlreadyInContractChangeError(_id); } pendingWaitTimes[_id] = _newWaitPeriod; entries[_id].changeStartTime = block.timestamp; // solhint-disable-line entries[_id].inWaitPeriodChange = true; emit StartWaitPeriodChange(msg.sender, _id, _newWaitPeriod); } /// @notice Changes new wait period, correct time must have passed /// @param _id Id of contract function approveWaitPeriodChange(bytes4 _id) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (!entries[_id].inWaitPeriodChange){ revert EntryNotInChangeError(_id); } if (block.timestamp < (entries[_id].changeStartTime + entries[_id].waitPeriod)){ // solhint-disable-line revert ChangeNotReadyError(block.timestamp, (entries[_id].changeStartTime + entries[_id].waitPeriod)); } uint256 oldWaitTime = entries[_id].waitPeriod; entries[_id].waitPeriod = pendingWaitTimes[_id]; entries[_id].inWaitPeriodChange = false; entries[_id].changeStartTime = 0; pendingWaitTimes[_id] = 0; emit ApproveWaitPeriodChange(msg.sender, _id, oldWaitTime, entries[_id].waitPeriod); } /// @notice Cancel wait period change /// @param _id Id of contract function cancelWaitPeriodChange(bytes4 _id) public onlyOwner { if (!entries[_id].exists){ revert EntryNonExistentError(_id); } if (!entries[_id].inWaitPeriodChange){ revert EntryNotInChangeError(_id); } uint256 oldWaitPeriod = pendingWaitTimes[_id]; pendingWaitTimes[_id] = 0; entries[_id].inWaitPeriodChange = false; entries[_id].changeStartTime = 0; emit CancelWaitPeriodChange(msg.sender, _id, oldWaitPeriod, entries[_id].waitPeriod); } } abstract contract DSAuthority { function canCall( address src, address dst, bytes4 sig ) public view virtual returns (bool); } contract DSAuthEvents { event LogSetAuthority(address indexed authority); event LogSetOwner(address indexed owner); } contract DSAuth is DSAuthEvents { DSAuthority public authority; address public owner; constructor() { owner = msg.sender; emit LogSetOwner(msg.sender); } function setOwner(address owner_) public auth { owner = owner_; emit LogSetOwner(owner); } function setAuthority(DSAuthority authority_) public auth { authority = authority_; emit LogSetAuthority(address(authority)); } modifier auth { require(isAuthorized(msg.sender, msg.sig), "Not authorized"); _; } function isAuthorized(address src, bytes4 sig) internal view returns (bool) { if (src == address(this)) { return true; } else if (src == owner) { return true; } else if (authority == DSAuthority(address(0))) { return false; } else { return authority.canCall(src, address(this), sig); } } } contract DSNote { event LogNote( bytes4 indexed sig, address indexed guy, bytes32 indexed foo, bytes32 indexed bar, uint256 wad, bytes fax ) anonymous; modifier note { bytes32 foo; bytes32 bar; assembly { foo := calldataload(4) bar := calldataload(36) } emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data); _; } } abstract contract DSProxy is DSAuth, DSNote { DSProxyCache public cache; // global cache for contracts constructor(address _cacheAddr) { if (!(setCache(_cacheAddr))){ require(isAuthorized(msg.sender, msg.sig), "Not authorized"); } } // solhint-disable-next-line no-empty-blocks receive() external payable {} // use the proxy to execute calldata _data on contract _code function execute(bytes memory _code, bytes memory _data) public payable virtual returns (address target, bytes32 response); function execute(address _target, bytes memory _data) public payable virtual returns (bytes32 response); //set new cache function setCache(address _cacheAddr) public payable virtual returns (bool); } contract DSProxyCache { mapping(bytes32 => address) cache; function read(bytes memory _code) public view returns (address) { bytes32 hash = keccak256(_code); return cache[hash]; } function write(bytes memory _code) public returns (address target) { assembly { target := create(0, add(_code, 0x20), mload(_code)) switch iszero(extcodesize(target)) case 1 { // throw if contract failed to deploy revert(0, 0) } } bytes32 hash = keccak256(_code); cache[hash] = target; } } interface ISafe { enum Operation { Call, DelegateCall } function setup( address[] calldata _owners, uint256 _threshold, address to, bytes calldata data, address fallbackHandler, address paymentToken, uint256 payment, address payable paymentReceiver ) external; function execTransaction( address to, uint256 value, bytes calldata data, Operation operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address payable refundReceiver, bytes memory signatures ) external payable returns (bool success); function execTransactionFromModule( address to, uint256 value, bytes memory data, Operation operation ) external returns (bool success); function checkSignatures( bytes32 dataHash, bytes memory data, bytes memory signatures ) external view; function checkNSignatures( address executor, bytes32 dataHash, bytes memory /* data */, bytes memory signatures, uint256 requiredSignatures ) external view; function approveHash(bytes32 hashToApprove) external; function domainSeparator() external view returns (bytes32); function getTransactionHash( address to, uint256 value, bytes calldata data, Operation operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address refundReceiver, uint256 _nonce ) external view returns (bytes32); function nonce() external view returns (uint256); function setFallbackHandler(address handler) external; function getOwners() external view returns (address[] memory); function isOwner(address owner) external view returns (bool); function getThreshold() external view returns (uint256); function enableModule(address module) external; function isModuleEnabled(address module) external view returns (bool); function disableModule(address prevModule, address module) external; function getModulesPaginated( address start, uint256 pageSize ) external view returns (address[] memory array, address next); } interface IDSProxyFactory { function isProxy(address _proxy) external view returns (bool); } contract MainnetProxyFactoryAddresses { address internal constant PROXY_FACTORY_ADDR = 0xA26e15C895EFc0616177B7c1e7270A4C7D51C997; } contract DSProxyFactoryHelper is MainnetProxyFactoryAddresses { } contract CheckWalletType is DSProxyFactoryHelper { function isDSProxy(address _proxy) public view returns (bool) { return IDSProxyFactory(PROXY_FACTORY_ADDR).isProxy(_proxy); } } contract DefisaverLogger { event RecipeEvent( address indexed caller, string indexed logName ); event ActionDirectEvent( address indexed caller, string indexed logName, bytes data ); function logRecipeEvent( string memory _logName ) public { emit RecipeEvent(msg.sender, _logName); } function logActionDirectEvent( string memory _logName, bytes memory _data ) public { emit ActionDirectEvent(msg.sender, _logName, _data); } } abstract contract ActionBase is AdminAuth, ActionsUtilHelper, CheckWalletType { event ActionEvent( string indexed logName, bytes data ); DFSRegistry public constant registry = DFSRegistry(REGISTRY_ADDR); DefisaverLogger public constant logger = DefisaverLogger( DFS_LOGGER_ADDR ); //Wrong sub index value error SubIndexValueError(); //Wrong return index value error ReturnIndexValueError(); /// @dev Subscription params index range [128, 255] uint8 public constant SUB_MIN_INDEX_VALUE = 128; uint8 public constant SUB_MAX_INDEX_VALUE = 255; /// @dev Return params index range [1, 127] uint8 public constant RETURN_MIN_INDEX_VALUE = 1; uint8 public constant RETURN_MAX_INDEX_VALUE = 127; /// @dev If the input value should not be replaced uint8 public constant NO_PARAM_MAPPING = 0; /// @dev We need to parse Flash loan actions in a different way enum ActionType { FL_ACTION, STANDARD_ACTION, FEE_ACTION, CHECK_ACTION, CUSTOM_ACTION } /// @notice Parses inputs and runs the implemented action through a user wallet /// @dev Is called by the RecipeExecutor chaining actions together /// @param _callData Array of input values each value encoded as bytes /// @param _subData Array of subscribed vales, replaces input values if specified /// @param _paramMapping Array that specifies how return and subscribed values are mapped in input /// @param _returnValues Returns values from actions before, which can be injected in inputs /// @return Returns a bytes32 value through user wallet, each actions implements what that value is function executeAction( bytes memory _callData, bytes32[] memory _subData, uint8[] memory _paramMapping, bytes32[] memory _returnValues ) public payable virtual returns (bytes32); /// @notice Parses inputs and runs the single implemented action through a user wallet /// @dev Used to save gas when executing a single action directly function executeActionDirect(bytes memory _callData) public virtual payable; /// @notice Returns the type of action we are implementing function actionType() public pure virtual returns (uint8); //////////////////////////// HELPER METHODS //////////////////////////// /// @notice Given an uint256 input, injects return/sub values if specified /// @param _param The original input value /// @param _mapType Indicated the type of the input in paramMapping /// @param _subData Array of subscription data we can replace the input value with /// @param _returnValues Array of subscription data we can replace the input value with function _parseParamUint( uint _param, uint8 _mapType, bytes32[] memory _subData, bytes32[] memory _returnValues ) internal pure returns (uint) { if (isReplaceable(_mapType)) { if (isReturnInjection(_mapType)) { _param = uint(_returnValues[getReturnIndex(_mapType)]); } else { _param = uint256(_subData[getSubIndex(_mapType)]); } } return _param; } /// @notice Given an addr input, injects return/sub values if specified /// @param _param The original input value /// @param _mapType Indicated the type of the input in paramMapping /// @param _subData Array of subscription data we can replace the input value with /// @param _returnValues Array of subscription data we can replace the input value with function _parseParamAddr( address _param, uint8 _mapType, bytes32[] memory _subData, bytes32[] memory _returnValues ) internal view returns (address) { if (isReplaceable(_mapType)) { if (isReturnInjection(_mapType)) { _param = address(bytes20((_returnValues[getReturnIndex(_mapType)]))); } else { /// @dev The last two values are specially reserved for proxy addr and owner addr if (_mapType == 254) return address(this); // wallet address if (_mapType == 255) return fetchOwnersOrWallet(); // owner if 1/1 wallet or the wallet itself _param = address(uint160(uint256(_subData[getSubIndex(_mapType)]))); } } return _param; } /// @notice Given an bytes32 input, injects return/sub values if specified /// @param _param The original input value /// @param _mapType Indicated the type of the input in paramMapping /// @param _subData Array of subscription data we can replace the input value with /// @param _returnValues Array of subscription data we can replace the input value with function _parseParamABytes32( bytes32 _param, uint8 _mapType, bytes32[] memory _subData, bytes32[] memory _returnValues ) internal pure returns (bytes32) { if (isReplaceable(_mapType)) { if (isReturnInjection(_mapType)) { _param = (_returnValues[getReturnIndex(_mapType)]); } else { _param = _subData[getSubIndex(_mapType)]; } } return _param; } /// @notice Checks if the paramMapping value indicated that we need to inject values /// @param _type Indicated the type of the input function isReplaceable(uint8 _type) internal pure returns (bool) { return _type != NO_PARAM_MAPPING; } /// @notice Checks if the paramMapping value is in the return value range /// @param _type Indicated the type of the input function isReturnInjection(uint8 _type) internal pure returns (bool) { return (_type >= RETURN_MIN_INDEX_VALUE) && (_type <= RETURN_MAX_INDEX_VALUE); } /// @notice Transforms the paramMapping value to the index in return array value /// @param _type Indicated the type of the input function getReturnIndex(uint8 _type) internal pure returns (uint8) { if (!(isReturnInjection(_type))){ revert SubIndexValueError(); } return (_type - RETURN_MIN_INDEX_VALUE); } /// @notice Transforms the paramMapping value to the index in sub array value /// @param _type Indicated the type of the input function getSubIndex(uint8 _type) internal pure returns (uint8) { if (_type < SUB_MIN_INDEX_VALUE){ revert ReturnIndexValueError(); } return (_type - SUB_MIN_INDEX_VALUE); } function fetchOwnersOrWallet() internal view returns (address) { if (isDSProxy(address(this))) return DSProxy(payable(address(this))).owner(); // if not DSProxy, we assume we are in context of Safe address[] memory owners = ISafe(address(this)).getOwners(); return owners.length == 1 ? owners[0] : address(this); } } contract MainnetFLAddresses { address internal constant SOLO_MARGIN_ADDRESS = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e; address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address internal constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address internal constant DYDX_FL_FEE_FAUCET = 0x47f159C90850D5cE09E21F931d504536840f34b4; address internal constant AAVE_LENDING_POOL = 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9; address internal constant AAVE_LENDING_POOL_ADDRESS_PROVIDER = 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5; address internal constant AAVE_V3_LENDING_POOL = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2; address internal constant AAVE_V3_LENDING_POOL_ADDRESS_PROVIDER = 0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e; address internal constant SPARK_LENDING_POOL_ADDRESS_PROVIDER = 0x02C3eA4e34C0cBd694D2adFa2c690EECbC1793eE; address internal constant SPARK_LENDING_POOL = 0xC13e21B648A5Ee794902342038FF3aDAB66BE987; address internal constant DSS_FLASH_ADDR = 0x60744434d6339a6B27d73d9Eda62b6F66a0a04FA; address internal constant DAI_ADDR = 0x6B175474E89094C44Da98b954EedeAC495271d0F; address internal constant ST_ETH_ADDR = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84; address internal constant VAULT_ADDR = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; address internal constant GHO_ADDR = 0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f; address internal constant GHO_FLASH_MINTER_ADDR = 0xb639D208Bcf0589D54FaC24E655C79EC529762B8; address internal constant UNI_V3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984; address internal constant MORPHO_BLUE_ADDR = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb; address internal constant CURVEUSD_FLASH_ADDR = 0xA7a4bb50AF91f90b6fEb3388E7f8286aF45b299B; address internal constant CURVEUSD_ADDR = 0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E; address internal constant RECIPE_EXECUTOR_ADDR = 0x5029336642814bC51a42bA80BF83a6322110035D; } contract StrategyModel { /// @dev Group of strategies bundled together so user can sub to multiple strategies at once /// @param creator Address of the user who created the bundle /// @param strategyIds Array of strategy ids stored in StrategyStorage struct StrategyBundle { address creator; uint64[] strategyIds; } /// @dev Template/Class which defines a Strategy /// @param name Name of the strategy useful for logging what strategy is executing /// @param creator Address of the user which created the strategy /// @param triggerIds Array of identifiers for trigger - bytes4(keccak256(TriggerName)) /// @param actionIds Array of identifiers for actions - bytes4(keccak256(ActionName)) /// @param paramMapping Describes how inputs to functions are piped from return/subbed values /// @param continuous If the action is repeated (continuos) or one time struct Strategy { string name; address creator; bytes4[] triggerIds; bytes4[] actionIds; uint8[][] paramMapping; bool continuous; } /// @dev List of actions grouped as a recipe /// @param name Name of the recipe useful for logging what recipe is executing /// @param callData Array of calldata inputs to each action /// @param subData Used only as part of strategy, subData injected from StrategySub.subData /// @param actionIds Array of identifiers for actions - bytes4(keccak256(ActionName)) /// @param paramMapping Describes how inputs to functions are piped from return/subbed values struct Recipe { string name; bytes[] callData; bytes32[] subData; bytes4[] actionIds; uint8[][] paramMapping; } /// @dev Actual data of the sub we store on-chain /// @dev In order to save on gas we store a keccak256(StrategySub) and verify later on /// @param walletAddr Address of the users smart wallet/proxy /// @param isEnabled Toggle if the subscription is active /// @param strategySubHash Hash of the StrategySub data the user inputted struct StoredSubData { bytes20 walletAddr; // address but put in bytes20 for gas savings bool isEnabled; bytes32 strategySubHash; } /// @dev Instance of a strategy, user supplied data /// @param strategyOrBundleId Id of the strategy or bundle, depending on the isBundle bool /// @param isBundle If true the id points to bundle, if false points directly to strategyId /// @param triggerData User supplied data needed for checking trigger conditions /// @param subData User supplied data used in recipe struct StrategySub { uint64 strategyOrBundleId; bool isBundle; bytes[] triggerData; bytes32[] subData; } /// @dev Data needed when signing relay transaction /// @param maxTxCostInFeeToken Max tx cost user is willing to pay in fee token /// @param feeToken Address of the token user is willing to pay fee in /// @param tokenPriceInEth Price of the token in ETH /// @param deadline Deadline for the relay transaction to be executed /// @param shouldTakeFeeFromPosition Flag to indicate if fee should be taken from position, otherwise from EOA/wallet struct TxSaverSignedData { uint256 maxTxCostInFeeToken; address feeToken; uint256 tokenPriceInEth; uint256 deadline; bool shouldTakeFeeFromPosition; } } abstract contract IDSProxy { // function execute(bytes memory _code, bytes memory _data) // public // payable // virtual // returns (address, bytes32); function execute(address _target, bytes memory _data) public payable virtual returns (bytes32); function setCache(address _cacheAddr) public payable virtual returns (bool); function owner() public view virtual returns (address); } contract FLFeeFaucet { using SafeERC20 for IERC20; /// @notice Sends 2 wei to msg.sender /// @dev Anyone can call this method but it's not economically feasible to drain /// @param _tokenAddr Address of the token we want 2 wei function my2Wei(address _tokenAddr) public { IERC20(_tokenAddr).safeTransfer(msg.sender, 2); } } contract FLHelper is MainnetFLAddresses, StrategyModel { uint16 internal constant AAVE_REFERRAL_CODE = 64; uint16 internal constant SPARK_REFERRAL_CODE = 0; FLFeeFaucet public constant flFeeFaucet = FLFeeFaucet(DYDX_FL_FEE_FAUCET); /// @dev Function sig of RecipeExecutor._executeActionsFromFL() bytes4 public constant CALLBACK_SELECTOR = bytes4( keccak256( "_executeActionsFromFL((string,bytes[],bytes32[],bytes4[],uint8[][]),bytes32)" ) ); // Revert if execution fails when using safe wallet error SafeExecutionError(); function _executeRecipe(address _wallet, bool _isDSProxy, Recipe memory _currRecipe, uint256 _paybackAmount) internal { if (_isDSProxy) { IDSProxy(_wallet).execute{value: address(this).balance}( RECIPE_EXECUTOR_ADDR, abi.encodeWithSelector(CALLBACK_SELECTOR, _currRecipe, _paybackAmount) ); } else { bool success = ISafe(_wallet).execTransactionFromModule( RECIPE_EXECUTOR_ADDR, address(this).balance, abi.encodeWithSelector(CALLBACK_SELECTOR, _currRecipe, _paybackAmount), ISafe.Operation.DelegateCall ); if (!success) { revert SafeExecutionError(); } } } } 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; } library DataTypes { // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties. struct ReserveData { //stores the reserve configuration ReserveConfigurationMap configuration; //the liquidity index. Expressed in ray uint128 liquidityIndex; //variable borrow index. Expressed in ray uint128 variableBorrowIndex; //the current supply rate. Expressed in ray uint128 currentLiquidityRate; //the current variable borrow rate. Expressed in ray uint128 currentVariableBorrowRate; //the current stable borrow rate. Expressed in ray uint128 currentStableBorrowRate; uint40 lastUpdateTimestamp; //tokens addresses address aTokenAddress; address stableDebtTokenAddress; address variableDebtTokenAddress; //address of the interest rate strategy address interestRateStrategyAddress; //the id of the reserve. Represents the position in the list of the active reserves uint8 id; } struct ReserveConfigurationMap { //bit 0-15: LTV //bit 16-31: Liq. threshold //bit 32-47: Liq. bonus //bit 48-55: Decimals //bit 56: Reserve is active //bit 57: reserve is frozen //bit 58: borrowing is enabled //bit 59: stable rate borrowing enabled //bit 60-63: reserved //bit 64-79: reserve factor uint256 data; } struct UserConfigurationMap { uint256 data; } enum InterestRateMode {NONE, STABLE, VARIABLE} } interface ILendingPoolV2 { /** * @dev Emitted on deposit() * @param reserve The address of the underlying asset of the reserve * @param user The address initiating the deposit * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens * @param amount The amount deposited * @param referral The referral code used **/ event Deposit( address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint16 indexed referral ); /** * @dev Emitted on withdraw() * @param reserve The address of the underlyng asset being withdrawn * @param user The address initiating the withdrawal, owner of aTokens * @param to Address that will receive the underlying * @param amount The amount to be withdrawn **/ event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount); /** * @dev Emitted on borrow() and flashLoan() when debt needs to be opened * @param reserve The address of the underlying asset being borrowed * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just * initiator of the transaction on flashLoan() * @param onBehalfOf The address that will be getting the debt * @param amount The amount borrowed out * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable * @param borrowRate The numeric rate at which the user has borrowed * @param referral The referral code used **/ event Borrow( address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint256 borrowRateMode, uint256 borrowRate, uint16 indexed referral ); /** * @dev Emitted on repay() * @param reserve The address of the underlying asset of the reserve * @param user The beneficiary of the repayment, getting his debt reduced * @param repayer The address of the user initiating the repay(), providing the funds * @param amount The amount repaid **/ event Repay( address indexed reserve, address indexed user, address indexed repayer, uint256 amount ); /** * @dev Emitted on swapBorrowRateMode() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user swapping his rate mode * @param rateMode The rate mode that the user wants to swap to **/ event Swap(address indexed reserve, address indexed user, uint256 rateMode); /** * @dev Emitted on setUserUseReserveAsCollateral() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user enabling the usage as collateral **/ event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user); /** * @dev Emitted on setUserUseReserveAsCollateral() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user enabling the usage as collateral **/ event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); /** * @dev Emitted on rebalanceStableBorrowRate() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user for which the rebalance has been executed **/ event RebalanceStableBorrowRate(address indexed reserve, address indexed user); /** * @dev Emitted on flashLoan() * @param target The address of the flash loan receiver contract * @param initiator The address initiating the flash loan * @param asset The address of the asset being flash borrowed * @param amount The amount flash borrowed * @param premium The fee flash borrowed * @param referralCode The referral code used **/ event FlashLoan( address indexed target, address indexed initiator, address indexed asset, uint256 amount, uint256 premium, uint16 referralCode ); /** * @dev Emitted when the pause is triggered. */ event Paused(); /** * @dev Emitted when the pause is lifted. */ event Unpaused(); /** * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via * LendingPoolCollateral manager using a DELEGATECALL * This allows to have the events in the generated ABI for LendingPool. * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation * @param user The address of the borrower getting liquidated * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator * @param liquidator The address of the liquidator * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants * to receive the underlying collateral asset directly **/ event LiquidationCall( address indexed collateralAsset, address indexed debtAsset, address indexed user, uint256 debtToCover, uint256 liquidatedCollateralAmount, address liquidator, bool receiveAToken ); /** * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal, * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it * gets added to the LendingPool ABI * @param reserve The address of the underlying asset of the reserve * @param liquidityRate The new liquidity rate * @param stableBorrowRate The new stable borrow rate * @param variableBorrowRate The new variable borrow rate * @param liquidityIndex The new liquidity index * @param variableBorrowIndex The new variable borrow index **/ event ReserveDataUpdated( address indexed reserve, uint256 liquidityRate, uint256 stableBorrowRate, uint256 variableBorrowRate, uint256 liquidityIndex, uint256 variableBorrowIndex ); /** * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. * - E.g. User deposits 100 USDC and gets in return 100 aUSDC * @param asset The address of the underlying asset to deposit * @param amount The amount to be deposited * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens * is a different wallet * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man **/ function deposit( address asset, uint256 amount, address onBehalfOf, uint16 referralCode ) external; /** * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC * @param asset The address of the underlying asset to withdraw * @param amount The underlying amount to be withdrawn * - Send the value type(uint256).max in order to withdraw the whole aToken balance * @param to Address that will receive the underlying, same as msg.sender if the user * wants to receive it on his own wallet, or a different address if the beneficiary is a * different wallet **/ function withdraw( address asset, uint256 amount, address to ) external; /** * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower * already deposited enough collateral, or he was given enough allowance by a credit delegator on the * corresponding debt token (StableDebtToken or VariableDebtToken) * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet * and 100 stable/variable debt tokens, depending on the `interestRateMode` * @param asset The address of the underlying asset to borrow * @param amount The amount to be borrowed * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator * if he has been given credit delegation allowance **/ function borrow( address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf ) external; /** * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address * @param asset The address of the borrowed underlying asset previously borrowed * @param amount The amount to repay * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the * user calling the function if he wants to reduce/remove his own debt, or the address of any other * other borrower whose debt should be removed **/ function repay( address asset, uint256 amount, uint256 rateMode, address onBehalfOf ) external; /** * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa * @param asset The address of the underlying asset borrowed * @param rateMode The rate mode that the user wants to swap to **/ function swapBorrowRateMode(address asset, uint256 rateMode) external; /** * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. * - Users can be rebalanced if the following conditions are satisfied: * 1. Usage ratio is above 95% * 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been * borrowed at a stable rate and depositors are not earning enough * @param asset The address of the underlying asset borrowed * @param user The address of the user to be rebalanced **/ function rebalanceStableBorrowRate(address asset, address user) external; /** * @dev Allows depositors to enable/disable a specific deposited asset as collateral * @param asset The address of the underlying asset deposited * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise **/ function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external; /** * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation * @param user The address of the borrower getting liquidated * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants * to receive the underlying collateral asset directly **/ function liquidationCall( address collateralAsset, address debtAsset, address user, uint256 debtToCover, bool receiveAToken ) external; /** * @dev Allows smartcontracts to access the liquidity of the pool within one transaction, * as long as the amount taken plus a fee is returned. * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. * For further details please visit https://developers.aave.com * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface * @param assets The addresses of the assets being flash-borrowed * @param amounts The amounts amounts being flash-borrowed * @param modes Types of the debt to open if the flash loan is not returned: * 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver * 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address * 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address * @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2 * @param params Variadic packed params to pass to the receiver as extra information * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man **/ function flashLoan( address receiverAddress, address[] calldata assets, uint256[] calldata amounts, uint256[] calldata modes, address onBehalfOf, bytes calldata params, uint16 referralCode ) external; /** * @dev Returns the user account data across all the reserves * @param user The address of the user * @return totalCollateralETH the total collateral in ETH of the user * @return totalDebtETH the total debt in ETH of the user * @return availableBorrowsETH the borrowing power left of the user * @return currentLiquidationThreshold the liquidation threshold of the user * @return ltv the loan to value of the user * @return healthFactor the current health factor of the user **/ function getUserAccountData(address user) external view returns ( uint256 totalCollateralETH, uint256 totalDebtETH, uint256 availableBorrowsETH, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor ); function initReserve( address reserve, address aTokenAddress, address stableDebtAddress, address variableDebtAddress, address interestRateStrategyAddress ) external; function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress) external; function setConfiguration(address reserve, uint256 configuration) external; /** * @dev Returns the configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The configuration of the reserve **/ function getConfiguration(address asset) external view returns (DataTypes.ReserveConfigurationMap memory); /** * @dev Returns the configuration of the user across all the reserves * @param user The user address * @return The configuration of the user **/ function getUserConfiguration(address user) external view returns (DataTypes.UserConfigurationMap memory); /** * @dev Returns the normalized income normalized income of the reserve * @param asset The address of the underlying asset of the reserve * @return The reserve's normalized income */ function getReserveNormalizedIncome(address asset) external view returns (uint256); /** * @dev Returns the normalized variable debt per unit of asset * @param asset The address of the underlying asset of the reserve * @return The reserve normalized variable debt */ function getReserveNormalizedVariableDebt(address asset) external view returns (uint256); /** * @dev Returns the state and configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The state of the reserve **/ function getReserveData(address asset) external view returns (DataTypes.ReserveData memory); function finalizeTransfer( address asset, address from, address to, uint256 amount, uint256 balanceFromAfter, uint256 balanceToBefore ) external; function getReservesList() external view returns (address[] memory); function getAddressesProvider() external view returns (ILendingPoolAddressesProviderV2); function setPause(bool val) external; function paused() external view returns (bool); } interface IFlashLoans { function flashLoan( address recipient, address[] memory tokens, uint256[] memory amounts, bytes memory userData ) external; } interface IERC3156FlashBorrower { /** * @dev Receive a flash loan. * @param initiator The initiator of the loan. * @param token The loan currency. * @param amount The amount of tokens lent. * @param fee The additional amount of tokens to repay. * @param data Arbitrary data structure, intended to contain user-defined parameters. * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" */ function onFlashLoan( address initiator, address token, uint256 amount, uint256 fee, bytes calldata data ) external returns (bytes32); } interface IERC3156FlashLender { /** * @dev The amount of currency available to be lent. * @param token The loan currency. * @return The amount of `token` that can be borrowed. */ function maxFlashLoan( address token ) external view returns (uint256); /** * @dev The fee to be charged for a given loan. * @param token The loan currency. * @param amount The amount of tokens lent. * @return The amount of `token` to be charged for the loan, on top of the returned principal. */ function flashFee( address token, uint256 amount ) external view returns (uint256); /** * @dev Initiate a flash loan. * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. * @param token The loan currency. * @param amount The amount of tokens lent. * @param data Arbitrary data structure, intended to contain user-defined parameters. */ function flashLoan( IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data ) external returns (bool); } abstract contract IFlashLoanBase{ struct FlashLoanParams { address[] tokens; uint256[] amounts; uint256[] modes; address onBehalfOf; address flParamGetterAddr; bytes flParamGetterData; bytes recipeData; } } type Id is bytes32; struct MarketParams { address loanToken; address collateralToken; address oracle; address irm; uint256 lltv; } struct MorphoBluePosition { uint256 supplyShares; uint128 borrowShares; uint128 collateral; } struct Market { uint128 totalSupplyAssets; uint128 totalSupplyShares; uint128 totalBorrowAssets; uint128 totalBorrowShares; uint128 lastUpdate; uint128 fee; } struct Authorization { address authorizer; address authorized; bool isAuthorized; uint256 nonce; uint256 deadline; } struct Signature { uint8 v; bytes32 r; bytes32 s; } interface IMorphoBase { /// @notice The EIP-712 domain separator. /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on another chain sharing /// the same chain id because the domain separator would be the same. function DOMAIN_SEPARATOR() external view returns (bytes32); /// @notice The owner of the contract. /// @dev It has the power to change the owner. /// @dev It has the power to set fees on markets and set the fee recipient. /// @dev It has the power to enable but not disable IRMs and LLTVs. function owner() external view returns (address); /// @notice The fee recipient of all markets. /// @dev The recipient receives the fees of a given market through a supply position on that market. function feeRecipient() external view returns (address); /// @notice Whether the `irm` is enabled. function isIrmEnabled(address irm) external view returns (bool); /// @notice Whether the `lltv` is enabled. function isLltvEnabled(uint256 lltv) external view returns (bool); /// @notice Whether `authorized` is authorized to modify `authorizer`'s positions. /// @dev Anyone is authorized to modify their own positions, regardless of this variable. function isAuthorized(address authorizer, address authorized) external view returns (bool); /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures. function nonce(address authorizer) external view returns (uint256); /// @notice Sets `newOwner` as `owner` of the contract. /// @dev Warning: No two-step transfer ownership. /// @dev Warning: The owner can be set to the zero address. function setOwner(address newOwner) external; /// @notice Enables `irm` as a possible IRM for market creation. /// @dev Warning: It is not possible to disable an IRM. function enableIrm(address irm) external; /// @notice Enables `lltv` as a possible LLTV for market creation. /// @dev Warning: It is not possible to disable a LLTV. function enableLltv(uint256 lltv) external; /// @notice Sets the `newFee` for the given market `marketParams`. /// @dev Warning: The recipient can be the zero address. function setFee(MarketParams memory marketParams, uint256 newFee) external; /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee. /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost. /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes. function setFeeRecipient(address newFeeRecipient) external; /// @notice Creates the market `marketParams`. /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees /// Morpho behaves as expected: /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`. /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with /// burn functions are not supported. /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`. /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported. /// - The IRM should not re-enter Morpho. /// - The oracle should return a price with the correct scaling. /// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties /// (funds could get stuck): /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue. /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and /// `toSharesDown` overflow. /// - The IRM can revert on `borrowRate`. /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest` /// overflow. /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and /// `liquidate` from being used under certain market conditions. /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or /// the computation of `assetsRepaid` in `liquidate` overflow. /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to /// the point where `totalBorrowShares` is very large and borrowing overflows. function createMarket(MarketParams memory marketParams) external; /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's /// `onMorphoSupply` function with the given `data`. /// @dev Either `assets` or `shares` should be zero. Most usecases should rely on `assets` as an input so the caller /// is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific amount /// of shares is given for full compatibility and precision. /// @dev If the supply of a market gets depleted, the supply share price instantly resets to /// `VIRTUAL_ASSETS`:`VIRTUAL_SHARES`. /// @dev Supplying a large amount can revert for overflow. /// @param marketParams The market to supply assets to. /// @param assets The amount of assets to supply. /// @param shares The amount of shares to mint. /// @param onBehalf The address that will own the increased supply position. /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed. /// @return assetsSupplied The amount of assets supplied. /// @return sharesSupplied The amount of shares minted. function supply( MarketParams memory marketParams, uint256 assets, uint256 shares, address onBehalf, bytes memory data ) external returns (uint256 assetsSupplied, uint256 sharesSupplied); /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` to `receiver`. /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`. /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow. /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to /// conversion roundings between shares and assets. /// @param marketParams The market to withdraw assets from. /// @param assets The amount of assets to withdraw. /// @param shares The amount of shares to burn. /// @param onBehalf The address of the owner of the supply position. /// @param receiver The address that will receive the withdrawn assets. /// @return assetsWithdrawn The amount of assets withdrawn. /// @return sharesWithdrawn The amount of shares burned. function withdraw( MarketParams memory marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn); /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` to `receiver`. /// @dev Either `assets` or `shares` should be zero. Most usecases should rely on `assets` as an input so the caller /// is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is given for /// full compatibility and precision. /// @dev If the borrow of a market gets depleted, the borrow share price instantly resets to /// `VIRTUAL_ASSETS`:`VIRTUAL_SHARES`. /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. /// @dev Borrowing a large amount can revert for overflow. /// @param marketParams The market to borrow assets from. /// @param assets The amount of assets to borrow. /// @param shares The amount of shares to mint. /// @param onBehalf The address that will own the increased borrow position. /// @param receiver The address that will receive the borrowed assets. /// @return assetsBorrowed The amount of assets borrowed. /// @return sharesBorrowed The amount of shares minted. function borrow( MarketParams memory marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed); /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's /// `onMorphoReplay` function with the given `data`. /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`. /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow. /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion /// roundings between shares and assets. /// @param marketParams The market to repay assets to. /// @param assets The amount of assets to repay. /// @param shares The amount of shares to burn. /// @param onBehalf The address of the owner of the debt position. /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed. /// @return assetsRepaid The amount of assets repaid. /// @return sharesRepaid The amount of shares burned. function repay( MarketParams memory marketParams, uint256 assets, uint256 shares, address onBehalf, bytes memory data ) external returns (uint256 assetsRepaid, uint256 sharesRepaid); /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's /// `onMorphoSupplyCollateral` function with the given `data`. /// @dev Interest are not accrued since it's not required and it saves gas. /// @dev Supplying a large amount can revert for overflow. /// @param marketParams The market to supply collateral to. /// @param assets The amount of collateral to supply. /// @param onBehalf The address that will own the increased collateral position. /// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed. function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data) external; /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` to `receiver`. /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow. /// @param marketParams The market to withdraw collateral from. /// @param assets The amount of collateral to withdraw. /// @param onBehalf The address of the owner of the collateral position. /// @param receiver The address that will receive the collateral assets. function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver) external; /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's /// `onMorphoLiquidate` function with the given `data`. /// @dev Either `seizedAssets` or `repaidShares` should be zero. /// @dev Seizing more than the collateral balance will underflow and revert without any error message. /// @dev Repaying more than the borrow balance will underflow and revert without any error message. /// @param marketParams The market of the position. /// @param borrower The owner of the position. /// @param seizedAssets The amount of collateral to seize. /// @param repaidShares The amount of shares to repay. /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed. /// @return The amount of assets seized. /// @return The amount of assets repaid. function liquidate( MarketParams memory marketParams, address borrower, uint256 seizedAssets, uint256 repaidShares, bytes memory data ) external returns (uint256, uint256); /// @notice Executes a flash loan. /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all /// markets combined, plus donations). /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached: /// - `flashFee` is zero. /// - `maxFlashLoan` is the token's balance of this contract. /// - The receiver of `assets` is the caller. /// @param token The token to flash loan. /// @param assets The amount of assets to flash loan. /// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback. function flashLoan(address token, uint256 assets, bytes calldata data) external; /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions. /// @param authorized The authorized address. /// @param newIsAuthorized The new authorization status. function setAuthorization(address authorized, bool newIsAuthorized) external; /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions. /// @dev Warning: Reverts if the signature has already been submitted. /// @dev The signature is malleable, but it has no impact on the security here. /// @dev The nonce is passed as argument to be able to revert with a different error message. /// @param authorization The `Authorization` struct. /// @param signature The signature. function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external; /// @notice Accrues interest for the given market `marketParams`. function accrueInterest(MarketParams memory marketParams) external; /// @notice Returns the data stored on the different `slots`. function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory); } interface IMorphoStaticTyping is IMorphoBase { /// @notice The state of the position of `user` on the market corresponding to `id`. /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest /// accrual. function position(Id id, address user) external view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral); /// @notice The state of the market corresponding to `id`. /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual. /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual. /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest /// accrual. function market(Id id) external view returns ( uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee ); /// @notice The market params corresponding to `id`. /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`. function idToMarketParams(Id id) external view returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv); } interface IMorphoBlue is IMorphoBase { /// @notice The state of the position of `user` on the market corresponding to `id`. /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest /// accrual. function position(Id id, address user) external view returns (MorphoBluePosition memory p); /// @notice The state of the market corresponding to `id`. /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual. /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual. /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last /// interest accrual. function market(Id id) external view returns (Market memory m); /// @notice The market params corresponding to `id`. /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`. function idToMarketParams(Id id) external view returns (MarketParams memory); } abstract contract IUniswapV3Factory { function getPool(address token0, address token1, uint24 fee) external virtual view returns (address poolAddress); } abstract contract IUniswapV3Pool { struct Slot0 { // the current price uint160 sqrtPriceX96; // the current tick int24 tick; // the most-recently updated index of the observations array uint16 observationIndex; // the current maximum number of observations that are being stored uint16 observationCardinality; // the next maximum number of observations to store, triggered in observations.write uint16 observationCardinalityNext; // the current protocol fee as a percentage of the swap fee taken on withdrawal // represented as an integer denominator (1/x)% uint8 feeProtocol; // whether the pool is locked bool unlocked; } function slot0() external virtual view returns (Slot0 memory); function fee() external virtual view returns (uint24 fee); function flash(address recipient, uint256 amount0, uint256 amount1, bytes memory data) external virtual; } abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; error ReentrantCall(); constructor () { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true if (_status == _ENTERED){ revert ReentrantCall(); } // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } } 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 FLAction is ActionBase, ReentrancyGuard, IFlashLoanBase, FLHelper { using TokenUtils for address; /// @dev FL Initiator must be this contract error UntrustedInitiator(); /// @dev Caller in these functions must be relevant FL source address error UntrustedLender(); // Wrong FL payback amount sent error WrongPaybackAmountError(); // When FL source is not found error NonexistentFLSource(); enum FLSource { EMPTY, AAVEV2, BALANCER, GHO, MAKER, AAVEV3, UNIV3, SPARK, MORPHO_BLUE, CURVEUSD } /// @inheritdoc ActionBase function actionType() public pure override returns (uint8) { return uint8(ActionType.FL_ACTION); } // solhint-disable-next-line no-empty-blocks function executeActionDirect(bytes memory _callData) public payable override {} /// @inheritdoc ActionBase /// @notice This action doesn't use flParamGetterAddr and flParamGetterData /// @notice flParamGetterData is used to choose between FL providers function executeAction( bytes memory _callData, bytes32[] memory, uint8[] memory, bytes32[] memory ) public payable override returns (bytes32) { FlashLoanParams memory params = abi.decode(_callData, (FlashLoanParams)); FLSource flSource = FLSource(uint8(bytes1(params.flParamGetterData))); handleFlashloan(params, flSource); return bytes32(params.amounts[0]); } function handleFlashloan(FlashLoanParams memory _flParams, FLSource _source) internal { if (_source == FLSource.AAVEV2) { _flAaveV2(_flParams); } else if (_source == FLSource.BALANCER) { _flBalancer(_flParams); } else if (_source == FLSource.GHO) { _flGho(_flParams); } else if (_source == FLSource.MAKER) { _flMaker(_flParams); } else if (_source == FLSource.AAVEV3) { _flAaveV3(_flParams); } else if (_source == FLSource.UNIV3) { _flUniV3(_flParams); } else if (_source == FLSource.SPARK) { _flSpark(_flParams); } else if (_source == FLSource.MORPHO_BLUE) { _flMorphoBlue(_flParams); } else if (_source == FLSource.CURVEUSD) { _flCurveUSD(_flParams); } else { revert NonexistentFLSource(); } } /// @notice Gets a Fl from Aave and returns back the execution to the action address /// @param _flParams All the amounts/tokens and related aave fl data function _flAaveV2(FlashLoanParams memory _flParams) internal { ILendingPoolV2(AAVE_LENDING_POOL).flashLoan( address(this), _flParams.tokens, _flParams.amounts, _flParams.modes, _flParams.onBehalfOf, _flParams.recipeData, AAVE_REFERRAL_CODE ); emit ActionEvent( "FLAction", abi.encode( "AAVEV2", _flParams.tokens, _flParams.amounts, _flParams.modes, _flParams.onBehalfOf ) ); } /// @notice Gets a Fl from Aave V3 and returns back the execution to the action address /// @param _flParams All the amounts/tokens and related aave fl data function _flAaveV3(FlashLoanParams memory _flParams) internal { ILendingPoolV2(AAVE_V3_LENDING_POOL).flashLoan( address(this), _flParams.tokens, _flParams.amounts, _flParams.modes, _flParams.onBehalfOf, _flParams.recipeData, AAVE_REFERRAL_CODE ); emit ActionEvent( "FLAction", abi.encode( "AAVEV3", _flParams.tokens, _flParams.amounts, _flParams.modes, _flParams.onBehalfOf ) ); } /// @notice Gets a FL from Balancer and returns back the execution to the action address function _flBalancer(FlashLoanParams memory _flParams) internal { IFlashLoans(VAULT_ADDR).flashLoan( address(this), _flParams.tokens, _flParams.amounts, _flParams.recipeData ); emit ActionEvent("FLAction", abi.encode("BALANCER", _flParams)); } /// @notice Gets a GHO FL from Gho Flash Minter function _flGho(FlashLoanParams memory _flParams) internal { IERC3156FlashLender(GHO_FLASH_MINTER_ADDR).flashLoan( IERC3156FlashBorrower(address(this)), GHO_ADDR, _flParams.amounts[0], _flParams.recipeData ); emit ActionEvent("FLAction", abi.encode("GHO", _flParams.amounts[0])); } /// @notice Gets a DAI flash loan from Maker and returns back the execution to the action address /// @param _flParams All the amounts/tokens and related aave fl data function _flMaker(FlashLoanParams memory _flParams) internal { IERC3156FlashLender(DSS_FLASH_ADDR).flashLoan( IERC3156FlashBorrower(address(this)), DAI_ADDR, _flParams.amounts[0], _flParams.recipeData ); emit ActionEvent("FLAction", abi.encode("MAKER", _flParams.amounts[0])); } function _flUniV3(FlashLoanParams memory _flParams) internal { // modes aren't used so we set them to later know starting balances _flParams.modes = new uint256[](2); _flParams.modes[0] = _flParams.amounts[0] > 0 ? _flParams.tokens[0].getBalance(address(this)) : 0; _flParams.modes[1] = _flParams.amounts[1] > 0 ? _flParams.tokens[1].getBalance(address(this)) : 0; /// @dev FlashLoanParams.tokens, first two array indexes contain tokens, third index contains pool address IUniswapV3Pool(_flParams.tokens[2]).flash( address(this), _flParams.amounts[0], _flParams.amounts[1], abi.encode(_flParams) ); emit ActionEvent("FLAction", abi.encode("UNIV3", _flParams.amounts[0])); } /// @notice Gets a Fl from Spark and returns back the execution to the action address function _flSpark(FlashLoanParams memory _flParams) internal { ILendingPoolV2(SPARK_LENDING_POOL).flashLoan( address(this), _flParams.tokens, _flParams.amounts, _flParams.modes, _flParams.onBehalfOf, _flParams.recipeData, SPARK_REFERRAL_CODE ); emit ActionEvent("FLAction", abi.encode("SPARK", _flParams.amounts[0])); } /// @notice Gets a FL from Morpho blue and returns back the execution to the action address function _flMorphoBlue(FlashLoanParams memory _params) internal { IMorphoBlue(MORPHO_BLUE_ADDR).flashLoan( _params.tokens[0], _params.amounts[0], abi.encode(_params.recipeData, _params.tokens[0]) ); emit ActionEvent("FLAction", abi.encode("MORPHOBLUE", _params.amounts[0])); } function _flCurveUSD(FlashLoanParams memory _params) internal { IERC3156FlashLender(CURVEUSD_FLASH_ADDR).flashLoan( IERC3156FlashBorrower(address(this)), CURVEUSD_ADDR, _params.amounts[0], _params.recipeData ); emit ActionEvent("FLAction", abi.encode("CURVEUSD", _params.amounts[0])); } /// @notice Aave callback function that formats and calls back RecipeExecutor /// FLSource == AAVE | SPARK function executeOperation( address[] memory _assets, uint256[] memory _amounts, uint256[] memory _fees, address _initiator, bytes memory _params ) public nonReentrant returns (bool) { if (msg.sender != AAVE_LENDING_POOL && msg.sender != AAVE_V3_LENDING_POOL && msg.sender != SPARK_LENDING_POOL) { revert UntrustedLender(); } if (_initiator != address(this)) { revert UntrustedInitiator(); } (Recipe memory currRecipe, address wallet) = abi.decode(_params, (Recipe, address)); uint256[] memory balancesBefore = new uint256[](_assets.length); // Send FL amounts to user wallet for (uint256 i = 0; i < _assets.length; ++i) { _assets[i].withdrawTokens(wallet, _amounts[i]); balancesBefore[i] = _assets[i].getBalance(address(this)); } _executeRecipe(wallet, isDSProxy(wallet), currRecipe, _amounts[0] + _fees[0]); // return FL for (uint256 i = 0; i < _assets.length; i++) { uint256 paybackAmount = _amounts[i] + _fees[i]; bool correctAmount = _assets[i].getBalance(address(this)) == paybackAmount + balancesBefore[i]; if (_assets[i] == ST_ETH_ADDR && !correctAmount) { flFeeFaucet.my2Wei(ST_ETH_ADDR); correctAmount = true; } if (!correctAmount) { revert WrongPaybackAmountError(); } _assets[i].approveToken(address(msg.sender), paybackAmount); } return true; } /// @notice Balancer FL callback function that formats and calls back RecipeExecutor /// FLSource == BALANCER function receiveFlashLoan( address[] memory _tokens, uint256[] memory _amounts, uint256[] memory _feeAmounts, bytes memory _userData ) external nonReentrant { if (msg.sender != VAULT_ADDR) { revert UntrustedLender(); } (Recipe memory currRecipe, address wallet) = abi.decode(_userData, (Recipe, address)); uint256[] memory balancesBefore = new uint256[](_tokens.length); for (uint256 i = 0; i < _tokens.length; i++) { _tokens[i].withdrawTokens(wallet, _amounts[i]); balancesBefore[i] = _tokens[i].getBalance(address(this)); } _executeRecipe(wallet, isDSProxy(wallet), currRecipe, _amounts[0] + _feeAmounts[0]); for (uint256 i = 0; i < _tokens.length; i++) { uint256 paybackAmount = _amounts[i] + (_feeAmounts[i]); if (_tokens[i].getBalance(address(this)) != paybackAmount + balancesBefore[i]) { revert WrongPaybackAmountError(); } _tokens[i].withdrawTokens(address(VAULT_ADDR), paybackAmount); } } /// @notice ERC3156 callback function that formats and calls back RecipeExecutor /// FLSource == MAKER | GHO | CURVEUSD function onFlashLoan( address _initiator, address _token, uint256 _amount, uint256 _fee, bytes calldata _data ) external nonReentrant returns (bytes32) { if (msg.sender != DSS_FLASH_ADDR && msg.sender != GHO_FLASH_MINTER_ADDR && msg.sender != CURVEUSD_FLASH_ADDR) { revert UntrustedLender(); } if (_initiator != address(this)) { revert UntrustedInitiator(); } (Recipe memory currRecipe, address wallet) = abi.decode(_data, (Recipe, address)); _token.withdrawTokens(wallet, _amount); uint256 balanceBefore = _token.getBalance(address(this)); uint256 paybackAmount = _amount +_fee; _executeRecipe(wallet, isDSProxy(wallet), currRecipe, paybackAmount); if (_token.getBalance(address(this)) != paybackAmount + balanceBefore) { revert WrongPaybackAmountError(); } if (msg.sender == CURVEUSD_FLASH_ADDR) { _token.withdrawTokens(msg.sender, paybackAmount); } else { _token.approveToken(msg.sender, paybackAmount); } return keccak256("ERC3156FlashBorrower.onFlashLoan"); } function uniswapV3FlashCallback( uint256 _fee0, uint256 _fee1, bytes memory _params ) external nonReentrant { FlashLoanParams memory params = abi.decode(_params, (FlashLoanParams)); { uint24 fee = IUniswapV3Pool(msg.sender).fee(); address realPool = IUniswapV3Factory(UNI_V3_FACTORY).getPool(params.tokens[0], params.tokens[1], uint24(fee)); if (msg.sender != realPool) revert UntrustedLender(); } (Recipe memory currRecipe, address wallet) = abi.decode(params.recipeData, (Recipe, address)); params.tokens[0].withdrawTokens(wallet, params.amounts[0]); params.tokens[1].withdrawTokens(wallet, params.amounts[1]); _executeRecipe(wallet, isDSProxy(wallet), currRecipe, params.amounts[0]); uint256 expectedBalance0 = params.modes[0] + params.amounts[0] + _fee0; uint256 expectedBalance1 = params.modes[1] + params.amounts[1] + _fee1; uint256 currBalance0 = params.amounts[0] > 0 ? params.tokens[0].getBalance(address(this)) : 0; uint256 currBalance1 = params.amounts[1] > 0 ? params.tokens[1].getBalance(address(this)) : 0; bool isCorrectAmount0 = currBalance0 == expectedBalance0; bool isCorrectAmount1 = currBalance1 == expectedBalance1; if (params.amounts[0] > 0 && params.tokens[0] == ST_ETH_ADDR && !isCorrectAmount0) { flFeeFaucet.my2Wei(ST_ETH_ADDR); isCorrectAmount0 = true; } if (params.amounts[1] > 0 && params.tokens[1] == ST_ETH_ADDR && !isCorrectAmount1) { flFeeFaucet.my2Wei(ST_ETH_ADDR); isCorrectAmount1 = true; } if (!isCorrectAmount0) revert WrongPaybackAmountError(); if (!isCorrectAmount1) revert WrongPaybackAmountError(); params.tokens[0].withdrawTokens(msg.sender, params.amounts[0] + _fee0); params.tokens[1].withdrawTokens(msg.sender, params.amounts[1] + _fee1); } function onMorphoFlashLoan(uint256 assets, bytes calldata data) external nonReentrant{ if (msg.sender != MORPHO_BLUE_ADDR) { revert UntrustedLender(); } (bytes memory taskData, address token) = abi.decode(data, (bytes, address)); (Recipe memory currRecipe, address wallet) = abi.decode(taskData, (Recipe, address)); token.withdrawTokens(wallet, assets); uint256 balanceBefore = token.getBalance(address(this)); _executeRecipe(wallet, isDSProxy(wallet), currRecipe, assets); if (token.getBalance(address(this)) != assets + balanceBefore) { revert WrongPaybackAmountError(); } token.approveToken(MORPHO_BLUE_ADDR, assets); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"NonContractCall","type":"error"},{"inputs":[],"name":"NonexistentFLSource","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"inputs":[],"name":"ReturnIndexValueError","type":"error"},{"inputs":[],"name":"SafeExecutionError","type":"error"},{"inputs":[],"name":"SenderNotAdmin","type":"error"},{"inputs":[],"name":"SenderNotOwner","type":"error"},{"inputs":[],"name":"SubIndexValueError","type":"error"},{"inputs":[],"name":"UntrustedInitiator","type":"error"},{"inputs":[],"name":"UntrustedLender","type":"error"},{"inputs":[],"name":"WrongPaybackAmountError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"logName","type":"string"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"ActionEvent","type":"event"},{"inputs":[],"name":"CALLBACK_SELECTOR","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NO_PARAM_MAPPING","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RETURN_MAX_INDEX_VALUE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RETURN_MIN_INDEX_VALUE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUB_MAX_INDEX_VALUE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUB_MIN_INDEX_VALUE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"actionType","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"adminVault","outputs":[{"internalType":"contract AdminVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_callData","type":"bytes"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"},{"internalType":"uint8[]","name":"","type":"uint8[]"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"executeAction","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_callData","type":"bytes"}],"name":"executeActionDirect","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_assets","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"_fees","type":"uint256[]"},{"internalType":"address","name":"_initiator","type":"address"},{"internalType":"bytes","name":"_params","type":"bytes"}],"name":"executeOperation","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flFeeFaucet","outputs":[{"internalType":"contract FLFeeFaucet","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_proxy","type":"address"}],"name":"isDSProxy","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"logger","outputs":[{"internalType":"contract DefisaverLogger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiator","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"onFlashLoan","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onMorphoFlashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"_feeAmounts","type":"uint256[]"},{"internalType":"bytes","name":"_userData","type":"bytes"}],"name":"receiveFlashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract DFSRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee0","type":"uint256"},{"internalType":"uint256","name":"_fee1","type":"uint256"},{"internalType":"bytes","name":"_params","type":"bytes"}],"name":"uniswapV3FlashCallback","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
608060405234801561000f575f80fd5b5060015f55614254806100215f395ff3fe608060405260043610610162575f3560e01c80638cedca71116100c6578063b20e80041161007c578063e9cbafb011610057578063e9cbafb0146103be578063f04f2707146103dd578063f24ccbfe146103fc575f80fd5b8063b20e800414610364578063c579d4901461038b578063d3c2e7ed146103aa575f80fd5b8063920f5c84116100ac578063920f5c84146102e55780639864dcdd146103045780639ce3e91914610318575f80fd5b80638cedca71146102ab5780638df50f74146102d2575f80fd5b806331f570721161011b57806341c0e1b51161010157806341c0e1b5146102445780637b103999146102585780638bcb621614610297575f80fd5b806331f5707214610212578063389f87ff14610233575f80fd5b806323e30c8b1161014b57806323e30c8b146101c0578063247492f8146101ed5780632fa13cb8146101ff575f80fd5b80630f2eee42146101665780631c451ceb14610191575b5f80fd5b348015610171575f80fd5b5061017a608081565b60405160ff90911681526020015b60405180910390f35b34801561019c575f80fd5b506101b06101ab366004612ce0565b610423565b6040519015158152602001610188565b3480156101cb575f80fd5b506101df6101da366004612d40565b6104be565b604051908152602001610188565b3480156101f8575f80fd5b505f61017a565b34801561020a575f80fd5b5061017a5f81565b34801561021d575f80fd5b5061023161022c366004612db7565b6106a7565b005b610231610241366004612f09565b50565b34801561024f575f80fd5b506102316107de565b348015610263575f80fd5b5061027f73287778f121f134c66212fb16c9b53ec991d32f5b81565b6040516001600160a01b039091168152602001610188565b3480156102a2575f80fd5b5061017a600181565b3480156102b6575f80fd5b5061027f73ccf3d848e08b94478ed8f46ffead3008faf581fd81565b6101df6102e0366004613038565b61089f565b3480156102f0575f80fd5b506101b06102ff366004613143565b610913565b34801561030f575f80fd5b5061017a607f81565b348015610323575f80fd5b5061034b7ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b881565b6040516001600160e01b03199091168152602001610188565b34801561036f575f80fd5b5061027f7347f159c90850d5ce09e21f931d504536840f34b481565b348015610396575f80fd5b506102316103a53660046131fd565b610d0a565b3480156103b5575f80fd5b5061017a60ff81565b3480156103c9575f80fd5b506102316103d836600461323b565b610e3e565b3480156103e8575f80fd5b506102316103f7366004613287565b611493565b348015610407575f80fd5b5061027f73ce7a977cac4a481bc84ac06b2da0df614e621cf381565b6040517f297103880000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201525f9073a26e15c895efc0616177b7c1e7270a4c7d51c99790632971038890602401602060405180830381865afa158015610494573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104b89190613321565b92915050565b5f60025f54036104e1576040516306fda65d60e31b815260040160405180910390fd5b60025f55337360744434d6339a6b27d73d9eda62b6f66a0a04fa1480159061051d57503373b639d208bcf0589d54fac24e655c79ec529762b814155b801561053d57503373a7a4bb50af91f90b6feb3388e7f8286af45b299b14155b1561055b5760405163245ffd9960e11b815260040160405180910390fd5b6001600160a01b0387163014610584576040516301000cbb60e11b815260040160405180910390fd5b5f80610592848601866134c0565b90925090506105ab6001600160a01b03891682896116da565b505f6105c06001600160a01b038a163061180b565b90505f6105cd888a6135c6565b90506105e3836105dc85610423565b86846118ca565b6105ed82826135c6565b6106006001600160a01b038c163061180b565b1461061e576040516333dcb1d760e21b815260040160405180910390fd5b7fffffffffffffffffffffffff585b44af506e06f49014cc771807d7950ba4d665330161065f576106596001600160a01b038b1633836116da565b50610673565b6106736001600160a01b038b163383611ada565b505060015f55507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd998975050505050505050565b60025f54036106c9576040516306fda65d60e31b815260040160405180910390fd5b60025f553373bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb146107015760405163245ffd9960e11b815260040160405180910390fd5b5f8061070f838501856135f9565b915091505f808380602001905181019061072991906138d7565b90925090506107426001600160a01b03841682896116da565b505f6107576001600160a01b0385163061180b565b905061076d8261076684610423565b858b6118ca565b61077781896135c6565b61078a6001600160a01b0386163061180b565b146107a8576040516333dcb1d760e21b815260040160405180910390fd5b6107d06001600160a01b03851673bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb8a611ada565b505060015f55505050505050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610838573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061085c91906139d4565b6001600160a01b03161461089c576040517fa6c827a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33ff5b5f80858060200190518101906108b59190613a54565b90505f8160a001516108c690613b6a565b60f81c60098111156108da576108da6135e5565b90506108e68282611ba8565b81602001515f815181106108fc576108fc613bb9565b60200260200101515f1b925050505b949350505050565b5f60025f5403610936576040516306fda65d60e31b815260040160405180910390fd5b60025f5533737d2768de32b0b80b7a3454c06bdac94a69ddc7a9148015906109725750337387870bca3f3fd6335c3f4ce8392d69350b4fa4e214155b801561099257503373c13e21b648a5ee794902342038ff3adab66be98714155b156109b05760405163245ffd9960e11b815260040160405180910390fd5b6001600160a01b03831630146109d9576040516301000cbb60e11b815260040160405180910390fd5b5f80838060200190518101906109ef91906138d7565b915091505f885167ffffffffffffffff811115610a0e57610a0e612dff565b604051908082528060200260200182016040528015610a37578160200160208202803683370190505b5090505f5b8951811015610af057610a95838a8381518110610a5b57610a5b613bb9565b60200260200101518c8481518110610a7557610a75613bb9565b60200260200101516001600160a01b03166116da9092919063ffffffff16565b50610acb308b8381518110610aac57610aac613bb9565b60200260200101516001600160a01b031661180b90919063ffffffff16565b828281518110610add57610add613bb9565b6020908102919091010152600101610a3c565b50610b4282610afe84610423565b858a5f81518110610b1157610b11613bb9565b60200260200101518c5f81518110610b2b57610b2b613bb9565b6020026020010151610b3d91906135c6565b6118ca565b5f5b8951811015610cf5575f888281518110610b6057610b60613bb9565b60200260200101518a8381518110610b7a57610b7a613bb9565b6020026020010151610b8c91906135c6565b90505f838381518110610ba157610ba1613bb9565b602002602001015182610bb491906135c6565b610bca308e8681518110610aac57610aac613bb9565b14905073ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b03168c8481518110610bfd57610bfd613bb9565b60200260200101516001600160a01b0316148015610c19575080155b15610c96576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b158015610c7b575f80fd5b505af1158015610c8d573d5f803e3d5ffd5b50505050600190505b80610cb4576040516333dcb1d760e21b815260040160405180910390fd5b610ceb33838e8681518110610ccb57610ccb613bb9565b60200260200101516001600160a01b0316611ada9092919063ffffffff16565b5050600101610b44565b506001935050505060015f5595945050505050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d64573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d8891906139d4565b6001600160a01b031614610dc8576040517f19494c8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03610e25576040516001600160a01b0383169082156108fc029083905f818181858888f19350505050158015610e1f573d5f803e3d5ffd5b50505050565b610e396001600160a01b0384168383611d10565b505050565b60025f5403610e60576040516306fda65d60e31b815260040160405180910390fd5b60025f9081558151610e7b9083016020908101908401613a54565b90505f336001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eba573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ede9190613bcd565b90505f731f98431c8ad98523631ae4a59f267346ea31f9846001600160a01b0316631698ee82845f01515f81518110610f1957610f19613bb9565b6020026020010151855f0151600181518110610f3757610f37613bb9565b60209081029190910101516040516001600160e01b031960e085901b1681526001600160a01b0392831660048201529116602482015262ffffff85166044820152606401602060405180830381865afa158015610f96573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fba91906139d4565b9050336001600160a01b03821614610fe55760405163245ffd9960e11b815260040160405180910390fd5b50505f808260c0015180602001905181019061100191906138d7565b9150915061103c8184602001515f8151811061101f5761101f613bb9565b6020026020010151855f01515f81518110610a7557610a75613bb9565b5061107681846020015160018151811061105857611058613bb9565b6020026020010151855f0151600181518110610a7557610a75613bb9565b506110a88161108483610423565b8486602001515f8151811061109b5761109b613bb9565b60200260200101516118ca565b5f8684602001515f815181106110c0576110c0613bb9565b602002602001015185604001515f815181106110de576110de613bb9565b60200260200101516110f091906135c6565b6110fa91906135c6565b90505f86856020015160018151811061111557611115613bb9565b6020026020010151866040015160018151811061113457611134613bb9565b602002602001015161114691906135c6565b61115091906135c6565b90505f8086602001515f8151811061116a5761116a613bb9565b60200260200101511161117d575f611196565b61119630875f01515f81518110610aac57610aac613bb9565b90505f8087602001516001815181106111b1576111b1613bb9565b6020026020010151116111c4575f6111de565b6111de30885f0151600181518110610aac57610aac613bb9565b6020880151805191925083861491858414915f91829061120057611200613bb9565b602002602001015111801561124c57508851805173ae7ab96520de3a18e5e111b5eaab095312d7fe8491905f9061123957611239613bb9565b60200260200101516001600160a01b0316145b8015611256575081155b156112d3576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b1580156112b8575f80fd5b505af11580156112ca573d5f803e3d5ffd5b50505050600191505b5f89602001516001815181106112eb576112eb613bb9565b602002602001015111801561133a57508851805173ae7ab96520de3a18e5e111b5eaab095312d7fe849190600190811061132757611327613bb9565b60200260200101516001600160a01b0316145b8015611344575080155b156113c1576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b1580156113a6575f80fd5b505af11580156113b8573d5f803e3d5ffd5b50505050600190505b816113df576040516333dcb1d760e21b815260040160405180910390fd5b806113fd576040516333dcb1d760e21b815260040160405180910390fd5b61143c338d8b602001515f8151811061141857611418613bb9565b602002602001015161142a91906135c6565b8b5180515f90610a7557610a75613bb9565b50611480338c8b6020015160018151811061145957611459613bb9565b602002602001015161146b91906135c6565b8b5180516001908110610a7557610a75613bb9565b505060015f555050505050505050505050565b60025f54036114b5576040516306fda65d60e31b815260040160405180910390fd5b60025f553373ba12222222228d8ba445958a75a0704d566bf2c8146114ed5760405163245ffd9960e11b815260040160405180910390fd5b5f808280602001905181019061150391906138d7565b915091505f865167ffffffffffffffff81111561152257611522612dff565b60405190808252806020026020018201604052801561154b578160200160208202803683370190505b5090505f5b87518110156115c5576115898388838151811061156f5761156f613bb9565b60200260200101518a8481518110610a7557610a75613bb9565b506115a030898381518110610aac57610aac613bb9565b8282815181106115b2576115b2613bb9565b6020908102919091010152600101611550565b50611600826115d384610423565b85885f815181106115e6576115e6613bb9565b60200260200101518a5f81518110610b2b57610b2b613bb9565b5f5b87518110156107d0575f86828151811061161e5761161e613bb9565b602002602001015188838151811061163857611638613bb9565b602002602001015161164a91906135c6565b905082828151811061165e5761165e613bb9565b60200260200101518161167191906135c6565b611687308b8581518110610aac57610aac613bb9565b146116a5576040516333dcb1d760e21b815260040160405180910390fd5b6116d073ba12222222228d8ba445958a75a0704d566bf2c8828b8581518110610a7557610a75613bb9565b5050600101611602565b5f5f1982036116f0576116ed843061180b565b91505b6001600160a01b0383161580159061171157506001600160a01b0383163014155b801561171c57508115155b15611804576001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461175e576117596001600160a01b0385168484611d10565b611804565b5f836001600160a01b0316836040515f6040518083038185875af1925050503d805f81146117a7576040519150601f19603f3d011682016040523d82523d5f602084013e6117ac565b606091505b50509050806118025760405162461bcd60e51b815260206004820152600d60248201527f4574682073656e64206661696c0000000000000000000000000000000000000060448201526064015b60405180910390fd5b505b5092915050565b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161184257506001600160a01b038116316104b8565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa15801561189f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118c39190613bef565b9392505050565b82156119b657836001600160a01b0316631cff79cd47735029336642814bc51a42ba80bf83a6322110035d7ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b88686604051602401611929929190613d15565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199485161790525160e086901b909216825261196f9291600401613df8565b60206040518083038185885af115801561198b573d5f803e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906119b09190613bef565b50610e1f565b5f846001600160a01b031663468721a7735029336642814bc51a42ba80bf83a6322110035d477ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b88787604051602401611a10929190613d15565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199485161790525160e086901b9092168252611a5a939291600190600401613e19565b6020604051808303815f875af1158015611a76573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a9a9190613321565b905080611ad3576040517fe540c1c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601611b0457505050565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015282919085169063dd62ed3e90604401602060405180830381865afa158015611b6a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b8e9190613bef565b1015610e3957610e396001600160a01b0384168383611d8c565b6001816009811115611bbc57611bbc6135e5565b03611bce57611bca82611e1a565b5050565b6002816009811115611be257611be26135e5565b03611bf057611bca82611f2c565b6003816009811115611c0457611c046135e5565b03611c1257611bca8261200a565b6004816009811115611c2657611c266135e5565b03611c3457611bca82612167565b6005816009811115611c4857611c486135e5565b03611c5657611bca826122c4565b6006816009811115611c6a57611c6a6135e5565b03611c7857611bca826123b1565b6007816009811115611c8c57611c8c6135e5565b03611c9a57611bca8261261d565b6008816009811115611cae57611cae6135e5565b03611cbc57611bca82612745565b6009816009811115611cd057611cd06135e5565b03611cde57611bca826128ce565b6040517f2be02e1100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516001600160a01b038316602482015260448101829052610e399084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612a2b565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611ddd8482612b11565b610e1f576040516001600160a01b03841660248201525f6044820152611e1090859063095ea7b360e01b90606401611d55565b610e1f8482612a2b565b80516020820151604080840151606085015160c0860151835163ab9c4b5d60e01b8152737d2768de32b0b80b7a3454c06bdac94a69ddc7a99663ab9c4b5d96611e70963096929591949293919291600401613ea5565b5f604051808303815f87803b158015611e87575f80fd5b505af1158015611e99573d5f803e3d5ffd5b50505050604051611eb89067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce3825f0151836020015184604001518560600151604051602001611f079493929190613f23565b60408051601f1981840301815290829052611f2191613faa565b60405180910390a250565b8051602082015160c08301516040517f5c38449e00000000000000000000000000000000000000000000000000000000815273ba12222222228d8ba445958a75a0704d566bf2c893635c38449e93611f88933093600401613fbc565b5f604051808303815f87803b158015611f9f575f80fd5b505af1158015611fb1573d5f803e3d5ffd5b50505050604051611fd09067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382604051602001611f0791906140ac565b73b639d208bcf0589d54fac24e655c79ec529762b86001600160a01b0316635cffe9de307340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f84602001515f8151811061205957612059613bb9565b60200260200101518560c001516040518563ffffffff1660e01b815260040161208594939291906140f2565b6020604051808303815f875af11580156120a1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120c59190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061211957612119613bb9565b60209081029190910181015160408051928301819052600360608401527f47484f0000000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b7360744434d6339a6b27d73d9eda62b6f66a0a04fa6001600160a01b0316635cffe9de30736b175474e89094c44da98b954eedeac495271d0f84602001515f815181106121b6576121b6613bb9565b60200260200101518560c001516040518563ffffffff1660e01b81526004016121e294939291906140f2565b6020604051808303815f875af11580156121fe573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122229190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061227657612276613bb9565b60209081029190910181015160408051928301819052600560608401527f4d414b4552000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b80516020820151604080840151606085015160c0860151835163ab9c4b5d60e01b81527387870bca3f3fd6335c3f4ce8392d69350b4fa4e29663ab9c4b5d9661231a963096929591949293919291600401613ea5565b5f604051808303815f87803b158015612331575f80fd5b505af1158015612343573d5f803e3d5ffd5b505050506040516123629067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce3825f0151836020015184604001518560600151604051602001611f07949392919061412d565b60408051600280825260608201835290916020830190803683375050506040820152602081015180515f919082906123eb576123eb613bb9565b6020026020010151116123fe575f612417565b61241730825f01515f81518110610aac57610aac613bb9565b81604001515f8151811061242d5761242d613bb9565b6020026020010181815250505f816020015160018151811061245157612451613bb9565b602002602001015111612464575f61247e565b61247e30825f0151600181518110610aac57610aac613bb9565b816040015160018151811061249557612495613bb9565b60209081029190910101528051805160029081106124b5576124b5613bb9565b60200260200101516001600160a01b031663490e6cbc3083602001515f815181106124e2576124e2613bb9565b6020026020010151846020015160018151811061250157612501613bb9565b60200260200101518560405160200161251a9190614173565b6040516020818303038152906040526040518563ffffffff1660e01b81526004016125489493929190614185565b5f604051808303815f87803b15801561255f575f80fd5b505af1158015612571573d5f803e3d5ffd5b505050506040516125909067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106125cf576125cf613bb9565b60209081029190910181015160408051928301819052600560608401527f554e495633000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b80516020820151604080840151606085015160c0860151925163ab9c4b5d60e01b815273c13e21b648a5ee794902342038ff3adab66be9879563ab9c4b5d95612670953095929491935f90600401613ea5565b5f604051808303815f87803b158015612687575f80fd5b505af1158015612699573d5f803e3d5ffd5b505050506040516126b89067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106126f7576126f7613bb9565b60209081029190910181015160408051928301819052600560608401527f535041524b000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b8051805173bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb9163e0232b42915f9061277357612773613bb9565b602002602001015183602001515f8151811061279157612791613bb9565b60200260200101518460c00151855f01515f815181106127b3576127b3613bb9565b60200260200101516040516020016127cc9291906141b2565b6040516020818303038152906040526040518463ffffffff1660e01b81526004016127f9939291906141dc565b5f604051808303815f87803b158015612810575f80fd5b505af1158015612822573d5f803e3d5ffd5b505050506040516128419067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061288057612880613bb9565b60209081029190910181015160408051928301819052600a60608401527f4d4f5250484f424c554500000000000000000000000000000000000000000000608084015282015260a001611f07565b73a7a4bb50af91f90b6feb3388e7f8286af45b299b6001600160a01b0316635cffe9de3073f939e0a03fb07f59a73314e73794be0e57ac1b4e84602001515f8151811061291d5761291d613bb9565b60200260200101518560c001516040518563ffffffff1660e01b815260040161294994939291906140f2565b6020604051808303815f875af1158015612965573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129899190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106129dd576129dd613bb9565b60209081029190910181015160408051928301819052600860608401527f4355525645555344000000000000000000000000000000000000000000000000608084015282015260a001611f07565b5f612a7f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612bb29092919063ffffffff16565b905080515f1480612a9f575080806020019051810190612a9f9190613321565b610e395760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016117f9565b5f805f846001600160a01b031684604051612b2c9190614203565b5f604051808303815f865af19150503d805f8114612b65576040519150601f19603f3d011682016040523d82523d5f602084013e612b6a565b606091505b5091509150818015612b94575080511580612b94575080806020019051810190612b949190613321565b8015612ba957505f856001600160a01b03163b115b95945050505050565b606061090b84845f856060612bc685612c94565b612bfc576040517f304619b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80866001600160a01b03168587604051612c179190614203565b5f6040518083038185875af1925050503d805f8114612c51576040519150601f19603f3d011682016040523d82523d5f602084013e612c56565b606091505b50915091508115612c6a57915061090b9050565b805115612c7a5780518082602001fd5b8360405162461bcd60e51b81526004016117f99190613faa565b5f813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061090b575050151592915050565b6001600160a01b0381168114610241575f80fd5b5f60208284031215612cf0575f80fd5b81356118c381612ccc565b5f8083601f840112612d0b575f80fd5b50813567ffffffffffffffff811115612d22575f80fd5b602083019150836020828501011115612d39575f80fd5b9250929050565b5f805f805f8060a08789031215612d55575f80fd5b8635612d6081612ccc565b95506020870135612d7081612ccc565b94506040870135935060608701359250608087013567ffffffffffffffff811115612d99575f80fd5b612da589828a01612cfb565b979a9699509497509295939492505050565b5f805f60408486031215612dc9575f80fd5b83359250602084013567ffffffffffffffff811115612de6575f80fd5b612df286828701612cfb565b9497909650939450505050565b634e487b7160e01b5f52604160045260245ffd5b60405160a0810167ffffffffffffffff81118282101715612e3657612e36612dff565b60405290565b60405160e0810167ffffffffffffffff81118282101715612e3657612e36612dff565b604051601f8201601f1916810167ffffffffffffffff81118282101715612e8857612e88612dff565b604052919050565b5f67ffffffffffffffff821115612ea957612ea9612dff565b50601f01601f191660200190565b5f82601f830112612ec6575f80fd5b8135612ed9612ed482612e90565b612e5f565b818152846020838601011115612eed575f80fd5b816020850160208301375f918101602001919091529392505050565b5f60208284031215612f19575f80fd5b813567ffffffffffffffff811115612f2f575f80fd5b61090b84828501612eb7565b5f67ffffffffffffffff821115612f5457612f54612dff565b5060051b60200190565b5f82601f830112612f6d575f80fd5b81356020612f7d612ed483612f3b565b8083825260208201915060208460051b870101935086841115612f9e575f80fd5b602086015b84811015612fba5780358352918301918301612fa3565b509695505050505050565b60ff81168114610241575f80fd5b5f82601f830112612fe2575f80fd5b81356020612ff2612ed483612f3b565b8083825260208201915060208460051b870101935086841115613013575f80fd5b602086015b84811015612fba57803561302b81612fc5565b8352918301918301613018565b5f805f806080858703121561304b575f80fd5b843567ffffffffffffffff80821115613062575f80fd5b61306e88838901612eb7565b95506020870135915080821115613083575f80fd5b61308f88838901612f5e565b945060408701359150808211156130a4575f80fd5b6130b088838901612fd3565b935060608701359150808211156130c5575f80fd5b506130d287828801612f5e565b91505092959194509250565b5f82601f8301126130ed575f80fd5b813560206130fd612ed483612f3b565b8083825260208201915060208460051b87010193508684111561311e575f80fd5b602086015b84811015612fba57803561313681612ccc565b8352918301918301613123565b5f805f805f60a08688031215613157575f80fd5b853567ffffffffffffffff8082111561316e575f80fd5b61317a89838a016130de565b9650602088013591508082111561318f575f80fd5b61319b89838a01612f5e565b955060408801359150808211156131b0575f80fd5b6131bc89838a01612f5e565b9450606088013591506131ce82612ccc565b909250608087013590808211156131e3575f80fd5b506131f088828901612eb7565b9150509295509295909350565b5f805f6060848603121561320f575f80fd5b833561321a81612ccc565b9250602084013561322a81612ccc565b929592945050506040919091013590565b5f805f6060848603121561324d575f80fd5b8335925060208401359150604084013567ffffffffffffffff811115613271575f80fd5b61327d86828701612eb7565b9150509250925092565b5f805f806080858703121561329a575f80fd5b843567ffffffffffffffff808211156132b1575f80fd5b6132bd888389016130de565b955060208701359150808211156132d2575f80fd5b6132de88838901612f5e565b945060408701359150808211156132f3575f80fd5b6132ff88838901612f5e565b93506060870135915080821115613314575f80fd5b506130d287828801612eb7565b5f60208284031215613331575f80fd5b815180151581146118c3575f80fd5b5f82601f83011261334f575f80fd5b8135602061335f612ed483612f3b565b82815260059290921b8401810191818101908684111561337d575f80fd5b8286015b84811015612fba57803567ffffffffffffffff81111561339f575f80fd5b6133ad8986838b0101612eb7565b845250918301918301613381565b6001600160e01b031981168114610241575f80fd5b5f82601f8301126133df575f80fd5b813560206133ef612ed483612f3b565b8083825260208201915060208460051b870101935086841115613410575f80fd5b602086015b84811015612fba578035613428816133bb565b8352918301918301613415565b5f82601f830112613444575f80fd5b81356020613454612ed483612f3b565b82815260059290921b84018101918181019086841115613472575f80fd5b8286015b84811015612fba57803567ffffffffffffffff811115613494575f80fd5b6134a28986838b0101612fd3565b845250918301918301613476565b80356134bb81612ccc565b919050565b5f80604083850312156134d1575f80fd5b823567ffffffffffffffff808211156134e8575f80fd5b9084019060a082870312156134fb575f80fd5b613503612e13565b823582811115613511575f80fd5b61351d88828601612eb7565b825250602083013582811115613531575f80fd5b61353d88828601613340565b602083015250604083013582811115613554575f80fd5b61356088828601612f5e565b604083015250606083013582811115613577575f80fd5b613583888286016133d0565b60608301525060808301358281111561359a575f80fd5b6135a688828601613435565b60808301525093506135bd915050602084016134b0565b90509250929050565b808201808211156104b857634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f806040838503121561360a575f80fd5b823567ffffffffffffffff811115613620575f80fd5b61362c85828601612eb7565b925050602083013561363d81612ccc565b809150509250929050565b5f5b8381101561366257818101518382015260200161364a565b50505f910152565b5f82601f830112613679575f80fd5b8151613687612ed482612e90565b81815284602083860101111561369b575f80fd5b61090b826020830160208701613648565b5f82601f8301126136bb575f80fd5b815160206136cb612ed483612f3b565b82815260059290921b840181019181810190868411156136e9575f80fd5b8286015b84811015612fba57805167ffffffffffffffff81111561370b575f80fd5b6137198986838b010161366a565b8452509183019183016136ed565b5f82601f830112613736575f80fd5b81516020613746612ed483612f3b565b8083825260208201915060208460051b870101935086841115613767575f80fd5b602086015b84811015612fba578051835291830191830161376c565b5f82601f830112613792575f80fd5b815160206137a2612ed483612f3b565b8083825260208201915060208460051b8701019350868411156137c3575f80fd5b602086015b84811015612fba5780516137db816133bb565b83529183019183016137c8565b5f82601f8301126137f7575f80fd5b81516020613807612ed483612f3b565b828152600592831b8501820192828201919087851115613825575f80fd5b8387015b858110156138bf57805167ffffffffffffffff811115613847575f80fd5b8801603f81018a13613857575f80fd5b858101516040613869612ed483612f3b565b82815291851b8301810191888101908d841115613884575f80fd5b938201935b838510156138ae578451925061389e83612fc5565b8282529389019390890190613889565b885250505093850193508401613829565b5090979650505050505050565b80516134bb81612ccc565b5f80604083850312156138e8575f80fd5b825167ffffffffffffffff808211156138ff575f80fd5b9084019060a08287031215613912575f80fd5b61391a612e13565b825182811115613928575f80fd5b6139348882860161366a565b825250602083015182811115613948575f80fd5b613954888286016136ac565b60208301525060408301518281111561396b575f80fd5b61397788828601613727565b60408301525060608301518281111561398e575f80fd5b61399a88828601613783565b6060830152506080830151828111156139b1575f80fd5b6139bd888286016137e8565b60808301525093506135bd915050602084016138cc565b5f602082840312156139e4575f80fd5b81516118c381612ccc565b5f82601f8301126139fe575f80fd5b81516020613a0e612ed483612f3b565b8083825260208201915060208460051b870101935086841115613a2f575f80fd5b602086015b84811015612fba578051613a4781612ccc565b8352918301918301613a34565b5f60208284031215613a64575f80fd5b815167ffffffffffffffff80821115613a7b575f80fd5b9083019060e08286031215613a8e575f80fd5b613a96612e3c565b825182811115613aa4575f80fd5b613ab0878286016139ef565b825250602083015182811115613ac4575f80fd5b613ad087828601613727565b602083015250604083015182811115613ae7575f80fd5b613af387828601613727565b604083015250613b05606084016138cc565b6060820152613b16608084016138cc565b608082015260a083015182811115613b2c575f80fd5b613b388782860161366a565b60a08301525060c083015182811115613b4f575f80fd5b613b5b8782860161366a565b60c08301525095945050505050565b5f815160208301517fff0000000000000000000000000000000000000000000000000000000000000080821693506001831015613bb15780818460010360031b1b83161693505b505050919050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613bdd575f80fd5b815162ffffff811681146118c3575f80fd5b5f60208284031215613bff575f80fd5b5051919050565b5f8151808452613c1d816020860160208601613648565b601f01601f19169290920160200192915050565b5f815180845260208085019450602084015f5b83811015613c6057815187529582019590820190600101613c44565b509495945050505050565b5f815180845260208085019450602084015f5b83811015613c605781516001600160e01b03191687529582019590820190600101613c7e565b5f82825180855260208086019550808260051b8401018186015f5b848110156138bf57858303601f190189528151805180855290850190858501905f5b81811015613d0057835160ff1683529287019291870191600101613ce1565b50509985019993505090830190600101613cbf565b604081525f835160a06040840152613d3060e0840182613c06565b9050602080860151603f19808685030160608701528382518086528486019150848160051b87010185850194505f5b82811015613d8d57601f19888303018452613d7b828751613c06565b95870195938701939150600101613d5f565b5060408b01519650838982030160808a0152613da98188613c31565b965050505060608801519150808685030160a0870152613dc98483613c6b565b935060808801519150808685030160c087015250613de78382613ca4565b935050848185015250509392505050565b6001600160a01b0383168152604060208201525f61090b6040830184613c06565b6001600160a01b0385168152836020820152608060408201525f613e406080830185613c06565b905060028310613e5e57634e487b7160e01b5f52602160045260245ffd5b82606083015295945050505050565b5f815180845260208085019450602084015f5b83811015613c605781516001600160a01b031687529582019590820190600101613e80565b5f6001600160a01b03808a16835260e06020840152613ec760e084018a613e6d565b8381036040850152613ed9818a613c31565b90508381036060850152613eed8189613c31565b9050818716608085015283810360a0850152613f098187613c06565b9250505061ffff831660c083015298975050505050505050565b60a08152600660a08201527f414156455632000000000000000000000000000000000000000000000000000060c082015260e060208201525f613f6960e0830187613e6d565b8281036040840152613f7b8187613c31565b90508281036060840152613f8f8186613c31565b9150506001600160a01b038316608083015295945050505050565b602081525f6118c36020830184613c06565b6001600160a01b0385168152608060208201525f613fdd6080830186613e6d565b8281036040840152613fef8186613c31565b905082810360608401526140038185613c06565b979650505050505050565b5f815160e0845261402260e0850182613e6d565b90506020830151848203602086015261403b8282613c31565b915050604083015184820360408601526140558282613c31565b91505060608301516001600160a01b038082166060870152806080860151166080870152505060a083015184820360a08601526140928282613c06565b91505060c083015184820360c0860152612ba98282613c06565b60408152600860408201527f42414c414e4345520000000000000000000000000000000000000000000000006060820152608060208201525f6118c3608083018461400e565b5f6001600160a01b038087168352808616602084015250836040830152608060608301526141236080830184613c06565b9695505050505050565b60a08152600660a08201527f414156455633000000000000000000000000000000000000000000000000000060c082015260e060208201525f613f6960e0830187613e6d565b602081525f6118c3602083018461400e565b6001600160a01b0385168152836020820152826040820152608060608201525f6141236080830184613c06565b604081525f6141c46040830185613c06565b90506001600160a01b03831660208301529392505050565b6001600160a01b0384168152826020820152606060408201525f612ba96060830184613c06565b5f8251614214818460208701613648565b919091019291505056fea264697066735822122097a88cdfe1242c80d5b121a1d4a50e116a7b5ba94936c1f4bb450f95c4bae95464736f6c63430008180033
Deployed Bytecode
0x608060405260043610610162575f3560e01c80638cedca71116100c6578063b20e80041161007c578063e9cbafb011610057578063e9cbafb0146103be578063f04f2707146103dd578063f24ccbfe146103fc575f80fd5b8063b20e800414610364578063c579d4901461038b578063d3c2e7ed146103aa575f80fd5b8063920f5c84116100ac578063920f5c84146102e55780639864dcdd146103045780639ce3e91914610318575f80fd5b80638cedca71146102ab5780638df50f74146102d2575f80fd5b806331f570721161011b57806341c0e1b51161010157806341c0e1b5146102445780637b103999146102585780638bcb621614610297575f80fd5b806331f5707214610212578063389f87ff14610233575f80fd5b806323e30c8b1161014b57806323e30c8b146101c0578063247492f8146101ed5780632fa13cb8146101ff575f80fd5b80630f2eee42146101665780631c451ceb14610191575b5f80fd5b348015610171575f80fd5b5061017a608081565b60405160ff90911681526020015b60405180910390f35b34801561019c575f80fd5b506101b06101ab366004612ce0565b610423565b6040519015158152602001610188565b3480156101cb575f80fd5b506101df6101da366004612d40565b6104be565b604051908152602001610188565b3480156101f8575f80fd5b505f61017a565b34801561020a575f80fd5b5061017a5f81565b34801561021d575f80fd5b5061023161022c366004612db7565b6106a7565b005b610231610241366004612f09565b50565b34801561024f575f80fd5b506102316107de565b348015610263575f80fd5b5061027f73287778f121f134c66212fb16c9b53ec991d32f5b81565b6040516001600160a01b039091168152602001610188565b3480156102a2575f80fd5b5061017a600181565b3480156102b6575f80fd5b5061027f73ccf3d848e08b94478ed8f46ffead3008faf581fd81565b6101df6102e0366004613038565b61089f565b3480156102f0575f80fd5b506101b06102ff366004613143565b610913565b34801561030f575f80fd5b5061017a607f81565b348015610323575f80fd5b5061034b7ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b881565b6040516001600160e01b03199091168152602001610188565b34801561036f575f80fd5b5061027f7347f159c90850d5ce09e21f931d504536840f34b481565b348015610396575f80fd5b506102316103a53660046131fd565b610d0a565b3480156103b5575f80fd5b5061017a60ff81565b3480156103c9575f80fd5b506102316103d836600461323b565b610e3e565b3480156103e8575f80fd5b506102316103f7366004613287565b611493565b348015610407575f80fd5b5061027f73ce7a977cac4a481bc84ac06b2da0df614e621cf381565b6040517f297103880000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201525f9073a26e15c895efc0616177b7c1e7270a4c7d51c99790632971038890602401602060405180830381865afa158015610494573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104b89190613321565b92915050565b5f60025f54036104e1576040516306fda65d60e31b815260040160405180910390fd5b60025f55337360744434d6339a6b27d73d9eda62b6f66a0a04fa1480159061051d57503373b639d208bcf0589d54fac24e655c79ec529762b814155b801561053d57503373a7a4bb50af91f90b6feb3388e7f8286af45b299b14155b1561055b5760405163245ffd9960e11b815260040160405180910390fd5b6001600160a01b0387163014610584576040516301000cbb60e11b815260040160405180910390fd5b5f80610592848601866134c0565b90925090506105ab6001600160a01b03891682896116da565b505f6105c06001600160a01b038a163061180b565b90505f6105cd888a6135c6565b90506105e3836105dc85610423565b86846118ca565b6105ed82826135c6565b6106006001600160a01b038c163061180b565b1461061e576040516333dcb1d760e21b815260040160405180910390fd5b7fffffffffffffffffffffffff585b44af506e06f49014cc771807d7950ba4d665330161065f576106596001600160a01b038b1633836116da565b50610673565b6106736001600160a01b038b163383611ada565b505060015f55507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd998975050505050505050565b60025f54036106c9576040516306fda65d60e31b815260040160405180910390fd5b60025f553373bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb146107015760405163245ffd9960e11b815260040160405180910390fd5b5f8061070f838501856135f9565b915091505f808380602001905181019061072991906138d7565b90925090506107426001600160a01b03841682896116da565b505f6107576001600160a01b0385163061180b565b905061076d8261076684610423565b858b6118ca565b61077781896135c6565b61078a6001600160a01b0386163061180b565b146107a8576040516333dcb1d760e21b815260040160405180910390fd5b6107d06001600160a01b03851673bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb8a611ada565b505060015f55505050505050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610838573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061085c91906139d4565b6001600160a01b03161461089c576040517fa6c827a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33ff5b5f80858060200190518101906108b59190613a54565b90505f8160a001516108c690613b6a565b60f81c60098111156108da576108da6135e5565b90506108e68282611ba8565b81602001515f815181106108fc576108fc613bb9565b60200260200101515f1b925050505b949350505050565b5f60025f5403610936576040516306fda65d60e31b815260040160405180910390fd5b60025f5533737d2768de32b0b80b7a3454c06bdac94a69ddc7a9148015906109725750337387870bca3f3fd6335c3f4ce8392d69350b4fa4e214155b801561099257503373c13e21b648a5ee794902342038ff3adab66be98714155b156109b05760405163245ffd9960e11b815260040160405180910390fd5b6001600160a01b03831630146109d9576040516301000cbb60e11b815260040160405180910390fd5b5f80838060200190518101906109ef91906138d7565b915091505f885167ffffffffffffffff811115610a0e57610a0e612dff565b604051908082528060200260200182016040528015610a37578160200160208202803683370190505b5090505f5b8951811015610af057610a95838a8381518110610a5b57610a5b613bb9565b60200260200101518c8481518110610a7557610a75613bb9565b60200260200101516001600160a01b03166116da9092919063ffffffff16565b50610acb308b8381518110610aac57610aac613bb9565b60200260200101516001600160a01b031661180b90919063ffffffff16565b828281518110610add57610add613bb9565b6020908102919091010152600101610a3c565b50610b4282610afe84610423565b858a5f81518110610b1157610b11613bb9565b60200260200101518c5f81518110610b2b57610b2b613bb9565b6020026020010151610b3d91906135c6565b6118ca565b5f5b8951811015610cf5575f888281518110610b6057610b60613bb9565b60200260200101518a8381518110610b7a57610b7a613bb9565b6020026020010151610b8c91906135c6565b90505f838381518110610ba157610ba1613bb9565b602002602001015182610bb491906135c6565b610bca308e8681518110610aac57610aac613bb9565b14905073ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b03168c8481518110610bfd57610bfd613bb9565b60200260200101516001600160a01b0316148015610c19575080155b15610c96576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b158015610c7b575f80fd5b505af1158015610c8d573d5f803e3d5ffd5b50505050600190505b80610cb4576040516333dcb1d760e21b815260040160405180910390fd5b610ceb33838e8681518110610ccb57610ccb613bb9565b60200260200101516001600160a01b0316611ada9092919063ffffffff16565b5050600101610b44565b506001935050505060015f5595945050505050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d64573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d8891906139d4565b6001600160a01b031614610dc8576040517f19494c8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03610e25576040516001600160a01b0383169082156108fc029083905f818181858888f19350505050158015610e1f573d5f803e3d5ffd5b50505050565b610e396001600160a01b0384168383611d10565b505050565b60025f5403610e60576040516306fda65d60e31b815260040160405180910390fd5b60025f9081558151610e7b9083016020908101908401613a54565b90505f336001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eba573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ede9190613bcd565b90505f731f98431c8ad98523631ae4a59f267346ea31f9846001600160a01b0316631698ee82845f01515f81518110610f1957610f19613bb9565b6020026020010151855f0151600181518110610f3757610f37613bb9565b60209081029190910101516040516001600160e01b031960e085901b1681526001600160a01b0392831660048201529116602482015262ffffff85166044820152606401602060405180830381865afa158015610f96573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fba91906139d4565b9050336001600160a01b03821614610fe55760405163245ffd9960e11b815260040160405180910390fd5b50505f808260c0015180602001905181019061100191906138d7565b9150915061103c8184602001515f8151811061101f5761101f613bb9565b6020026020010151855f01515f81518110610a7557610a75613bb9565b5061107681846020015160018151811061105857611058613bb9565b6020026020010151855f0151600181518110610a7557610a75613bb9565b506110a88161108483610423565b8486602001515f8151811061109b5761109b613bb9565b60200260200101516118ca565b5f8684602001515f815181106110c0576110c0613bb9565b602002602001015185604001515f815181106110de576110de613bb9565b60200260200101516110f091906135c6565b6110fa91906135c6565b90505f86856020015160018151811061111557611115613bb9565b6020026020010151866040015160018151811061113457611134613bb9565b602002602001015161114691906135c6565b61115091906135c6565b90505f8086602001515f8151811061116a5761116a613bb9565b60200260200101511161117d575f611196565b61119630875f01515f81518110610aac57610aac613bb9565b90505f8087602001516001815181106111b1576111b1613bb9565b6020026020010151116111c4575f6111de565b6111de30885f0151600181518110610aac57610aac613bb9565b6020880151805191925083861491858414915f91829061120057611200613bb9565b602002602001015111801561124c57508851805173ae7ab96520de3a18e5e111b5eaab095312d7fe8491905f9061123957611239613bb9565b60200260200101516001600160a01b0316145b8015611256575081155b156112d3576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b1580156112b8575f80fd5b505af11580156112ca573d5f803e3d5ffd5b50505050600191505b5f89602001516001815181106112eb576112eb613bb9565b602002602001015111801561133a57508851805173ae7ab96520de3a18e5e111b5eaab095312d7fe849190600190811061132757611327613bb9565b60200260200101516001600160a01b0316145b8015611344575080155b156113c1576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b1580156113a6575f80fd5b505af11580156113b8573d5f803e3d5ffd5b50505050600190505b816113df576040516333dcb1d760e21b815260040160405180910390fd5b806113fd576040516333dcb1d760e21b815260040160405180910390fd5b61143c338d8b602001515f8151811061141857611418613bb9565b602002602001015161142a91906135c6565b8b5180515f90610a7557610a75613bb9565b50611480338c8b6020015160018151811061145957611459613bb9565b602002602001015161146b91906135c6565b8b5180516001908110610a7557610a75613bb9565b505060015f555050505050505050505050565b60025f54036114b5576040516306fda65d60e31b815260040160405180910390fd5b60025f553373ba12222222228d8ba445958a75a0704d566bf2c8146114ed5760405163245ffd9960e11b815260040160405180910390fd5b5f808280602001905181019061150391906138d7565b915091505f865167ffffffffffffffff81111561152257611522612dff565b60405190808252806020026020018201604052801561154b578160200160208202803683370190505b5090505f5b87518110156115c5576115898388838151811061156f5761156f613bb9565b60200260200101518a8481518110610a7557610a75613bb9565b506115a030898381518110610aac57610aac613bb9565b8282815181106115b2576115b2613bb9565b6020908102919091010152600101611550565b50611600826115d384610423565b85885f815181106115e6576115e6613bb9565b60200260200101518a5f81518110610b2b57610b2b613bb9565b5f5b87518110156107d0575f86828151811061161e5761161e613bb9565b602002602001015188838151811061163857611638613bb9565b602002602001015161164a91906135c6565b905082828151811061165e5761165e613bb9565b60200260200101518161167191906135c6565b611687308b8581518110610aac57610aac613bb9565b146116a5576040516333dcb1d760e21b815260040160405180910390fd5b6116d073ba12222222228d8ba445958a75a0704d566bf2c8828b8581518110610a7557610a75613bb9565b5050600101611602565b5f5f1982036116f0576116ed843061180b565b91505b6001600160a01b0383161580159061171157506001600160a01b0383163014155b801561171c57508115155b15611804576001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461175e576117596001600160a01b0385168484611d10565b611804565b5f836001600160a01b0316836040515f6040518083038185875af1925050503d805f81146117a7576040519150601f19603f3d011682016040523d82523d5f602084013e6117ac565b606091505b50509050806118025760405162461bcd60e51b815260206004820152600d60248201527f4574682073656e64206661696c0000000000000000000000000000000000000060448201526064015b60405180910390fd5b505b5092915050565b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161184257506001600160a01b038116316104b8565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa15801561189f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118c39190613bef565b9392505050565b82156119b657836001600160a01b0316631cff79cd47735029336642814bc51a42ba80bf83a6322110035d7ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b88686604051602401611929929190613d15565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199485161790525160e086901b909216825261196f9291600401613df8565b60206040518083038185885af115801561198b573d5f803e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906119b09190613bef565b50610e1f565b5f846001600160a01b031663468721a7735029336642814bc51a42ba80bf83a6322110035d477ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b88787604051602401611a10929190613d15565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199485161790525160e086901b9092168252611a5a939291600190600401613e19565b6020604051808303815f875af1158015611a76573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a9a9190613321565b905080611ad3576040517fe540c1c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601611b0457505050565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015282919085169063dd62ed3e90604401602060405180830381865afa158015611b6a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b8e9190613bef565b1015610e3957610e396001600160a01b0384168383611d8c565b6001816009811115611bbc57611bbc6135e5565b03611bce57611bca82611e1a565b5050565b6002816009811115611be257611be26135e5565b03611bf057611bca82611f2c565b6003816009811115611c0457611c046135e5565b03611c1257611bca8261200a565b6004816009811115611c2657611c266135e5565b03611c3457611bca82612167565b6005816009811115611c4857611c486135e5565b03611c5657611bca826122c4565b6006816009811115611c6a57611c6a6135e5565b03611c7857611bca826123b1565b6007816009811115611c8c57611c8c6135e5565b03611c9a57611bca8261261d565b6008816009811115611cae57611cae6135e5565b03611cbc57611bca82612745565b6009816009811115611cd057611cd06135e5565b03611cde57611bca826128ce565b6040517f2be02e1100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516001600160a01b038316602482015260448101829052610e399084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612a2b565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611ddd8482612b11565b610e1f576040516001600160a01b03841660248201525f6044820152611e1090859063095ea7b360e01b90606401611d55565b610e1f8482612a2b565b80516020820151604080840151606085015160c0860151835163ab9c4b5d60e01b8152737d2768de32b0b80b7a3454c06bdac94a69ddc7a99663ab9c4b5d96611e70963096929591949293919291600401613ea5565b5f604051808303815f87803b158015611e87575f80fd5b505af1158015611e99573d5f803e3d5ffd5b50505050604051611eb89067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce3825f0151836020015184604001518560600151604051602001611f079493929190613f23565b60408051601f1981840301815290829052611f2191613faa565b60405180910390a250565b8051602082015160c08301516040517f5c38449e00000000000000000000000000000000000000000000000000000000815273ba12222222228d8ba445958a75a0704d566bf2c893635c38449e93611f88933093600401613fbc565b5f604051808303815f87803b158015611f9f575f80fd5b505af1158015611fb1573d5f803e3d5ffd5b50505050604051611fd09067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382604051602001611f0791906140ac565b73b639d208bcf0589d54fac24e655c79ec529762b86001600160a01b0316635cffe9de307340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f84602001515f8151811061205957612059613bb9565b60200260200101518560c001516040518563ffffffff1660e01b815260040161208594939291906140f2565b6020604051808303815f875af11580156120a1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120c59190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061211957612119613bb9565b60209081029190910181015160408051928301819052600360608401527f47484f0000000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b7360744434d6339a6b27d73d9eda62b6f66a0a04fa6001600160a01b0316635cffe9de30736b175474e89094c44da98b954eedeac495271d0f84602001515f815181106121b6576121b6613bb9565b60200260200101518560c001516040518563ffffffff1660e01b81526004016121e294939291906140f2565b6020604051808303815f875af11580156121fe573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122229190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061227657612276613bb9565b60209081029190910181015160408051928301819052600560608401527f4d414b4552000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b80516020820151604080840151606085015160c0860151835163ab9c4b5d60e01b81527387870bca3f3fd6335c3f4ce8392d69350b4fa4e29663ab9c4b5d9661231a963096929591949293919291600401613ea5565b5f604051808303815f87803b158015612331575f80fd5b505af1158015612343573d5f803e3d5ffd5b505050506040516123629067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce3825f0151836020015184604001518560600151604051602001611f07949392919061412d565b60408051600280825260608201835290916020830190803683375050506040820152602081015180515f919082906123eb576123eb613bb9565b6020026020010151116123fe575f612417565b61241730825f01515f81518110610aac57610aac613bb9565b81604001515f8151811061242d5761242d613bb9565b6020026020010181815250505f816020015160018151811061245157612451613bb9565b602002602001015111612464575f61247e565b61247e30825f0151600181518110610aac57610aac613bb9565b816040015160018151811061249557612495613bb9565b60209081029190910101528051805160029081106124b5576124b5613bb9565b60200260200101516001600160a01b031663490e6cbc3083602001515f815181106124e2576124e2613bb9565b6020026020010151846020015160018151811061250157612501613bb9565b60200260200101518560405160200161251a9190614173565b6040516020818303038152906040526040518563ffffffff1660e01b81526004016125489493929190614185565b5f604051808303815f87803b15801561255f575f80fd5b505af1158015612571573d5f803e3d5ffd5b505050506040516125909067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106125cf576125cf613bb9565b60209081029190910181015160408051928301819052600560608401527f554e495633000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b80516020820151604080840151606085015160c0860151925163ab9c4b5d60e01b815273c13e21b648a5ee794902342038ff3adab66be9879563ab9c4b5d95612670953095929491935f90600401613ea5565b5f604051808303815f87803b158015612687575f80fd5b505af1158015612699573d5f803e3d5ffd5b505050506040516126b89067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106126f7576126f7613bb9565b60209081029190910181015160408051928301819052600560608401527f535041524b000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b8051805173bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb9163e0232b42915f9061277357612773613bb9565b602002602001015183602001515f8151811061279157612791613bb9565b60200260200101518460c00151855f01515f815181106127b3576127b3613bb9565b60200260200101516040516020016127cc9291906141b2565b6040516020818303038152906040526040518463ffffffff1660e01b81526004016127f9939291906141dc565b5f604051808303815f87803b158015612810575f80fd5b505af1158015612822573d5f803e3d5ffd5b505050506040516128419067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061288057612880613bb9565b60209081029190910181015160408051928301819052600a60608401527f4d4f5250484f424c554500000000000000000000000000000000000000000000608084015282015260a001611f07565b73a7a4bb50af91f90b6feb3388e7f8286af45b299b6001600160a01b0316635cffe9de3073f939e0a03fb07f59a73314e73794be0e57ac1b4e84602001515f8151811061291d5761291d613bb9565b60200260200101518560c001516040518563ffffffff1660e01b815260040161294994939291906140f2565b6020604051808303815f875af1158015612965573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129899190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106129dd576129dd613bb9565b60209081029190910181015160408051928301819052600860608401527f4355525645555344000000000000000000000000000000000000000000000000608084015282015260a001611f07565b5f612a7f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612bb29092919063ffffffff16565b905080515f1480612a9f575080806020019051810190612a9f9190613321565b610e395760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016117f9565b5f805f846001600160a01b031684604051612b2c9190614203565b5f604051808303815f865af19150503d805f8114612b65576040519150601f19603f3d011682016040523d82523d5f602084013e612b6a565b606091505b5091509150818015612b94575080511580612b94575080806020019051810190612b949190613321565b8015612ba957505f856001600160a01b03163b115b95945050505050565b606061090b84845f856060612bc685612c94565b612bfc576040517f304619b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80866001600160a01b03168587604051612c179190614203565b5f6040518083038185875af1925050503d805f8114612c51576040519150601f19603f3d011682016040523d82523d5f602084013e612c56565b606091505b50915091508115612c6a57915061090b9050565b805115612c7a5780518082602001fd5b8360405162461bcd60e51b81526004016117f99190613faa565b5f813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061090b575050151592915050565b6001600160a01b0381168114610241575f80fd5b5f60208284031215612cf0575f80fd5b81356118c381612ccc565b5f8083601f840112612d0b575f80fd5b50813567ffffffffffffffff811115612d22575f80fd5b602083019150836020828501011115612d39575f80fd5b9250929050565b5f805f805f8060a08789031215612d55575f80fd5b8635612d6081612ccc565b95506020870135612d7081612ccc565b94506040870135935060608701359250608087013567ffffffffffffffff811115612d99575f80fd5b612da589828a01612cfb565b979a9699509497509295939492505050565b5f805f60408486031215612dc9575f80fd5b83359250602084013567ffffffffffffffff811115612de6575f80fd5b612df286828701612cfb565b9497909650939450505050565b634e487b7160e01b5f52604160045260245ffd5b60405160a0810167ffffffffffffffff81118282101715612e3657612e36612dff565b60405290565b60405160e0810167ffffffffffffffff81118282101715612e3657612e36612dff565b604051601f8201601f1916810167ffffffffffffffff81118282101715612e8857612e88612dff565b604052919050565b5f67ffffffffffffffff821115612ea957612ea9612dff565b50601f01601f191660200190565b5f82601f830112612ec6575f80fd5b8135612ed9612ed482612e90565b612e5f565b818152846020838601011115612eed575f80fd5b816020850160208301375f918101602001919091529392505050565b5f60208284031215612f19575f80fd5b813567ffffffffffffffff811115612f2f575f80fd5b61090b84828501612eb7565b5f67ffffffffffffffff821115612f5457612f54612dff565b5060051b60200190565b5f82601f830112612f6d575f80fd5b81356020612f7d612ed483612f3b565b8083825260208201915060208460051b870101935086841115612f9e575f80fd5b602086015b84811015612fba5780358352918301918301612fa3565b509695505050505050565b60ff81168114610241575f80fd5b5f82601f830112612fe2575f80fd5b81356020612ff2612ed483612f3b565b8083825260208201915060208460051b870101935086841115613013575f80fd5b602086015b84811015612fba57803561302b81612fc5565b8352918301918301613018565b5f805f806080858703121561304b575f80fd5b843567ffffffffffffffff80821115613062575f80fd5b61306e88838901612eb7565b95506020870135915080821115613083575f80fd5b61308f88838901612f5e565b945060408701359150808211156130a4575f80fd5b6130b088838901612fd3565b935060608701359150808211156130c5575f80fd5b506130d287828801612f5e565b91505092959194509250565b5f82601f8301126130ed575f80fd5b813560206130fd612ed483612f3b565b8083825260208201915060208460051b87010193508684111561311e575f80fd5b602086015b84811015612fba57803561313681612ccc565b8352918301918301613123565b5f805f805f60a08688031215613157575f80fd5b853567ffffffffffffffff8082111561316e575f80fd5b61317a89838a016130de565b9650602088013591508082111561318f575f80fd5b61319b89838a01612f5e565b955060408801359150808211156131b0575f80fd5b6131bc89838a01612f5e565b9450606088013591506131ce82612ccc565b909250608087013590808211156131e3575f80fd5b506131f088828901612eb7565b9150509295509295909350565b5f805f6060848603121561320f575f80fd5b833561321a81612ccc565b9250602084013561322a81612ccc565b929592945050506040919091013590565b5f805f6060848603121561324d575f80fd5b8335925060208401359150604084013567ffffffffffffffff811115613271575f80fd5b61327d86828701612eb7565b9150509250925092565b5f805f806080858703121561329a575f80fd5b843567ffffffffffffffff808211156132b1575f80fd5b6132bd888389016130de565b955060208701359150808211156132d2575f80fd5b6132de88838901612f5e565b945060408701359150808211156132f3575f80fd5b6132ff88838901612f5e565b93506060870135915080821115613314575f80fd5b506130d287828801612eb7565b5f60208284031215613331575f80fd5b815180151581146118c3575f80fd5b5f82601f83011261334f575f80fd5b8135602061335f612ed483612f3b565b82815260059290921b8401810191818101908684111561337d575f80fd5b8286015b84811015612fba57803567ffffffffffffffff81111561339f575f80fd5b6133ad8986838b0101612eb7565b845250918301918301613381565b6001600160e01b031981168114610241575f80fd5b5f82601f8301126133df575f80fd5b813560206133ef612ed483612f3b565b8083825260208201915060208460051b870101935086841115613410575f80fd5b602086015b84811015612fba578035613428816133bb565b8352918301918301613415565b5f82601f830112613444575f80fd5b81356020613454612ed483612f3b565b82815260059290921b84018101918181019086841115613472575f80fd5b8286015b84811015612fba57803567ffffffffffffffff811115613494575f80fd5b6134a28986838b0101612fd3565b845250918301918301613476565b80356134bb81612ccc565b919050565b5f80604083850312156134d1575f80fd5b823567ffffffffffffffff808211156134e8575f80fd5b9084019060a082870312156134fb575f80fd5b613503612e13565b823582811115613511575f80fd5b61351d88828601612eb7565b825250602083013582811115613531575f80fd5b61353d88828601613340565b602083015250604083013582811115613554575f80fd5b61356088828601612f5e565b604083015250606083013582811115613577575f80fd5b613583888286016133d0565b60608301525060808301358281111561359a575f80fd5b6135a688828601613435565b60808301525093506135bd915050602084016134b0565b90509250929050565b808201808211156104b857634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f806040838503121561360a575f80fd5b823567ffffffffffffffff811115613620575f80fd5b61362c85828601612eb7565b925050602083013561363d81612ccc565b809150509250929050565b5f5b8381101561366257818101518382015260200161364a565b50505f910152565b5f82601f830112613679575f80fd5b8151613687612ed482612e90565b81815284602083860101111561369b575f80fd5b61090b826020830160208701613648565b5f82601f8301126136bb575f80fd5b815160206136cb612ed483612f3b565b82815260059290921b840181019181810190868411156136e9575f80fd5b8286015b84811015612fba57805167ffffffffffffffff81111561370b575f80fd5b6137198986838b010161366a565b8452509183019183016136ed565b5f82601f830112613736575f80fd5b81516020613746612ed483612f3b565b8083825260208201915060208460051b870101935086841115613767575f80fd5b602086015b84811015612fba578051835291830191830161376c565b5f82601f830112613792575f80fd5b815160206137a2612ed483612f3b565b8083825260208201915060208460051b8701019350868411156137c3575f80fd5b602086015b84811015612fba5780516137db816133bb565b83529183019183016137c8565b5f82601f8301126137f7575f80fd5b81516020613807612ed483612f3b565b828152600592831b8501820192828201919087851115613825575f80fd5b8387015b858110156138bf57805167ffffffffffffffff811115613847575f80fd5b8801603f81018a13613857575f80fd5b858101516040613869612ed483612f3b565b82815291851b8301810191888101908d841115613884575f80fd5b938201935b838510156138ae578451925061389e83612fc5565b8282529389019390890190613889565b885250505093850193508401613829565b5090979650505050505050565b80516134bb81612ccc565b5f80604083850312156138e8575f80fd5b825167ffffffffffffffff808211156138ff575f80fd5b9084019060a08287031215613912575f80fd5b61391a612e13565b825182811115613928575f80fd5b6139348882860161366a565b825250602083015182811115613948575f80fd5b613954888286016136ac565b60208301525060408301518281111561396b575f80fd5b61397788828601613727565b60408301525060608301518281111561398e575f80fd5b61399a88828601613783565b6060830152506080830151828111156139b1575f80fd5b6139bd888286016137e8565b60808301525093506135bd915050602084016138cc565b5f602082840312156139e4575f80fd5b81516118c381612ccc565b5f82601f8301126139fe575f80fd5b81516020613a0e612ed483612f3b565b8083825260208201915060208460051b870101935086841115613a2f575f80fd5b602086015b84811015612fba578051613a4781612ccc565b8352918301918301613a34565b5f60208284031215613a64575f80fd5b815167ffffffffffffffff80821115613a7b575f80fd5b9083019060e08286031215613a8e575f80fd5b613a96612e3c565b825182811115613aa4575f80fd5b613ab0878286016139ef565b825250602083015182811115613ac4575f80fd5b613ad087828601613727565b602083015250604083015182811115613ae7575f80fd5b613af387828601613727565b604083015250613b05606084016138cc565b6060820152613b16608084016138cc565b608082015260a083015182811115613b2c575f80fd5b613b388782860161366a565b60a08301525060c083015182811115613b4f575f80fd5b613b5b8782860161366a565b60c08301525095945050505050565b5f815160208301517fff0000000000000000000000000000000000000000000000000000000000000080821693506001831015613bb15780818460010360031b1b83161693505b505050919050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613bdd575f80fd5b815162ffffff811681146118c3575f80fd5b5f60208284031215613bff575f80fd5b5051919050565b5f8151808452613c1d816020860160208601613648565b601f01601f19169290920160200192915050565b5f815180845260208085019450602084015f5b83811015613c6057815187529582019590820190600101613c44565b509495945050505050565b5f815180845260208085019450602084015f5b83811015613c605781516001600160e01b03191687529582019590820190600101613c7e565b5f82825180855260208086019550808260051b8401018186015f5b848110156138bf57858303601f190189528151805180855290850190858501905f5b81811015613d0057835160ff1683529287019291870191600101613ce1565b50509985019993505090830190600101613cbf565b604081525f835160a06040840152613d3060e0840182613c06565b9050602080860151603f19808685030160608701528382518086528486019150848160051b87010185850194505f5b82811015613d8d57601f19888303018452613d7b828751613c06565b95870195938701939150600101613d5f565b5060408b01519650838982030160808a0152613da98188613c31565b965050505060608801519150808685030160a0870152613dc98483613c6b565b935060808801519150808685030160c087015250613de78382613ca4565b935050848185015250509392505050565b6001600160a01b0383168152604060208201525f61090b6040830184613c06565b6001600160a01b0385168152836020820152608060408201525f613e406080830185613c06565b905060028310613e5e57634e487b7160e01b5f52602160045260245ffd5b82606083015295945050505050565b5f815180845260208085019450602084015f5b83811015613c605781516001600160a01b031687529582019590820190600101613e80565b5f6001600160a01b03808a16835260e06020840152613ec760e084018a613e6d565b8381036040850152613ed9818a613c31565b90508381036060850152613eed8189613c31565b9050818716608085015283810360a0850152613f098187613c06565b9250505061ffff831660c083015298975050505050505050565b60a08152600660a08201527f414156455632000000000000000000000000000000000000000000000000000060c082015260e060208201525f613f6960e0830187613e6d565b8281036040840152613f7b8187613c31565b90508281036060840152613f8f8186613c31565b9150506001600160a01b038316608083015295945050505050565b602081525f6118c36020830184613c06565b6001600160a01b0385168152608060208201525f613fdd6080830186613e6d565b8281036040840152613fef8186613c31565b905082810360608401526140038185613c06565b979650505050505050565b5f815160e0845261402260e0850182613e6d565b90506020830151848203602086015261403b8282613c31565b915050604083015184820360408601526140558282613c31565b91505060608301516001600160a01b038082166060870152806080860151166080870152505060a083015184820360a08601526140928282613c06565b91505060c083015184820360c0860152612ba98282613c06565b60408152600860408201527f42414c414e4345520000000000000000000000000000000000000000000000006060820152608060208201525f6118c3608083018461400e565b5f6001600160a01b038087168352808616602084015250836040830152608060608301526141236080830184613c06565b9695505050505050565b60a08152600660a08201527f414156455633000000000000000000000000000000000000000000000000000060c082015260e060208201525f613f6960e0830187613e6d565b602081525f6118c3602083018461400e565b6001600160a01b0385168152836020820152826040820152608060608201525f6141236080830184613c06565b604081525f6141c46040830185613c06565b90506001600160a01b03831660208301529392505050565b6001600160a01b0384168152826020820152606060408201525f612ba96060830184613c06565b5f8251614214818460208701613648565b919091019291505056fea264697066735822122097a88cdfe1242c80d5b121a1d4a50e116a7b5ba94936c1f4bb450f95c4bae95464736f6c63430008180033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.