Feature Tip: Add private address tag to any address under My Name Tag !
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 27 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Execute Liquidat... | 18001010 | 513 days ago | IN | 0 ETH | 0.00312972 | ||||
Execute Liquidat... | 18000994 | 513 days ago | IN | 0 ETH | 0.00271344 | ||||
Execute Liquidat... | 17935503 | 522 days ago | IN | 0 ETH | 0.02254857 | ||||
Execute Liquidat... | 17934663 | 522 days ago | IN | 0 ETH | 0.0206463 | ||||
Execute Liquidat... | 17843618 | 535 days ago | IN | 0 ETH | 0.02206394 | ||||
Execute Liquidat... | 17843416 | 535 days ago | IN | 0 ETH | 0.02690981 | ||||
Execute Liquidat... | 17843234 | 535 days ago | IN | 0 ETH | 0.0324428 | ||||
Execute Liquidat... | 17843218 | 535 days ago | IN | 0 ETH | 0.03725487 | ||||
Execute Liquidat... | 17843078 | 535 days ago | IN | 0 ETH | 0.0343561 | ||||
Execute Liquidat... | 17843013 | 535 days ago | IN | 0 ETH | 0.03543883 | ||||
Execute Liquidat... | 17828514 | 537 days ago | IN | 0 ETH | 0.01139062 | ||||
Set Magicians | 17512599 | 581 days ago | IN | 0 ETH | 0.00074967 | ||||
Set Magicians | 17512597 | 581 days ago | IN | 0 ETH | 0.00080201 | ||||
Transfer Ownersh... | 17512587 | 581 days ago | IN | 0 ETH | 0.00039552 | ||||
Execute Liquidat... | 17447701 | 590 days ago | IN | 0 ETH | 0.0090728 | ||||
Execute Liquidat... | 17415614 | 595 days ago | IN | 0 ETH | 0.01814319 | ||||
Set Magicians | 17393423 | 598 days ago | IN | 0 ETH | 0.0011693 | ||||
Set Magicians | 17393386 | 598 days ago | IN | 0 ETH | 0.00141841 | ||||
Set Magicians | 17393037 | 598 days ago | IN | 0 ETH | 0.00165053 | ||||
Set Magicians | 17392965 | 598 days ago | IN | 0 ETH | 0.00190329 | ||||
Set Magicians | 17392963 | 598 days ago | IN | 0 ETH | 0.00188527 | ||||
Set Magicians | 17392956 | 598 days ago | IN | 0 ETH | 0.00182134 | ||||
Set Magicians | 17392954 | 598 days ago | IN | 0 ETH | 0.00187659 | ||||
Set Magicians | 17392948 | 598 days ago | IN | 0 ETH | 0.00189602 | ||||
Set Magicians | 17392946 | 598 days ago | IN | 0 ETH | 0.00195878 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
17935503 | 522 days ago | 0.14379041 ETH | ||||
17935503 | 522 days ago | 0.14379041 ETH | ||||
17934663 | 522 days ago | 0.00001241 ETH | ||||
17934663 | 522 days ago | 0.00001241 ETH | ||||
17843618 | 535 days ago | 0.00002965 ETH | ||||
17843618 | 535 days ago | 0.00002965 ETH | ||||
17843416 | 535 days ago | 0.00003093 ETH | ||||
17843416 | 535 days ago | 0.00003093 ETH | ||||
17843234 | 535 days ago | 0.00508832 ETH | ||||
17843234 | 535 days ago | 0.00508832 ETH | ||||
17843234 | 535 days ago | 0.02932066 ETH | ||||
17843234 | 535 days ago | 0.02932066 ETH | ||||
17843218 | 535 days ago | 0.00002994 ETH | ||||
17843218 | 535 days ago | 0.00002994 ETH | ||||
17843078 | 535 days ago | 0.00508827 ETH | ||||
17843078 | 535 days ago | 0.00508827 ETH | ||||
17843078 | 535 days ago | 0.0293207 ETH | ||||
17843078 | 535 days ago | 0.0293207 ETH | ||||
17843013 | 535 days ago | 0.00003154 ETH | ||||
17843013 | 535 days ago | 0.00003154 ETH | ||||
17447701 | 590 days ago | 0.11065031 ETH | ||||
17447701 | 590 days ago | 0.11065031 ETH | ||||
17415614 | 595 days ago | 0.19608442 ETH | ||||
17415614 | 595 days ago | 0.19608442 ETH | ||||
17233931 | 621 days ago | 0.0073313 ETH |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
LiquidationHelper
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "./magicians/interfaces/IMagician.sol"; import "../SiloLens.sol"; import "../interfaces/ISiloFactory.sol"; import "../interfaces/IPriceProviderV2.sol"; import "../interfaces/ISwapper.sol"; import "../interfaces/ISiloRepository.sol"; import "../interfaces/IPriceProvidersRepository.sol"; import "../interfaces/IWrappedNativeToken.sol"; import "../priceProviders/chainlinkV3/ChainlinkV3PriceProvider.sol"; import "../lib/Ping.sol"; import "../lib/RevertBytes.sol"; import "./ZeroExSwap.sol"; import "./lib/LiquidationScenarioDetector.sol"; import "./LiquidationRepay.sol"; /// @notice LiquidationHelper IS NOT PART OF THE PROTOCOL. SILO CREATED THIS TOOL, MOSTLY AS AN EXAMPLE. /// see https://github.com/silo-finance/liquidation#readme for details how liquidation process should look like contract LiquidationHelper is ILiquidationHelper, IFlashLiquidationReceiver, ZeroExSwap, LiquidationRepay { using RevertBytes for bytes; using SafeERC20 for IERC20; using Address for address payable; using LiquidationScenarioDetector for LiquidationScenario; struct MagicianConfig { address asset; IMagician magician; } struct SwapperConfig { IPriceProvider provider; ISwapper swapper; } uint256 immutable private _BASE_TX_COST; // solhint-disable-line var-name-mixedcase ISiloRepository public immutable SILO_REPOSITORY; // solhint-disable-line var-name-mixedcase IPriceProvidersRepository public immutable PRICE_PROVIDERS_REPOSITORY; // solhint-disable-line var-name-mixedcase SiloLens public immutable LENS; // solhint-disable-line var-name-mixedcase IERC20 public immutable QUOTE_TOKEN; // solhint-disable-line var-name-mixedcase ChainlinkV3PriceProvider public immutable CHAINLINK_PRICE_PROVIDER; // solhint-disable-line var-name-mixedcase mapping(IPriceProvider => ISwapper) public swappers; // asset => magician mapping(address => IMagician) public magicians; error InvalidSiloLens(); error InvalidSiloRepository(); error LiquidationNotProfitable(uint256 inTheRed); error NotSilo(); error PriceProviderNotFound(); error FallbackPriceProviderNotSet(); error SwapperNotFound(); error MagicianNotFound(); error SwapAmountInFailed(); error SwapAmountOutFailed(); error UsersMustMatchSilos(); error InvalidChainlinkProviders(); error InvalidMagicianConfig(); error InvalidSwapperConfig(); error InvalidTowardsAssetConvertion(); error InvalidScenario(); error Max0xSwapsIs2(); event SwapperConfigured(IPriceProvider provider, ISwapper swapper); event MagicianConfigured(address asset, IMagician magician); /// @dev event emitted on user liquidation /// @param silo Silo where liquidation happen /// @param user User that been liquidated /// @param earned amount of ETH earned (excluding gas cost) /// @param estimatedEarnings for LiquidationScenario.Full0xWithChange `earned` amount is estimated, /// because tokens were not sold for ETH inside transaction event LiquidationExecuted(address indexed silo, address indexed user, uint256 earned, bool estimatedEarnings); constructor ( address _repository, address _chainlinkPriceProvider, address _lens, address _exchangeProxy, MagicianConfig[] memory _magicians, SwapperConfig[] memory _swappers, uint256 _baseCost ) ZeroExSwap(_exchangeProxy) { if (!Ping.pong(SiloLens(_lens).lensPing)) revert InvalidSiloLens(); if (!Ping.pong(ISiloRepository(_repository).siloRepositoryPing)) { revert InvalidSiloRepository(); } SILO_REPOSITORY = ISiloRepository(_repository); LENS = SiloLens(_lens); // configure swappers _configureSwappers(_swappers); // configure magicians _configureMagicians(_magicians); PRICE_PROVIDERS_REPOSITORY = ISiloRepository(_repository).priceProvidersRepository(); CHAINLINK_PRICE_PROVIDER = ChainlinkV3PriceProvider(_chainlinkPriceProvider); QUOTE_TOKEN = IERC20(PRICE_PROVIDERS_REPOSITORY.quoteToken()); _BASE_TX_COST = _baseCost; } receive() external payable {} function executeLiquidation( address _user, ISilo _silo, LiquidationScenario _scenario, SwapInput0x[] calldata _swapsInputs0x ) external { if (_swapsInputs0x.length > 2) revert Max0xSwapsIs2(); uint256 gasStart = gasleft(); address[] memory users = new address[](1); users[0] = _user; _silo.flashLiquidate(users, abi.encode(msg.sender, gasStart, _scenario, _swapsInputs0x)); } function setSwappers(SwapperConfig[] calldata _swappers) external onlyOwner { _configureSwappers(_swappers); } function setMagicians(MagicianConfig[] calldata _magicians) external onlyOwner { _configureMagicians(_magicians); } /// @notice this is working example of how to perform liquidation, this method will be called by Silo /// Keep in mind, that this helper might NOT choose the best swap option. /// For best results (highest earnings) you probably want to implement your own callback and maybe use some /// dex aggregators. /// @dev after liquidation we always send remaining tokens so contract should never has any leftover function siloLiquidationCallback( address _user, address[] calldata _assets, uint256[] calldata _receivedCollaterals, uint256[] calldata _shareAmountsToRepaid, bytes calldata _flashReceiverData ) external override { if (!SILO_REPOSITORY.isSilo(msg.sender)) revert NotSilo(); ( address payable executor, uint256 gasStart, LiquidationScenario scenario, SwapInput0x[] memory swapInputs ) = abi.decode(_flashReceiverData, (address, uint256, LiquidationScenario, SwapInput0x[])); if (swapInputs.length != 0) { _execute0x(swapInputs); } uint256 earned = _siloLiquidationCallbackExecution( scenario, _user, _assets, _receivedCollaterals, _shareAmountsToRepaid ); // I needed to move some part of execution from from `_siloLiquidationCallbackExecution`, // because of "stack too deep" error bool isFull0xWithChange = scenario.isFull0xWithChange(); bool calculateEarnings = scenario.calculateEarnings(); if (isFull0xWithChange) { earned = _estimateEarningsAndTransferChange(_assets, _shareAmountsToRepaid, executor, calculateEarnings); } else { _transferNative(executor, earned); } emit LiquidationExecuted(msg.sender, _user, earned, isFull0xWithChange); // do not check for profitability when forcing if (calculateEarnings) { ensureTxIsProfitable(gasStart, earned); } } /// @dev This method should be used to made decision about `Full0x` vs `Full0xWithChange` liquidation scenario. /// @return TRUE, if asset liquidation is supported internally, otherwise FALSE function liquidationSupported(address _asset) external view returns (bool) { if (_asset == address(QUOTE_TOKEN)) return true; if (address(magicians[_asset]) != address(0)) return true; try this.findPriceProvider(_asset) returns (IPriceProvider) { return true; } catch (bytes memory) { // we do not care about reason } return false; } function checkSolvency(address[] calldata _users, ISilo[] calldata _silos) external view returns (bool[] memory) { if (_users.length != _silos.length) revert UsersMustMatchSilos(); bool[] memory solvency = new bool[](_users.length); for (uint256 i; i < _users.length;) { solvency[i] = _silos[i].isSolvent(_users[i]); // we will never have that many users to overflow unchecked { i++; } } return solvency; } function checkDebt(address[] calldata _users, ISilo[] calldata _silos) external view returns (bool[] memory) { bool[] memory hasDebt = new bool[](_users.length); for (uint256 i; i < _users.length;) { hasDebt[i] = LENS.inDebt(_silos[i], _users[i]); // we will never have that many users to overflow unchecked { i++; } } return hasDebt; } function ensureTxIsProfitable(uint256 _gasStart, uint256 _earnedEth) public view returns (uint256 txFee) { unchecked { // gas calculation will not overflow because values are never that high // `gasStart` is external value, but it value that we initiating and Silo contract passing it to us uint256 gasSpent = _gasStart - gasleft() + _BASE_TX_COST; txFee = tx.gasprice * gasSpent; if (txFee > _earnedEth) { // it will not underflow because we check above revert LiquidationNotProfitable(txFee - _earnedEth); } } } function findPriceProvider(address _asset) public view returns (IPriceProvider priceProvider) { priceProvider = PRICE_PROVIDERS_REPOSITORY.priceProviders(_asset); if (address(priceProvider) == address(0)) revert PriceProviderNotFound(); // check for backwards compatibility with chainlink provider if (priceProvider == CHAINLINK_PRICE_PROVIDER) { priceProvider = CHAINLINK_PRICE_PROVIDER.getFallbackProvider(_asset); if (address(priceProvider) == address(0)) revert FallbackPriceProviderNotSet(); return priceProvider; } // only IPriceProviderV2 has `IPriceProviderV2()` try IPriceProviderV2(address(priceProvider)).offChainProvider() returns (bool isOffChainProvider) { if (isOffChainProvider) { priceProvider = IPriceProviderV2(address(priceProvider)).getFallbackProvider(_asset); if (address(priceProvider) == address(0)) revert FallbackPriceProviderNotSet(); } } catch (bytes memory) {} } function _execute0x(SwapInput0x[] memory _swapInputs) internal { for (uint256 i; i < _swapInputs.length;) { fillQuote(_swapInputs[i].sellToken, _swapInputs[i].allowanceTarget, _swapInputs[i].swapCallData); // we can not have that much data in array to overflow unchecked { i++; } } } function _siloLiquidationCallbackExecution( LiquidationScenario _scenario, address _user, address[] calldata _assets, uint256[] calldata _receivedCollaterals, uint256[] calldata _shareAmountsToRepaid ) internal returns (uint256 earned) { if (_scenario.isFull0x()) { return _runFull0xScenario( _user, _assets, _shareAmountsToRepaid ); } if (_scenario.isInternal()) { return _runInternalScenario( _user, _assets, _receivedCollaterals, _shareAmountsToRepaid ); } if (_scenario.isFull0xWithChange()) { // we should have repay tokens ready to repay _repay(ISilo(msg.sender), _user, _assets, _shareAmountsToRepaid); // change that left after repay will be send to `_liquidator` by `_estimateEarningsAndTransferChange` return 0; } if (_scenario.isCollateral0x()) { return _runCollateral0xScenario( _user, _assets, _shareAmountsToRepaid ); } revert InvalidScenario(); } function _runFull0xScenario( address _user, address[] calldata _assets, uint256[] calldata _shareAmountsToRepaid ) internal returns (uint256 earned) { // we should have repay tokens ready to repay _repay(ISilo(msg.sender), _user, _assets, _shareAmountsToRepaid); // we left with some change, let's try to swap to WETH earned = _swapAssetsForQuote(_assets, _shareAmountsToRepaid); } function _runCollateral0xScenario( address _user, address[] calldata _assets, uint256[] calldata _shareAmountsToRepaid ) internal returns (uint256 earned) { // we have WETH, we need to deal with swap WETH -> repay asset internally _swapWrappedNativeForRepayAssets(_assets, _shareAmountsToRepaid); _repay(ISilo(msg.sender), _user, _assets, _shareAmountsToRepaid); earned = QUOTE_TOKEN.balanceOf(address(this)); } function _runInternalScenario( address _user, address[] calldata _assets, uint256[] calldata _receivedCollaterals, uint256[] calldata _shareAmountsToRepaid ) internal returns (uint256 earned) { uint256 quoteAmountFromCollaterals = _swapAllForQuote(_assets, _receivedCollaterals); uint256 quoteSpentOnRepay = _swapWrappedNativeForRepayAssets(_assets, _shareAmountsToRepaid); _repay(ISilo(msg.sender), _user, _assets, _shareAmountsToRepaid); earned = quoteAmountFromCollaterals - quoteSpentOnRepay; } function _estimateEarningsAndTransferChange( address[] calldata _assets, uint256[] calldata _shareAmountsToRepaid, address payable _liquidator, bool _returnEarnedAmount ) internal returns (uint256 earned) { // change that left after repay will be send to `_liquidator` for (uint256 i = 0; i < _assets.length;) { if (_shareAmountsToRepaid[i] != 0) { uint256 amount = IERC20(_assets[i]).balanceOf(address(this)); if (_assets[i] == address(QUOTE_TOKEN)) { if (_returnEarnedAmount) { // balance will not overflow unchecked { earned += amount; } } _transferNative(_liquidator, amount); } else { if (_returnEarnedAmount) { // we processing numbers that Silo created, if Silo did not over/under flow, we will not as well unchecked { earned += amount * PRICE_PROVIDERS_REPOSITORY.getPrice(_assets[i]); } } IERC20(_assets[i]).transfer(_liquidator, amount); } } // we will never have that many assets to overflow unchecked { i++; } } } function _swapAllForQuote( address[] calldata _assets, uint256[] calldata _receivedCollaterals ) internal returns (uint256 quoteAmount) { // swap all for quote token unchecked { // we will not overflow with `i` in a lifetime for (uint256 i = 0; i < _assets.length; i++) { // if silo was able to handle solvency calculations, then we can handle quoteAmount without safe math quoteAmount += _swapForQuote(_assets[i], _receivedCollaterals[i]); } } } function _swapWrappedNativeForRepayAssets( address[] calldata _assets, uint256[] calldata _shareAmountsToRepaid ) internal returns (uint256 quoteSpendOnRepay) { for (uint256 i = 0; i < _assets.length;) { if (_shareAmountsToRepaid[i] != 0) { // if silo was able to handle solvency calculations, then we can handle amounts without safe math here unchecked { quoteSpendOnRepay += _swapForAsset(_assets[i], _shareAmountsToRepaid[i]); } } // we will never have that many assets to overflow unchecked { i++; } } } /// @notice We assume that quoteToken is wrapped native token function _transferNative(address payable _to, uint256 _amount) internal { IWrappedNativeToken(address(QUOTE_TOKEN)).withdraw(_amount); _to.sendValue(_amount); } function _swapAssetsForQuote( address[] calldata _assets, uint256[] calldata _amounts ) internal returns (uint256 received) { for (uint256 i = 0; i < _assets.length;) { if (_amounts[i] != 0) { uint256 amount = IERC20(_assets[i]).balanceOf(address(this)); received += _swapForQuote(_assets[i], amount); } // we will never have that many assets to overflow unchecked { i++; } } } /// @dev it swaps asset token for quote /// @param _asset address /// @param _amount exact amount of asset to swap /// @return amount of quote token function _swapForQuote(address _asset, uint256 _amount) internal returns (uint256) { address quoteToken = address(QUOTE_TOKEN); if (_amount == 0 || _asset == quoteToken) return _amount; address magician = address(magicians[_asset]); if (magician != address(0)) { bytes memory result = _safeDelegateCall( magician, abi.encodeCall(IMagician.towardsNative, (_asset, _amount)), "towardsNativeFailed" ); (address tokenOut, uint256 amountOut) = abi.decode(result, (address, uint256)); return _swapForQuote(tokenOut, amountOut); } (IPriceProvider provider, ISwapper swapper) = _resolveProviderAndSwapper(_asset); // no need for safe approval, because we always using 100% // Low level call needed to support non-standard `ERC20.approve` eg like `USDT.approve` // solhint-disable-next-line avoid-low-level-calls _asset.call(abi.encodeCall(IERC20.approve, (swapper.spenderToApprove(), _amount))); bytes memory callData = abi.encodeCall(ISwapper.swapAmountIn, ( _asset, quoteToken, _amount, address(provider), _asset )); bytes memory data = _safeDelegateCall(address(swapper), callData, "swapAmountIn"); return abi.decode(data, (uint256)); } /// @dev it swaps quote token for asset /// @param _asset address /// @param _amount exact amount OUT, what we want to receive /// @return amount of quote token used for swap function _swapForAsset(address _asset, uint256 _amount) internal returns (uint256) { address quoteToken = address(QUOTE_TOKEN); if (_amount == 0 || quoteToken == _asset) return _amount; address magician = address(magicians[_asset]); if (magician != address(0)) { bytes memory result = _safeDelegateCall( magician, abi.encodeCall(IMagician.towardsAsset, (_asset, _amount)), "towardsAssetFailed" ); (address tokenOut, uint256 amountOut) = abi.decode(result, (address, uint256)); // towardsAsset should convert to `_asset` if (tokenOut != _asset) revert InvalidTowardsAssetConvertion(); return amountOut; } (IPriceProvider provider, ISwapper swapper) = _resolveProviderAndSwapper(_asset); address spender = swapper.spenderToApprove(); IERC20(quoteToken).approve(spender, type(uint256).max); bytes memory callData = abi.encodeCall(ISwapper.swapAmountOut, ( quoteToken, _asset, _amount, address(provider), _asset )); bytes memory data = _safeDelegateCall(address(swapper), callData, "SwapAmountOutFailed"); IERC20(quoteToken).approve(spender, 0); return abi.decode(data, (uint256)); } function _resolveProviderAndSwapper(address _asset) internal view returns (IPriceProvider, ISwapper) { IPriceProvider priceProvider = findPriceProvider(_asset); ISwapper swapper = _resolveSwapper(priceProvider); return (priceProvider, swapper); } function _resolveSwapper(IPriceProvider priceProvider) internal view returns (ISwapper) { ISwapper swapper = swappers[priceProvider]; if (address(swapper) == address(0)) { revert SwapperNotFound(); } return swapper; } function _safeDelegateCall( address _target, bytes memory _callData, string memory _mgs ) internal returns (bytes memory data) { bool success; // solhint-disable-next-line avoid-low-level-calls (success, data) = address(_target).delegatecall(_callData); if (!success || data.length == 0) data.revertBytes(_mgs); } function _configureSwappers(SwapperConfig[] memory _swappers) internal { for (uint256 i = 0; i < _swappers.length; i++) { IPriceProvider provider = _swappers[i].provider; ISwapper swapper = _swappers[i].swapper; if (address(provider) == address(0) || address(swapper) == address(0)) { revert InvalidSwapperConfig(); } swappers[provider] = swapper; emit SwapperConfigured(provider, swapper); } } function _configureMagicians(MagicianConfig[] memory _magicians) internal { for (uint256 i = 0; i < _magicians.length; i++) { address asset = _magicians[i].asset; IMagician magician = _magicians[i].magician; if (asset == address(0) || address(magician) == address(0)) { revert InvalidMagicianConfig(); } magicians[asset] = magician; emit MagicianConfigured(asset, magician); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); uint256 currentAllowance = _allowances[sender][_msgSender()]; require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); unchecked { _approve(sender, _msgSender(), currentAllowance - amount); } return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { uint256 currentAllowance = _allowances[_msgSender()][spender]; require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(_msgSender(), spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `sender` to `recipient`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); uint256 senderBalance = _balances[sender]; require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[sender] = senderBalance - amount; } _balances[recipient] += amount; emit Transfer(sender, recipient, amount); _afterTokenTransfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; _balances[account] += amount; emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; } _totalSupply -= amount; emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Address.sol) pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol) pragma solidity ^0.8.0; // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler * now has built in overflow checking. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "./IShareToken.sol"; import "./IFlashLiquidationReceiver.sol"; import "./ISiloRepository.sol"; interface IBaseSilo { enum AssetStatus { Undefined, Active, Removed } /// @dev Storage struct that holds all required data for a single token market struct AssetStorage { /// @dev Token that represents a share in totalDeposits of Silo IShareToken collateralToken; /// @dev Token that represents a share in collateralOnlyDeposits of Silo IShareToken collateralOnlyToken; /// @dev Token that represents a share in totalBorrowAmount of Silo IShareToken debtToken; /// @dev COLLATERAL: Amount of asset token that has been deposited to Silo with interest earned by depositors. /// It also includes token amount that has been borrowed. uint256 totalDeposits; /// @dev COLLATERAL ONLY: Amount of asset token that has been deposited to Silo that can be ONLY used /// as collateral. These deposits do NOT earn interest and CANNOT be borrowed. uint256 collateralOnlyDeposits; /// @dev DEBT: Amount of asset token that has been borrowed with accrued interest. uint256 totalBorrowAmount; } /// @dev Storage struct that holds data related to fees and interest struct AssetInterestData { /// @dev Total amount of already harvested protocol fees uint256 harvestedProtocolFees; /// @dev Total amount (ever growing) of asset token that has been earned by the protocol from /// generated interest. uint256 protocolFees; /// @dev Timestamp of the last time `interestRate` has been updated in storage. uint64 interestRateTimestamp; /// @dev True if asset was removed from the protocol. If so, deposit and borrow functions are disabled /// for that asset AssetStatus status; } /// @notice data that InterestModel needs for calculations struct UtilizationData { uint256 totalDeposits; uint256 totalBorrowAmount; /// @dev timestamp of last interest accrual uint64 interestRateTimestamp; } /// @dev Shares names and symbols that are generated while asset initialization struct AssetSharesMetadata { /// @dev Name for the collateral shares token string collateralName; /// @dev Symbol for the collateral shares token string collateralSymbol; /// @dev Name for the collateral only (protected collateral) shares token string protectedName; /// @dev Symbol for the collateral only (protected collateral) shares token string protectedSymbol; /// @dev Name for the debt shares token string debtName; /// @dev Symbol for the debt shares token string debtSymbol; } /// @notice Emitted when deposit is made /// @param asset asset address that was deposited /// @param depositor wallet address that deposited asset /// @param amount amount of asset that was deposited /// @param collateralOnly type of deposit, true if collateralOnly deposit was used event Deposit(address indexed asset, address indexed depositor, uint256 amount, bool collateralOnly); /// @notice Emitted when withdraw is made /// @param asset asset address that was withdrawn /// @param depositor wallet address that deposited asset /// @param receiver wallet address that received asset /// @param amount amount of asset that was withdrew /// @param collateralOnly type of withdraw, true if collateralOnly deposit was used event Withdraw( address indexed asset, address indexed depositor, address indexed receiver, uint256 amount, bool collateralOnly ); /// @notice Emitted on asset borrow /// @param asset asset address that was borrowed /// @param user wallet address that borrowed asset /// @param amount amount of asset that was borrowed event Borrow(address indexed asset, address indexed user, uint256 amount); /// @notice Emitted on asset repay /// @param asset asset address that was repaid /// @param user wallet address that repaid asset /// @param amount amount of asset that was repaid event Repay(address indexed asset, address indexed user, uint256 amount); /// @notice Emitted on user liquidation /// @param asset asset address that was liquidated /// @param user wallet address that was liquidated /// @param shareAmountRepaid amount of collateral-share token that was repaid. This is collateral token representing /// ownership of underlying deposit. /// @param seizedCollateral amount of underlying token that was seized by liquidator event Liquidate(address indexed asset, address indexed user, uint256 shareAmountRepaid, uint256 seizedCollateral); /// @notice Emitted when the status for an asset is updated /// @param asset asset address that was updated /// @param status new asset status event AssetStatusUpdate(address indexed asset, AssetStatus indexed status); /// @return version of the silo contract function VERSION() external returns (uint128); // solhint-disable-line func-name-mixedcase /// @notice Synchronize current bridge assets with Silo /// @dev This function needs to be called on Silo deployment to setup all assets for Silo. It needs to be /// called every time a bridged asset is added or removed. When bridge asset is removed, depositing and borrowing /// should be disabled during asset sync. function syncBridgeAssets() external; /// @notice Get Silo Repository contract address /// @return Silo Repository contract address function siloRepository() external view returns (ISiloRepository); /// @notice Get asset storage data /// @param _asset asset address /// @return AssetStorage struct function assetStorage(address _asset) external view returns (AssetStorage memory); /// @notice Get asset interest data /// @param _asset asset address /// @return AssetInterestData struct function interestData(address _asset) external view returns (AssetInterestData memory); /// @dev helper method for InterestRateModel calculations function utilizationData(address _asset) external view returns (UtilizationData memory data); /// @notice Calculates solvency of an account /// @param _user wallet address for which solvency is calculated /// @return true if solvent, false otherwise function isSolvent(address _user) external view returns (bool); /// @notice Returns all initialized (synced) assets of Silo including current and removed bridge assets /// @return assets array of initialized assets of Silo function getAssets() external view returns (address[] memory assets); /// @notice Returns all initialized (synced) assets of Silo including current and removed bridge assets /// with corresponding state /// @return assets array of initialized assets of Silo /// @return assetsStorage array of assets state corresponding to `assets` array function getAssetsWithState() external view returns (address[] memory assets, AssetStorage[] memory assetsStorage); /// @notice Check if depositing an asset for given account is possible /// @dev Depositing an asset that has been already borrowed (and vice versa) is disallowed /// @param _asset asset we want to deposit /// @param _depositor depositor address /// @return true if asset can be deposited by depositor function depositPossible(address _asset, address _depositor) external view returns (bool); /// @notice Check if borrowing an asset for given account is possible /// @dev Borrowing an asset that has been already deposited (and vice versa) is disallowed /// @param _asset asset we want to deposit /// @param _borrower borrower address /// @return true if asset can be borrowed by borrower function borrowPossible(address _asset, address _borrower) external view returns (bool); /// @dev Amount of token that is available for borrowing /// @param _asset asset to get liquidity for /// @return Silo liquidity function liquidity(address _asset) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; /// @dev when performing Silo flash liquidation, FlashReceiver contract will receive all collaterals interface IFlashLiquidationReceiver { /// @dev this method is called when doing Silo flash liquidation /// one can NOT assume, that if _seizedCollateral[i] != 0, then _shareAmountsToRepaid[i] must be 0 /// one should assume, that any combination of amounts is possible /// on callback, one must call `Silo.repayFor` because at the end of transaction, /// Silo will check if borrower is solvent. /// @param _user user address, that is liquidated /// @param _assets array of collateral assets received during user liquidation /// this array contains all assets (collateral borrowed) without any order /// @param _receivedCollaterals array of collateral amounts received during user liquidation /// indexes of amounts are related to `_assets`, /// @param _shareAmountsToRepaid array of amounts to repay for each asset /// indexes of amounts are related to `_assets`, /// @param _flashReceiverData data that are passed from sender that executes liquidation function siloLiquidationCallback( address _user, address[] calldata _assets, uint256[] calldata _receivedCollaterals, uint256[] calldata _shareAmountsToRepaid, bytes memory _flashReceiverData ) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; interface IInterestRateModel { /* solhint-disable */ struct Config { // uopt ∈ (0, 1) – optimal utilization; int256 uopt; // ucrit ∈ (uopt, 1) – threshold of large utilization; int256 ucrit; // ulow ∈ (0, uopt) – threshold of low utilization int256 ulow; // ki > 0 – integrator gain int256 ki; // kcrit > 0 – proportional gain for large utilization int256 kcrit; // klow ≥ 0 – proportional gain for low utilization int256 klow; // klin ≥ 0 – coefficient of the lower linear bound int256 klin; // beta ≥ 0 - a scaling factor int256 beta; // ri ≥ 0 – initial value of the integrator int256 ri; // Tcrit ≥ 0 - the time during which the utilization exceeds the critical value int256 Tcrit; } /* solhint-enable */ /// @dev Set dedicated config for given asset in a Silo. Config is per asset per Silo so different assets /// in different Silo can have different configs. /// It will try to call `_silo.accrueInterest(_asset)` before updating config, but it is not guaranteed, /// that this call will be successful, if it fail config will be set anyway. /// @param _silo Silo address for which config should be set /// @param _asset asset address for which config should be set function setConfig(address _silo, address _asset, Config calldata _config) external; /// @dev get compound interest rate and update model storage /// @param _asset address of an asset in Silo for which interest rate should be calculated /// @param _blockTimestamp current block timestamp /// @return rcomp compounded interest rate from last update until now (1e18 == 100%) function getCompoundInterestRateAndUpdate( address _asset, uint256 _blockTimestamp ) external returns (uint256 rcomp); /// @dev Get config for given asset in a Silo. If dedicated config is not set, default one will be returned. /// @param _silo Silo address for which config should be set /// @param _asset asset address for which config should be set /// @return Config struct for asset in Silo function getConfig(address _silo, address _asset) external view returns (Config memory); /// @dev get compound interest rate /// @param _silo address of Silo /// @param _asset address of an asset in Silo for which interest rate should be calculated /// @param _blockTimestamp current block timestamp /// @return rcomp compounded interest rate from last update until now (1e18 == 100%) function getCompoundInterestRate( address _silo, address _asset, uint256 _blockTimestamp ) external view returns (uint256 rcomp); /// @dev get current annual interest rate /// @param _silo address of Silo /// @param _asset address of an asset in Silo for which interest rate should be calculated /// @param _blockTimestamp current block timestamp /// @return rcur current annual interest rate (1e18 == 100%) function getCurrentInterestRate( address _silo, address _asset, uint256 _blockTimestamp ) external view returns (uint256 rcur); /// @notice get the flag to detect rcomp restriction (zero current interest) due to overflow /// overflow boolean flag to detect rcomp restriction function overflowDetected( address _silo, address _asset, uint256 _blockTimestamp ) external view returns (bool overflow); /// @dev pure function that calculates current annual interest rate /// @param _c configuration object, InterestRateModel.Config /// @param _totalBorrowAmount current total borrows for asset /// @param _totalDeposits current total deposits for asset /// @param _interestRateTimestamp timestamp of last interest rate update /// @param _blockTimestamp current block timestamp /// @return rcur current annual interest rate (1e18 == 100%) function calculateCurrentInterestRate( Config memory _c, uint256 _totalDeposits, uint256 _totalBorrowAmount, uint256 _interestRateTimestamp, uint256 _blockTimestamp ) external pure returns (uint256 rcur); /// @dev pure function that calculates interest rate based on raw input data /// @param _c configuration object, InterestRateModel.Config /// @param _totalBorrowAmount current total borrows for asset /// @param _totalDeposits current total deposits for asset /// @param _interestRateTimestamp timestamp of last interest rate update /// @param _blockTimestamp current block timestamp /// @return rcomp compounded interest rate from last update until now (1e18 == 100%) /// @return ri current integral part of the rate /// @return Tcrit time during which the utilization exceeds the critical value /// @return overflow boolean flag to detect rcomp restriction function calculateCompoundInterestRateWithOverflowDetection( Config memory _c, uint256 _totalDeposits, uint256 _totalBorrowAmount, uint256 _interestRateTimestamp, uint256 _blockTimestamp ) external pure returns ( uint256 rcomp, int256 ri, int256 Tcrit, // solhint-disable-line var-name-mixedcase bool overflow ); /// @dev pure function that calculates interest rate based on raw input data /// @param _c configuration object, InterestRateModel.Config /// @param _totalBorrowAmount current total borrows for asset /// @param _totalDeposits current total deposits for asset /// @param _interestRateTimestamp timestamp of last interest rate update /// @param _blockTimestamp current block timestamp /// @return rcomp compounded interest rate from last update until now (1e18 == 100%) /// @return ri current integral part of the rate /// @return Tcrit time during which the utilization exceeds the critical value function calculateCompoundInterestRate( Config memory _c, uint256 _totalDeposits, uint256 _totalBorrowAmount, uint256 _interestRateTimestamp, uint256 _blockTimestamp ) external pure returns ( uint256 rcomp, int256 ri, int256 Tcrit // solhint-disable-line var-name-mixedcase ); /// @dev returns decimal points used by model function DP() external pure returns (uint256); // solhint-disable-line func-name-mixedcase /// @dev just a helper method to see if address is a InterestRateModel /// @return always true function interestRateModelPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; /// @title Common interface for Silo Incentive Contract interface INotificationReceiver { /// @dev Informs the contract about token transfer /// @param _token address of the token that was transferred /// @param _from sender /// @param _to receiver /// @param _amount amount that was transferred function onAfterTransfer(address _token, address _from, address _to, uint256 _amount) external; /// @dev Sanity check function /// @return always true function notificationReceiverPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.7.6 <0.9.0; /// @title Common interface for Silo Price Providers interface IPriceProvider { /// @notice Returns "Time-Weighted Average Price" for an asset. Calculates TWAP price for quote/asset. /// It unifies all tokens decimal to 18, examples: /// - if asses == quote it returns 1e18 /// - if asset is USDC and quote is ETH and ETH costs ~$3300 then it returns ~0.0003e18 WETH per 1 USDC /// @param _asset address of an asset for which to read price /// @return price of asses with 18 decimals, throws when pool is not ready yet to provide price function getPrice(address _asset) external view returns (uint256 price); /// @dev Informs if PriceProvider is setup for asset. It does not means PriceProvider can provide price right away. /// Some providers implementations need time to "build" buffer for TWAP price, /// so price may not be available yet but this method will return true. /// @param _asset asset in question /// @return TRUE if asset has been setup, otherwise false function assetSupported(address _asset) external view returns (bool); /// @notice Gets token address in which prices are quoted /// @return quoteToken address function quoteToken() external view returns (address); /// @notice Helper method that allows easily detects, if contract is PriceProvider /// @dev this can save us from simple human errors, in case we use invalid address /// but this should NOT be treated as security check /// @return always true function priceProviderPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.7.6 <0.9.0; import "./IPriceProvider.sol"; interface IPriceProvidersRepository { /// @notice Emitted when price provider is added /// @param newPriceProvider new price provider address event NewPriceProvider(IPriceProvider indexed newPriceProvider); /// @notice Emitted when price provider is removed /// @param priceProvider removed price provider address event PriceProviderRemoved(IPriceProvider indexed priceProvider); /// @notice Emitted when asset is assigned to price provider /// @param asset assigned asset address /// @param priceProvider price provider address event PriceProviderForAsset(address indexed asset, IPriceProvider indexed priceProvider); /// @notice Register new price provider /// @param _priceProvider address of price provider function addPriceProvider(IPriceProvider _priceProvider) external; /// @notice Unregister price provider /// @param _priceProvider address of price provider to be removed function removePriceProvider(IPriceProvider _priceProvider) external; /// @notice Sets price provider for asset /// @dev Request for asset price is forwarded to the price provider assigned to that asset /// @param _asset address of an asset for which price provider will be used /// @param _priceProvider address of price provider function setPriceProviderForAsset(address _asset, IPriceProvider _priceProvider) external; /// @notice Returns "Time-Weighted Average Price" for an asset /// @param _asset address of an asset for which to read price /// @return price TWAP price of a token with 18 decimals function getPrice(address _asset) external view returns (uint256 price); /// @notice Gets price provider assigned to an asset /// @param _asset address of an asset for which to get price provider /// @return priceProvider address of price provider function priceProviders(address _asset) external view returns (IPriceProvider priceProvider); /// @notice Gets token address in which prices are quoted /// @return quoteToken address function quoteToken() external view returns (address); /// @notice Gets manager role address /// @return manager role address function manager() external view returns (address); /// @notice Checks if providers are available for an asset /// @param _asset asset address to check /// @return returns TRUE if price feed is ready, otherwise false function providersReadyForAsset(address _asset) external view returns (bool); /// @notice Returns true if address is a registered price provider /// @param _provider address of price provider to be removed /// @return true if address is a registered price provider, otherwise false function isPriceProvider(IPriceProvider _provider) external view returns (bool); /// @notice Gets number of price providers registered /// @return number of price providers registered function providersCount() external view returns (uint256); /// @notice Gets an array of price providers /// @return array of price providers function providerList() external view returns (address[] memory); /// @notice Sanity check function /// @return returns always TRUE function priceProvidersRepositoryPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.7.6 <0.9.0; import "./IPriceProvider.sol"; /// @title Common interface V2 for Silo Price Providers interface IPriceProviderV2 is IPriceProvider { /// @dev for liquidation purposes and for compatibility with naming convention we already using in LiquidationHelper /// we have this method to return on-chain provider that can be useful for liquidation function getFallbackProvider(address _asset) external view returns (IPriceProvider); /// @dev this is info method for LiquidationHelper /// @return bool TRUE if provider is off-chain, means it is not a dex function offChainProvider() external pure returns (bool); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./INotificationReceiver.sol"; interface IShareToken is IERC20Metadata { /// @notice Emitted every time receiver is notified about token transfer /// @param notificationReceiver receiver address /// @param success false if TX reverted on `notificationReceiver` side, otherwise true event NotificationSent( INotificationReceiver indexed notificationReceiver, bool success ); /// @notice Mint method for Silo to create debt position /// @param _account wallet for which to mint token /// @param _amount amount of token to be minted function mint(address _account, uint256 _amount) external; /// @notice Burn method for Silo to close debt position /// @param _account wallet for which to burn token /// @param _amount amount of token to be burned function burn(address _account, uint256 _amount) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "./IBaseSilo.sol"; interface ISilo is IBaseSilo { /// @notice Deposit `_amount` of `_asset` tokens from `msg.sender` to the Silo /// @param _asset The address of the token to deposit /// @param _amount The amount of the token to deposit /// @param _collateralOnly True if depositing collateral only /// @return collateralAmount deposited amount /// @return collateralShare user collateral shares based on deposited amount function deposit(address _asset, uint256 _amount, bool _collateralOnly) external returns (uint256 collateralAmount, uint256 collateralShare); /// @notice Router function to deposit `_amount` of `_asset` tokens to the Silo for the `_depositor` /// @param _asset The address of the token to deposit /// @param _depositor The address of the recipient of collateral tokens /// @param _amount The amount of the token to deposit /// @param _collateralOnly True if depositing collateral only /// @return collateralAmount deposited amount /// @return collateralShare `_depositor` collateral shares based on deposited amount function depositFor(address _asset, address _depositor, uint256 _amount, bool _collateralOnly) external returns (uint256 collateralAmount, uint256 collateralShare); /// @notice Withdraw `_amount` of `_asset` tokens from the Silo to `msg.sender` /// @param _asset The address of the token to withdraw /// @param _amount The amount of the token to withdraw /// @param _collateralOnly True if withdrawing collateral only deposit /// @return withdrawnAmount withdrawn amount that was transferred to user /// @return withdrawnShare burned share based on `withdrawnAmount` function withdraw(address _asset, uint256 _amount, bool _collateralOnly) external returns (uint256 withdrawnAmount, uint256 withdrawnShare); /// @notice Router function to withdraw `_amount` of `_asset` tokens from the Silo for the `_depositor` /// @param _asset The address of the token to withdraw /// @param _depositor The address that originally deposited the collateral tokens being withdrawn, /// it should be the one initiating the withdrawal through the router /// @param _receiver The address that will receive the withdrawn tokens /// @param _amount The amount of the token to withdraw /// @param _collateralOnly True if withdrawing collateral only deposit /// @return withdrawnAmount withdrawn amount that was transferred to `_receiver` /// @return withdrawnShare burned share based on `withdrawnAmount` function withdrawFor( address _asset, address _depositor, address _receiver, uint256 _amount, bool _collateralOnly ) external returns (uint256 withdrawnAmount, uint256 withdrawnShare); /// @notice Borrow `_amount` of `_asset` tokens from the Silo to `msg.sender` /// @param _asset The address of the token to borrow /// @param _amount The amount of the token to borrow /// @return debtAmount borrowed amount /// @return debtShare user debt share based on borrowed amount function borrow(address _asset, uint256 _amount) external returns (uint256 debtAmount, uint256 debtShare); /// @notice Router function to borrow `_amount` of `_asset` tokens from the Silo for the `_receiver` /// @param _asset The address of the token to borrow /// @param _borrower The address that will take the loan, /// it should be the one initiating the borrowing through the router /// @param _receiver The address of the asset receiver /// @param _amount The amount of the token to borrow /// @return debtAmount borrowed amount /// @return debtShare `_receiver` debt share based on borrowed amount function borrowFor(address _asset, address _borrower, address _receiver, uint256 _amount) external returns (uint256 debtAmount, uint256 debtShare); /// @notice Repay `_amount` of `_asset` tokens from `msg.sender` to the Silo /// @param _asset The address of the token to repay /// @param _amount amount of asset to repay, includes interests /// @return repaidAmount amount repaid /// @return burnedShare burned debt share function repay(address _asset, uint256 _amount) external returns (uint256 repaidAmount, uint256 burnedShare); /// @notice Allows to repay in behalf of borrower to execute liquidation /// @param _asset The address of the token to repay /// @param _borrower The address of the user to have debt tokens burned /// @param _amount amount of asset to repay, includes interests /// @return repaidAmount amount repaid /// @return burnedShare burned debt share function repayFor(address _asset, address _borrower, uint256 _amount) external returns (uint256 repaidAmount, uint256 burnedShare); /// @dev harvest protocol fees from an array of assets /// @return harvestedAmounts amount harvested during tx execution for each of silo asset function harvestProtocolFees() external returns (uint256[] memory harvestedAmounts); /// @notice Function to update interests for `_asset` token since the last saved state /// @param _asset The address of the token to be updated /// @return interest accrued interest function accrueInterest(address _asset) external returns (uint256 interest); /// @notice this methods does not requires to have tokens in order to liquidate user /// @dev during liquidation process, msg.sender will be notified once all collateral will be send to him /// msg.sender needs to be `IFlashLiquidationReceiver` /// @param _users array of users to liquidate /// @param _flashReceiverData this data will be forward to msg.sender on notification /// @return assets array of all processed assets (collateral + debt, including removed) /// @return receivedCollaterals receivedCollaterals[userId][assetId] => amount /// amounts of collaterals send to `_flashReceiver` /// @return shareAmountsToRepaid shareAmountsToRepaid[userId][assetId] => amount /// required amounts of debt to be repaid function flashLiquidate(address[] memory _users, bytes memory _flashReceiverData) external returns ( address[] memory assets, uint256[][] memory receivedCollaterals, uint256[][] memory shareAmountsToRepaid ); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; interface ISiloFactory { /// @notice Emitted when Silo is deployed /// @param silo address of deployed Silo /// @param asset address of asset for which Silo was deployed /// @param version version of silo implementation event NewSiloCreated(address indexed silo, address indexed asset, uint128 version); /// @notice Must be called by repository on constructor /// @param _siloRepository the SiloRepository to set function initRepository(address _siloRepository) external; /// @notice Deploys Silo /// @param _siloAsset unique asset for which Silo is deployed /// @param _version version of silo implementation /// @param _data (optional) data that may be needed during silo creation /// @return silo deployed Silo address function createSilo(address _siloAsset, uint128 _version, bytes memory _data) external returns (address silo); /// @dev just a helper method to see if address is a factory function siloFactoryPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "./ISiloFactory.sol"; import "./ITokensFactory.sol"; import "./IPriceProvidersRepository.sol"; import "./INotificationReceiver.sol"; import "./IInterestRateModel.sol"; interface ISiloRepository { /// @dev protocol fees in precision points (Solvency._PRECISION_DECIMALS), we do allow for fee == 0 struct Fees { /// @dev One time protocol fee for opening a borrow position in precision points (Solvency._PRECISION_DECIMALS) uint64 entryFee; /// @dev Protocol revenue share in interest paid in precision points (Solvency._PRECISION_DECIMALS) uint64 protocolShareFee; /// @dev Protocol share in liquidation profit in precision points (Solvency._PRECISION_DECIMALS). /// It's calculated from total collateral amount to be transferred to liquidator. uint64 protocolLiquidationFee; } struct SiloVersion { /// @dev Default version of Silo. If set to 0, it means it is not set. By default it is set to 1 uint128 byDefault; /// @dev Latest added version of Silo. If set to 0, it means it is not set. By default it is set to 1 uint128 latest; } /// @dev AssetConfig struct represents configurable parameters for each Silo struct AssetConfig { /// @dev Loan-to-Value ratio represents the maximum borrowing power of a specific collateral. /// For example, if the collateral asset has an LTV of 75%, the user can borrow up to 0.75 worth /// of quote token in the principal currency for every quote token worth of collateral. /// value uses 18 decimals eg. 100% == 1e18 /// max valid value is 1e18 so it needs storage of 60 bits uint64 maxLoanToValue; /// @dev Liquidation Threshold represents the threshold at which a borrow position will be considered /// undercollateralized and subject to liquidation for each collateral. For example, /// if a collateral has a liquidation threshold of 80%, it means that the loan will be /// liquidated when the borrowAmount value is worth 80% of the collateral value. /// value uses 18 decimals eg. 100% == 1e18 uint64 liquidationThreshold; /// @dev interest rate model address IInterestRateModel interestRateModel; } event NewDefaultMaximumLTV(uint64 defaultMaximumLTV); event NewDefaultLiquidationThreshold(uint64 defaultLiquidationThreshold); /// @notice Emitted on new Silo creation /// @param silo deployed Silo address /// @param asset unique asset for deployed Silo /// @param siloVersion version of deployed Silo event NewSilo(address indexed silo, address indexed asset, uint128 siloVersion); /// @notice Emitted when new Silo (or existing one) becomes a bridge pool (pool with only bridge tokens). /// @param pool address of the bridge pool, It can be zero address when bridge asset is removed and pool no longer /// is treated as bridge pool event BridgePool(address indexed pool); /// @notice Emitted on new bridge asset /// @param newBridgeAsset address of added bridge asset event BridgeAssetAdded(address indexed newBridgeAsset); /// @notice Emitted on removed bridge asset /// @param bridgeAssetRemoved address of removed bridge asset event BridgeAssetRemoved(address indexed bridgeAssetRemoved); /// @notice Emitted when default interest rate model is changed /// @param newModel address of new interest rate model event InterestRateModel(IInterestRateModel indexed newModel); /// @notice Emitted on price provider repository address update /// @param newProvider address of new oracle repository event PriceProvidersRepositoryUpdate( IPriceProvidersRepository indexed newProvider ); /// @notice Emitted on token factory address update /// @param newTokensFactory address of new token factory event TokensFactoryUpdate(address indexed newTokensFactory); /// @notice Emitted on router address update /// @param newRouter address of new router event RouterUpdate(address indexed newRouter); /// @notice Emitted on INotificationReceiver address update /// @param newIncentiveContract address of new INotificationReceiver event NotificationReceiverUpdate(INotificationReceiver indexed newIncentiveContract); /// @notice Emitted when new Silo version is registered /// @param factory factory address that deploys registered Silo version /// @param siloLatestVersion Silo version of registered Silo /// @param siloDefaultVersion current default Silo version event RegisterSiloVersion(address indexed factory, uint128 siloLatestVersion, uint128 siloDefaultVersion); /// @notice Emitted when Silo version is unregistered /// @param factory factory address that deploys unregistered Silo version /// @param siloVersion version that was unregistered event UnregisterSiloVersion(address indexed factory, uint128 siloVersion); /// @notice Emitted when default Silo version is updated /// @param newDefaultVersion new default version event SiloDefaultVersion(uint128 newDefaultVersion); /// @notice Emitted when default fee is updated /// @param newEntryFee new entry fee /// @param newProtocolShareFee new protocol share fee /// @param newProtocolLiquidationFee new protocol liquidation fee event FeeUpdate( uint64 newEntryFee, uint64 newProtocolShareFee, uint64 newProtocolLiquidationFee ); /// @notice Emitted when asset config is updated for a silo /// @param silo silo for which asset config is being set /// @param asset asset for which asset config is being set /// @param assetConfig new asset config event AssetConfigUpdate(address indexed silo, address indexed asset, AssetConfig assetConfig); /// @notice Emitted when silo (silo factory) version is set for asset /// @param asset asset for which asset config is being set /// @param version Silo version event VersionForAsset(address indexed asset, uint128 version); /// @param _siloAsset silo asset /// @return version of Silo that is assigned for provided asset, if not assigned it returns zero (default) function getVersionForAsset(address _siloAsset) external returns (uint128); /// @notice setter for `getVersionForAsset` mapping /// @param _siloAsset silo asset /// @param _version version of Silo that will be assigned for `_siloAsset`, zero (default) is acceptable function setVersionForAsset(address _siloAsset, uint128 _version) external; /// @notice use this method only when off-chain verification is OFF /// @dev Silo does NOT support rebase and deflationary tokens /// @param _siloAsset silo asset /// @param _siloData (optional) data that may be needed during silo creation /// @return createdSilo address of created silo function newSilo(address _siloAsset, bytes memory _siloData) external returns (address createdSilo); /// @notice use this method to deploy new version of Silo for an asset that already has Silo deployed. /// Only owner (DAO) can replace. /// @dev Silo does NOT support rebase and deflationary tokens /// @param _siloAsset silo asset /// @param _siloVersion version of silo implementation. Use 0 for default version which is fine /// for 99% of cases. /// @param _siloData (optional) data that may be needed during silo creation /// @return createdSilo address of created silo function replaceSilo( address _siloAsset, uint128 _siloVersion, bytes memory _siloData ) external returns (address createdSilo); /// @notice Set factory contract for debt and collateral tokens for each Silo asset /// @dev Callable only by owner /// @param _tokensFactory address of TokensFactory contract that deploys debt and collateral tokens function setTokensFactory(address _tokensFactory) external; /// @notice Set default fees /// @dev Callable only by owner /// @param _fees: /// - _entryFee one time protocol fee for opening a borrow position in precision points /// (Solvency._PRECISION_DECIMALS) /// - _protocolShareFee protocol revenue share in interest paid in precision points /// (Solvency._PRECISION_DECIMALS) /// - _protocolLiquidationFee protocol share in liquidation profit in precision points /// (Solvency._PRECISION_DECIMALS). It's calculated from total collateral amount to be transferred /// to liquidator. function setFees(Fees calldata _fees) external; /// @notice Set configuration for given asset in given Silo /// @dev Callable only by owner /// @param _silo Silo address for which config applies /// @param _asset asset address for which config applies /// @param _assetConfig: /// - _maxLoanToValue maximum Loan-to-Value, for details see `Repository.AssetConfig.maxLoanToValue` /// - _liquidationThreshold liquidation threshold, for details see `Repository.AssetConfig.maxLoanToValue` /// - _interestRateModel interest rate model address, for details see `Repository.AssetConfig.interestRateModel` function setAssetConfig( address _silo, address _asset, AssetConfig calldata _assetConfig ) external; /// @notice Set default interest rate model /// @dev Callable only by owner /// @param _defaultInterestRateModel default interest rate model function setDefaultInterestRateModel(IInterestRateModel _defaultInterestRateModel) external; /// @notice Set default maximum LTV /// @dev Callable only by owner /// @param _defaultMaxLTV default maximum LTV in precision points (Solvency._PRECISION_DECIMALS) function setDefaultMaximumLTV(uint64 _defaultMaxLTV) external; /// @notice Set default liquidation threshold /// @dev Callable only by owner /// @param _defaultLiquidationThreshold default liquidation threshold in precision points /// (Solvency._PRECISION_DECIMALS) function setDefaultLiquidationThreshold(uint64 _defaultLiquidationThreshold) external; /// @notice Set price provider repository /// @dev Callable only by owner /// @param _repository price provider repository address function setPriceProvidersRepository(IPriceProvidersRepository _repository) external; /// @notice Set router contract /// @dev Callable only by owner /// @param _router router address function setRouter(address _router) external; /// @notice Set NotificationReceiver contract /// @dev Callable only by owner /// @param _silo silo address for which to set `_notificationReceiver` /// @param _notificationReceiver NotificationReceiver address function setNotificationReceiver(address _silo, INotificationReceiver _notificationReceiver) external; /// @notice Adds new bridge asset /// @dev New bridge asset must be unique. Duplicates in bridge assets are not allowed. It's possible to add /// bridge asset that has been removed in the past. Note that all Silos must be synced manually. Callable /// only by owner. /// @param _newBridgeAsset bridge asset address function addBridgeAsset(address _newBridgeAsset) external; /// @notice Removes bridge asset /// @dev Note that all Silos must be synced manually. Callable only by owner. /// @param _bridgeAssetToRemove bridge asset address to be removed function removeBridgeAsset(address _bridgeAssetToRemove) external; /// @notice Registers new Silo version /// @dev User can choose which Silo version he wants to deploy. It's possible to have multiple versions of Silo. /// Callable only by owner. /// @param _factory factory contract that deploys new version of Silo /// @param _isDefault true if this version should be used as default function registerSiloVersion(ISiloFactory _factory, bool _isDefault) external; /// @notice Unregisters Silo version /// @dev Callable only by owner. /// @param _siloVersion Silo version to be unregistered function unregisterSiloVersion(uint128 _siloVersion) external; /// @notice Sets default Silo version /// @dev Callable only by owner. /// @param _defaultVersion Silo version to be set as default function setDefaultSiloVersion(uint128 _defaultVersion) external; /// @notice Check if contract address is a Silo deployment /// @param _silo address of expected Silo /// @return true if address is Silo deployment, otherwise false function isSilo(address _silo) external view returns (bool); /// @notice Get Silo address of asset /// @param _asset address of asset /// @return address of corresponding Silo deployment function getSilo(address _asset) external view returns (address); /// @notice Get Silo Factory for given version /// @param _siloVersion version of Silo implementation /// @return ISiloFactory contract that deploys Silos of given version function siloFactory(uint256 _siloVersion) external view returns (ISiloFactory); /// @notice Get debt and collateral Token Factory /// @return ITokensFactory contract that deploys debt and collateral tokens function tokensFactory() external view returns (ITokensFactory); /// @notice Get Router contract /// @return address of router contract function router() external view returns (address); /// @notice Get current bridge assets /// @dev Keep in mind that not all Silos may be synced with current bridge assets so it's possible that some /// assets in that list are not part of given Silo. /// @return address array of bridge assets function getBridgeAssets() external view returns (address[] memory); /// @notice Get removed bridge assets /// @dev Keep in mind that not all Silos may be synced with bridge assets so it's possible that some /// assets in that list are still part of given Silo. /// @return address array of bridge assets function getRemovedBridgeAssets() external view returns (address[] memory); /// @notice Get maximum LTV for asset in given Silo /// @dev If dedicated config is not set, method returns default config /// @param _silo address of Silo /// @param _asset address of an asset /// @return maximum LTV in precision points (Solvency._PRECISION_DECIMALS) function getMaximumLTV(address _silo, address _asset) external view returns (uint256); /// @notice Get Interest Rate Model address for asset in given Silo /// @dev If dedicated config is not set, method returns default config /// @param _silo address of Silo /// @param _asset address of an asset /// @return address of interest rate model function getInterestRateModel(address _silo, address _asset) external view returns (IInterestRateModel); /// @notice Get liquidation threshold for asset in given Silo /// @dev If dedicated config is not set, method returns default config /// @param _silo address of Silo /// @param _asset address of an asset /// @return liquidation threshold in precision points (Solvency._PRECISION_DECIMALS) function getLiquidationThreshold(address _silo, address _asset) external view returns (uint256); /// @notice Get incentive contract address. Incentive contracts are responsible for distributing rewards /// to debt and/or collateral token holders of given Silo /// @param _silo address of Silo /// @return incentive contract address function getNotificationReceiver(address _silo) external view returns (INotificationReceiver); /// @notice Get owner role address of Repository /// @return owner role address function owner() external view returns (address); /// @notice get PriceProvidersRepository contract that manages price providers implementations /// @return IPriceProvidersRepository address function priceProvidersRepository() external view returns (IPriceProvidersRepository); /// @dev Get protocol fee for opening a borrow position /// @return fee in precision points (Solvency._PRECISION_DECIMALS == 100%) function entryFee() external view returns (uint256); /// @dev Get protocol share fee /// @return protocol share fee in precision points (Solvency._PRECISION_DECIMALS == 100%) function protocolShareFee() external view returns (uint256); /// @dev Get protocol liquidation fee /// @return protocol liquidation fee in precision points (Solvency._PRECISION_DECIMALS == 100%) function protocolLiquidationFee() external view returns (uint256); /// @dev Checks all conditions for new silo creation and throws when not possible to create /// @param _asset address of asset for which you want to create silo /// @param _assetIsABridge bool TRUE when `_asset` is bridge asset, FALSE when it is not function ensureCanCreateSiloFor(address _asset, bool _assetIsABridge) external view; function siloRepositoryPing() external pure returns (bytes4); }
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9.0; interface ISwapper { /// @dev swaps `_amountIn` of `_tokenIn` for `_tokenOut`. It might require approvals. /// @return amountOut amount of _tokenOut received function swapAmountIn( address _tokenIn, address _tokenOut, uint256 _amountIn, address _priceProvider, address _siloAsset ) external returns (uint256 amountOut); /// @dev swaps `_tokenIn` for `_amountOut` of `_tokenOut`. It might require approvals /// @return amountIn amount of _tokenIn spend function swapAmountOut( address _tokenIn, address _tokenOut, uint256 _amountOut, address _priceProvider, address _siloAsset ) external returns (uint256 amountIn); /// @return address that needs to have approval to spend tokens to execute a swap function spenderToApprove() external view returns (address); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "./IShareToken.sol"; interface ITokensFactory { /// @notice Emitted when collateral token is deployed /// @param token address of deployed collateral token event NewShareCollateralTokenCreated(address indexed token); /// @notice Emitted when collateral token is deployed /// @param token address of deployed debt token event NewShareDebtTokenCreated(address indexed token); ///@notice Must be called by repository on constructor /// @param _siloRepository the SiloRepository to set function initRepository(address _siloRepository) external; /// @notice Deploys collateral token /// @param _name name of the token /// @param _symbol symbol of the token /// @param _asset underlying asset for which token is deployed /// @return address of deployed collateral share token function createShareCollateralToken( string memory _name, string memory _symbol, address _asset ) external returns (IShareToken); /// @notice Deploys debt token /// @param _name name of the token /// @param _symbol symbol of the token /// @param _asset underlying asset for which token is deployed /// @return address of deployed debt share token function createShareDebtToken( string memory _name, string memory _symbol, address _asset ) external returns (IShareToken); /// @dev just a helper method to see if address is a factory /// @return always true function tokensFactoryPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IWrappedNativeToken is IERC20 { function deposit() external payable; function withdraw(uint256 amount) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; library EasyMath { error ZeroAssets(); error ZeroShares(); function toShare(uint256 amount, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) { if (totalShares == 0 || totalAmount == 0) { return amount; } uint256 result = amount * totalShares / totalAmount; // Prevent rounding error if (result == 0 && amount != 0) { revert ZeroShares(); } return result; } function toShareRoundUp(uint256 amount, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) { if (totalShares == 0 || totalAmount == 0) { return amount; } uint256 numerator = amount * totalShares; uint256 result = numerator / totalAmount; // Round up if (numerator % totalAmount != 0) { result += 1; } return result; } function toAmount(uint256 share, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) { if (totalShares == 0 || totalAmount == 0) { return 0; } uint256 result = share * totalAmount / totalShares; // Prevent rounding error if (result == 0 && share != 0) { revert ZeroAssets(); } return result; } function toAmountRoundUp(uint256 share, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) { if (totalShares == 0 || totalAmount == 0) { return 0; } uint256 numerator = share * totalAmount; uint256 result = numerator / totalShares; // Round up if (numerator % totalShares != 0) { result += 1; } return result; } function toValue(uint256 _assetAmount, uint256 _assetPrice, uint256 _assetDecimals) internal pure returns (uint256) { return _assetAmount * _assetPrice / 10 ** _assetDecimals; } function sum(uint256[] memory _numbers) internal pure returns (uint256 s) { for(uint256 i; i < _numbers.length; i++) { s += _numbers[i]; } } /// @notice Calculates fraction between borrowed and deposited amount of tokens denominated in percentage /// @dev It assumes `_dp` = 100%. /// @param _dp decimal points used by model /// @param _totalDeposits current total deposits for assets /// @param _totalBorrowAmount current total borrows for assets /// @return utilization value function calculateUtilization(uint256 _dp, uint256 _totalDeposits, uint256 _totalBorrowAmount) internal pure returns (uint256) { if (_totalDeposits == 0 || _totalBorrowAmount == 0) return 0; return _totalBorrowAmount * _dp / _totalDeposits; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; /// @dev EasyMathV2 is optimised version of EasyMath, many places was `unchecked` for lower gas cost. /// There is also fixed version of `calculateUtilization()` method. library EasyMathV2 { error ZeroAssets(); error ZeroShares(); function toShare(uint256 amount, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256 result) { if (totalShares == 0 || totalAmount == 0) { return amount; } result = amount * totalShares; // totalAmount is never 0 based on above check, so we can uncheck unchecked { result /= totalAmount; } // Prevent rounding error if (result == 0 && amount != 0) { revert ZeroShares(); } } function toShareRoundUp(uint256 amount, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256 result) { if (totalShares == 0 || totalAmount == 0) { return amount; } uint256 numerator = amount * totalShares; // totalAmount is not 0, so it is safe to uncheck unchecked { result = numerator / totalAmount; } // Round up if (numerator % totalAmount != 0) { unchecked { result += 1; } } } function toAmount(uint256 share, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256 result) { if (totalShares == 0 || totalAmount == 0) { return 0; } result = share * totalAmount; // totalShares are not 0, so we can uncheck unchecked { result /= totalShares; } // Prevent rounding error if (result == 0 && share != 0) { revert ZeroAssets(); } } function toAmountRoundUp(uint256 share, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256 result) { if (totalShares == 0 || totalAmount == 0) { return 0; } uint256 numerator = share * totalAmount; // totalShares are not 0, based on above check, so we can uncheck unchecked { result = numerator / totalShares; } // Round up if (numerator % totalShares != 0) { unchecked { result += 1; } } } function toValue(uint256 _assetAmount, uint256 _assetPrice, uint256 _assetDecimals) internal pure returns (uint256 value) { value = _assetAmount * _assetPrice; // power of 10 can not be 0, so we can uncheck unchecked { value /= 10 ** _assetDecimals; } } function sum(uint256[] memory _numbers) internal pure returns (uint256 s) { for(uint256 i; i < _numbers.length;) { s += _numbers[i]; unchecked { i++; } } } /// @notice Calculates fraction between borrowed and deposited amount of tokens denominated in percentage /// @dev It assumes `_dp` = 100%. /// @param _dp decimal points used by model /// @param _totalDeposits current total deposits for assets /// @param _totalBorrowAmount current total borrows for assets /// @return utilization value, capped to 100% /// Limiting utilisation ratio by 100% max will allows us to perform better interest rate computations /// and should not affect any other part of protocol. function calculateUtilization(uint256 _dp, uint256 _totalDeposits, uint256 _totalBorrowAmount) internal pure returns (uint256 utilization) { if (_totalDeposits == 0 || _totalBorrowAmount == 0) return 0; utilization = _totalBorrowAmount * _dp; // _totalDeposits is not 0 based on above check, so it is safe to uncheck this division unchecked { utilization /= _totalDeposits; } // cap at 100% if (utilization > _dp) utilization = _dp; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.7.6 <0.9.0; library Ping { function pong(function() external pure returns(bytes4) pingFunction) internal pure returns (bool) { return pingFunction.address != address(0) && pingFunction.selector == pingFunction(); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.7.6 <=0.9.0; library RevertBytes { function revertBytes(bytes memory _errMsg, string memory _customErr) internal pure { if (_errMsg.length > 0) { assembly { // solhint-disable-line no-inline-assembly revert(add(32, _errMsg), mload(_errMsg)) } } revert(_customErr); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "../interfaces/IPriceProvidersRepository.sol"; import "../interfaces/ISilo.sol"; import "../interfaces/IInterestRateModel.sol"; import "../interfaces/ISiloRepository.sol"; import "./EasyMath.sol"; library SolvencyV2 { using EasyMath for uint256; /// @notice /// MaximumLTV - Maximum Loan-to-Value ratio represents the maximum borrowing power of all user's collateral /// positions in a Silo /// LiquidationThreshold - Liquidation Threshold represents the threshold at which all user's borrow positions /// in a Silo will be considered under collateralized and subject to liquidation enum TypeofLTV { MaximumLTV, LiquidationThreshold } error DifferentArrayLength(); error UnsupportedLTVType(); struct SolvencyParams { /// @param siloRepository SiloRepository address ISiloRepository siloRepository; /// @param silo Silo address ISilo silo; /// @param assets array with assets address[] assets; /// @param assetStates array of states for each asset, where index match the `assets` index ISilo.AssetStorage[] assetStates; /// @param user wallet address for which to read debt address user; } /// @dev is value that used for integer calculations and decimal points for utilization ratios, LTV, protocol fees uint256 internal constant _PRECISION_DECIMALS = 1e18; uint256 internal constant _INFINITY = type(uint256).max; /// @notice Returns current user LTV and second LTV chosen in params /// @dev This function is optimized for protocol use. In some cases there is no need to keep the calculation /// going and predefined results can be returned. /// @param _params `SolvencyV2.SolvencyParams` struct with needed params for calculation /// @param _secondLtvType type of LTV to be returned as second value /// @return currentUserLTV Loan-to-Value ratio represents current user's proportion of debt to collateral /// @return secondLTV second type of LTV which depends on _secondLtvType, zero is returned if the value of the loan /// or the collateral are zero function calculateLTVs(SolvencyParams memory _params, TypeofLTV _secondLtvType) internal view returns (uint256 currentUserLTV, uint256 secondLTV) { uint256[] memory totalBorrowAmounts = getBorrowAmounts(_params); // this return avoids eg. additional checks on withdraw, when user did not borrow any asset if (EasyMath.sum(totalBorrowAmounts) == 0) return (0, 0); IPriceProvidersRepository priceProvidersRepository = _params.siloRepository.priceProvidersRepository(); uint256[] memory borrowValues = convertAmountsToValues( priceProvidersRepository, _params.assets, totalBorrowAmounts ); // value of user's total debt uint256 borrowTotalValue = EasyMath.sum(borrowValues); if (borrowTotalValue == 0) return (0, 0); uint256[] memory collateralValues = getUserCollateralValues(priceProvidersRepository, _params); // value of user's collateral uint256 collateralTotalValue = EasyMath.sum(collateralValues); if (collateralTotalValue == 0) return (_INFINITY, 0); // value of theoretical debt user can have depending on TypeofLTV uint256 borrowAvailableTotalValue = _getTotalAvailableToBorrowValue( _params.siloRepository, address(_params.silo), _params.assets, _secondLtvType, collateralValues ); currentUserLTV = borrowTotalValue * _PRECISION_DECIMALS / collateralTotalValue; // one of SolvencyV2.TypeofLTV secondLTV = borrowAvailableTotalValue * _PRECISION_DECIMALS / collateralTotalValue; } /// @notice Calculates chosen LTV limit /// @dev This function should be used by external actors like SiloLens and UI/subgraph. `calculateLTVs` is /// optimized for protocol use and may not return second LVT calculation when they are not needed. /// @param _params `SolvencyV2.SolvencyParams` struct with needed params for calculation /// @param _ltvType acceptable values are only TypeofLTV.MaximumLTV or TypeofLTV.LiquidationThreshold /// @return limit theoretical LTV limit of `_ltvType` function calculateLTVLimit(SolvencyParams memory _params, TypeofLTV _ltvType) internal view returns (uint256 limit) { IPriceProvidersRepository priceProvidersRepository = _params.siloRepository.priceProvidersRepository(); uint256[] memory collateralValues = getUserCollateralValues(priceProvidersRepository, _params); // value of user's collateral uint256 collateralTotalValue = EasyMath.sum(collateralValues); if (collateralTotalValue == 0) return 0; // value of theoretical debt user can have depending on TypeofLTV uint256 borrowAvailableTotalValue = _getTotalAvailableToBorrowValue( _params.siloRepository, address(_params.silo), _params.assets, _ltvType, collateralValues ); limit = borrowAvailableTotalValue * _PRECISION_DECIMALS / collateralTotalValue; } /// @notice Returns worth (in quote token) of each collateral deposit of a user /// @param _priceProvidersRepository address of IPriceProvidersRepository where prices are read /// @param _params `SolvencyV2.SolvencyParams` struct with needed params for calculation /// @return collateralValues worth of each collateral deposit of a user as an array function getUserCollateralValues(IPriceProvidersRepository _priceProvidersRepository, SolvencyParams memory _params) internal view returns(uint256[] memory collateralValues) { uint256[] memory collateralAmounts = getCollateralAmounts(_params); collateralValues = convertAmountsToValues(_priceProvidersRepository, _params.assets, collateralAmounts); } /// @notice Convert assets amounts to values in quote token (amount * price) /// @param _priceProviderRepo address of IPriceProvidersRepository where prices are read /// @param _assets array with assets for which prices are read /// @param _amounts array of amounts /// @return values array of values for corresponding assets function convertAmountsToValues( IPriceProvidersRepository _priceProviderRepo, address[] memory _assets, uint256[] memory _amounts ) internal view returns (uint256[] memory values) { if (_assets.length != _amounts.length) revert DifferentArrayLength(); values = new uint256[](_assets.length); for (uint256 i = 0; i < _assets.length; i++) { if (_amounts[i] == 0) continue; uint256 assetPrice = _priceProviderRepo.getPrice(_assets[i]); uint8 assetDecimals = ERC20(_assets[i]).decimals(); values[i] = _amounts[i].toValue(assetPrice, assetDecimals); } } /// @notice Get amount of collateral for each asset /// @param _params `SolvencyV2.SolvencyParams` struct with needed params for calculation /// @return collateralAmounts array of amounts for each token in Silo. May contain zero values if user /// did not deposit given collateral token. function getCollateralAmounts(SolvencyParams memory _params) internal view returns (uint256[] memory collateralAmounts) { if (_params.assets.length != _params.assetStates.length) { revert DifferentArrayLength(); } collateralAmounts = new uint256[](_params.assets.length); for (uint256 i = 0; i < _params.assets.length; i++) { uint256 userCollateralTokenBalance = _params.assetStates[i].collateralToken.balanceOf(_params.user); uint256 userCollateralOnlyTokenBalance = _params.assetStates[i].collateralOnlyToken.balanceOf(_params.user); if (userCollateralTokenBalance + userCollateralOnlyTokenBalance == 0) continue; uint256 rcomp = getRcomp(_params.silo, _params.siloRepository, _params.assets[i], block.timestamp); collateralAmounts[i] = getUserCollateralAmount( _params.assetStates[i], userCollateralTokenBalance, userCollateralOnlyTokenBalance, rcomp, _params.siloRepository ); } } /// @notice Get amount of debt for each asset /// @param _params `SolvencyV2.SolvencyParams` struct with needed params for calculation /// @return totalBorrowAmounts array of amounts for each token in Silo. May contain zero values if user /// did not borrow given token. function getBorrowAmounts(SolvencyParams memory _params) internal view returns (uint256[] memory totalBorrowAmounts) { if (_params.assets.length != _params.assetStates.length) { revert DifferentArrayLength(); } totalBorrowAmounts = new uint256[](_params.assets.length); for (uint256 i = 0; i < _params.assets.length; i++) { uint256 rcomp = getRcomp(_params.silo, _params.siloRepository, _params.assets[i], block.timestamp); totalBorrowAmounts[i] = getUserBorrowAmount(_params.assetStates[i], _params.user, rcomp); } } /// @notice Get amount of deposited token, including collateralOnly deposits /// @param _assetStates state of deposited asset in Silo /// @param _userCollateralTokenBalance balance of user's share collateral token /// @param _userCollateralOnlyTokenBalance balance of user's share collateralOnly token /// @param _rcomp compounded interest rate to account for during calculations, could be 0 /// @param _siloRepository SiloRepository address /// @return amount of underlying token deposited, including collateralOnly deposit function getUserCollateralAmount( ISilo.AssetStorage memory _assetStates, uint256 _userCollateralTokenBalance, uint256 _userCollateralOnlyTokenBalance, uint256 _rcomp, ISiloRepository _siloRepository ) internal view returns (uint256) { uint256 assetAmount = _userCollateralTokenBalance == 0 ? 0 : _userCollateralTokenBalance.toAmount( totalDepositsWithInterest( _assetStates.totalDeposits, _assetStates.totalBorrowAmount, _siloRepository.protocolShareFee(), _rcomp ), _assetStates.collateralToken.totalSupply() ); uint256 assetCollateralOnlyAmount = _userCollateralOnlyTokenBalance == 0 ? 0 : _userCollateralOnlyTokenBalance.toAmount( _assetStates.collateralOnlyDeposits, _assetStates.collateralOnlyToken.totalSupply() ); return assetAmount + assetCollateralOnlyAmount; } /// @notice Get amount of borrowed token /// @param _assetStates state of borrowed asset in Silo /// @param _user user wallet address for which to read debt /// @param _rcomp compounded interest rate to account for during calculations, could be 0 /// @return amount of borrowed token function getUserBorrowAmount(ISilo.AssetStorage memory _assetStates, address _user, uint256 _rcomp) internal view returns (uint256) { uint256 balance = _assetStates.debtToken.balanceOf(_user); if (balance == 0) return 0; uint256 totalBorrowAmountCached = totalBorrowAmountWithInterest(_assetStates.totalBorrowAmount, _rcomp); return balance.toAmountRoundUp(totalBorrowAmountCached, _assetStates.debtToken.totalSupply()); } /// @notice Get compounded interest rate from the model /// @param _silo Silo address /// @param _siloRepository SiloRepository address /// @param _asset address of asset for which to read interest rate /// @param _timestamp used to determine amount of time from last rate update /// @return rcomp compounded interest rate for an asset function getRcomp(ISilo _silo, ISiloRepository _siloRepository, address _asset, uint256 _timestamp) internal view returns (uint256 rcomp) { IInterestRateModel model = _siloRepository.getInterestRateModel(address(_silo), _asset); rcomp = model.getCompoundInterestRate(address(_silo), _asset, _timestamp); } /// @notice Returns total deposits with interest dynamically calculated with the provided rComp /// @param _assetTotalDeposits total deposits for asset /// @param _assetTotalBorrows total borrows for asset /// @param _protocolShareFee `siloRepository.protocolShareFee()` /// @param _rcomp compounded interest rate /// @return _totalDepositsWithInterests total deposits amount with interest function totalDepositsWithInterest( uint256 _assetTotalDeposits, uint256 _assetTotalBorrows, uint256 _protocolShareFee, uint256 _rcomp ) internal pure returns (uint256 _totalDepositsWithInterests) { uint256 depositorsShare = _PRECISION_DECIMALS - _protocolShareFee; return _assetTotalDeposits + _assetTotalBorrows * _rcomp / _PRECISION_DECIMALS * depositorsShare / _PRECISION_DECIMALS; } /// @notice Returns total borrow amount with interest dynamically calculated with the provided rComp /// @param _totalBorrowAmount total borrow amount /// @param _rcomp compounded interest rate /// @return totalBorrowAmountWithInterests total borrow amount with interest function totalBorrowAmountWithInterest(uint256 _totalBorrowAmount, uint256 _rcomp) internal pure returns (uint256 totalBorrowAmountWithInterests) { totalBorrowAmountWithInterests = _totalBorrowAmount + _totalBorrowAmount * _rcomp / _PRECISION_DECIMALS; } /// @notice Calculates protocol liquidation fee and new protocol total fees collected /// @param _protocolEarnedFees amount of total collected fees so far /// @param _amount amount on which we will apply fee /// @param _liquidationFee liquidation fee in SolvencyV2._PRECISION_DECIMALS /// @return liquidationFeeAmount calculated interest /// @return newProtocolEarnedFees the new total amount of protocol fees function calculateLiquidationFee(uint256 _protocolEarnedFees, uint256 _amount, uint256 _liquidationFee) internal pure returns (uint256 liquidationFeeAmount, uint256 newProtocolEarnedFees) { unchecked { // If we overflow on multiplication it should not revert tx, we will get lower fees liquidationFeeAmount = _amount * _liquidationFee / SolvencyV2._PRECISION_DECIMALS; if (_protocolEarnedFees > type(uint256).max - liquidationFeeAmount) { newProtocolEarnedFees = type(uint256).max; liquidationFeeAmount = type(uint256).max - _protocolEarnedFees; } else { newProtocolEarnedFees = _protocolEarnedFees + liquidationFeeAmount; } } } /// @notice Calculates theoretical value (in quote token) that user could borrow based given collateral value /// @param _siloRepository SiloRepository address /// @param _silo Silo address /// @param _asset address of collateral token /// @param _type type of LTV limit to use for calculations /// @param _collateralValue value of collateral deposit (in quote token) /// @return availableToBorrow value (in quote token) that user can borrow against collateral value function _getAvailableToBorrowValue( ISiloRepository _siloRepository, address _silo, address _asset, TypeofLTV _type, uint256 _collateralValue ) private view returns (uint256 availableToBorrow) { uint256 assetLTV; if (_type == TypeofLTV.MaximumLTV) { assetLTV = _siloRepository.getMaximumLTV(_silo, _asset); } else if (_type == TypeofLTV.LiquidationThreshold) { assetLTV = _siloRepository.getLiquidationThreshold(_silo, _asset); } else { revert UnsupportedLTVType(); } // value that can be borrowed against the deposit // ie. for assetLTV = 50%, 1 ETH * 50% = 0.5 ETH of available to borrow availableToBorrow = _collateralValue * assetLTV / _PRECISION_DECIMALS; } /// @notice Calculates theoretical value (in quote token) that user can borrow based on deposited collateral /// @param _siloRepository SiloRepository address /// @param _silo Silo address /// @param _assets array with assets /// @param _ltvType type of LTV limit to use for calculations /// acceptable values are only TypeofLTV.MaximumLTV or TypeofLTV.LiquidationThreshold /// @param _collateralValues value (worth in quote token) of each deposit made by user /// @return totalAvailableToBorrowValue value (in quote token) that user can borrow against collaterals function _getTotalAvailableToBorrowValue( ISiloRepository _siloRepository, address _silo, address[] memory _assets, TypeofLTV _ltvType, uint256[] memory _collateralValues ) private view returns (uint256 totalAvailableToBorrowValue) { if (_assets.length != _collateralValues.length) revert DifferentArrayLength(); for (uint256 i = 0; i < _assets.length; i++) { totalAvailableToBorrowValue += _getAvailableToBorrowValue( _siloRepository, _silo, _assets[i], _ltvType, _collateralValues[i] ); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; /// @notice LiquidationHelper IS NOT PART OF THE PROTOCOL. SILO CREATED THIS TOOL, MOSTLY AS AN EXAMPLE. interface ILiquidationHelper { /// @dev Liquidation scenarios that are supported by helper: /// - Internal: fully on-chain, using internal swappers, magicians etc /// When 0x API can not handle the swap, we will use internal. /// - Full0x: 0x will handle swap for collateral -> repay asset, then contract needs to do repay. /// Change that left after repay will be swapped to WETH using internal methods. /// This scenario is for A -> B or A, B -> C cases. /// - Full0xWithChange: similar to Full0x, but all repay tokens that left, will be send to liquidator. /// BE bot needs to do another tx to swap change to ETH /// This scenario is for A -> B or A, B -> C cases /// Exception: WETH -> A, it should be full or internal /// Helper is supporting all the tokens internally, so only case, when we would need Full0xWithChange is when /// we didn't develop swapper/magician for some new asset yet. Call `liquidationSupported` to check it. /// - Collateral0x: 0x will swap collateral to native token, then from native -> repay asset contract handle it /// This is for A -> XAI, WETH, other cases of multiple repay tokens are not supported by 0x /// - *Force: force option allows to liquidate even when liquidation is not profitable enum LiquidationScenario { Internal, Collateral0x, Full0x, Full0xWithChange, InternalForce, Collateral0xForce, Full0xForce, Full0xWithChangeForce } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "../interface/ILiquidationHelper.sol"; /// @notice Library for processing LiquidationScenarios data library LiquidationScenarioDetector { function isFull0x(ILiquidationHelper.LiquidationScenario _scenario) internal pure returns (bool) { return _scenario == ILiquidationHelper.LiquidationScenario.Full0x || _scenario == ILiquidationHelper.LiquidationScenario.Full0xForce; } function isFull0xWithChange(ILiquidationHelper.LiquidationScenario _scenario) internal pure returns (bool) { return _scenario == ILiquidationHelper.LiquidationScenario.Full0xWithChange || _scenario == ILiquidationHelper.LiquidationScenario.Full0xWithChangeForce; } function isCollateral0x(ILiquidationHelper.LiquidationScenario _scenario) internal pure returns (bool) { return _scenario == ILiquidationHelper.LiquidationScenario.Collateral0x || _scenario == ILiquidationHelper.LiquidationScenario.Collateral0xForce; } function isInternal(ILiquidationHelper.LiquidationScenario _scenario) internal pure returns (bool) { return _scenario == ILiquidationHelper.LiquidationScenario.Internal || _scenario == ILiquidationHelper.LiquidationScenario.InternalForce; } function calculateEarnings(ILiquidationHelper.LiquidationScenario _scenario) internal pure returns (bool) { return _scenario == ILiquidationHelper.LiquidationScenario.Internal || _scenario == ILiquidationHelper.LiquidationScenario.Collateral0x || _scenario == ILiquidationHelper.LiquidationScenario.Full0x || _scenario == ILiquidationHelper.LiquidationScenario.Full0xWithChange; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../interfaces/ISilo.sol"; /// @notice LiquidationHelper IS NOT PART OF THE PROTOCOL. SILO CREATED THIS TOOL, MOSTLY AS AN EXAMPLE. /// see https://github.com/silo-finance/liquidation#readme for details how liquidation process should look like abstract contract LiquidationRepay { error RepayFailed(); function _repay( ISilo _silo, address _user, address[] calldata _assets, uint256[] calldata _shareAmountsToRepaid ) internal virtual { for (uint256 i = 0; i < _assets.length;) { if (_shareAmountsToRepaid[i] != 0) { _repayAsset(_silo, _user, _assets[i], _shareAmountsToRepaid[i]); } // we will never have that many assets to overflow unchecked { i++; } } } function _repayAsset( ISilo _silo, address _user, address _asset, uint256 _shareAmountToRepaid ) internal virtual { // Low level call needed to support non-standard `ERC20.approve` eg like `USDT.approve` // solhint-disable-next-line avoid-low-level-calls _asset.call(abi.encodeCall(IERC20.approve, (address(_silo), _shareAmountToRepaid))); _silo.repayFor(_asset, _user, _shareAmountToRepaid); // DEFLATIONARY TOKENS ARE NOT SUPPORTED // we are not using lower limits for swaps so we may not get enough tokens to do full repay // our assumption here is that `_shareAmountsToRepaid[i]` is total amount to repay the full debt // if after repay user has no debt in this asset, the swap is acceptable if (_silo.assetStorage(_asset).debtToken.balanceOf(_user) != 0) { revert RepayFailed(); } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.7.6 <0.9.0; /// @notice Extension for the Liquidation helper to support such operations as unwrapping interface IMagician { /// @notice Operates to unwrap an `_asset` /// @param _asset Asset to be unwrapped /// @param _amount Amount of the `_asset` /// @return tokenOut A token that the `_asset` has been converted to /// @return amountOut Amount of the `tokenOut` that we received function towardsNative(address _asset, uint256 _amount) external returns (address tokenOut, uint256 amountOut); /// @notice Performs operation opposit to `towardsNative` /// @param _asset Asset to be wrapped /// @param _amount Amount of the `_asset` /// @return tokenOut A token that the `_asset` has been converted to /// @return amountOut Amount of the quote token that we spent to get `_amoun` of the `_asset` function towardsAsset(address _asset, uint256 _amount) external returns (address tokenOut, uint256 amountOut); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../lib/RevertBytes.sol"; /// @dev Based on demo contract that swaps its ERC20 balance for another ERC20. /// demo source: https://github.com/0xProject/0x-api-starter-guide-code/blob/master/contracts/SimpleTokenSwap.sol contract ZeroExSwap is Ownable { using RevertBytes for bytes; /// @param sellToken The `sellTokenAddress` field from the API response. /// @param buyToken The `buyTokenAddress` field from the API response. /// @param allowanceTarget The `allowanceTarget` field from the API response. /// @param swapCallData The `data` field from the API response. struct SwapInput0x { address sellToken; address allowanceTarget; bytes swapCallData; } /// @dev 0x ExchangeProxy address. /// See https://docs.0x.org/developer-resources/contract-addresses /// The `to` field from the API response, but at the same time, /// TODO: maybe unit test that will check, if it does not changed? // solhint-disable-next-line var-name-mixedcase address public immutable EXCHANGE_PROXY; event BoughtTokens(address sellToken, address buyToken, uint256 boughtAmount); error AddressZero(); error TargetNotExchangeProxy(); error ApprovalFailed(); constructor(address _exchangeProxy) { if (_exchangeProxy == address(0)) revert AddressZero(); EXCHANGE_PROXY = _exchangeProxy; } /// @dev Swaps ERC20->ERC20 tokens held by this contract using a 0x-API quote. /// Must attach ETH equal to the `value` field from the API response. /// @param _sellToken The `sellTokenAddress` field from the API response. /// @param _spender The `allowanceTarget` field from the API response. /// @param _swapCallData The `data` field from the API response. function fillQuote(address _sellToken, address _spender, bytes memory _swapCallData) public { IERC20(_sellToken).approve(_spender, type(uint256).max); // Call the encoded swap function call on the contract at `swapTarget` // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory data) = EXCHANGE_PROXY.call(_swapCallData); if (!success) data.revertBytes("SWAP_CALL_FAILED"); IERC20(_sellToken).approve(_spender, 0); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import "../PriceProvider.sol"; import "../IERC20LikeV2.sol"; contract ChainlinkV3PriceProvider is PriceProvider { using SafeMath for uint256; struct AssetData { // Time threshold to invalidate stale prices uint256 heartbeat; // If true, we bypass the aggregator and consult the fallback provider directly bool forceFallback; // If true, the aggregator returns price in USD, so we need to convert to QUOTE bool convertToQuote; // Chainlink aggregator proxy AggregatorV3Interface aggregator; // Provider used if the aggregator's price is invalid or if it became disabled IPriceProvider fallbackProvider; } /// @dev Aggregator that converts from USD to quote token AggregatorV3Interface internal immutable _QUOTE_AGGREGATOR; // solhint-disable-line var-name-mixedcase /// @dev Decimals used by the _QUOTE_AGGREGATOR uint8 internal immutable _QUOTE_AGGREGATOR_DECIMALS; // solhint-disable-line var-name-mixedcase /// @dev Used to optimize calculations in emergency disable function // solhint-disable-next-line var-name-mixedcase uint256 internal immutable _MAX_PRICE_DIFF = type(uint256).max / (100 * EMERGENCY_PRECISION); // @dev Precision to use for the EMERGENCY_THRESHOLD uint256 public constant EMERGENCY_PRECISION = 1e6; /// @dev Disable the aggregator if the difference with the fallback is higher than this percentage (10%) uint256 public constant EMERGENCY_THRESHOLD = 10 * EMERGENCY_PRECISION; // solhint-disable-line var-name-mixedcase /// @dev this is basically `PriceProvider.quoteToken.decimals()` uint8 internal immutable _QUOTE_TOKEN_DECIMALS; // solhint-disable-line var-name-mixedcase /// @dev Address allowed to call the emergencyDisable function, can be set by the owner address public emergencyManager; /// @dev Threshold used to determine if the price returned by the _QUOTE_AGGREGATOR is valid uint256 public quoteAggregatorHeartbeat; /// @dev Data used for each asset mapping(address => AssetData) public assetData; event NewAggregator(address indexed asset, AggregatorV3Interface indexed aggregator, bool convertToQuote); event NewFallbackPriceProvider(address indexed asset, IPriceProvider indexed fallbackProvider); event NewHeartbeat(address indexed asset, uint256 heartbeat); event NewQuoteAggregatorHeartbeat(uint256 heartbeat); event NewEmergencyManager(address indexed emergencyManager); event AggregatorDisabled(address indexed asset, AggregatorV3Interface indexed aggregator); error AggregatorDidNotChange(); error AggregatorPriceNotAvailable(); error AssetNotSupported(); error EmergencyManagerDidNotChange(); error EmergencyThresholdNotReached(); error FallbackProviderAlreadySet(); error FallbackProviderDidNotChange(); error FallbackProviderNotSet(); error HeartbeatDidNotChange(); error InvalidAggregator(); error InvalidAggregatorDecimals(); error InvalidFallbackPriceProvider(); error InvalidHeartbeat(); error OnlyEmergencyManager(); error QuoteAggregatorHeartbeatDidNotChange(); modifier onlyAssetSupported(address _asset) { if (!assetSupported(_asset)) { revert AssetNotSupported(); } _; } constructor( IPriceProvidersRepository _priceProvidersRepository, address _emergencyManager, AggregatorV3Interface _quoteAggregator, uint256 _quoteAggregatorHeartbeat ) PriceProvider(_priceProvidersRepository) { _setEmergencyManager(_emergencyManager); _QUOTE_TOKEN_DECIMALS = IERC20LikeV2(quoteToken).decimals(); _QUOTE_AGGREGATOR = _quoteAggregator; _QUOTE_AGGREGATOR_DECIMALS = _quoteAggregator.decimals(); quoteAggregatorHeartbeat = _quoteAggregatorHeartbeat; } /// @inheritdoc IPriceProvider function assetSupported(address _asset) public view virtual override returns (bool) { AssetData storage data = assetData[_asset]; // Asset is supported if: // - the asset is the quote token // OR // - the aggregator address is defined AND // - the aggregator is not disabled // OR // - the fallback is defined if (_asset == quoteToken) { return true; } if (address(data.aggregator) != address(0)) { return !data.forceFallback || address(data.fallbackProvider) != address(0); } return false; } /// @dev Returns price directly from aggregator using all internal settings except of fallback provider /// @param _asset Asset for which we want to get the price function getAggregatorPrice(address _asset) public view virtual returns (bool success, uint256 price) { (success, price) = _getAggregatorPrice(_asset); } /// @inheritdoc IPriceProvider function getPrice(address _asset) public view virtual override returns (uint256) { address quote = quoteToken; if (_asset == quote) { return 10 ** _QUOTE_TOKEN_DECIMALS; } (bool success, uint256 price) = _getAggregatorPrice(_asset); return success ? price : _getFallbackPrice(_asset); } /// @dev Sets the aggregator, fallbackProvider and heartbeat for an asset. Can only be called by the manager. /// @param _asset Asset to setup /// @param _aggregator Chainlink aggregator proxy /// @param _fallbackProvider Provider to use if the price is invalid or if the aggregator was disabled /// @param _heartbeat Threshold in seconds to invalidate a stale price function setupAsset( address _asset, AggregatorV3Interface _aggregator, IPriceProvider _fallbackProvider, uint256 _heartbeat, bool _convertToQuote ) external virtual onlyManager { // This has to be done first so that `_setAggregator` works _setHeartbeat(_asset, _heartbeat); if (!_setAggregator(_asset, _aggregator, _convertToQuote)) revert AggregatorDidNotChange(); // We don't care if this doesn't change _setFallbackPriceProvider(_asset, _fallbackProvider); } /// @dev Sets the aggregator for an asset. Can only be called by the manager. /// @param _asset Asset for which to set the aggregator /// @param _aggregator Aggregator to set function setAggregator(address _asset, AggregatorV3Interface _aggregator, bool _convertToQuote) external virtual onlyManager onlyAssetSupported(_asset) { if (!_setAggregator(_asset, _aggregator, _convertToQuote)) revert AggregatorDidNotChange(); } /// @dev Sets the fallback provider for an asset. Can only be called by the manager. /// @param _asset Asset for which to set the fallback provider /// @param _fallbackProvider Provider to set function setFallbackPriceProvider(address _asset, IPriceProvider _fallbackProvider) external virtual onlyManager onlyAssetSupported(_asset) { if (!_setFallbackPriceProvider(_asset, _fallbackProvider)) { revert FallbackProviderDidNotChange(); } } /// @dev Sets the heartbeat threshold for an asset. Can only be called by the manager. /// @param _asset Asset for which to set the heartbeat threshold /// @param _heartbeat Threshold to set function setHeartbeat(address _asset, uint256 _heartbeat) external virtual onlyManager onlyAssetSupported(_asset) { if (!_setHeartbeat(_asset, _heartbeat)) revert HeartbeatDidNotChange(); } /// @dev Sets the quote aggregator heartbeat threshold. Can only be called by the manager. /// @param _heartbeat Threshold to set function setQuoteAggregatorHeartbeat(uint256 _heartbeat) external virtual onlyManager { if (!_setQuoteAggregatorHeartbeat(_heartbeat)) revert QuoteAggregatorHeartbeatDidNotChange(); } /// @dev Sets the emergencyManager. Can only be called by the manager. /// @param _emergencyManager Emergency manager to set function setEmergencyManager(address _emergencyManager) external virtual onlyManager { if (!_setEmergencyManager(_emergencyManager)) revert EmergencyManagerDidNotChange(); } /// @dev Disables the aggregator for an asset if there is a big discrepancy between the aggregator and the /// fallback provider. The only way to reenable the asset is by calling setupAsset or setAggregator again. /// Can only be called by the emergencyManager. /// @param _asset Asset for which to disable the aggregator function emergencyDisable(address _asset) external virtual { if (msg.sender != emergencyManager) { revert OnlyEmergencyManager(); } (bool success, uint256 price) = _getAggregatorPrice(_asset); if (!success) { revert AggregatorPriceNotAvailable(); } uint256 fallbackPrice = _getFallbackPrice(_asset); uint256 diff; unchecked { // It is ok to uncheck because of the initial fallbackPrice >= price check diff = fallbackPrice >= price ? fallbackPrice - price : price - fallbackPrice; } if (diff > _MAX_PRICE_DIFF || (diff * 100 * EMERGENCY_PRECISION) / price < EMERGENCY_THRESHOLD) { revert EmergencyThresholdNotReached(); } // Disable main aggregator, fallback stays enabled assetData[_asset].forceFallback = true; emit AggregatorDisabled(_asset, assetData[_asset].aggregator); } function getFallbackProvider(address _asset) external view virtual returns (IPriceProvider) { return assetData[_asset].fallbackProvider; } function _getAggregatorPrice(address _asset) internal view virtual returns (bool success, uint256 price) { AssetData storage data = assetData[_asset]; uint256 heartbeat = data.heartbeat; bool forceFallback = data.forceFallback; AggregatorV3Interface aggregator = data.aggregator; if (address(aggregator) == address(0)) revert AssetNotSupported(); ( /*uint80 roundID*/, int256 aggregatorPrice, /*uint256 startedAt*/, uint256 timestamp, /*uint80 answeredInRound*/ ) = aggregator.latestRoundData(); // If a valid price is returned and it was updated recently if (!forceFallback && _isValidPrice(aggregatorPrice, timestamp, heartbeat)) { uint256 result; if (data.convertToQuote) { // _toQuote performs decimal normalization internally result = _toQuote(uint256(aggregatorPrice)); } else { uint8 aggregatorDecimals = aggregator.decimals(); result = _normalizeWithDecimals(uint256(aggregatorPrice), aggregatorDecimals); } return (true, result); } return (false, 0); } function _getFallbackPrice(address _asset) internal view virtual returns (uint256) { IPriceProvider fallbackProvider = assetData[_asset].fallbackProvider; if (address(fallbackProvider) == address(0)) revert FallbackProviderNotSet(); return fallbackProvider.getPrice(_asset); } function _setEmergencyManager(address _emergencyManager) internal virtual returns (bool changed) { if (_emergencyManager == emergencyManager) { return false; } emergencyManager = _emergencyManager; emit NewEmergencyManager(_emergencyManager); return true; } function _setAggregator( address _asset, AggregatorV3Interface _aggregator, bool _convertToQuote ) internal virtual returns (bool changed) { if (address(_aggregator) == address(0)) revert InvalidAggregator(); AssetData storage data = assetData[_asset]; if (data.aggregator == _aggregator && data.forceFallback == false) { return false; } // There doesn't seem to be a way to verify if this is a "valid" aggregator (other than getting the price) data.forceFallback = false; data.aggregator = _aggregator; (bool success,) = _getAggregatorPrice(_asset); if (!success) revert AggregatorPriceNotAvailable(); if (_convertToQuote && _aggregator.decimals() != _QUOTE_AGGREGATOR_DECIMALS) { revert InvalidAggregatorDecimals(); } // We want to always update this assetData[_asset].convertToQuote = _convertToQuote; emit NewAggregator(_asset, _aggregator, _convertToQuote); return true; } function _setFallbackPriceProvider(address _asset, IPriceProvider _fallbackProvider) internal virtual returns (bool changed) { if (_fallbackProvider == assetData[_asset].fallbackProvider) { return false; } assetData[_asset].fallbackProvider = _fallbackProvider; if (address(_fallbackProvider) != address(0)) { if ( !priceProvidersRepository.isPriceProvider(_fallbackProvider) || !_fallbackProvider.assetSupported(_asset) || _fallbackProvider.quoteToken() != quoteToken ) { revert InvalidFallbackPriceProvider(); } // Make sure it doesn't revert _getFallbackPrice(_asset); } emit NewFallbackPriceProvider(_asset, _fallbackProvider); return true; } function _setHeartbeat(address _asset, uint256 _heartbeat) internal virtual returns (bool changed) { // Arbitrary limit, Chainlink's threshold is always less than a day if (_heartbeat > 2 days) revert InvalidHeartbeat(); if (_heartbeat == assetData[_asset].heartbeat) { return false; } assetData[_asset].heartbeat = _heartbeat; emit NewHeartbeat(_asset, _heartbeat); return true; } function _setQuoteAggregatorHeartbeat(uint256 _heartbeat) internal virtual returns (bool changed) { // Arbitrary limit, Chainlink's threshold is always less than a day if (_heartbeat > 2 days) revert InvalidHeartbeat(); if (_heartbeat == quoteAggregatorHeartbeat) { return false; } quoteAggregatorHeartbeat = _heartbeat; emit NewQuoteAggregatorHeartbeat(_heartbeat); return true; } /// @dev Adjusts the given price to use the same decimals as the quote token. /// @param _price Price to adjust decimals /// @param _decimals Decimals considered in `_price` function _normalizeWithDecimals(uint256 _price, uint8 _decimals) internal view virtual returns (uint256) { // We want to return the price of 1 asset token, but with the decimals of the quote token if (_QUOTE_TOKEN_DECIMALS == _decimals) { return _price; } else if (_QUOTE_TOKEN_DECIMALS < _decimals) { return _price / 10 ** (_decimals - _QUOTE_TOKEN_DECIMALS); } else { return _price * 10 ** (_QUOTE_TOKEN_DECIMALS - _decimals); } } /// @dev Converts a price returned by an aggregator to quote units function _toQuote(uint256 _price) internal view virtual returns (uint256) { ( /*uint80 roundID*/, int256 aggregatorPrice, /*uint256 startedAt*/, uint256 timestamp, /*uint80 answeredInRound*/ ) = _QUOTE_AGGREGATOR.latestRoundData(); // If an invalid price is returned if (!_isValidPrice(aggregatorPrice, timestamp, quoteAggregatorHeartbeat)) { revert AggregatorPriceNotAvailable(); } // _price and aggregatorPrice should both have the same decimals so we normalize here return _price * 10 ** _QUOTE_TOKEN_DECIMALS / uint256(aggregatorPrice); } function _isValidPrice(int256 _price, uint256 _timestamp, uint256 _heartbeat) internal view virtual returns (bool) { return _price > 0 && block.timestamp - _timestamp < _heartbeat; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.7.6; /// @dev This is only meant to be used by price providers, which use a different /// Solidity version than the rest of the codebase. This way de won't need to include /// an additional version of OpenZeppelin's library. interface IERC20LikeV2 { function decimals() external view returns (uint8); function balanceOf(address) external view returns(uint256); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.7.6 <0.9.0; import "../lib/Ping.sol"; import "../interfaces/IPriceProvider.sol"; import "../interfaces/IPriceProvidersRepository.sol"; /// @title PriceProvider /// @notice Abstract PriceProvider contract, parent of all PriceProviders /// @dev Price provider is a contract that directly integrates with a price source, ie. a DEX or alternative system /// like Chainlink to calculate TWAP prices for assets. Each price provider should support a single price source /// and multiple assets. abstract contract PriceProvider is IPriceProvider { /// @notice PriceProvidersRepository address IPriceProvidersRepository public immutable priceProvidersRepository; /// @notice Token address which prices are quoted in. Must be the same as PriceProvidersRepository.quoteToken address public immutable override quoteToken; modifier onlyManager() { if (priceProvidersRepository.manager() != msg.sender) revert("OnlyManager"); _; } /// @param _priceProvidersRepository address of PriceProvidersRepository constructor(IPriceProvidersRepository _priceProvidersRepository) { if ( !Ping.pong(_priceProvidersRepository.priceProvidersRepositoryPing) ) { revert("InvalidPriceProviderRepository"); } priceProvidersRepository = _priceProvidersRepository; quoteToken = _priceProvidersRepository.quoteToken(); } /// @inheritdoc IPriceProvider function priceProviderPing() external pure override returns (bytes4) { return this.priceProviderPing.selector; } function _revertBytes(bytes memory _errMsg, string memory _customErr) internal pure { if (_errMsg.length > 0) { assembly { // solhint-disable-line no-inline-assembly revert(add(32, _errMsg), mload(_errMsg)) } } revert(_customErr); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "./interfaces/IBaseSilo.sol"; import "./interfaces/ISilo.sol"; import "./lib/EasyMathV2.sol"; import "./lib/Ping.sol"; import "./lib/SolvencyV2.sol"; /// @title SiloLens /// @notice Utility contract that simplifies reading data from Silo protocol contracts /// @custom:security-contact [email protected] contract SiloLens { using EasyMathV2 for uint256; ISiloRepository immutable public siloRepository; error InvalidRepository(); error UserIsZero(); constructor (ISiloRepository _siloRepo) { if (!Ping.pong(_siloRepo.siloRepositoryPing)) revert InvalidRepository(); siloRepository = _siloRepo; } /// @dev calculates solvency using SolvencyV2 library /// @param _silo Silo address from which to read data /// @param _user wallet address /// @return true if solvent, false otherwise function isSolvent(ISilo _silo, address _user) external view returns (bool) { if (_user == address(0)) revert UserIsZero(); (address[] memory assets, IBaseSilo.AssetStorage[] memory assetsStates) = _silo.getAssetsWithState(); (uint256 userLTV, uint256 liquidationThreshold) = SolvencyV2.calculateLTVs( SolvencyV2.SolvencyParams( siloRepository, ISilo(address(this)), assets, assetsStates, _user ), SolvencyV2.TypeofLTV.LiquidationThreshold ); return userLTV <= liquidationThreshold; } /// @dev Amount of token that is available for borrowing. /// @param _silo Silo address from which to read data /// @param _asset asset address for which to read data /// @return Silo liquidity function liquidity(ISilo _silo, address _asset) external view returns (uint256) { return ERC20(_asset).balanceOf(address(_silo)) - _silo.assetStorage(_asset).collateralOnlyDeposits; } /// @notice Get amount of asset token that has been deposited to Silo /// @dev It reads directly from storage so interest generated between last update and now is not taken for account /// @param _silo Silo address from which to read data /// @param _asset asset address for which to read data /// @return amount of all deposits made for given asset function totalDeposits(ISilo _silo, address _asset) external view returns (uint256) { return _silo.utilizationData(_asset).totalDeposits; } /// @notice Get amount of asset token that has been deposited to Silo with option "collateralOnly" /// @dev It reads directly from storage so interest generated between last update and now is not taken for account /// @param _silo Silo address from which to read data /// @param _asset asset address for which to read data /// @return amount of all "collateralOnly" deposits made for given asset function collateralOnlyDeposits(ISilo _silo, address _asset) external view returns (uint256) { return _silo.assetStorage(_asset).collateralOnlyDeposits; } /// @notice Get amount of asset that has been borrowed /// @dev It reads directly from storage so interest generated between last update and now is not taken for account /// @param _silo Silo address from which to read data /// @param _asset asset address for which to read data /// @return amount of asset that has been borrowed function totalBorrowAmount(ISilo _silo, address _asset) external view returns (uint256) { return _silo.assetStorage(_asset).totalBorrowAmount; } /// @notice Get amount of fees earned by protocol to date /// @dev It reads directly from storage so interest generated between last update and now is not taken for account /// @param _silo Silo address from which to read data /// @param _asset asset address for which to read data /// @return amount of fees earned by protocol to date function protocolFees(ISilo _silo, address _asset) external view returns (uint256) { return _silo.interestData(_asset).protocolFees; } /// @notice Returns Loan-To-Value for an account /// @dev Each Silo has multiple asset markets (bridge assets + unique asset). This function calculates /// a sum of all deposits and all borrows denominated in quote token. Returns fraction between borrow value /// and deposit value with 18 decimals. /// @param _silo Silo address from which to read data /// @param _user wallet address for which LTV is calculated /// @return userLTV user current LTV with 18 decimals function getUserLTV(ISilo _silo, address _user) external view returns (uint256 userLTV) { (address[] memory assets, ISilo.AssetStorage[] memory assetsStates) = _silo.getAssetsWithState(); (userLTV, ) = SolvencyV2.calculateLTVs( SolvencyV2.SolvencyParams( siloRepository, _silo, assets, assetsStates, _user ), SolvencyV2.TypeofLTV.MaximumLTV ); } /// @notice Get totalSupply of debt token /// @dev Debt token represents a share in total debt of given asset /// @param _silo Silo address from which to read data /// @param _asset asset address for which to read data /// @return totalSupply of debt token function totalBorrowShare(ISilo _silo, address _asset) external view returns (uint256) { return _silo.assetStorage(_asset).debtToken.totalSupply(); } /// @notice Calculates current borrow amount for user with interest /// @dev Interest is calculated based on the provided timestamp with is expected to be current time. /// @param _silo Silo address from which to read data /// @param _asset token address for which calculation are done /// @param _user account for which calculation are done /// @param _timestamp timestamp used for interest calculations /// @return total amount of asset user needs to repay at provided timestamp function getBorrowAmount(ISilo _silo, address _asset, address _user, uint256 _timestamp) external view returns (uint256) { return SolvencyV2.getUserBorrowAmount( _silo.assetStorage(_asset), _user, SolvencyV2.getRcomp(_silo, siloRepository, _asset, _timestamp) ); } /// @notice Get debt token balance of a user /// @dev Debt token represents a share in total debt of given asset. This method calls balanceOf(_user) /// on that token. /// @param _silo Silo address from which to read data /// @param _asset asset address for which to read data /// @param _user wallet address for which to read data /// @return balance of debt token of given user function borrowShare(ISilo _silo, address _asset, address _user) external view returns (uint256) { return _silo.assetStorage(_asset).debtToken.balanceOf(_user); } /// @notice Get underlying balance of all deposits of given token of given user including "collateralOnly" /// deposits /// @dev It reads directly from storage so interest generated between last update and now is not taken for account /// @param _silo Silo address from which to read data /// @param _asset asset address for which to read data /// @param _user wallet address for which to read data /// @return balance of underlying tokens for the given user function collateralBalanceOfUnderlying(ISilo _silo, address _asset, address _user) external view returns (uint256) { ISilo.AssetStorage memory _state = _silo.assetStorage(_asset); // Overflow shouldn't happen if the underlying token behaves correctly, as the total supply of underlying // tokens can't overflow by definition unchecked { return balanceOfUnderlying(_state.totalDeposits, _state.collateralToken, _user) + balanceOfUnderlying(_state.collateralOnlyDeposits, _state.collateralOnlyToken, _user); } } /// @notice Get amount of debt of underlying token for given user /// @dev It reads directly from storage so interest generated between last update and now is not taken for account /// @param _silo Silo address from which to read data /// @param _asset asset address for which to read data /// @param _user wallet address for which to read data /// @return balance of underlying token owed function debtBalanceOfUnderlying(ISilo _silo, address _asset, address _user) external view returns (uint256) { ISilo.AssetStorage memory _state = _silo.assetStorage(_asset); return balanceOfUnderlying(_state.totalBorrowAmount, _state.debtToken, _user); } /// @notice Calculate value of collateral asset for user /// @dev It dynamically adds interest earned. Takes for account collateral only deposits as well. /// @param _silo Silo address from which to read data /// @param _user account for which calculation are done /// @param _asset token address for which calculation are done /// @return value of collateral denominated in quote token with 18 decimal function calculateCollateralValue(ISilo _silo, address _user, address _asset) external view returns (uint256) { IPriceProvidersRepository priceProviderRepo = siloRepository.priceProvidersRepository(); ISilo.AssetStorage memory assetStorage = _silo.assetStorage(_asset); uint256 assetPrice = priceProviderRepo.getPrice(_asset); uint8 assetDecimals = ERC20(_asset).decimals(); uint256 userCollateralTokenBalance = assetStorage.collateralToken.balanceOf(_user); uint256 userCollateralOnlyTokenBalance = assetStorage.collateralOnlyToken.balanceOf(_user); uint256 assetAmount = SolvencyV2.getUserCollateralAmount( assetStorage, userCollateralTokenBalance, userCollateralOnlyTokenBalance, SolvencyV2.getRcomp(_silo, siloRepository, _asset, block.timestamp), siloRepository ); return assetAmount.toValue(assetPrice, assetDecimals); } /// @notice Calculate value of borrowed asset by user /// @dev It dynamically adds interest earned to borrowed amount /// @param _silo Silo address from which to read data /// @param _user account for which calculation are done /// @param _asset token address for which calculation are done /// @return value of debt denominated in quote token with 18 decimal function calculateBorrowValue(ISilo _silo, address _user, address _asset) external view returns (uint256) { IPriceProvidersRepository priceProviderRepo = siloRepository.priceProvidersRepository(); uint256 assetPrice = priceProviderRepo.getPrice(_asset); uint256 assetDecimals = ERC20(_asset).decimals(); uint256 rcomp = SolvencyV2.getRcomp(_silo, siloRepository, _asset, block.timestamp); uint256 borrowAmount = SolvencyV2.getUserBorrowAmount(_silo.assetStorage(_asset), _user, rcomp); return borrowAmount.toValue(assetPrice, assetDecimals); } /// @notice Get combined liquidation threshold for a user /// @dev Methodology for calculating liquidation threshold is as follows. Each Silo is combined form multiple /// assets (bridge assets + unique asset). Each of these assets may have different liquidation threshold. /// That means effective liquidation threshold must be calculated per asset based on current deposits and /// borrows of given account. /// @param _silo Silo address from which to read data /// @param _user wallet address for which to read data /// @return liquidationThreshold liquidation threshold of given user function getUserLiquidationThreshold(ISilo _silo, address _user) external view returns (uint256 liquidationThreshold) { (address[] memory assets, ISilo.AssetStorage[] memory assetsStates) = _silo.getAssetsWithState(); liquidationThreshold = SolvencyV2.calculateLTVLimit( SolvencyV2.SolvencyParams( siloRepository, _silo, assets, assetsStates, _user ), SolvencyV2.TypeofLTV.LiquidationThreshold ); } /// @notice Get combined maximum Loan-To-Value for a user /// @dev Methodology for calculating maximum LTV is as follows. Each Silo is combined form multiple assets /// (bridge assets + unique asset). Each of these assets may have different maximum Loan-To-Value for /// opening borrow position. That means effective maximum LTV must be calculated per asset based on /// current deposits and borrows of given account. /// @param _silo Silo address from which to read data /// @param _user wallet address for which to read data /// @return maximumLTV Maximum Loan-To-Value of given user function getUserMaximumLTV(ISilo _silo, address _user) external view returns (uint256 maximumLTV) { (address[] memory assets, ISilo.AssetStorage[] memory assetsStates) = _silo.getAssetsWithState(); maximumLTV = SolvencyV2.calculateLTVLimit( SolvencyV2.SolvencyParams( siloRepository, _silo, assets, assetsStates, _user ), SolvencyV2.TypeofLTV.MaximumLTV ); } /// @notice Check if user is in debt /// @param _silo Silo address from which to read data /// @param _user wallet address for which to read data /// @return TRUE if user borrowed any amount of any asset, otherwise FALSE function inDebt(ISilo _silo, address _user) external view returns (bool) { address[] memory allAssets = _silo.getAssets(); for (uint256 i; i < allAssets.length;) { if (_silo.assetStorage(allAssets[i]).debtToken.balanceOf(_user) != 0) return true; unchecked { i++; } } return false; } /// @notice Check if user has position (debt or borrow) in any asset /// @param _silo Silo address from which to read data /// @param _user wallet address for which to read data /// @return TRUE if user has position (debt or borrow) in any asset function hasPosition(ISilo _silo, address _user) external view returns (bool) { (, ISilo.AssetStorage[] memory assetsStorage) = _silo.getAssetsWithState(); for (uint256 i; i < assetsStorage.length; i++) { if (assetsStorage[i].debtToken.balanceOf(_user) != 0) return true; if (assetsStorage[i].collateralToken.balanceOf(_user) != 0) return true; if (assetsStorage[i].collateralOnlyToken.balanceOf(_user) != 0) return true; } return false; } /// @notice Calculates fraction between borrowed amount and the current liquidity of tokens for given asset /// denominated in percentage /// @dev Utilization is calculated current values in storage so it does not take for account earned /// interest and ever-increasing total borrow amount. It assumes `Model.DP()` = 100%. /// @param _silo Silo address from which to read data /// @param _asset asset address /// @return utilization value function getUtilization(ISilo _silo, address _asset) external view returns (uint256) { ISilo.UtilizationData memory data = ISilo(_silo).utilizationData(_asset); return EasyMathV2.calculateUtilization( getModel(_silo, _asset).DP(), data.totalDeposits, data.totalBorrowAmount ); } /// @notice Yearly interest rate for depositing asset token, dynamically calculated for current block timestamp /// @param _silo Silo address from which to read data /// @param _asset asset address /// @return APY with 18 decimals function depositAPY(ISilo _silo, address _asset) external view returns (uint256) { uint256 dp = getModel(_silo, _asset).DP(); // amount of deposits in asset decimals uint256 totalDepositsAmount = totalDepositsWithInterest(_silo, _asset); if (totalDepositsAmount == 0) return 0; // amount of debt generated per year in asset decimals uint256 generatedDebtAmount = totalBorrowAmountWithInterest(_silo, _asset) * borrowAPY(_silo, _asset) / dp; return generatedDebtAmount * SolvencyV2._PRECISION_DECIMALS / totalDepositsAmount; } /// @notice Calculate amount of entry fee for given amount /// @param _amount amount for which to calculate fee /// @return Amount of token fee to be paid function calcFee(uint256 _amount) external view returns (uint256) { uint256 entryFee = siloRepository.entryFee(); if (entryFee == 0) return 0; // no fee unchecked { // If we overflow on multiplication it should not revert tx, we will get lower fees return _amount * entryFee / SolvencyV2._PRECISION_DECIMALS; } } /// @dev Method for sanity check /// @return always true function lensPing() external pure returns (bytes4) { return this.lensPing.selector; } /// @notice Yearly interest rate for borrowing asset token, dynamically calculated for current block timestamp /// @param _silo Silo address from which to read data /// @param _asset asset address /// @return APY with 18 decimals function borrowAPY(ISilo _silo, address _asset) public view returns (uint256) { return getModel(_silo, _asset).getCurrentInterestRate(address(_silo), _asset, block.timestamp); } /// @notice returns total deposits with interest dynamically calculated at current block timestamp /// @param _asset asset address /// @return _totalDeposits total deposits amount with interest function totalDepositsWithInterest(ISilo _silo, address _asset) public view returns (uint256 _totalDeposits) { uint256 rcomp = getModel(_silo, _asset).getCompoundInterestRate(address(_silo), _asset, block.timestamp); uint256 protocolShareFee = siloRepository.protocolShareFee(); ISilo.UtilizationData memory data = _silo.utilizationData(_asset); return SolvencyV2.totalDepositsWithInterest( data.totalDeposits, data.totalBorrowAmount, protocolShareFee, rcomp ); } /// @notice Calculates current deposit (with interest) for user /// Collateral only deposits are not counted here. To get collateral only deposit call: /// `_silo.assetStorage(_asset).collateralOnlyDeposits` /// @dev Interest is calculated based on the provided timestamp with is expected to be current time. /// @param _silo Silo address from which to read data /// @param _asset token address for which calculation are done /// @param _user account for which calculation are done /// @param _timestamp timestamp used for interest calculations /// @return totalUserDeposits amount of asset user posses function getDepositAmount(ISilo _silo, address _asset, address _user, uint256 _timestamp) public view returns (uint256 totalUserDeposits) { ISilo.AssetStorage memory data = _silo.assetStorage(_asset); uint256 share = data.collateralToken.balanceOf(_user); if (share == 0) { return 0; } uint256 rcomp = getModel(_silo, _asset).getCompoundInterestRate(address(_silo), _asset, _timestamp); uint256 protocolShareFee = siloRepository.protocolShareFee(); uint256 assetTotalDeposits = SolvencyV2.totalDepositsWithInterest( data.totalDeposits, data.totalBorrowAmount, protocolShareFee, rcomp ); return share.toAmount(assetTotalDeposits, data.collateralToken.totalSupply()); } /// @notice returns total borrow amount with interest dynamically calculated at current block timestamp /// @param _asset asset address /// @return _totalBorrowAmount total deposits amount with interest function totalBorrowAmountWithInterest(ISilo _silo, address _asset) public view returns (uint256 _totalBorrowAmount) { uint256 rcomp = SolvencyV2.getRcomp(_silo, siloRepository, _asset, block.timestamp); ISilo.UtilizationData memory data = _silo.utilizationData(_asset); return SolvencyV2.totalBorrowAmountWithInterest(data.totalBorrowAmount, rcomp); } /// @notice Get underlying balance of collateral or debt token /// @dev You can think about debt and collateral tokens as cToken in compound. They represent ownership of /// debt or collateral in given Silo. This method converts that ownership to exact amount of underlying token. /// @param _assetTotalDeposits Total amount of assets that has been deposited or borrowed. For collateral token, /// use `totalDeposits` to get this value. For debt token, use `totalBorrowAmount` to get this value. /// @param _shareToken share token address. It's the collateral and debt share token address. You can find /// these addresses in: /// - `ISilo.AssetStorage.collateralToken` /// - `ISilo.AssetStorage.collateralOnlyToken` /// - `ISilo.AssetStorage.debtToken` /// @param _user wallet address for which to read data /// @return balance of underlying token deposited or borrowed of given user function balanceOfUnderlying(uint256 _assetTotalDeposits, IShareToken _shareToken, address _user) public view returns (uint256) { uint256 share = _shareToken.balanceOf(_user); return share.toAmount(_assetTotalDeposits, _shareToken.totalSupply()); } /// @dev gets interest rates model object /// @param _silo Silo address from which to read data /// @param _asset asset for which to calculate interest rate /// @return IInterestRateModel interest rates model object function getModel(ISilo _silo, address _asset) public view returns (IInterestRateModel) { return IInterestRateModel(siloRepository.getInterestRateModel(address(_silo), _asset)); } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_repository","type":"address"},{"internalType":"address","name":"_chainlinkPriceProvider","type":"address"},{"internalType":"address","name":"_lens","type":"address"},{"internalType":"address","name":"_exchangeProxy","type":"address"},{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"contract IMagician","name":"magician","type":"address"}],"internalType":"struct LiquidationHelper.MagicianConfig[]","name":"_magicians","type":"tuple[]"},{"components":[{"internalType":"contract IPriceProvider","name":"provider","type":"address"},{"internalType":"contract ISwapper","name":"swapper","type":"address"}],"internalType":"struct LiquidationHelper.SwapperConfig[]","name":"_swappers","type":"tuple[]"},{"internalType":"uint256","name":"_baseCost","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[],"name":"ApprovalFailed","type":"error"},{"inputs":[],"name":"FallbackPriceProviderNotSet","type":"error"},{"inputs":[],"name":"InvalidChainlinkProviders","type":"error"},{"inputs":[],"name":"InvalidMagicianConfig","type":"error"},{"inputs":[],"name":"InvalidScenario","type":"error"},{"inputs":[],"name":"InvalidSiloLens","type":"error"},{"inputs":[],"name":"InvalidSiloRepository","type":"error"},{"inputs":[],"name":"InvalidSwapperConfig","type":"error"},{"inputs":[],"name":"InvalidTowardsAssetConvertion","type":"error"},{"inputs":[{"internalType":"uint256","name":"inTheRed","type":"uint256"}],"name":"LiquidationNotProfitable","type":"error"},{"inputs":[],"name":"MagicianNotFound","type":"error"},{"inputs":[],"name":"Max0xSwapsIs2","type":"error"},{"inputs":[],"name":"NotSilo","type":"error"},{"inputs":[],"name":"PriceProviderNotFound","type":"error"},{"inputs":[],"name":"RepayFailed","type":"error"},{"inputs":[],"name":"SwapAmountInFailed","type":"error"},{"inputs":[],"name":"SwapAmountOutFailed","type":"error"},{"inputs":[],"name":"SwapperNotFound","type":"error"},{"inputs":[],"name":"TargetNotExchangeProxy","type":"error"},{"inputs":[],"name":"UsersMustMatchSilos","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sellToken","type":"address"},{"indexed":false,"internalType":"address","name":"buyToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"boughtAmount","type":"uint256"}],"name":"BoughtTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"silo","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"earned","type":"uint256"},{"indexed":false,"internalType":"bool","name":"estimatedEarnings","type":"bool"}],"name":"LiquidationExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"contract IMagician","name":"magician","type":"address"}],"name":"MagicianConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IPriceProvider","name":"provider","type":"address"},{"indexed":false,"internalType":"contract ISwapper","name":"swapper","type":"address"}],"name":"SwapperConfigured","type":"event"},{"inputs":[],"name":"CHAINLINK_PRICE_PROVIDER","outputs":[{"internalType":"contract ChainlinkV3PriceProvider","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXCHANGE_PROXY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LENS","outputs":[{"internalType":"contract SiloLens","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE_PROVIDERS_REPOSITORY","outputs":[{"internalType":"contract IPriceProvidersRepository","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"QUOTE_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SILO_REPOSITORY","outputs":[{"internalType":"contract ISiloRepository","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_users","type":"address[]"},{"internalType":"contract ISilo[]","name":"_silos","type":"address[]"}],"name":"checkDebt","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_users","type":"address[]"},{"internalType":"contract ISilo[]","name":"_silos","type":"address[]"}],"name":"checkSolvency","outputs":[{"internalType":"bool[]","name":"","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasStart","type":"uint256"},{"internalType":"uint256","name":"_earnedEth","type":"uint256"}],"name":"ensureTxIsProfitable","outputs":[{"internalType":"uint256","name":"txFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"contract ISilo","name":"_silo","type":"address"},{"internalType":"enum ILiquidationHelper.LiquidationScenario","name":"_scenario","type":"uint8"},{"components":[{"internalType":"address","name":"sellToken","type":"address"},{"internalType":"address","name":"allowanceTarget","type":"address"},{"internalType":"bytes","name":"swapCallData","type":"bytes"}],"internalType":"struct ZeroExSwap.SwapInput0x[]","name":"_swapsInputs0x","type":"tuple[]"}],"name":"executeLiquidation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sellToken","type":"address"},{"internalType":"address","name":"_spender","type":"address"},{"internalType":"bytes","name":"_swapCallData","type":"bytes"}],"name":"fillQuote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"findPriceProvider","outputs":[{"internalType":"contract IPriceProvider","name":"priceProvider","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"liquidationSupported","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"magicians","outputs":[{"internalType":"contract IMagician","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"contract IMagician","name":"magician","type":"address"}],"internalType":"struct LiquidationHelper.MagicianConfig[]","name":"_magicians","type":"tuple[]"}],"name":"setMagicians","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IPriceProvider","name":"provider","type":"address"},{"internalType":"contract ISwapper","name":"swapper","type":"address"}],"internalType":"struct LiquidationHelper.SwapperConfig[]","name":"_swappers","type":"tuple[]"}],"name":"setSwappers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address[]","name":"_assets","type":"address[]"},{"internalType":"uint256[]","name":"_receivedCollaterals","type":"uint256[]"},{"internalType":"uint256[]","name":"_shareAmountsToRepaid","type":"uint256[]"},{"internalType":"bytes","name":"_flashReceiverData","type":"bytes"}],"name":"siloLiquidationCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPriceProvider","name":"","type":"address"}],"name":"swappers","outputs":[{"internalType":"contract ISwapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6101606040523480156200001257600080fd5b5060405162003ef938038062003ef9833981016040819052620000359162000696565b83620000413362000219565b6001600160a01b0381166200006957604051639fabe1c160e01b815260040160405180910390fd5b6001600160a01b039081166080526200009590861663035054cd62000269602090811b6200125817901c565b620000b35760405163778e646d60e11b815260040160405180910390fd5b620000d7876001600160a01b031663e99ed41d6200026960201b620012581760201c565b620000f4576040516295241d60e21b815260040160405180910390fd5b6001600160a01b0380881660c0528516610100526200011382620002f3565b6200011e8362000415565b866001600160a01b0316635ddf2be36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200015d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000183919062000805565b6001600160a01b0390811660e08190529087166101405260408051630217a4b760e41b8152905163217a4b70916004808201926020929091908290030181865afa158015620001d6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001fc919062000805565b6001600160a01b03166101205260a052506200088f945050505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006001600160a01b03831615801590620002ec575082826040518163ffffffff1660e01b8152600401602060405180830381865afa158015620002b1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002d7919062000825565b60e083901b6001600160e01b03199081169116145b9392505050565b60005b81518110156200041157600082828151811062000317576200031762000851565b602002602001015160000151905060008383815181106200033c576200033c62000851565b602002602001015160200151905060006001600160a01b0316826001600160a01b031614806200037357506001600160a01b038116155b156200039257604051635d7aa74360e11b815260040160405180910390fd5b6001600160a01b0382811660008181526001602090815260409182902080546001600160a01b031916948616948517905581519283528201929092527f388056c710663a451dfea6adfe8ff11b52af5529b0a3d1c69ba90f1fc3f2dc56910160405180910390a150508080620004089062000867565b915050620002f6565b5050565b60005b81518110156200041157600082828151811062000439576200043962000851565b602002602001015160000151905060008383815181106200045e576200045e62000851565b602002602001015160200151905060006001600160a01b0316826001600160a01b031614806200049557506001600160a01b038116155b15620004b45760405163acb22e5160e01b815260040160405180910390fd5b6001600160a01b0382811660008181526002602090815260409182902080546001600160a01b031916948616948517905581519283528201929092527f53db4a2342abba949712a6909b124e7452f4c84834894b2a75a377f5201ef610910160405180910390a1505080806200052a9062000867565b91505062000418565b6001600160a01b03811681146200054957600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b03811182821017156200058757620005876200054c565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620005b857620005b86200054c565b604052919050565b60006001600160401b03821115620005dc57620005dc6200054c565b5060051b60200190565b600082601f830112620005f857600080fd5b81516020620006116200060b83620005c0565b6200058d565b82815260069290921b840181019181810190868411156200063157600080fd5b8286015b848110156200068b5760408189031215620006505760008081fd5b6200065a62000562565b8151620006678162000533565b815281850151620006788162000533565b8186015283529183019160400162000635565b509695505050505050565b600080600080600080600060e0888a031215620006b257600080fd5b8751620006bf8162000533565b6020890151909750620006d28162000533565b6040890151909650620006e58162000533565b6060890151909550620006f88162000533565b60808901519094506001600160401b03808211156200071657600080fd5b818a0191508a601f8301126200072b57600080fd5b81516200073c6200060b82620005c0565b8082825260208201915060208360061b86010192508d8311156200075f57600080fd5b6020850194505b82851015620007c6576040858f0312156200078057600080fd5b6200078a62000562565b8551620007978162000533565b81526020860151620007a98162000533565b806020830152508083525060208201915060408501945062000766565b60a08d01519097509350505080821115620007e057600080fd5b50620007ef8a828b01620005e6565b92505060c0880151905092959891949750929550565b6000602082840312156200081857600080fd5b8151620002ec8162000533565b6000602082840312156200083857600080fd5b81516001600160e01b031981168114620002ec57600080fd5b634e487b7160e01b600052603260045260246000fd5b6000600182016200088857634e487b7160e01b600052601160045260246000fd5b5060010190565b60805160a05160c05160e0516101005161012051610140516135b4620009456000396000818161021f01528181610b990152610bef0152600081816102f0015281816106c70152818161171c0152818161192701528181611c41015281816121db01526124c50152600081816101a301526105a501526000818161027301528181610b01015261179b0152600081816103e60152610f8c01526000610dc00152600081816102a701526109b001526135b46000f3fe60806040526004361061012d5760003560e01c80638cad7fbe116100ab578063a7e8489d1161006f578063a7e8489d146103d4578063be52003914610408578063dde643031461043e578063e7b43da51461045e578063f2fde38b1461047e578063fa40a0041461049e57600080fd5b80638cad7fbe146103125780638cf16261146103485780638da5cb5b1461036857806394fa3add14610386578063a143e5f7146103a657600080fd5b80633e4504c8116100f25780633e4504c8146102415780636f0ad2ca146102615780636fca4f8f14610295578063715018a6146102c957806378892cea146102de57600080fd5b80624796c61461013957806303a69c9a1461015b57806318a4619a14610191578063338799b0146101dd57806339060ea41461020d57600080fd5b3661013457005b600080fd5b34801561014557600080fd5b5061015961015436600461294d565b6104be565b005b34801561016757600080fd5b5061017b6101763660046129d2565b610550565b6040516101889190612a3d565b60405180910390f35b34801561019d57600080fd5b506101c57f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610188565b3480156101e957600080fd5b506101fd6101f8366004612a98565b6106c3565b6040519015158152602001610188565b34801561021957600080fd5b506101c57f000000000000000000000000000000000000000000000000000000000000000081565b34801561024d57600080fd5b5061015961025c366004612acb565b6107d9565b34801561026d57600080fd5b506101c57f000000000000000000000000000000000000000000000000000000000000000081565b3480156102a157600080fd5b506101c57f000000000000000000000000000000000000000000000000000000000000000081565b3480156102d557600080fd5b50610159610900565b3480156102ea57600080fd5b506101c57f000000000000000000000000000000000000000000000000000000000000000081565b34801561031e57600080fd5b506101c561032d366004612a98565b6001602052600090815260409020546001600160a01b031681565b34801561035457600080fd5b50610159610363366004612c43565b610936565b34801561037457600080fd5b506000546001600160a01b03166101c5565b34801561039257600080fd5b506101c56103a1366004612a98565b610adf565b3480156103b257600080fd5b506103c66103c1366004612ca4565b610dbb565b604051908152602001610188565b3480156103e057600080fd5b506101c57f000000000000000000000000000000000000000000000000000000000000000081565b34801561041457600080fd5b506101c5610423366004612a98565b6002602052600090815260409020546001600160a01b031681565b34801561044a57600080fd5b5061017b6104593660046129d2565b610e16565b34801561046a57600080fd5b50610159610479366004612cc6565b610f77565b34801561048a57600080fd5b50610159610499366004612a98565b611138565b3480156104aa57600080fd5b506101596104b936600461294d565b6111d3565b6000546001600160a01b031633146104f15760405162461bcd60e51b81526004016104e890612dc8565b60405180910390fd5b61054c8282808060200260200160405190810160405280939291908181526020016000905b828210156105425761053360408302860136819003810190612dfd565b81526020019060010190610516565b50505050506112e0565b5050565b60606000846001600160401b0381111561056c5761056c612b44565b604051908082528060200260200182016040528015610595578160200160208202803683370190505b50905060005b858110156106b9577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638705c3598686848181106105e4576105e4612e3e565b90506020020160208101906105f99190612a98565b89898581811061060b5761060b612e3e565b90506020020160208101906106209190612a98565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015291166024820152604401602060405180830381865afa15801561066b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068f9190612e54565b8282815181106106a1576106a1612e3e565b9115156020928302919091019091015260010161059b565b5095945050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03160361070657506001919050565b6001600160a01b03828116600090815260026020526040902054161561072e57506001919050565b6040516394fa3add60e01b81526001600160a01b038316600482015230906394fa3add90602401602060405180830381865afa92505050801561078e575060408051601f3d908101601f1916820190925261078b91810190612e76565b60015b6107c8573d8080156107bc576040519150601f19603f3d011682016040523d82523d6000602084013e6107c1565b606091505b50506107d1565b50600192915050565b506000919050565b60028111156107fb5760405163f554afbd60e01b815260040160405180910390fd5b60005a6040805160018082528183019092529192506000919060208083019080368337019050509050868160008151811061083857610838612e3e565b60200260200101906001600160a01b031690816001600160a01b031681525050856001600160a01b03166393a94ca3823385898989604051602001610881959493929190612ed2565b6040516020818303038152906040526040518363ffffffff1660e01b81526004016108ad929190613051565b6000604051808303816000875af11580156108cc573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108f491908101906131af565b50505050505050505050565b6000546001600160a01b0316331461092a5760405162461bcd60e51b81526004016104e890612dc8565b61093460006113f2565b565b60405163095ea7b360e01b81526001600160a01b038381166004830152600019602483015284169063095ea7b3906044016020604051808303816000875af1158015610986573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109aa9190612e54565b506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836040516109e6919061328d565b6000604051808303816000865af19150503d8060008114610a23576040519150601f19603f3d011682016040523d82523d6000602084013e610a28565b606091505b509150915081610a645760408051808201909152601081526f14d5d05417d0d0531317d1905253115160821b6020820152610a64908290611442565b60405163095ea7b360e01b81526001600160a01b0385811660048301526000602483015286169063095ea7b3906044016020604051808303816000875af1158015610ab3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad79190612e54565b505050505050565b604051635d54e39560e01b81526001600160a01b0382811660048301526000917f000000000000000000000000000000000000000000000000000000000000000090911690635d54e39590602401602060405180830381865afa158015610b4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6e9190612e76565b90506001600160a01b038116610b9757604051637de7a71560e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031603610c885760405163db09c3fd60e01b81526001600160a01b0383811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063db09c3fd90602401602060405180830381865afa158015610c36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5a9190612e76565b90506001600160a01b038116610c835760405163dc3810cb60e01b815260040160405180910390fd5b919050565b806001600160a01b031663ca0a8f226040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610ce2575060408051601f3d908101601f19168201909252610cdf91810190612e54565b60015b610d1c573d808015610d10576040519150601f19603f3d011682016040523d82523d6000602084013e610d15565b606091505b5050919050565b8015610db55760405163db09c3fd60e01b81526001600160a01b03848116600483015283169063db09c3fd90602401602060405180830381865afa158015610d68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8c9190612e76565b91506001600160a01b038216610db55760405163dc3810cb60e01b815260040160405180910390fd5b50919050565b6000807f00000000000000000000000000000000000000000000000000000000000000005a8503019050803a02915082821115610e0f5760405163e1c4c60560e01b815283830360048201526024016104e8565b5092915050565b6060838214610e385760405163fedad93960e01b815260040160405180910390fd5b6000846001600160401b03811115610e5257610e52612b44565b604051908082528060200260200182016040528015610e7b578160200160208202803683370190505b50905060005b858110156106b957848482818110610e9b57610e9b612e3e565b9050602002016020810190610eb09190612a98565b6001600160a01b03166338b51ce1888884818110610ed057610ed0612e3e565b9050602002016020810190610ee59190612a98565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015610f29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4d9190612e54565b828281518110610f5f57610f5f612e3e565b91151560209283029190910190910152600101610e81565b60405163025e1b9b60e31b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906312f0dcd890602401602060405180830381865afa158015610fdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fff9190612e54565b61101c5760405163a812ea3160e01b815260040160405180910390fd5b600080808061102d858701876132a9565b93509350935093508051600014611047576110478161146b565b6000611059838f8f8f8f8f8f8f6114e1565b9050600061107784600781111561107257611072612e93565b6115c5565b9050600061109585600781111561109057611090612e93565b6115fe565b905081156110b2576110ab8f8f8d8d8b86611659565b92506110bc565b6110bc8784611911565b8f6001600160a01b0316336001600160a01b03167f7d233b2ffded63eccd0879cd7afc44e74118b788331db4d67730c6037d142696858560405161110c9291909182521515602082015260400190565b60405180910390a38015611126576111248684610dbb565b505b50505050505050505050505050505050565b6000546001600160a01b031633146111625760405162461bcd60e51b81526004016104e890612dc8565b6001600160a01b0381166111c75760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016104e8565b6111d0816113f2565b50565b6000546001600160a01b031633146111fd5760405162461bcd60e51b81526004016104e890612dc8565b61054c8282808060200260200160405190810160405280939291908181526020016000905b8282101561124e5761123f60408302860136819003810190612dfd565b81526020019060010190611222565b505050505061199f565b60006001600160a01b038316158015906112d7575082826040518163ffffffff1660e01b8152600401602060405180830381865afa15801561129e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112c291906133e4565b60e083901b6001600160e01b03199081169116145b90505b92915050565b60005b815181101561054c57600082828151811061130057611300612e3e565b6020026020010151600001519050600083838151811061132257611322612e3e565b602002602001015160200151905060006001600160a01b0316826001600160a01b0316148061135857506001600160a01b038116155b1561137657604051635d7aa74360e11b815260040160405180910390fd5b6001600160a01b0382811660008181526001602090815260409182902080546001600160a01b031916948616948517905581519283528201929092527f388056c710663a451dfea6adfe8ff11b52af5529b0a3d1c69ba90f1fc3f2dc56910160405180910390a1505080806113ea90613424565b9150506112e3565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b81511561145157815182602001fd5b8060405162461bcd60e51b81526004016104e8919061343d565b60005b815181101561054c576114d982828151811061148c5761148c612e3e565b6020026020010151600001518383815181106114aa576114aa612e3e565b6020026020010151602001518484815181106114c8576114c8612e3e565b602002602001015160400151610936565b60010161146e565b60006114fd8960078111156114f8576114f8612e93565b611ab1565b156115165761150f8888888686611ad5565b90506115b9565b61153089600781111561152b5761152b612e93565b611afb565b156115445761150f88888888888888611b1e565b61155989600781111561107257611072612e93565b156115745761156c338989898787611b65565b5060006115b9565b61158e89600781111561158957611589612e93565b611beb565b156115a05761150f8888888686611c0f565b60405163596de99760e01b815260040160405180910390fd5b98975050505050505050565b600060038260078111156115db576115db612e93565b14806112da575060075b8260078111156115f7576115f7612e93565b1492915050565b60008082600781111561161357611613612e93565b14806116305750600182600781111561162e5761162e612e93565b145b8061164c5750600282600781111561164a5761164a612e93565b145b806112da575060036115e5565b6000805b868110156119065785858281811061167757611677612e3e565b905060200201356000146118fe57600088888381811061169957611699612e3e565b90506020020160208101906116ae9190612a98565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156116f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117189190613450565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031689898481811061175657611756612e3e565b905060200201602081019061176b9190612a98565b6001600160a01b03160361179357831561178457918201915b61178e8582611911565b6118fc565b831561185e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166341976e098a8a858181106117da576117da612e3e565b90506020020160208101906117ef9190612a98565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015611833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118579190613450565b8102830192505b88888381811061187057611870612e3e565b90506020020160208101906118859190612a98565b60405163a9059cbb60e01b81526001600160a01b03878116600483015260248201849052919091169063a9059cbb906044016020604051808303816000875af11580156118d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fa9190612e54565b505b505b60010161165d565b509695505050505050565b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561197357600080fd5b505af1158015611987573d6000803e3d6000fd5b5061054c925050506001600160a01b03831682611cb4565b60005b815181101561054c5760008282815181106119bf576119bf612e3e565b602002602001015160000151905060008383815181106119e1576119e1612e3e565b602002602001015160200151905060006001600160a01b0316826001600160a01b03161480611a1757506001600160a01b038116155b15611a355760405163acb22e5160e01b815260040160405180910390fd5b6001600160a01b0382811660008181526002602090815260409182902080546001600160a01b031916948616948517905581519283528201929092527f53db4a2342abba949712a6909b124e7452f4c84834894b2a75a377f5201ef610910160405180910390a150508080611aa990613424565b9150506119a2565b60006002826007811115611ac757611ac7612e93565b14806112da575060066115e5565b6000611ae5338787878787611b65565b611af185858585611dd2565b9695505050505050565b600080826007811115611b1057611b10612e93565b14806112da575060046115e5565b600080611b2d88888888611ee2565b90506000611b3d89898787611f42565b9050611b4d338b8b8b8989611b65565b611b578183613469565b9a9950505050505050505050565b60005b83811015611be257828282818110611b8257611b82612e3e565b90506020020135600014611bda57611bda8787878785818110611ba757611ba7612e3e565b9050602002016020810190611bbc9190612a98565b868686818110611bce57611bce612e3e565b90506020020135611fc3565b600101611b68565b50505050505050565b60006001826007811115611c0157611c01612e93565b14806112da575060056115e5565b6000611c1d85858585611f42565b50611c2c338787878787611b65565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611c90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af19190613450565b80471015611d045760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064016104e8565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611d51576040519150601f19603f3d011682016040523d82523d6000602084013e611d56565b606091505b5050905080611dcd5760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016104e8565b505050565b6000805b84811015611ed957838382818110611df057611df0612e3e565b90506020020135600014611ed1576000868683818110611e1257611e12612e3e565b9050602002016020810190611e279190612a98565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015611e6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e919190613450565b9050611ec3878784818110611ea857611ea8612e3e565b9050602002016020810190611ebd9190612a98565b826121d7565b611ecd9084613480565b9250505b600101611dd6565b50949350505050565b6000805b84811015611ed957611f36868683818110611f0357611f03612e3e565b9050602002016020810190611f189190612a98565b858584818110611f2a57611f2a612e3e565b905060200201356121d7565b90910190600101611ee6565b6000805b84811015611ed957838382818110611f6057611f60612e3e565b90506020020135600014611fbb57611fb6868683818110611f8357611f83612e3e565b9050602002016020810190611f989190612a98565b858584818110611faa57611faa612e3e565b905060200201356124c1565b820191505b600101611f46565b6040516001600160a01b0385811660248301526044820183905283169060640160408051601f198184030181529181526020820180516001600160e01b031663095ea7b360e01b17905251612018919061328d565b6000604051808303816000865af19150503d8060008114612055576040519150601f19603f3d011682016040523d82523d6000602084013e61205a565b606091505b505060405163976ce49560e01b81526001600160a01b0384811660048301528581166024830152604482018490528616915063976ce4959060640160408051808303816000875af11580156120b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120d79190613498565b505060405163bf27304160e01b81526001600160a01b03838116600483015285169063bf2730419060240160c060405180830381865afa15801561211f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214391906134bc565b60409081015190516370a0823160e01b81526001600160a01b038581166004830152909116906370a0823190602401602060405180830381865afa15801561218f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121b39190613450565b156121d157604051639e703a0560e01b815260040160405180910390fd5b50505050565b60007f00000000000000000000000000000000000000000000000000000000000000008215806122185750806001600160a01b0316846001600160a01b0316145b1561222657829150506112da565b6001600160a01b038085166000908152600260205260409020541680156122f6576040516001600160a01b0386166024820152604481018590526000906122c390839060640160408051601f19818403018152918152602080830180516001600160e01b03166321ebbf0160e01b179052815180830190925260138252721d1bddd85c991cd3985d1a5d9951985a5b1959606a1b9082015261281c565b9050600080828060200190518101906122dc9190613550565b915091506122ea82826121d7565b955050505050506112da565b600080612302876128a0565b91509150866001600160a01b0316816001600160a01b031663db36eb2e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561234e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123729190612e76565b6040516001600160a01b0390911660248201526044810188905260640160408051601f198184030181529181526020820180516001600160e01b031663095ea7b360e01b179052516123c4919061328d565b6000604051808303816000865af19150503d8060008114612401576040519150601f19603f3d011682016040523d82523d6000602084013e612406565b606091505b50506040516001600160a01b03808a16602483018190528188166044840152606483018a9052908516608483015260a48201526000915060c40160408051601f19818403018152918152602080830180516001600160e01b0316639cffaf6f60e01b1790528151808301909252600c82526b39bbb0b820b6b7bab73a24b760a11b9082015290915060009061249e908490849061281c565b9050808060200190518101906124b49190613450565b9998505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000008215806125025750836001600160a01b0316816001600160a01b0316145b1561251057829150506112da565b6001600160a01b03808516600090815260026020526040902054168015612607576040516001600160a01b0386166024820152604481018590526000906125ac90839060640160408051601f19818403018152918152602080830180516001600160e01b03166337523bed60e11b179052815180830190925260128252711d1bddd85c991cd05cdcd95d11985a5b195960721b9082015261281c565b9050600080828060200190518101906125c59190613550565b91509150876001600160a01b0316826001600160a01b0316146125fb5760405163245feacb60e01b815260040160405180910390fd5b94506112da9350505050565b600080612613876128a0565b915091506000816001600160a01b031663db36eb2e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612657573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061267b9190612e76565b60405163095ea7b360e01b81526001600160a01b03808316600483015260001960248301529192509086169063095ea7b3906044016020604051808303816000875af11580156126cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126f39190612e54565b506040516001600160a01b038087166024830152808a1660448301819052606483018a9052908516608483015260a482015260009060c40160408051601f19818403018152918152602080830180516001600160e01b03166305a319ad60e21b1790528151808301909252601382527214ddd85c105b5bdd5b9d13dd5d11985a5b1959606a1b90820152909150600090612790908590849061281c565b60405163095ea7b360e01b81526001600160a01b038581166004830152600060248301529192509088169063095ea7b3906044016020604051808303816000875af11580156127e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128079190612e54565b5080806020019051810190611b579190613450565b60606000846001600160a01b031684604051612838919061328d565b600060405180830381855af49150503d8060008114612873576040519150601f19603f3d011682016040523d82523d6000602084013e612878565b606091505b509250905080158061288957508151155b15612898576128988284611442565b509392505050565b60008060006128ae84610adf565b905060006128bb826128c7565b91959194509092505050565b6001600160a01b03808216600090815260016020526040812054909116806112da57604051636ce6ae9b60e11b815260040160405180910390fd5b60008083601f84011261291457600080fd5b5081356001600160401b0381111561292b57600080fd5b6020830191508360208260061b850101111561294657600080fd5b9250929050565b6000806020838503121561296057600080fd5b82356001600160401b0381111561297657600080fd5b61298285828601612902565b90969095509350505050565b60008083601f8401126129a057600080fd5b5081356001600160401b038111156129b757600080fd5b6020830191508360208260051b850101111561294657600080fd5b600080600080604085870312156129e857600080fd5b84356001600160401b03808211156129ff57600080fd5b612a0b8883890161298e565b90965094506020870135915080821115612a2457600080fd5b50612a318782880161298e565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b81811015612a77578351151583529284019291840191600101612a59565b50909695505050505050565b6001600160a01b03811681146111d057600080fd5b600060208284031215612aaa57600080fd5b8135612ab581612a83565b9392505050565b803560088110610c8357600080fd5b600080600080600060808688031215612ae357600080fd5b8535612aee81612a83565b94506020860135612afe81612a83565b9350612b0c60408701612abc565b925060608601356001600160401b03811115612b2757600080fd5b612b338882890161298e565b969995985093965092949392505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715612b7c57612b7c612b44565b60405290565b604051606081016001600160401b0381118282101715612b7c57612b7c612b44565b604051601f8201601f191681016001600160401b0381118282101715612bcc57612bcc612b44565b604052919050565b600082601f830112612be557600080fd5b81356001600160401b03811115612bfe57612bfe612b44565b612c11601f8201601f1916602001612ba4565b818152846020838601011115612c2657600080fd5b816020850160208301376000918101602001919091529392505050565b600080600060608486031215612c5857600080fd5b8335612c6381612a83565b92506020840135612c7381612a83565b915060408401356001600160401b03811115612c8e57600080fd5b612c9a86828701612bd4565b9150509250925092565b60008060408385031215612cb757600080fd5b50508035926020909101359150565b600080600080600080600080600060a08a8c031215612ce457600080fd5b8935612cef81612a83565b985060208a01356001600160401b0380821115612d0b57600080fd5b612d178d838e0161298e565b909a50985060408c0135915080821115612d3057600080fd5b612d3c8d838e0161298e565b909850965060608c0135915080821115612d5557600080fd5b612d618d838e0161298e565b909650945060808c0135915080821115612d7a57600080fd5b818c0191508c601f830112612d8e57600080fd5b813581811115612d9d57600080fd5b8d6020828501011115612daf57600080fd5b6020830194508093505050509295985092959850929598565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b600060408284031215612e0f57600080fd5b612e17612b5a565b8235612e2281612a83565b81526020830135612e3281612a83565b60208201529392505050565b634e487b7160e01b600052603260045260246000fd5b600060208284031215612e6657600080fd5b81518015158114612ab557600080fd5b600060208284031215612e8857600080fd5b8151612ab581612a83565b634e487b7160e01b600052602160045260246000fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b03868116825260208083018790526000916080840191604060088910612f0f57634e487b7160e01b600052602160045260246000fd5b888187015260606080818801528488865260a08801905060a08960051b89010195508960005b8a811015612fe657898803609f190183528135368d9003605e19018112612f5b57600080fd5b8c018035612f6881612a83565b8816895280870135612f7981612a83565b8816898801528086013536829003601e19018112612f9657600080fd5b810180356001600160401b03811115612fae57600080fd5b803603831315612fbd57600080fd5b86888c0152612fd1878c01828b8501612ea9565b9a505050928601925090850190600101612f35565b50959d9c50505050505050505050505050565b60005b83811015613014578181015183820152602001612ffc565b838111156121d15750506000910152565b6000815180845261303d816020860160208601612ff9565b601f01601f19169290920160200192915050565b604080825283519082018190526000906020906060840190828701845b828110156130935781516001600160a01b03168452928401929084019060010161306e565b50505083810382850152611af18186613025565b60006001600160401b038211156130c0576130c0612b44565b5060051b60200190565b600082601f8301126130db57600080fd5b815160206130f06130eb836130a7565b612ba4565b828152600592831b850182019282820191908785111561310f57600080fd5b8387015b858110156131a25780516001600160401b038111156131325760008081fd5b8801603f81018a136131445760008081fd5b8581015160406131566130eb836130a7565b82815291851b8301810191888101908d8411156131735760008081fd5b938201935b8385101561319157845182529389019390890190613178565b885250505093850193508401613113565b5090979650505050505050565b6000806000606084860312156131c457600080fd5b83516001600160401b03808211156131db57600080fd5b818601915086601f8301126131ef57600080fd5b815160206131ff6130eb836130a7565b82815260059290921b8401810191818101908a84111561321e57600080fd5b948201945b8386101561324557855161323681612a83565b82529482019490820190613223565b9189015191975090935050508082111561325e57600080fd5b61326a878388016130ca565b9350604086015191508082111561328057600080fd5b50612c9a868287016130ca565b6000825161329f818460208701612ff9565b9190910192915050565b600080600080608085870312156132bf57600080fd5b6132c98535612a83565b84359350602085013592506132e060408601612abc565b915060608501356001600160401b03808211156132fc57600080fd5b818701915087601f83011261331057600080fd5b813561331e6130eb826130a7565b8082825260208201915060208360051b86010192508a83111561334057600080fd5b602085015b838110156133d457848135111561335b57600080fd5b803586016060818e03601f1901121561337357600080fd5b61337b612b82565b602082013561338981612a83565b8152604082013561339981612a83565b60208201526060820135878111156133b057600080fd5b6133bf8f602083860101612bd4565b60408301525084525060209283019201613345565b50979a9699509497505050505050565b6000602082840312156133f657600080fd5b81516001600160e01b031981168114612ab557600080fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016134365761343661340e565b5060010190565b6020815260006112d76020830184613025565b60006020828403121561346257600080fd5b5051919050565b60008282101561347b5761347b61340e565b500390565b600082198211156134935761349361340e565b500190565b600080604083850312156134ab57600080fd5b505080516020909101519092909150565b600060c082840312156134ce57600080fd5b60405160c081018181106001600160401b03821117156134f0576134f0612b44565b60405282516134fe81612a83565b8152602083015161350e81612a83565b6020820152604083015161352181612a83565b80604083015250606083015160608201526080830151608082015260a083015160a08201528091505092915050565b6000806040838503121561356357600080fd5b825161356e81612a83565b602093909301519294929350505056fea26469706673582212202bbaf7bc1a5263011e40d728797c8266cad2c12f416dc3114b1536347a3b41a264736f6c634300080d0033000000000000000000000000d998c35b7900b344bbbe6555cc11576942cf309d000000000000000000000000e37b8c83138caf12e57632d19c06eb561d47e4230000000000000000000000000e466fc22386997dac23d1f89a43ecb2cb1e76e9000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000520800000000000000000000000000000000000000000000000000000000000000050000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0000000000000000000000000a2ed88339701c209f3a72215164f0d97e7403a05000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000c07b94b2a1f1bfc7381ffc72fe217063bf6946fb0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f000000000000000000000000d4240aed295ff0fc2d76b3160e5d63692ba7213300000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5000000000000000000000000d4bdddca31668f37dba5115f68422d043cb5ee08000000000000000000000000d7c9f0e536dc865ae858b0c0453fe76d13c3beac000000000000000000000000138f032e839f247e5fa913e7ba52bea509c1622500000000000000000000000000000000000000000000000000000000000000040000000000000000000000009d46f86516d6083aca3f9850d060cb62cd0f6924000000000000000000000000ff36fdf7de2aa239b0295d406a6ca5c25c6c9e99000000000000000000000000fae64471ae04068e872c6a73c9f5a255c37ddbdc00000000000000000000000096fbf6f0a431e64e05e22e5dbf6f1d92e5dda81f0000000000000000000000002f0a401966219351887a608493954a7078ef1bc000000000000000000000000096fbf6f0a431e64e05e22e5dbf6f1d92e5dda81f0000000000000000000000002f1aa93249bcc0df7d89739f86308c7a270f5eed000000000000000000000000a40158686a5bdbe6c3ea8a32f6e4e045c1c13f90
Deployed Bytecode
0x60806040526004361061012d5760003560e01c80638cad7fbe116100ab578063a7e8489d1161006f578063a7e8489d146103d4578063be52003914610408578063dde643031461043e578063e7b43da51461045e578063f2fde38b1461047e578063fa40a0041461049e57600080fd5b80638cad7fbe146103125780638cf16261146103485780638da5cb5b1461036857806394fa3add14610386578063a143e5f7146103a657600080fd5b80633e4504c8116100f25780633e4504c8146102415780636f0ad2ca146102615780636fca4f8f14610295578063715018a6146102c957806378892cea146102de57600080fd5b80624796c61461013957806303a69c9a1461015b57806318a4619a14610191578063338799b0146101dd57806339060ea41461020d57600080fd5b3661013457005b600080fd5b34801561014557600080fd5b5061015961015436600461294d565b6104be565b005b34801561016757600080fd5b5061017b6101763660046129d2565b610550565b6040516101889190612a3d565b60405180910390f35b34801561019d57600080fd5b506101c57f0000000000000000000000000e466fc22386997dac23d1f89a43ecb2cb1e76e981565b6040516001600160a01b039091168152602001610188565b3480156101e957600080fd5b506101fd6101f8366004612a98565b6106c3565b6040519015158152602001610188565b34801561021957600080fd5b506101c57f000000000000000000000000e37b8c83138caf12e57632d19c06eb561d47e42381565b34801561024d57600080fd5b5061015961025c366004612acb565b6107d9565b34801561026d57600080fd5b506101c57f0000000000000000000000007c2ca9d502f2409beceafa68e97a176ff805029f81565b3480156102a157600080fd5b506101c57f000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff81565b3480156102d557600080fd5b50610159610900565b3480156102ea57600080fd5b506101c57f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b34801561031e57600080fd5b506101c561032d366004612a98565b6001602052600090815260409020546001600160a01b031681565b34801561035457600080fd5b50610159610363366004612c43565b610936565b34801561037457600080fd5b506000546001600160a01b03166101c5565b34801561039257600080fd5b506101c56103a1366004612a98565b610adf565b3480156103b257600080fd5b506103c66103c1366004612ca4565b610dbb565b604051908152602001610188565b3480156103e057600080fd5b506101c57f000000000000000000000000d998c35b7900b344bbbe6555cc11576942cf309d81565b34801561041457600080fd5b506101c5610423366004612a98565b6002602052600090815260409020546001600160a01b031681565b34801561044a57600080fd5b5061017b6104593660046129d2565b610e16565b34801561046a57600080fd5b50610159610479366004612cc6565b610f77565b34801561048a57600080fd5b50610159610499366004612a98565b611138565b3480156104aa57600080fd5b506101596104b936600461294d565b6111d3565b6000546001600160a01b031633146104f15760405162461bcd60e51b81526004016104e890612dc8565b60405180910390fd5b61054c8282808060200260200160405190810160405280939291908181526020016000905b828210156105425761053360408302860136819003810190612dfd565b81526020019060010190610516565b50505050506112e0565b5050565b60606000846001600160401b0381111561056c5761056c612b44565b604051908082528060200260200182016040528015610595578160200160208202803683370190505b50905060005b858110156106b9577f0000000000000000000000000e466fc22386997dac23d1f89a43ecb2cb1e76e96001600160a01b0316638705c3598686848181106105e4576105e4612e3e565b90506020020160208101906105f99190612a98565b89898581811061060b5761060b612e3e565b90506020020160208101906106209190612a98565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015291166024820152604401602060405180830381865afa15801561066b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068f9190612e54565b8282815181106106a1576106a1612e3e565b9115156020928302919091019091015260010161059b565b5095945050505050565b60007f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316826001600160a01b03160361070657506001919050565b6001600160a01b03828116600090815260026020526040902054161561072e57506001919050565b6040516394fa3add60e01b81526001600160a01b038316600482015230906394fa3add90602401602060405180830381865afa92505050801561078e575060408051601f3d908101601f1916820190925261078b91810190612e76565b60015b6107c8573d8080156107bc576040519150601f19603f3d011682016040523d82523d6000602084013e6107c1565b606091505b50506107d1565b50600192915050565b506000919050565b60028111156107fb5760405163f554afbd60e01b815260040160405180910390fd5b60005a6040805160018082528183019092529192506000919060208083019080368337019050509050868160008151811061083857610838612e3e565b60200260200101906001600160a01b031690816001600160a01b031681525050856001600160a01b03166393a94ca3823385898989604051602001610881959493929190612ed2565b6040516020818303038152906040526040518363ffffffff1660e01b81526004016108ad929190613051565b6000604051808303816000875af11580156108cc573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108f491908101906131af565b50505050505050505050565b6000546001600160a01b0316331461092a5760405162461bcd60e51b81526004016104e890612dc8565b61093460006113f2565b565b60405163095ea7b360e01b81526001600160a01b038381166004830152600019602483015284169063095ea7b3906044016020604051808303816000875af1158015610986573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109aa9190612e54565b506000807f000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff6001600160a01b0316836040516109e6919061328d565b6000604051808303816000865af19150503d8060008114610a23576040519150601f19603f3d011682016040523d82523d6000602084013e610a28565b606091505b509150915081610a645760408051808201909152601081526f14d5d05417d0d0531317d1905253115160821b6020820152610a64908290611442565b60405163095ea7b360e01b81526001600160a01b0385811660048301526000602483015286169063095ea7b3906044016020604051808303816000875af1158015610ab3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad79190612e54565b505050505050565b604051635d54e39560e01b81526001600160a01b0382811660048301526000917f0000000000000000000000007c2ca9d502f2409beceafa68e97a176ff805029f90911690635d54e39590602401602060405180830381865afa158015610b4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6e9190612e76565b90506001600160a01b038116610b9757604051637de7a71560e11b815260040160405180910390fd5b7f000000000000000000000000e37b8c83138caf12e57632d19c06eb561d47e4236001600160a01b0316816001600160a01b031603610c885760405163db09c3fd60e01b81526001600160a01b0383811660048301527f000000000000000000000000e37b8c83138caf12e57632d19c06eb561d47e423169063db09c3fd90602401602060405180830381865afa158015610c36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5a9190612e76565b90506001600160a01b038116610c835760405163dc3810cb60e01b815260040160405180910390fd5b919050565b806001600160a01b031663ca0a8f226040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610ce2575060408051601f3d908101601f19168201909252610cdf91810190612e54565b60015b610d1c573d808015610d10576040519150601f19603f3d011682016040523d82523d6000602084013e610d15565b606091505b5050919050565b8015610db55760405163db09c3fd60e01b81526001600160a01b03848116600483015283169063db09c3fd90602401602060405180830381865afa158015610d68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8c9190612e76565b91506001600160a01b038216610db55760405163dc3810cb60e01b815260040160405180910390fd5b50919050565b6000807f00000000000000000000000000000000000000000000000000000000000052085a8503019050803a02915082821115610e0f5760405163e1c4c60560e01b815283830360048201526024016104e8565b5092915050565b6060838214610e385760405163fedad93960e01b815260040160405180910390fd5b6000846001600160401b03811115610e5257610e52612b44565b604051908082528060200260200182016040528015610e7b578160200160208202803683370190505b50905060005b858110156106b957848482818110610e9b57610e9b612e3e565b9050602002016020810190610eb09190612a98565b6001600160a01b03166338b51ce1888884818110610ed057610ed0612e3e565b9050602002016020810190610ee59190612a98565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015610f29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4d9190612e54565b828281518110610f5f57610f5f612e3e565b91151560209283029190910190910152600101610e81565b60405163025e1b9b60e31b81523360048201527f000000000000000000000000d998c35b7900b344bbbe6555cc11576942cf309d6001600160a01b0316906312f0dcd890602401602060405180830381865afa158015610fdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fff9190612e54565b61101c5760405163a812ea3160e01b815260040160405180910390fd5b600080808061102d858701876132a9565b93509350935093508051600014611047576110478161146b565b6000611059838f8f8f8f8f8f8f6114e1565b9050600061107784600781111561107257611072612e93565b6115c5565b9050600061109585600781111561109057611090612e93565b6115fe565b905081156110b2576110ab8f8f8d8d8b86611659565b92506110bc565b6110bc8784611911565b8f6001600160a01b0316336001600160a01b03167f7d233b2ffded63eccd0879cd7afc44e74118b788331db4d67730c6037d142696858560405161110c9291909182521515602082015260400190565b60405180910390a38015611126576111248684610dbb565b505b50505050505050505050505050505050565b6000546001600160a01b031633146111625760405162461bcd60e51b81526004016104e890612dc8565b6001600160a01b0381166111c75760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016104e8565b6111d0816113f2565b50565b6000546001600160a01b031633146111fd5760405162461bcd60e51b81526004016104e890612dc8565b61054c8282808060200260200160405190810160405280939291908181526020016000905b8282101561124e5761123f60408302860136819003810190612dfd565b81526020019060010190611222565b505050505061199f565b60006001600160a01b038316158015906112d7575082826040518163ffffffff1660e01b8152600401602060405180830381865afa15801561129e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112c291906133e4565b60e083901b6001600160e01b03199081169116145b90505b92915050565b60005b815181101561054c57600082828151811061130057611300612e3e565b6020026020010151600001519050600083838151811061132257611322612e3e565b602002602001015160200151905060006001600160a01b0316826001600160a01b0316148061135857506001600160a01b038116155b1561137657604051635d7aa74360e11b815260040160405180910390fd5b6001600160a01b0382811660008181526001602090815260409182902080546001600160a01b031916948616948517905581519283528201929092527f388056c710663a451dfea6adfe8ff11b52af5529b0a3d1c69ba90f1fc3f2dc56910160405180910390a1505080806113ea90613424565b9150506112e3565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b81511561145157815182602001fd5b8060405162461bcd60e51b81526004016104e8919061343d565b60005b815181101561054c576114d982828151811061148c5761148c612e3e565b6020026020010151600001518383815181106114aa576114aa612e3e565b6020026020010151602001518484815181106114c8576114c8612e3e565b602002602001015160400151610936565b60010161146e565b60006114fd8960078111156114f8576114f8612e93565b611ab1565b156115165761150f8888888686611ad5565b90506115b9565b61153089600781111561152b5761152b612e93565b611afb565b156115445761150f88888888888888611b1e565b61155989600781111561107257611072612e93565b156115745761156c338989898787611b65565b5060006115b9565b61158e89600781111561158957611589612e93565b611beb565b156115a05761150f8888888686611c0f565b60405163596de99760e01b815260040160405180910390fd5b98975050505050505050565b600060038260078111156115db576115db612e93565b14806112da575060075b8260078111156115f7576115f7612e93565b1492915050565b60008082600781111561161357611613612e93565b14806116305750600182600781111561162e5761162e612e93565b145b8061164c5750600282600781111561164a5761164a612e93565b145b806112da575060036115e5565b6000805b868110156119065785858281811061167757611677612e3e565b905060200201356000146118fe57600088888381811061169957611699612e3e565b90506020020160208101906116ae9190612a98565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156116f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117189190613450565b90507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031689898481811061175657611756612e3e565b905060200201602081019061176b9190612a98565b6001600160a01b03160361179357831561178457918201915b61178e8582611911565b6118fc565b831561185e577f0000000000000000000000007c2ca9d502f2409beceafa68e97a176ff805029f6001600160a01b03166341976e098a8a858181106117da576117da612e3e565b90506020020160208101906117ef9190612a98565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015611833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118579190613450565b8102830192505b88888381811061187057611870612e3e565b90506020020160208101906118859190612a98565b60405163a9059cbb60e01b81526001600160a01b03878116600483015260248201849052919091169063a9059cbb906044016020604051808303816000875af11580156118d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fa9190612e54565b505b505b60010161165d565b509695505050505050565b604051632e1a7d4d60e01b8152600481018290527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561197357600080fd5b505af1158015611987573d6000803e3d6000fd5b5061054c925050506001600160a01b03831682611cb4565b60005b815181101561054c5760008282815181106119bf576119bf612e3e565b602002602001015160000151905060008383815181106119e1576119e1612e3e565b602002602001015160200151905060006001600160a01b0316826001600160a01b03161480611a1757506001600160a01b038116155b15611a355760405163acb22e5160e01b815260040160405180910390fd5b6001600160a01b0382811660008181526002602090815260409182902080546001600160a01b031916948616948517905581519283528201929092527f53db4a2342abba949712a6909b124e7452f4c84834894b2a75a377f5201ef610910160405180910390a150508080611aa990613424565b9150506119a2565b60006002826007811115611ac757611ac7612e93565b14806112da575060066115e5565b6000611ae5338787878787611b65565b611af185858585611dd2565b9695505050505050565b600080826007811115611b1057611b10612e93565b14806112da575060046115e5565b600080611b2d88888888611ee2565b90506000611b3d89898787611f42565b9050611b4d338b8b8b8989611b65565b611b578183613469565b9a9950505050505050505050565b60005b83811015611be257828282818110611b8257611b82612e3e565b90506020020135600014611bda57611bda8787878785818110611ba757611ba7612e3e565b9050602002016020810190611bbc9190612a98565b868686818110611bce57611bce612e3e565b90506020020135611fc3565b600101611b68565b50505050505050565b60006001826007811115611c0157611c01612e93565b14806112da575060056115e5565b6000611c1d85858585611f42565b50611c2c338787878787611b65565b6040516370a0823160e01b81523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401602060405180830381865afa158015611c90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af19190613450565b80471015611d045760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064016104e8565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611d51576040519150601f19603f3d011682016040523d82523d6000602084013e611d56565b606091505b5050905080611dcd5760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016104e8565b505050565b6000805b84811015611ed957838382818110611df057611df0612e3e565b90506020020135600014611ed1576000868683818110611e1257611e12612e3e565b9050602002016020810190611e279190612a98565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015611e6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e919190613450565b9050611ec3878784818110611ea857611ea8612e3e565b9050602002016020810190611ebd9190612a98565b826121d7565b611ecd9084613480565b9250505b600101611dd6565b50949350505050565b6000805b84811015611ed957611f36868683818110611f0357611f03612e3e565b9050602002016020810190611f189190612a98565b858584818110611f2a57611f2a612e3e565b905060200201356121d7565b90910190600101611ee6565b6000805b84811015611ed957838382818110611f6057611f60612e3e565b90506020020135600014611fbb57611fb6868683818110611f8357611f83612e3e565b9050602002016020810190611f989190612a98565b858584818110611faa57611faa612e3e565b905060200201356124c1565b820191505b600101611f46565b6040516001600160a01b0385811660248301526044820183905283169060640160408051601f198184030181529181526020820180516001600160e01b031663095ea7b360e01b17905251612018919061328d565b6000604051808303816000865af19150503d8060008114612055576040519150601f19603f3d011682016040523d82523d6000602084013e61205a565b606091505b505060405163976ce49560e01b81526001600160a01b0384811660048301528581166024830152604482018490528616915063976ce4959060640160408051808303816000875af11580156120b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120d79190613498565b505060405163bf27304160e01b81526001600160a01b03838116600483015285169063bf2730419060240160c060405180830381865afa15801561211f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214391906134bc565b60409081015190516370a0823160e01b81526001600160a01b038581166004830152909116906370a0823190602401602060405180830381865afa15801561218f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121b39190613450565b156121d157604051639e703a0560e01b815260040160405180910390fd5b50505050565b60007f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28215806122185750806001600160a01b0316846001600160a01b0316145b1561222657829150506112da565b6001600160a01b038085166000908152600260205260409020541680156122f6576040516001600160a01b0386166024820152604481018590526000906122c390839060640160408051601f19818403018152918152602080830180516001600160e01b03166321ebbf0160e01b179052815180830190925260138252721d1bddd85c991cd3985d1a5d9951985a5b1959606a1b9082015261281c565b9050600080828060200190518101906122dc9190613550565b915091506122ea82826121d7565b955050505050506112da565b600080612302876128a0565b91509150866001600160a01b0316816001600160a01b031663db36eb2e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561234e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123729190612e76565b6040516001600160a01b0390911660248201526044810188905260640160408051601f198184030181529181526020820180516001600160e01b031663095ea7b360e01b179052516123c4919061328d565b6000604051808303816000865af19150503d8060008114612401576040519150601f19603f3d011682016040523d82523d6000602084013e612406565b606091505b50506040516001600160a01b03808a16602483018190528188166044840152606483018a9052908516608483015260a48201526000915060c40160408051601f19818403018152918152602080830180516001600160e01b0316639cffaf6f60e01b1790528151808301909252600c82526b39bbb0b820b6b7bab73a24b760a11b9082015290915060009061249e908490849061281c565b9050808060200190518101906124b49190613450565b9998505050505050505050565b60007f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28215806125025750836001600160a01b0316816001600160a01b0316145b1561251057829150506112da565b6001600160a01b03808516600090815260026020526040902054168015612607576040516001600160a01b0386166024820152604481018590526000906125ac90839060640160408051601f19818403018152918152602080830180516001600160e01b03166337523bed60e11b179052815180830190925260128252711d1bddd85c991cd05cdcd95d11985a5b195960721b9082015261281c565b9050600080828060200190518101906125c59190613550565b91509150876001600160a01b0316826001600160a01b0316146125fb5760405163245feacb60e01b815260040160405180910390fd5b94506112da9350505050565b600080612613876128a0565b915091506000816001600160a01b031663db36eb2e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612657573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061267b9190612e76565b60405163095ea7b360e01b81526001600160a01b03808316600483015260001960248301529192509086169063095ea7b3906044016020604051808303816000875af11580156126cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126f39190612e54565b506040516001600160a01b038087166024830152808a1660448301819052606483018a9052908516608483015260a482015260009060c40160408051601f19818403018152918152602080830180516001600160e01b03166305a319ad60e21b1790528151808301909252601382527214ddd85c105b5bdd5b9d13dd5d11985a5b1959606a1b90820152909150600090612790908590849061281c565b60405163095ea7b360e01b81526001600160a01b038581166004830152600060248301529192509088169063095ea7b3906044016020604051808303816000875af11580156127e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128079190612e54565b5080806020019051810190611b579190613450565b60606000846001600160a01b031684604051612838919061328d565b600060405180830381855af49150503d8060008114612873576040519150601f19603f3d011682016040523d82523d6000602084013e612878565b606091505b509250905080158061288957508151155b15612898576128988284611442565b509392505050565b60008060006128ae84610adf565b905060006128bb826128c7565b91959194509092505050565b6001600160a01b03808216600090815260016020526040812054909116806112da57604051636ce6ae9b60e11b815260040160405180910390fd5b60008083601f84011261291457600080fd5b5081356001600160401b0381111561292b57600080fd5b6020830191508360208260061b850101111561294657600080fd5b9250929050565b6000806020838503121561296057600080fd5b82356001600160401b0381111561297657600080fd5b61298285828601612902565b90969095509350505050565b60008083601f8401126129a057600080fd5b5081356001600160401b038111156129b757600080fd5b6020830191508360208260051b850101111561294657600080fd5b600080600080604085870312156129e857600080fd5b84356001600160401b03808211156129ff57600080fd5b612a0b8883890161298e565b90965094506020870135915080821115612a2457600080fd5b50612a318782880161298e565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b81811015612a77578351151583529284019291840191600101612a59565b50909695505050505050565b6001600160a01b03811681146111d057600080fd5b600060208284031215612aaa57600080fd5b8135612ab581612a83565b9392505050565b803560088110610c8357600080fd5b600080600080600060808688031215612ae357600080fd5b8535612aee81612a83565b94506020860135612afe81612a83565b9350612b0c60408701612abc565b925060608601356001600160401b03811115612b2757600080fd5b612b338882890161298e565b969995985093965092949392505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715612b7c57612b7c612b44565b60405290565b604051606081016001600160401b0381118282101715612b7c57612b7c612b44565b604051601f8201601f191681016001600160401b0381118282101715612bcc57612bcc612b44565b604052919050565b600082601f830112612be557600080fd5b81356001600160401b03811115612bfe57612bfe612b44565b612c11601f8201601f1916602001612ba4565b818152846020838601011115612c2657600080fd5b816020850160208301376000918101602001919091529392505050565b600080600060608486031215612c5857600080fd5b8335612c6381612a83565b92506020840135612c7381612a83565b915060408401356001600160401b03811115612c8e57600080fd5b612c9a86828701612bd4565b9150509250925092565b60008060408385031215612cb757600080fd5b50508035926020909101359150565b600080600080600080600080600060a08a8c031215612ce457600080fd5b8935612cef81612a83565b985060208a01356001600160401b0380821115612d0b57600080fd5b612d178d838e0161298e565b909a50985060408c0135915080821115612d3057600080fd5b612d3c8d838e0161298e565b909850965060608c0135915080821115612d5557600080fd5b612d618d838e0161298e565b909650945060808c0135915080821115612d7a57600080fd5b818c0191508c601f830112612d8e57600080fd5b813581811115612d9d57600080fd5b8d6020828501011115612daf57600080fd5b6020830194508093505050509295985092959850929598565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b600060408284031215612e0f57600080fd5b612e17612b5a565b8235612e2281612a83565b81526020830135612e3281612a83565b60208201529392505050565b634e487b7160e01b600052603260045260246000fd5b600060208284031215612e6657600080fd5b81518015158114612ab557600080fd5b600060208284031215612e8857600080fd5b8151612ab581612a83565b634e487b7160e01b600052602160045260246000fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b03868116825260208083018790526000916080840191604060088910612f0f57634e487b7160e01b600052602160045260246000fd5b888187015260606080818801528488865260a08801905060a08960051b89010195508960005b8a811015612fe657898803609f190183528135368d9003605e19018112612f5b57600080fd5b8c018035612f6881612a83565b8816895280870135612f7981612a83565b8816898801528086013536829003601e19018112612f9657600080fd5b810180356001600160401b03811115612fae57600080fd5b803603831315612fbd57600080fd5b86888c0152612fd1878c01828b8501612ea9565b9a505050928601925090850190600101612f35565b50959d9c50505050505050505050505050565b60005b83811015613014578181015183820152602001612ffc565b838111156121d15750506000910152565b6000815180845261303d816020860160208601612ff9565b601f01601f19169290920160200192915050565b604080825283519082018190526000906020906060840190828701845b828110156130935781516001600160a01b03168452928401929084019060010161306e565b50505083810382850152611af18186613025565b60006001600160401b038211156130c0576130c0612b44565b5060051b60200190565b600082601f8301126130db57600080fd5b815160206130f06130eb836130a7565b612ba4565b828152600592831b850182019282820191908785111561310f57600080fd5b8387015b858110156131a25780516001600160401b038111156131325760008081fd5b8801603f81018a136131445760008081fd5b8581015160406131566130eb836130a7565b82815291851b8301810191888101908d8411156131735760008081fd5b938201935b8385101561319157845182529389019390890190613178565b885250505093850193508401613113565b5090979650505050505050565b6000806000606084860312156131c457600080fd5b83516001600160401b03808211156131db57600080fd5b818601915086601f8301126131ef57600080fd5b815160206131ff6130eb836130a7565b82815260059290921b8401810191818101908a84111561321e57600080fd5b948201945b8386101561324557855161323681612a83565b82529482019490820190613223565b9189015191975090935050508082111561325e57600080fd5b61326a878388016130ca565b9350604086015191508082111561328057600080fd5b50612c9a868287016130ca565b6000825161329f818460208701612ff9565b9190910192915050565b600080600080608085870312156132bf57600080fd5b6132c98535612a83565b84359350602085013592506132e060408601612abc565b915060608501356001600160401b03808211156132fc57600080fd5b818701915087601f83011261331057600080fd5b813561331e6130eb826130a7565b8082825260208201915060208360051b86010192508a83111561334057600080fd5b602085015b838110156133d457848135111561335b57600080fd5b803586016060818e03601f1901121561337357600080fd5b61337b612b82565b602082013561338981612a83565b8152604082013561339981612a83565b60208201526060820135878111156133b057600080fd5b6133bf8f602083860101612bd4565b60408301525084525060209283019201613345565b50979a9699509497505050505050565b6000602082840312156133f657600080fd5b81516001600160e01b031981168114612ab557600080fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016134365761343661340e565b5060010190565b6020815260006112d76020830184613025565b60006020828403121561346257600080fd5b5051919050565b60008282101561347b5761347b61340e565b500390565b600082198211156134935761349361340e565b500190565b600080604083850312156134ab57600080fd5b505080516020909101519092909150565b600060c082840312156134ce57600080fd5b60405160c081018181106001600160401b03821117156134f0576134f0612b44565b60405282516134fe81612a83565b8152602083015161350e81612a83565b6020820152604083015161352181612a83565b80604083015250606083015160608201526080830151608082015260a083015160a08201528091505092915050565b6000806040838503121561356357600080fd5b825161356e81612a83565b602093909301519294929350505056fea26469706673582212202bbaf7bc1a5263011e40d728797c8266cad2c12f416dc3114b1536347a3b41a264736f6c634300080d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000d998c35b7900b344bbbe6555cc11576942cf309d000000000000000000000000e37b8c83138caf12e57632d19c06eb561d47e4230000000000000000000000000e466fc22386997dac23d1f89a43ecb2cb1e76e9000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000520800000000000000000000000000000000000000000000000000000000000000050000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0000000000000000000000000a2ed88339701c209f3a72215164f0d97e7403a05000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000c07b94b2a1f1bfc7381ffc72fe217063bf6946fb0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f000000000000000000000000d4240aed295ff0fc2d76b3160e5d63692ba7213300000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5000000000000000000000000d4bdddca31668f37dba5115f68422d043cb5ee08000000000000000000000000d7c9f0e536dc865ae858b0c0453fe76d13c3beac000000000000000000000000138f032e839f247e5fa913e7ba52bea509c1622500000000000000000000000000000000000000000000000000000000000000040000000000000000000000009d46f86516d6083aca3f9850d060cb62cd0f6924000000000000000000000000ff36fdf7de2aa239b0295d406a6ca5c25c6c9e99000000000000000000000000fae64471ae04068e872c6a73c9f5a255c37ddbdc00000000000000000000000096fbf6f0a431e64e05e22e5dbf6f1d92e5dda81f0000000000000000000000002f0a401966219351887a608493954a7078ef1bc000000000000000000000000096fbf6f0a431e64e05e22e5dbf6f1d92e5dda81f0000000000000000000000002f1aa93249bcc0df7d89739f86308c7a270f5eed000000000000000000000000a40158686a5bdbe6c3ea8a32f6e4e045c1c13f90
-----Decoded View---------------
Arg [0] : _repository (address): 0xd998C35B7900b344bbBe6555cc11576942Cf309d
Arg [1] : _chainlinkPriceProvider (address): 0xe37B8c83138caF12E57632D19c06Eb561D47e423
Arg [2] : _lens (address): 0x0e466FC22386997daC23D1f89A43ecb2CB1e76E9
Arg [3] : _exchangeProxy (address): 0xDef1C0ded9bec7F1a1670819833240f027b25EfF
Arg [4] : _magicians (tuple[]): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [5] : _swappers (tuple[]): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput],System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [6] : _baseCost (uint256): 21000
-----Encoded View---------------
27 Constructor Arguments found :
Arg [0] : 000000000000000000000000d998c35b7900b344bbbe6555cc11576942cf309d
Arg [1] : 000000000000000000000000e37b8c83138caf12e57632d19c06eb561d47e423
Arg [2] : 0000000000000000000000000e466fc22386997dac23d1f89a43ecb2cb1e76e9
Arg [3] : 000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff
Arg [4] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000240
Arg [6] : 0000000000000000000000000000000000000000000000000000000000005208
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [8] : 0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0
Arg [9] : 000000000000000000000000a2ed88339701c209f3a72215164f0d97e7403a05
Arg [10] : 000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84
Arg [11] : 000000000000000000000000c07b94b2a1f1bfc7381ffc72fe217063bf6946fb
Arg [12] : 0000000000000000000000000ab87046fbb341d058f17cbc4c1133f25a20a52f
Arg [13] : 000000000000000000000000d4240aed295ff0fc2d76b3160e5d63692ba72133
Arg [14] : 00000000000000000000000064aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5
Arg [15] : 000000000000000000000000d4bdddca31668f37dba5115f68422d043cb5ee08
Arg [16] : 000000000000000000000000d7c9f0e536dc865ae858b0c0453fe76d13c3beac
Arg [17] : 000000000000000000000000138f032e839f247e5fa913e7ba52bea509c16225
Arg [18] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [19] : 0000000000000000000000009d46f86516d6083aca3f9850d060cb62cd0f6924
Arg [20] : 000000000000000000000000ff36fdf7de2aa239b0295d406a6ca5c25c6c9e99
Arg [21] : 000000000000000000000000fae64471ae04068e872c6a73c9f5a255c37ddbdc
Arg [22] : 00000000000000000000000096fbf6f0a431e64e05e22e5dbf6f1d92e5dda81f
Arg [23] : 0000000000000000000000002f0a401966219351887a608493954a7078ef1bc0
Arg [24] : 00000000000000000000000096fbf6f0a431e64e05e22e5dbf6f1d92e5dda81f
Arg [25] : 0000000000000000000000002f1aa93249bcc0df7d89739f86308c7a270f5eed
Arg [26] : 000000000000000000000000a40158686a5bdbe6c3ea8a32f6e4e045c1c13f90
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.