Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 98 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
New Silo | 19873834 | 207 days ago | IN | 0 ETH | 0.07873504 | ||||
New Silo | 19768199 | 221 days ago | IN | 0 ETH | 0.4199748 | ||||
New Silo | 19574550 | 249 days ago | IN | 0 ETH | 0.37869454 | ||||
New Silo | 19573122 | 249 days ago | IN | 0 ETH | 0.32788081 | ||||
New Silo | 19568212 | 249 days ago | IN | 0 ETH | 0.44524082 | ||||
New Silo | 19559426 | 251 days ago | IN | 0 ETH | 0.27948073 | ||||
New Silo | 19466629 | 264 days ago | IN | 0 ETH | 0.31152793 | ||||
New Silo | 19135023 | 310 days ago | IN | 0 ETH | 0.49624599 | ||||
New Silo | 19089657 | 317 days ago | IN | 0 ETH | 0.29106304 | ||||
New Silo | 17512368 | 538 days ago | IN | 0 ETH | 0.24246553 | ||||
New Silo | 17393035 | 554 days ago | IN | 0 ETH | 0.46117507 | ||||
New Silo | 17392960 | 554 days ago | IN | 0 ETH | 0.53125097 | ||||
New Silo | 17392950 | 554 days ago | IN | 0 ETH | 0.59896994 | ||||
New Silo | 17392944 | 554 days ago | IN | 0 ETH | 0.56583011 | ||||
New Silo | 17231848 | 577 days ago | IN | 0 ETH | 1.47595165 | ||||
New Silo | 17231843 | 577 days ago | IN | 0 ETH | 1.42410722 | ||||
New Silo | 17031275 | 606 days ago | IN | 0 ETH | 0.31370753 | ||||
New Silo | 17031070 | 606 days ago | IN | 0 ETH | 0.30296311 | ||||
New Silo | 17031067 | 606 days ago | IN | 0 ETH | 0.30120095 | ||||
New Silo | 16520928 | 677 days ago | IN | 0 ETH | 0.28231083 | ||||
New Silo | 16477633 | 683 days ago | IN | 0 ETH | 0.60024635 | ||||
New Silo | 16477630 | 683 days ago | IN | 0 ETH | 0.57278938 | ||||
New Silo | 15875945 | 767 days ago | IN | 0 ETH | 1.25924208 | ||||
New Silo | 15823417 | 775 days ago | IN | 0 ETH | 0.13614477 | ||||
New Silo | 15823410 | 775 days ago | IN | 0 ETH | 0.13361448 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
SiloRepository
Compiler Version
v0.8.13+commit.abaa5c0e
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./SiloRouter.sol"; import "./interfaces/ISiloFactory.sol"; import "./interfaces/ISiloRepository.sol"; import "./interfaces/IPriceProvidersRepository.sol"; import "./interfaces/INotificationReceiver.sol"; import "./utils/GuardedLaunch.sol"; import "./Silo.sol"; import "./interfaces/ITokensFactory.sol"; import "./lib/Ping.sol"; /// @title SiloRepository /// @notice SiloRepository handles the creation and configuration of Silos. /// @dev Stores configuration for each asset in each silo. /// Each asset in each Silo starts with a default config that later on can be changed by the contract owner. /// Stores registry of Factory contracts that deploy different versions of Silos /// It is possible to have multiple versions/implementations of Silo and use different versions for different /// tokens. For example, one version can be used for UNI (ERC20) and the other can be used for UniV3LP tokens (ERC721). /// Manages bridge assets. Each Silo can have 1 or more bridge assets. New Silos are created with all currently active /// bridge assets. Silos that are already developed must synchronize bridge assets. Sync can be done by anyone, /// function has public access. /// Is a single source of truth for other contract addresses. /// @custom:security-contact [email protected] /* solhint-disable max-states-count */ contract SiloRepository is ISiloRepository, GuardedLaunch { using EnumerableSet for EnumerableSet.AddressSet; /// @dev Default values for AssetConfig. Used if values are not configured manually. AssetConfig public defaultAssetConfig; /// @dev Protocol fee configuration Fees public fees; /// @dev Factory contracts that deploys debt and collateral tokens for each asset in Silo ITokensFactory public override tokensFactory; /// @dev PriceProvidersRepository contract that manages PriceProviders implementations and is an entry point /// for reading prices for Silos. IPriceProvidersRepository public override priceProvidersRepository; /// @dev SiloRouter utility contract that combines number of actions (Deposit, Withdraw, Borrow, Repay) /// for batch execution in single transaction. address public override router; /// @dev Silo for bridge asset. We can have only one bridge pool address public bridgePool; /// @dev Silo version data SiloVersion public siloVersion; /// @dev Maps asset => silo version mapping(address => uint128) public override getVersionForAsset; /// @dev Maps Silo address to incentive contract that will distribute rewards for that Silo mapping(address => INotificationReceiver) public override getNotificationReceiver; /// @dev Maps version => ISiloFactory. Versions start at 1 and are incremented by 1. mapping(uint256 => ISiloFactory) public override siloFactory; /// @dev maps token address to silo address, asset => silo mapping(address => address) public override getSilo; /// @dev maps silo address to token address, silo => asset mapping(address => address) public siloReverse; /// @dev maps silo => asset => config mapping(address => mapping(address => AssetConfig)) public assetConfigs; /// @dev Value used to initialize the Silo's version data uint128 private constant _INITIAL_SILO_VERSION = 1; /// @dev representation of 100% uint256 private constant _ONE_HUNDRED_PERCENT = 1e18; /// @dev List of bridge assets supported by the protocol EnumerableSet.AddressSet private _bridgeAssets; /// @dev List of bridge assets removed by the protocol EnumerableSet.AddressSet private _removedBridgeAssets; error AssetAlreadyAdded(); error AssetIsNotABridge(); error AssetIsZero(); error BridgeAssetIsZero(); error ConfigDidNotChange(); error EmptyBridgeAssets(); error FeesDidNotChange(); error InterestRateModelDidNotChange(); error InvalidEntryFee(); error InvalidInterestRateModel(); error InvalidLiquidationThreshold(); error InvalidLTV(); error InvalidNotificationReceiver(); error InvalidPriceProvidersRepository(); error InvalidProtocolLiquidationFee(); error InvalidProtocolShareFee(); error InvalidSiloFactory(); error InvalidSiloRouter(); error InvalidSiloVersion(); error InvalidTokensFactory(); error LastBridgeAsset(); error LiquidationThresholdDidNotChange(); error MaximumLTVDidNotChange(); error VersionForAssetDidNotChange(); error NoPriceProviderForAsset(); error NotificationReceiverDidNotChange(); error PriceProviderRepositoryDidNotChange(); error RouterDidNotChange(); error SiloAlreadyExistsForAsset(); error SiloAlreadyExistsForBridgeAssets(); error SiloDoesNotExist(); error SiloIsZero(); error SiloNotAllowedForBridgeAsset(); error SiloVersionDoesNotExist(); modifier ensureValidLiquidationThreshold(uint256 _ltv, uint256 _liquidationThreshold) { if (_liquidationThreshold >= _ONE_HUNDRED_PERCENT) { revert InvalidLiquidationThreshold(); } if (_ltv == 0 || _ltv >= _liquidationThreshold) { revert InvalidLTV(); } _; } /// @param _siloFactory address of SiloFactory contract that deploys Silos /// @param _tokensFactory address of TokensFactory contract that deploys debt and collateral tokens /// for each Silo asset /// @param _defaultMaxLTV maximum Loan-to-Value for default configuration /// @param _defaultLiquidationThreshold liquidation threshold for default configuration /// @param _initialBridgeAssets bridge assets to start with constructor( address _siloFactory, address _tokensFactory, uint64 _defaultMaxLTV, uint64 _defaultLiquidationThreshold, address[] memory _initialBridgeAssets ) ensureValidLiquidationThreshold(_defaultMaxLTV, _defaultLiquidationThreshold) GuardedLaunch() { if (!Ping.pong(ISiloFactory(_siloFactory).siloFactoryPing)) { revert InvalidSiloFactory(); } if (!Ping.pong(ITokensFactory(_tokensFactory).tokensFactoryPing)) { revert InvalidTokensFactory(); } if (_initialBridgeAssets.length == 0) revert EmptyBridgeAssets(); ISiloFactory(_siloFactory).initRepository(address(this)); ITokensFactory(_tokensFactory).initRepository(address(this)); for (uint256 i = 0; i < _initialBridgeAssets.length; i++) { TokenHelper.assertAndGetDecimals(_initialBridgeAssets[i]); _bridgeAssets.add(_initialBridgeAssets[i]); emit BridgeAssetAdded(_initialBridgeAssets[i]); } siloVersion.byDefault = _INITIAL_SILO_VERSION; siloVersion.latest = _INITIAL_SILO_VERSION; siloFactory[_INITIAL_SILO_VERSION] = ISiloFactory(_siloFactory); emit RegisterSiloVersion(_siloFactory, _INITIAL_SILO_VERSION, _INITIAL_SILO_VERSION); emit SiloDefaultVersion(_INITIAL_SILO_VERSION); tokensFactory = ITokensFactory(_tokensFactory); emit TokensFactoryUpdate(_tokensFactory); defaultAssetConfig.maxLoanToValue = _defaultMaxLTV; defaultAssetConfig.liquidationThreshold = _defaultLiquidationThreshold; } /// @inheritdoc ISiloRepository function setVersionForAsset(address _siloAsset, uint128 _version) external override onlyOwner { if (getVersionForAsset[_siloAsset] == _version) revert VersionForAssetDidNotChange(); if (_version != 0 && address(siloFactory[_version]) == address(0)) { revert InvalidSiloVersion(); } emit VersionForAsset(_siloAsset, _version); getVersionForAsset[_siloAsset] = _version; } /// @inheritdoc ISiloRepository function setTokensFactory(address _tokensFactory) external override onlyOwner { if (!Ping.pong(ITokensFactory(_tokensFactory).tokensFactoryPing)) { revert InvalidTokensFactory(); } emit TokensFactoryUpdate(_tokensFactory); tokensFactory = ITokensFactory(_tokensFactory); } /// @inheritdoc ISiloRepository function setFees(Fees calldata _fees) external override onlyOwner { if (_fees.entryFee >= Solvency._PRECISION_DECIMALS) { revert InvalidEntryFee(); } if (_fees.protocolShareFee >= Solvency._PRECISION_DECIMALS) { revert InvalidProtocolShareFee(); } if (_fees.protocolLiquidationFee >= Solvency._PRECISION_DECIMALS) { revert InvalidProtocolLiquidationFee(); } Fees memory currentFees = fees; if ( _fees.entryFee == currentFees.entryFee && _fees.protocolShareFee == currentFees.protocolShareFee && _fees.protocolLiquidationFee == currentFees.protocolLiquidationFee ) { revert FeesDidNotChange(); } emit FeeUpdate(_fees.entryFee, _fees.protocolShareFee, _fees.protocolLiquidationFee); fees = _fees; } /// @inheritdoc ISiloRepository function setAssetConfig(address _silo, address _asset, AssetConfig calldata _assetConfig) external override ensureValidLiquidationThreshold(_assetConfig.maxLoanToValue, _assetConfig.liquidationThreshold) onlyOwner { if (_silo == address(0)) revert SiloIsZero(); if (_asset == address(0)) revert AssetIsZero(); if ( !Ping.pong(_assetConfig.interestRateModel.interestRateModelPing) || _assetConfig.interestRateModel.DP() == 0 ) { revert InvalidInterestRateModel(); } AssetConfig memory currentConfig = assetConfigs[_silo][_asset]; if ( currentConfig.maxLoanToValue == _assetConfig.maxLoanToValue && currentConfig.liquidationThreshold == _assetConfig.liquidationThreshold && currentConfig.interestRateModel == _assetConfig.interestRateModel ) { revert ConfigDidNotChange(); } emit AssetConfigUpdate(_silo, _asset, _assetConfig); assetConfigs[_silo][_asset] = _assetConfig; } /// @inheritdoc ISiloRepository function setDefaultInterestRateModel(IInterestRateModel _defaultInterestRateModel) external override onlyOwner { if (!Ping.pong(_defaultInterestRateModel.interestRateModelPing)) { revert InvalidInterestRateModel(); } if (defaultAssetConfig.interestRateModel == _defaultInterestRateModel) { revert InterestRateModelDidNotChange(); } emit InterestRateModel(_defaultInterestRateModel); defaultAssetConfig.interestRateModel = _defaultInterestRateModel; } /// @inheritdoc ISiloRepository function setDefaultMaximumLTV(uint64 _defaultMaxLTV) external override ensureValidLiquidationThreshold(_defaultMaxLTV, defaultAssetConfig.liquidationThreshold) onlyOwner { if (defaultAssetConfig.maxLoanToValue == _defaultMaxLTV) { revert MaximumLTVDidNotChange(); } defaultAssetConfig.maxLoanToValue = _defaultMaxLTV; emit NewDefaultMaximumLTV(_defaultMaxLTV); } /// @inheritdoc ISiloRepository function setDefaultLiquidationThreshold(uint64 _defaultLiquidationThreshold) external override ensureValidLiquidationThreshold(defaultAssetConfig.maxLoanToValue, _defaultLiquidationThreshold) onlyOwner { if (defaultAssetConfig.liquidationThreshold == _defaultLiquidationThreshold) { revert LiquidationThresholdDidNotChange(); } defaultAssetConfig.liquidationThreshold = _defaultLiquidationThreshold; emit NewDefaultLiquidationThreshold(_defaultLiquidationThreshold); } /// @inheritdoc ISiloRepository function setPriceProvidersRepository(IPriceProvidersRepository _repository) external override onlyOwner { if (!Ping.pong(_repository.priceProvidersRepositoryPing)) { revert InvalidPriceProvidersRepository(); } if (priceProvidersRepository == _repository) { revert PriceProviderRepositoryDidNotChange(); } emit PriceProvidersRepositoryUpdate(_repository); priceProvidersRepository = _repository; } /// @inheritdoc ISiloRepository function setRouter(address _router) external override onlyOwner { if (!Ping.pong(SiloRouter(payable(_router)).siloRouterPing)) { revert InvalidSiloRouter(); } if (router == _router) revert RouterDidNotChange(); emit RouterUpdate(_router); router = _router; } /// @inheritdoc ISiloRepository function setNotificationReceiver( address _silo, INotificationReceiver _newNotificationReceiver ) external override onlyOwner { if (!Ping.pong(_newNotificationReceiver.notificationReceiverPing)) { revert InvalidNotificationReceiver(); } if (getNotificationReceiver[_silo] == _newNotificationReceiver) { revert NotificationReceiverDidNotChange(); } emit NotificationReceiverUpdate(_newNotificationReceiver); getNotificationReceiver[_silo] = _newNotificationReceiver; } /// @inheritdoc ISiloRepository function addBridgeAsset(address _newBridgeAsset) external override onlyOwner { if (!priceProvidersRepository.providersReadyForAsset(_newBridgeAsset)) { revert NoPriceProviderForAsset(); } TokenHelper.assertAndGetDecimals(_newBridgeAsset); // We need to add first, so it can be synchronized after if (!_bridgeAssets.add(_newBridgeAsset)) revert AssetAlreadyAdded(); // We don't care about the return value, because we are doing this even if the asset isn't in the list _removedBridgeAssets.remove(_newBridgeAsset); emit BridgeAssetAdded(_newBridgeAsset); address silo = getSilo[_newBridgeAsset]; address bridge = bridgePool; // Check if we already have silo for the new bridge asset. // Note that if there are at least two bridge assets in the system, it is possible to prevent the addition // of a specific new bridge asset by creating a Silo for it. if (silo != address(0)) { // Silo for new bridge asset exists, if we already have bridge, then revert if (bridge != address(0)) revert SiloAlreadyExistsForBridgeAssets(); bridgePool = silo; bridge = silo; emit BridgePool(silo); } // syncBridgeAssets when: // - we discovered bridge pool, we need to sync it with new bridge asset // - silo for asset does not exist, but if we already have bridge, we need to sync if (bridge != address(0)) ISilo(bridge).syncBridgeAssets(); } /// @inheritdoc ISiloRepository function removeBridgeAsset(address _bridgeAssetToRemove) external override onlyOwner { if (_bridgeAssetToRemove == address(0)) revert BridgeAssetIsZero(); if (_bridgeAssets.length() == 1) revert LastBridgeAsset(); if (!_bridgeAssets.remove(_bridgeAssetToRemove)) revert AssetIsNotABridge(); _removedBridgeAssets.add(_bridgeAssetToRemove); emit BridgeAssetRemoved(_bridgeAssetToRemove); address silo = getSilo[_bridgeAssetToRemove]; // we have silo and it is for sure bridge pool if (silo != address(0)) { ISilo(silo).syncBridgeAssets(); bridgePool = address(0); emit BridgePool(address(0)); return; } address pool = bridgePool; // we have bridge pool but it is not directly for `_bridgeAssetToRemove` if (pool != address(0)) { ISilo(pool).syncBridgeAssets(); } } /// @inheritdoc ISiloRepository function newSilo(address _siloAsset, bytes memory _siloData) external override returns (address) { bool assetIsABridge = _bridgeAssets.contains(_siloAsset); ensureCanCreateSiloFor(_siloAsset, assetIsABridge); return _createSilo(_siloAsset, getVersionForAsset[_siloAsset], assetIsABridge, _siloData); } /// @inheritdoc ISiloRepository function replaceSilo( address _siloAsset, uint128 _siloVersion, bytes memory _siloData ) external override onlyOwner returns (address) { address siloToReplace = getSilo[_siloAsset]; if (siloToReplace == address(0)) revert SiloDoesNotExist(); return _createSilo(_siloAsset, _siloVersion, _bridgeAssets.contains(_siloAsset), _siloData); } /// @inheritdoc ISiloRepository function registerSiloVersion(ISiloFactory _factory, bool _isDefault) external override onlyOwner { if (!Ping.pong(_factory.siloFactoryPing)) revert InvalidSiloFactory(); SiloVersion memory v = siloVersion; v.latest += 1; siloFactory[v.latest] = _factory; emit RegisterSiloVersion(address(_factory), v.latest, v.byDefault); if (_isDefault) { v.byDefault = v.latest; emit SiloDefaultVersion(v.latest); } siloVersion = v; } /// @inheritdoc ISiloRepository function unregisterSiloVersion(uint128 _siloVersion) external override onlyOwner { address factory = address(siloFactory[_siloVersion]); // Can't unregister nonexistent or default silo version if (factory == address(0) || _siloVersion == siloVersion.byDefault) { revert InvalidSiloVersion(); } emit UnregisterSiloVersion(factory, _siloVersion); siloFactory[_siloVersion] = ISiloFactory(address(0)); } /// @inheritdoc ISiloRepository function setDefaultSiloVersion(uint128 _defaultVersion) external override onlyOwner { if (address(siloFactory[_defaultVersion]) == address(0)) { revert SiloVersionDoesNotExist(); } emit SiloDefaultVersion(_defaultVersion); siloVersion.byDefault = _defaultVersion; } /// @inheritdoc ISiloRepository function entryFee() external view override returns (uint256) { return fees.entryFee; } /// @inheritdoc ISiloRepository function protocolShareFee() external view override returns (uint256) { return fees.protocolShareFee; } /// @inheritdoc ISiloRepository function protocolLiquidationFee() external view override returns (uint256) { return fees.protocolLiquidationFee; } /// @inheritdoc ISiloRepository function isSilo(address _silo) external view override returns (bool) { return siloReverse[_silo] != address(0); } /// @inheritdoc ISiloRepository function getBridgeAssets() external view override returns (address[] memory) { return _bridgeAssets.values(); } /// @inheritdoc ISiloRepository function getRemovedBridgeAssets() external view override returns (address[] memory) { return _removedBridgeAssets.values(); } /// @inheritdoc ISiloRepository function getMaximumLTV(address _silo, address _asset) external view override returns (uint256) { uint256 maxLoanToValue = assetConfigs[_silo][_asset].maxLoanToValue; if (maxLoanToValue != 0) { return maxLoanToValue; } return defaultAssetConfig.maxLoanToValue; } /// @inheritdoc ISiloRepository function getInterestRateModel(address _silo, address _asset) external view override returns (IInterestRateModel model) { model = assetConfigs[_silo][_asset].interestRateModel; if (address(model) == address(0)) { return defaultAssetConfig.interestRateModel; } } /// @inheritdoc ISiloRepository function getLiquidationThreshold(address _silo, address _asset) external view override returns (uint256) { uint256 liquidationThreshold = assetConfigs[_silo][_asset].liquidationThreshold; if (liquidationThreshold != 0) { return liquidationThreshold; } return defaultAssetConfig.liquidationThreshold; } /// @inheritdoc ISiloRepository function siloRepositoryPing() external pure override returns (bytes4) { return this.siloRepositoryPing.selector; } /// @inheritdoc ISiloRepository function ensureCanCreateSiloFor(address _asset, bool _assetIsABridge) public view override { if (getSilo[_asset] != address(0)) revert SiloAlreadyExistsForAsset(); if (_assetIsABridge) { if (bridgePool != address(0)) revert SiloAlreadyExistsForBridgeAssets(); if (_bridgeAssets.length() == 1) revert SiloNotAllowedForBridgeAsset(); } } /// @inheritdoc ISiloRepository function owner() public view override(ISiloRepository, GuardedLaunch) returns (address) { return GuardedLaunch.owner(); } /// @dev Deploys Silo /// @param _siloAsset silo asset /// @param _siloVersion version of silo implementation /// @param _assetIsABridge flag indicating if asset is a bridge asset /// @param _siloData (optional) data that may be needed during silo creation function _createSilo( address _siloAsset, uint128 _siloVersion, bool _assetIsABridge, bytes memory _siloData ) internal returns (address createdSilo) { // 0 means default version if (_siloVersion == 0) { _siloVersion = siloVersion.byDefault; } ISiloFactory factory = siloFactory[_siloVersion]; if (address(factory) == address(0)) revert InvalidSiloVersion(); createdSilo = factory.createSilo(_siloAsset, _siloVersion, _siloData); // We do this before the asset sync so that functions like `isSilo` can be used by factories getSilo[_siloAsset] = createdSilo; siloReverse[createdSilo] = _siloAsset; Silo(createdSilo).syncBridgeAssets(); emit NewSilo(createdSilo, _siloAsset, _siloVersion); if (_assetIsABridge) { bridgePool = createdSilo; emit BridgePool(createdSilo); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// 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/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/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/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/structs/EnumerableSet.sol) pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastvalue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastvalue; // Update the index for the moved value set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { return _values(set._inner); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; assembly { result := store } return result; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "./utils/LiquidationReentrancyGuard.sol"; import "./interfaces/IBaseSilo.sol"; import "./interfaces/IGuardedLaunch.sol"; import "./interfaces/ISiloRepository.sol"; import "./interfaces/IPriceProvidersRepository.sol"; import "./interfaces/IInterestRateModel.sol"; import "./interfaces/IShareToken.sol"; import "./lib/Ping.sol"; import "./lib/EasyMath.sol"; import "./lib/TokenHelper.sol"; import "./lib/Solvency.sol"; /// @title BaseSilo /// @dev Base contract for Silo core logic. /// @custom:security-contact [email protected] abstract contract BaseSilo is IBaseSilo, ReentrancyGuard, LiquidationReentrancyGuard { using SafeERC20 for ERC20; using EasyMath for uint256; ISiloRepository immutable public override siloRepository; // asset address for which Silo was created address public immutable siloAsset; /// @dev version of silo /// @notice It tells us which `SiloRepository.siloFactory(version)` created this Silo uint128 public immutable VERSION; // solhint-disable-line var-name-mixedcase // solhint-disable-next-line var-name-mixedcase uint256 private immutable _ASSET_DECIMAL_POINTS; /// @dev stores all *synced* assets (bridge assets + removed bridge assets + siloAsset) address[] private _allSiloAssets; /// @dev asset => AssetStorage mapping(address => AssetStorage) private _assetStorage; /// @dev asset => AssetInterestData mapping(address => AssetInterestData) private _interestData; error AssetDoesNotExist(); error BorrowNotPossible(); error DepositNotPossible(); error DepositsExceedLimit(); error InvalidRepository(); error InvalidSiloVersion(); error MaximumLTVReached(); error NotEnoughLiquidity(); error NotEnoughDeposits(); error NotSolvent(); error OnlyRouter(); error Paused(); error UnexpectedEmptyReturn(); error UserIsZero(); modifier onlyExistingAsset(address _asset) { if (_interestData[_asset].status == AssetStatus.Undefined) { revert AssetDoesNotExist(); } _; } modifier onlyRouter() { if (msg.sender != siloRepository.router()) revert OnlyRouter(); _; } modifier validateMaxDepositsAfter(address _asset) { _; IPriceProvidersRepository priceProviderRepo = siloRepository.priceProvidersRepository(); AssetStorage storage _assetState = _assetStorage[_asset]; uint256 allDeposits = _assetState.totalDeposits + _assetState.collateralOnlyDeposits; if ( priceProviderRepo.getPrice(_asset) * allDeposits / (10 ** IERC20Metadata(_asset).decimals()) > IGuardedLaunch(address(siloRepository)).getMaxSiloDepositsValue(address(this), _asset) ) { revert DepositsExceedLimit(); } } constructor (ISiloRepository _repository, address _siloAsset, uint128 _version) { if (!Ping.pong(_repository.siloRepositoryPing)) revert InvalidRepository(); if (_version == 0) revert InvalidSiloVersion(); uint256 decimals = TokenHelper.assertAndGetDecimals(_siloAsset); VERSION = _version; siloRepository = _repository; siloAsset = _siloAsset; _ASSET_DECIMAL_POINTS = 10**decimals; } /// @dev this is exposed only for test purposes, but it is safe to leave it like that function initAssetsTokens() external nonReentrant { _initAssetsTokens(); } /// @inheritdoc IBaseSilo function syncBridgeAssets() external override nonReentrant { // sync removed assets address[] memory removedBridgeAssets = siloRepository.getRemovedBridgeAssets(); for (uint256 i = 0; i < removedBridgeAssets.length; i++) { // If removed bridge asset is the silo asset for this silo, do not remove it address removedBridgeAsset = removedBridgeAssets[i]; if (removedBridgeAsset != siloAsset) { _interestData[removedBridgeAsset].status = AssetStatus.Removed; emit AssetStatusUpdate(removedBridgeAsset, AssetStatus.Removed); } } // must be called at the end, because we overriding `_assetStorage[removedBridgeAssets[i]].removed` _initAssetsTokens(); } /// @inheritdoc IBaseSilo function assetStorage(address _asset) external view override returns (AssetStorage memory) { return _assetStorage[_asset]; } /// @inheritdoc IBaseSilo function interestData(address _asset) external view override returns (AssetInterestData memory) { return _interestData[_asset]; } /// @inheritdoc IBaseSilo function utilizationData(address _asset) external view override returns (UtilizationData memory data) { AssetStorage storage _assetState = _assetStorage[_asset]; return UtilizationData( _assetState.totalDeposits, _assetState.totalBorrowAmount, _interestData[_asset].interestRateTimestamp ); } /// @inheritdoc IBaseSilo function getAssets() public view override returns (address[] memory assets) { return _allSiloAssets; } /// @inheritdoc IBaseSilo function getAssetsWithState() public view override returns ( address[] memory assets, AssetStorage[] memory assetsStorage ) { assets = _allSiloAssets; assetsStorage = new AssetStorage[](assets.length); for (uint256 i = 0; i < assets.length; i++) { assetsStorage[i] = _assetStorage[assets[i]]; } } /// @inheritdoc IBaseSilo function isSolvent(address _user) public view override returns (bool) { if (_user == address(0)) revert UserIsZero(); (address[] memory assets, AssetStorage[] memory assetsStates) = getAssetsWithState(); (uint256 userLTV, uint256 liquidationThreshold) = Solvency.calculateLTVs( Solvency.SolvencyParams( siloRepository, ISilo(address(this)), assets, assetsStates, _user ), Solvency.TypeofLTV.LiquidationThreshold ); return userLTV <= liquidationThreshold; } /// @inheritdoc IBaseSilo function depositPossible(address _asset, address _depositor) public view override returns (bool) { return _assetStorage[_asset].debtToken.balanceOf(_depositor) == 0 && _interestData[_asset].status == AssetStatus.Active; } /// @inheritdoc IBaseSilo function borrowPossible(address _asset, address _borrower) public view override returns (bool) { AssetStorage storage _assetState = _assetStorage[_asset]; return _assetState.collateralToken.balanceOf(_borrower) == 0 && _assetState.collateralOnlyToken.balanceOf(_borrower) == 0 && _interestData[_asset].status == AssetStatus.Active; } /// @inheritdoc IBaseSilo function liquidity(address _asset) public view returns (uint256) { return ERC20(_asset).balanceOf(address(this)) - _assetStorage[_asset].collateralOnlyDeposits; } /// @dev Initiate asset by deploying accounting EC20 tokens for collateral and debt /// @param _tokensFactory factory contract that deploys collateral and debt tokens /// @param _asset which asset to initialize /// @param _isBridgeAsset true if initialized asset is a bridge asset function _initAsset(ITokensFactory _tokensFactory, address _asset, bool _isBridgeAsset) internal { AssetSharesMetadata memory metadata = _generateSharesNames(_asset, _isBridgeAsset); AssetStorage storage _assetState = _assetStorage[_asset]; _assetState.collateralToken = _tokensFactory.createShareCollateralToken( metadata.collateralName, metadata.collateralSymbol, _asset ); _assetState.collateralOnlyToken = _tokensFactory.createShareCollateralToken( metadata.protectedName, metadata.protectedSymbol, _asset ); _assetState.debtToken = _tokensFactory.createShareDebtToken( metadata.debtName, metadata.debtSymbol, _asset ); // keep synced asset in storage array _allSiloAssets.push(_asset); _interestData[_asset].status = AssetStatus.Active; emit AssetStatusUpdate(_asset, AssetStatus.Active); } /// @dev Initializes all assets (bridge assets + unique asset) for Silo but only if asset has not been /// initialized already. It's safe to call it multiple times. It's safe for anyone to call it at any time. function _initAssetsTokens() internal { ITokensFactory tokensFactory = siloRepository.tokensFactory(); // init silo asset if needed if (address(_assetStorage[siloAsset].collateralToken) == address(0)) { _initAsset(tokensFactory, siloAsset, false); } // sync active assets address[] memory bridgeAssets = siloRepository.getBridgeAssets(); for (uint256 i = 0; i < bridgeAssets.length; i++) { address bridgeAsset = bridgeAssets[i]; // In case a bridge asset is added that already has a Silo, // do not initiate that asset in its Silo if (address(_assetStorage[bridgeAsset].collateralToken) == address(0)) { _initAsset(tokensFactory, bridgeAsset, true); } else { _interestData[bridgeAsset].status = AssetStatus.Active; emit AssetStatusUpdate(bridgeAsset, AssetStatus.Active); } } } /// @dev Generate asset shares tokens names and symbols /// @param _asset asset for which shares tokens will be initializaed /// @param _isBridgeAsset true if initialized asset is a bridge asset function _generateSharesNames(address _asset, bool _isBridgeAsset) internal view returns (AssetSharesMetadata memory metadata) { // Naming convention in UNI example: // - for siloAsset: sUNI, dUNI, spUNI // - for bridgeAsset: sWETH-UNI, dWETH-UNI, spWETH-UNI string memory assetSymbol = TokenHelper.symbol(_asset); metadata = AssetSharesMetadata({ collateralName: string.concat("Silo Finance Borrowable ", assetSymbol, " Deposit"), collateralSymbol: string.concat("s", assetSymbol), protectedName: string.concat("Silo Finance Protected ", assetSymbol, " Deposit"), protectedSymbol: string.concat("sp", assetSymbol), debtName: string.concat("Silo Finance ", assetSymbol, " Debt"), debtSymbol: string.concat("d", assetSymbol) }); if (_isBridgeAsset) { string memory baseSymbol = TokenHelper.symbol(siloAsset); metadata.collateralName = string.concat(metadata.collateralName, " in ", baseSymbol, " Silo"); metadata.collateralSymbol = string.concat(metadata.collateralSymbol, "-", baseSymbol); metadata.protectedName = string.concat(metadata.protectedName, " in ", baseSymbol, " Silo"); metadata.protectedSymbol = string.concat(metadata.protectedSymbol, "-", baseSymbol); metadata.debtName = string.concat(metadata.debtName, " in ", baseSymbol, " Silo"); metadata.debtSymbol = string.concat(metadata.debtSymbol, "-", baseSymbol); } } /// @dev Main deposit function that handles all deposit logic and validation /// @param _asset asset address that is being deposited /// @param _from wallet address form which to pull asset tokens /// @param _depositor wallet address that will be granted ownership of deposited tokens. Keep in mind /// that deposit can be made by Router contract but the owner of the deposit should be user. /// @param _amount deposit amount /// @param _collateralOnly true if deposit should be used for collateral only. Otherwise false. /// Collateral only deposit cannot be borrowed by anyone and does not earn any interest. However, /// it can be used as collateral and can be subject to liquidation. /// @return collateralAmount deposited amount /// @return collateralShare `_depositor` collateral shares based on deposited amount function _deposit( address _asset, address _from, address _depositor, uint256 _amount, bool _collateralOnly ) internal nonReentrant validateMaxDepositsAfter(_asset) returns (uint256 collateralAmount, uint256 collateralShare) { // MUST BE CALLED AS FIRST METHOD! _accrueInterest(_asset); if (!depositPossible(_asset, _depositor)) revert DepositNotPossible(); AssetStorage storage _state = _assetStorage[_asset]; collateralAmount = _amount; uint256 totalDepositsCached = _collateralOnly ? _state.collateralOnlyDeposits : _state.totalDeposits; if (_collateralOnly) { collateralShare = _amount.toShare(totalDepositsCached, _state.collateralOnlyToken.totalSupply()); _state.collateralOnlyDeposits = totalDepositsCached + _amount; _state.collateralOnlyToken.mint(_depositor, collateralShare); } else { collateralShare = _amount.toShare(totalDepositsCached, _state.collateralToken.totalSupply()); _state.totalDeposits = totalDepositsCached + _amount; _state.collateralToken.mint(_depositor, collateralShare); } ERC20(_asset).safeTransferFrom(_from, address(this), _amount); emit Deposit(_asset, _depositor, _amount, _collateralOnly); } /// @dev Main withdraw function that handles all withdraw logic and validation /// @param _asset asset address that is being withdrawn /// @param _depositor wallet address that is an owner of the deposited tokens /// @param _receiver wallet address that will receive withdrawn tokens. It's possible that Router /// contract is the owner of deposited tokens but we want user to get these tokens directly. /// @param _amount amount to withdraw. If amount is equal to maximum value stored by uint256 type /// (type(uint256).max), it will be assumed that user wants to withdraw all tokens and final account /// will be dynamically calculated including interest. /// @param _collateralOnly true if collateral only tokens are to be withdrawn. Otherwise false. /// User can deposit the same asset as collateral only and as regular deposit. During withdraw, /// it must be specified which tokens are to be withdrawn. /// @return withdrawnAmount withdrawn amount that was transferred to user /// @return withdrawnShare burned share based on `withdrawnAmount` function _withdraw(address _asset, address _depositor, address _receiver, uint256 _amount, bool _collateralOnly) internal nonReentrant // because we transferring tokens onlyExistingAsset(_asset) returns (uint256 withdrawnAmount, uint256 withdrawnShare) { // MUST BE CALLED AS FIRST METHOD! _accrueInterest(_asset); (withdrawnAmount, withdrawnShare) = _withdrawAsset( _asset, _amount, _depositor, _receiver, _collateralOnly, 0 // do not apply any fees on regular withdraw ); if (withdrawnAmount == 0) revert UnexpectedEmptyReturn(); if (!isSolvent(_depositor)) revert NotSolvent(); emit Withdraw(_asset, _depositor, _receiver, withdrawnAmount, _collateralOnly); } /// @dev Main borrow function that handles all borrow logic and validation /// @param _asset asset address that is being borrowed /// @param _borrower wallet address that will own debt /// @param _receiver wallet address that will receive borrowed tokens. It's possible that Router /// contract is executing borrowing for user and should be the one receiving tokens, however, /// the owner of the debt should be user himself. /// @param _amount amount of asset to borrow /// @return debtAmount borrowed amount /// @return debtShare user debt share based on borrowed amount function _borrow(address _asset, address _borrower, address _receiver, uint256 _amount) internal nonReentrant returns (uint256 debtAmount, uint256 debtShare) { // MUST BE CALLED AS FIRST METHOD! _accrueInterest(_asset); if (!borrowPossible(_asset, _borrower)) revert BorrowNotPossible(); if (liquidity(_asset) < _amount) revert NotEnoughLiquidity(); AssetStorage storage _state = _assetStorage[_asset]; uint256 totalBorrowAmount = _state.totalBorrowAmount; uint256 entryFee = siloRepository.entryFee(); uint256 fee = entryFee == 0 ? 0 : _amount * entryFee / Solvency._PRECISION_DECIMALS; debtShare = (_amount + fee).toShareRoundUp(totalBorrowAmount, _state.debtToken.totalSupply()); debtAmount = _amount; _state.totalBorrowAmount = totalBorrowAmount + _amount + fee; _interestData[_asset].protocolFees += fee; _state.debtToken.mint(_borrower, debtShare); emit Borrow(_asset, _borrower, _amount); ERC20(_asset).safeTransfer(_receiver, _amount); // IMPORTANT - keep `validateBorrowAfter` at the end _validateBorrowAfter(_borrower); } /// @dev Main repay function that handles all repay logic and validation /// @param _asset asset address that is being repaid /// @param _borrower wallet address for which debt is being repaid /// @param _repayer wallet address that will pay the debt. It's possible that Router /// contract is executing repay for user and should be the one paying the debt. /// @param _amount amount of asset to repay /// @return repaidAmount amount repaid /// @return repaidShare burned debt share function _repay(address _asset, address _borrower, address _repayer, uint256 _amount) internal onlyExistingAsset(_asset) nonReentrant returns (uint256 repaidAmount, uint256 repaidShare) { // MUST BE CALLED AS FIRST METHOD! _accrueInterest(_asset); AssetStorage storage _state = _assetStorage[_asset]; (repaidAmount, repaidShare) = _calculateDebtAmountAndShare(_state, _borrower, _amount); if (repaidShare == 0) revert UnexpectedEmptyReturn(); emit Repay(_asset, _borrower, repaidAmount); ERC20(_asset).safeTransferFrom(_repayer, address(this), repaidAmount); // change debt state before, because share token state is changes the same way (notification is after burn) _state.totalBorrowAmount -= repaidAmount; _state.debtToken.burn(_borrower, repaidShare); } /// @param _assets all current assets, this is an optimization, so we don't have to read it from storage few times /// @param _user user to liquidate /// @param _flashReceiver address which will get all collaterals and will be notified once collaterals will be send /// @param _flashReceiverData custom data to forward to receiver /// @return receivedCollaterals amounts of collaterals transferred to `_flashReceiver` /// @return shareAmountsToRepay expected amounts to repay function _userLiquidation( address[] memory _assets, address _user, IFlashLiquidationReceiver _flashReceiver, bytes memory _flashReceiverData ) internal // we can not use `nonReentrant` because we are using it in `_repay`, // and `_repay` needs to be reentered as part of a liquidation liquidationNonReentrant returns (uint256[] memory receivedCollaterals, uint256[] memory shareAmountsToRepay) { // gracefully fail if _user is solvent if (isSolvent(_user)) { uint256[] memory empty = new uint256[](_assets.length); return (empty, empty); } (receivedCollaterals, shareAmountsToRepay) = _flashUserLiquidation(_assets, _user, address(_flashReceiver)); // _flashReceiver needs to repayFor user _flashReceiver.siloLiquidationCallback( _user, _assets, receivedCollaterals, shareAmountsToRepay, _flashReceiverData ); for (uint256 i = 0; i < _assets.length; i++) { if (receivedCollaterals[i] != 0 || shareAmountsToRepay[i] != 0) { emit Liquidate(_assets[i], _user, shareAmountsToRepay[i], receivedCollaterals[i]); } } if (!isSolvent(_user)) revert NotSolvent(); } function _flashUserLiquidation(address[] memory _assets, address _borrower, address _liquidator) internal returns (uint256[] memory receivedCollaterals, uint256[] memory amountsToRepay) { uint256 assetsLength = _assets.length; receivedCollaterals = new uint256[](assetsLength); amountsToRepay = new uint256[](assetsLength); uint256 protocolLiquidationFee = siloRepository.protocolLiquidationFee(); for (uint256 i = 0; i < assetsLength; i++) { _accrueInterest(_assets[i]); AssetStorage storage _state = _assetStorage[_assets[i]]; // we do not allow for partial repayment on liquidation, that's why max (amountsToRepay[i],) = _calculateDebtAmountAndShare(_state, _borrower, type(uint256).max); (uint256 withdrawnOnlyAmount,) = _withdrawAsset( _assets[i], type(uint256).max, _borrower, _liquidator, true, // collateral only protocolLiquidationFee ); (uint256 withdrawnAmount,) = _withdrawAsset( _assets[i], type(uint256).max, _borrower, _liquidator, false, // collateral only protocolLiquidationFee ); receivedCollaterals[i] = withdrawnOnlyAmount + withdrawnAmount; } } /// @dev Utility function for withdrawing an asset /// @param _asset asset to withdraw /// @param _assetAmount amount of asset to withdraw /// @param _depositor wallet address that is an owner of the deposit /// @param _receiver wallet address that is receiving the token /// @param _collateralOnly true if withdraw collateral only. /// @param _protocolLiquidationFee if provided (!=0) liquidation fees will be applied and returned /// `withdrawnAmount` will be decreased /// @return withdrawnAmount amount of asset that has been sent to receiver /// @return burnedShare burned share based on `withdrawnAmount` function _withdrawAsset( address _asset, uint256 _assetAmount, address _depositor, address _receiver, bool _collateralOnly, uint256 _protocolLiquidationFee ) internal returns (uint256 withdrawnAmount, uint256 burnedShare) { (uint256 assetTotalDeposits, IShareToken shareToken, uint256 availableLiquidity) = _getWithdrawAssetData(_asset, _collateralOnly); if (_assetAmount == type(uint256).max) { burnedShare = shareToken.balanceOf(_depositor); withdrawnAmount = burnedShare.toAmount(assetTotalDeposits, shareToken.totalSupply()); } else { burnedShare = _assetAmount.toShareRoundUp(assetTotalDeposits, shareToken.totalSupply()); withdrawnAmount = _assetAmount; } if (withdrawnAmount == 0) { // we can not revert here, because liquidation will fail when one of collaterals will be empty return (0, 0); } if (assetTotalDeposits < withdrawnAmount) revert NotEnoughDeposits(); unchecked { // can be unchecked because of the `if` above assetTotalDeposits -= withdrawnAmount; } uint256 amountToTransfer = _applyLiquidationFee(_asset, withdrawnAmount, _protocolLiquidationFee); if (availableLiquidity < amountToTransfer) revert NotEnoughLiquidity(); AssetStorage storage _state = _assetStorage[_asset]; if (_collateralOnly) { _state.collateralOnlyDeposits = assetTotalDeposits; } else { _state.totalDeposits = assetTotalDeposits; } shareToken.burn(_depositor, burnedShare); // in case token sent in fee-on-transfer type of token we do not care when withdrawing ERC20(_asset).safeTransfer(_receiver, amountToTransfer); } /// @notice Calculates liquidations fee and returns amount of asset transferred to liquidator /// @param _asset asset address /// @param _amount amount on which we will apply fee /// @param _protocolLiquidationFee liquidation fee in Solvency._PRECISION_DECIMALS /// @return change amount left after subtracting liquidation fee function _applyLiquidationFee(address _asset, uint256 _amount, uint256 _protocolLiquidationFee) internal returns (uint256 change) { if (_protocolLiquidationFee == 0) { return _amount; } uint256 liquidationFeeAmount; ( liquidationFeeAmount, _interestData[_asset].protocolFees ) = Solvency.calculateLiquidationFee(_interestData[_asset].protocolFees, _amount, _protocolLiquidationFee); unchecked { // if fees will not be higher than 100% this will not underflow, this is responsibility of siloRepository // in case we do underflow, we can expect liquidator reject tx because of too little change change = _amount - liquidationFeeAmount; } } /// @dev harvest protocol fees from particular asset /// @param _asset asset we want to harvest fees from /// @param _receiver address of fees receiver /// @return harvestedFees harvested fee function _harvestProtocolFees(address _asset, address _receiver) internal nonReentrant returns (uint256 harvestedFees) { AssetInterestData storage data = _interestData[_asset]; harvestedFees = data.protocolFees - data.harvestedProtocolFees; uint256 currentLiquidity = liquidity(_asset); if (harvestedFees > currentLiquidity) { harvestedFees = currentLiquidity; } if (harvestedFees == 0) { return 0; } unchecked { // This can't overflow because this addition is less than or equal to data.protocolFees data.harvestedProtocolFees += harvestedFees; } ERC20(_asset).safeTransfer(_receiver, harvestedFees); } /// @notice Accrue interest for asset /// @dev Silo Interest Rate Model implements dynamic interest rate that changes every second. Returned /// interest rate by the model is compounded rate so it can be used in math calculations as if it was /// static. Rate is calculated for the time range between last update and current timestamp. /// @param _asset address of the asset for which interest should be accrued /// @return accruedInterest total accrued interest function _accrueInterest(address _asset) internal returns (uint256 accruedInterest) { /// @dev `_accrueInterest` is called on every user action, including liquidation. It's enough to check /// if Silo is paused in this function. if (IGuardedLaunch(address(siloRepository)).isSiloPaused(address(this), _asset)) { revert Paused(); } AssetStorage storage _state = _assetStorage[_asset]; AssetInterestData storage _assetInterestData = _interestData[_asset]; uint256 lastTimestamp = _assetInterestData.interestRateTimestamp; // This is the first time, so we can return early and save some gas if (lastTimestamp == 0) { _assetInterestData.interestRateTimestamp = uint64(block.timestamp); return 0; } // Interest has already been accrued this block if (lastTimestamp == block.timestamp) { return 0; } uint256 rcomp = _getModel(_asset).getCompoundInterestRateAndUpdate(_asset, block.timestamp); uint256 protocolShareFee = siloRepository.protocolShareFee(); uint256 totalBorrowAmountCached = _state.totalBorrowAmount; uint256 protocolFeesCached = _assetInterestData.protocolFees; uint256 newProtocolFees; uint256 protocolShare; uint256 depositorsShare; accruedInterest = totalBorrowAmountCached * rcomp / Solvency._PRECISION_DECIMALS; unchecked { // If we overflow on multiplication it should not revert tx, we will get lower fees protocolShare = accruedInterest * protocolShareFee / Solvency._PRECISION_DECIMALS; newProtocolFees = protocolFeesCached + protocolShare; if (newProtocolFees < protocolFeesCached) { protocolShare = type(uint256).max - protocolFeesCached; newProtocolFees = type(uint256).max; } depositorsShare = accruedInterest - protocolShare; } // update contract state _state.totalBorrowAmount = totalBorrowAmountCached + accruedInterest; _state.totalDeposits = _state.totalDeposits + depositorsShare; _assetInterestData.protocolFees = newProtocolFees; _assetInterestData.interestRateTimestamp = uint64(block.timestamp); } /// @dev gets interest rates model object /// @param _asset asset for which to calculate interest rate /// @return IInterestRateModel interest rates model object function _getModel(address _asset) internal view returns (IInterestRateModel) { return IInterestRateModel(siloRepository.getInterestRateModel(address(this), _asset)); } /// @dev calculates amount to repay based on user shares, we do not apply virtual balances here, /// if needed, they need to be apply beforehand /// @param _state asset storage struct /// @param _borrower borrower address /// @param _amount proposed amount of asset to repay. Based on that,`repayShare` is calculated. /// @return amount amount of asset to repay /// @return repayShare amount of debt token representing debt ownership function _calculateDebtAmountAndShare(AssetStorage storage _state, address _borrower, uint256 _amount) internal view returns (uint256 amount, uint256 repayShare) { uint256 borrowerDebtShare = _state.debtToken.balanceOf(_borrower); uint256 debtTokenTotalSupply = _state.debtToken.totalSupply(); uint256 totalBorrowed = _state.totalBorrowAmount; uint256 maxAmount = borrowerDebtShare.toAmountRoundUp(totalBorrowed, debtTokenTotalSupply); if (_amount >= maxAmount) { amount = maxAmount; repayShare = borrowerDebtShare; } else { amount = _amount; repayShare = _amount.toShare(totalBorrowed, debtTokenTotalSupply); } } /// @dev verifies if user did not borrow more than allowed maximum function _validateBorrowAfter(address _user) private view { (address[] memory assets, AssetStorage[] memory assetsStates) = getAssetsWithState(); (uint256 userLTV, uint256 maximumAllowedLTV) = Solvency.calculateLTVs( Solvency.SolvencyParams( siloRepository, ISilo(address(this)), assets, assetsStates, _user ), Solvency.TypeofLTV.MaximumLTV ); if (userLTV > maximumAllowedLTV) revert MaximumLTVReached(); } function _getWithdrawAssetData(address _asset, bool _collateralOnly) private view returns(uint256 assetTotalDeposits, IShareToken shareToken, uint256 availableLiquidity) { AssetStorage storage _state = _assetStorage[_asset]; if (_collateralOnly) { assetTotalDeposits = _state.collateralOnlyDeposits; shareToken = _state.collateralOnlyToken; availableLiquidity = assetTotalDeposits; } else { assetTotalDeposits = _state.totalDeposits; shareToken = _state.collateralToken; availableLiquidity = liquidity(_asset); } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "./interfaces/ISilo.sol"; import "./lib/EasyMath.sol"; import "./BaseSilo.sol"; /// @title Silo /// @notice Silo is the main component of the protocol. It implements lending logic, manages and isolates /// risk, acts as a vault for assets, and performs liquidations. Each Silo is composed of the unique asset /// for which it was created (ie. UNI) and bridge assets (ie. ETH and SiloDollar). There may be multiple /// bridge assets at any given time. /// @dev Main Silo contact that inherits from Base contract. It implements all user/UI facing methods. /// @custom:security-contact [email protected] contract Silo is ISilo, BaseSilo { using SafeERC20 for ERC20; using EasyMath for uint256; constructor (ISiloRepository _repository, address _siloAsset, uint128 _version) BaseSilo(_repository, _siloAsset, _version) { // initial setup is done in BaseSilo, nothing to do here } /// @inheritdoc ISilo function deposit(address _asset, uint256 _amount, bool _collateralOnly) external override returns (uint256 collateralAmount, uint256 collateralShare) { return _deposit(_asset, msg.sender, msg.sender, _amount, _collateralOnly); } /// @inheritdoc ISilo function depositFor( address _asset, address _depositor, uint256 _amount, bool _collateralOnly ) external override returns (uint256 collateralAmount, uint256 collateralShare) { return _deposit(_asset, msg.sender, _depositor, _amount, _collateralOnly); } /// @inheritdoc ISilo function withdraw(address _asset, uint256 _amount, bool _collateralOnly) external override returns (uint256 withdrawnAmount, uint256 withdrawnShare) { return _withdraw(_asset, msg.sender, msg.sender, _amount, _collateralOnly); } /// @inheritdoc ISilo function withdrawFor(address _asset, address _depositor, address _receiver, uint256 _amount, bool _collateralOnly) external override onlyRouter returns (uint256 withdrawnAmount, uint256 withdrawnShare) { return _withdraw(_asset, _depositor, _receiver, _amount, _collateralOnly); } /// @inheritdoc ISilo function borrow(address _asset, uint256 _amount) external override returns (uint256 debtAmount, uint256 debtShare) { return _borrow(_asset, msg.sender, msg.sender, _amount); } /// @inheritdoc ISilo function borrowFor(address _asset, address _borrower, address _receiver, uint256 _amount) external override onlyRouter returns (uint256 debtAmount, uint256 debtShare) { return _borrow(_asset, _borrower, _receiver, _amount); } /// @inheritdoc ISilo function repay(address _asset, uint256 _amount) external override returns (uint256 repaidAmount, uint256 repaidShare) { return _repay(_asset, msg.sender, msg.sender, _amount); } /// @inheritdoc ISilo function repayFor(address _asset, address _borrower, uint256 _amount) external override returns (uint256 repaidAmount, uint256 repaidShare) { return _repay(_asset, _borrower, msg.sender, _amount); } /// @inheritdoc ISilo function flashLiquidate(address[] memory _users, bytes memory _flashReceiverData) external override returns ( address[] memory assets, uint256[][] memory receivedCollaterals, uint256[][] memory shareAmountsToRepay ) { assets = getAssets(); uint256 usersLength = _users.length; receivedCollaterals = new uint256[][](usersLength); shareAmountsToRepay = new uint256[][](usersLength); for (uint256 i = 0; i < usersLength; i++) { ( receivedCollaterals[i], shareAmountsToRepay[i] ) = _userLiquidation(assets, _users[i], IFlashLiquidationReceiver(msg.sender), _flashReceiverData); } } /// @inheritdoc ISilo function harvestProtocolFees() external override returns (uint256[] memory harvestedAmounts) { address[] memory assets = getAssets(); harvestedAmounts = new uint256[](assets.length); address repositoryOwner = siloRepository.owner(); for (uint256 i; i < assets.length;) { unchecked { // it will not overflow because fee is much lower than any other amounts harvestedAmounts[i] = _harvestProtocolFees(assets[i], repositoryOwner); // we run out of gas before we overflow i i++; } } } /// @inheritdoc ISilo function accrueInterest(address _asset) public override returns (uint256 interest) { return _accrueInterest(_asset); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "./interfaces/IWrappedNativeToken.sol"; import "./interfaces/ISilo.sol"; import "./interfaces/ISiloRepository.sol"; import "./lib/Ping.sol"; import "./lib/TokenHelper.sol"; import "./lib/EasyMath.sol"; /// @title SiloRouter /// @notice Silo Router is a utility contract that aims to improve UX. It can batch any number or combination /// of actions (Deposit, Withdraw, Borrow, Repay) and execute them in a single transaction. /// @dev SiloRouter requires only first action asset to be approved /// @custom:security-contact [email protected] contract SiloRouter is ReentrancyGuard { using SafeERC20 for IERC20; using EasyMath for uint256; // @notice Action types that are supported enum ActionType { Deposit, Withdraw, Borrow, Repay } struct Action { // what do you want to do? ActionType actionType; // which Silo are you interacting with? ISilo silo; // what asset do you want to use? IERC20 asset; // how much asset do you want to use? uint256 amount; // is it an action on collateral only? bool collateralOnly; } // @dev native asset wrapped token. In case of Ether, it's WETH. IWrappedNativeToken public immutable wrappedNativeToken; ISiloRepository public immutable siloRepository; error ApprovalFailed(); error ERC20TransferFailed(); error EthTransferFailed(); error InvalidSilo(); error InvalidSiloRepository(); error UnsupportedAction(); constructor (address _wrappedNativeToken, address _siloRepository) { if (!Ping.pong(ISiloRepository(_siloRepository).siloRepositoryPing)) { revert InvalidSiloRepository(); } TokenHelper.assertAndGetDecimals(_wrappedNativeToken); wrappedNativeToken = IWrappedNativeToken(_wrappedNativeToken); siloRepository = ISiloRepository(_siloRepository); } /// @dev needed for unwrapping WETH receive() external payable { // `execute` method calls `IWrappedNativeToken.withdraw()` // and we need to receive the withdrawn ETH unconditionally } /// @notice Execute actions /// @dev User can bundle any combination and number of actions. It's possible to do multiple deposits, /// withdraws etc. For that reason router may need to send multiple tokens back to the user. Combining /// Ether and WETH deposits will make this function revert. /// @param _actions array of actions to execute function execute(Action[] calldata _actions) external payable nonReentrant { uint256 len = _actions.length; // execute actions for (uint256 i = 0; i < len; i++) { _executeAction(_actions[i]); } // send all assets to user for (uint256 i = 0; i < len; i++) { uint256 remainingBalance = _actions[i].asset.balanceOf(address(this)); if (remainingBalance != 0) { _sendAsset(_actions[i].asset, remainingBalance); } } // should never have leftover ETH, however if (msg.value != 0 && address(this).balance != 0) { // solhint-disable-next-line avoid-low-level-calls (bool success, ) = msg.sender.call{value: address(this).balance}(""); if (!success) revert EthTransferFailed(); } } function siloRouterPing() external pure returns (bytes4) { return this.siloRouterPing.selector; } /// @dev Execute actions /// @param _action action to execute, this can be one of many actions in the whole flow // solhint-disable-next-line code-complexity function _executeAction(Action calldata _action) internal { if (!siloRepository.isSilo(address(_action.silo))) revert InvalidSilo(); if (_action.actionType == ActionType.Deposit) { _pullAssetIfNeeded(_action.asset, _action.amount); _approveIfNeeded(_action.asset, address(_action.silo), _action.amount); _action.silo.depositFor(address(_action.asset), msg.sender, _action.amount, _action.collateralOnly); } else if (_action.actionType == ActionType.Withdraw) { _action.silo.withdrawFor( address(_action.asset), msg.sender, address(this), _action.amount, _action.collateralOnly ); } else if (_action.actionType == ActionType.Borrow) { _action.silo.borrowFor(address(_action.asset), msg.sender, address(this), _action.amount); } else if (_action.actionType == ActionType.Repay) { uint256 repayAmount; if (_action.amount == type(uint256).max) { _action.silo.accrueInterest(address(_action.asset)); repayAmount = _getRepayAmount(_action.silo, _action.asset, msg.sender); } else { repayAmount = _action.amount; } _pullAssetIfNeeded(_action.asset, repayAmount); _approveIfNeeded(_action.asset, address(_action.silo), repayAmount); _action.silo.repayFor(address(_action.asset), msg.sender, repayAmount); } else { revert UnsupportedAction(); } } /// @dev Approve Silo to transfer token if current allowance is not enough /// @param _asset token to be approved /// @param _spender Silo address that spends the token /// @param _amount amount of token to be spent function _approveIfNeeded( IERC20 _asset, address _spender, uint256 _amount ) internal { if (_asset.allowance(address(this), _spender) < _amount) { // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory data) = address(_asset).call( abi.encodeCall(IERC20.approve, (_spender, type(uint256).max)) ); // Support non-standard tokens that don't return bool if(!success || !(data.length == 0 || abi.decode(data, (bool)))) { revert ApprovalFailed(); } } } /// @dev Transfer funds from msg.sender to this contract if balance is not enough /// @param _asset token to be approved /// @param _amount amount of token to be spent function _pullAssetIfNeeded(IERC20 _asset, uint256 _amount) internal { uint256 remainingBalance = _asset.balanceOf(address(this)); if (remainingBalance < _amount) { // There can't be an underflow in the subtraction because of the previous check unchecked { _pullAsset(_asset, _amount - remainingBalance); } } } /// @dev Transfer asset from user to router /// @param _asset asset address to be transferred /// @param _amount amount of asset to be transferred function _pullAsset(IERC20 _asset, uint256 _amount) internal { if (msg.value != 0 && _asset == wrappedNativeToken) { wrappedNativeToken.deposit{value: _amount}(); } else { _asset.safeTransferFrom(msg.sender, address(this), _amount); } } /// @dev Transfer asset from router to user /// @param _asset asset address to be transferred /// @param _amount amount of asset to be transferred function _sendAsset(IERC20 _asset, uint256 _amount) internal { if (address(_asset) == address(wrappedNativeToken)) { wrappedNativeToken.withdraw(_amount); // solhint-disable-next-line avoid-low-level-calls (bool success, ) = msg.sender.call{value: _amount}(""); if (!success) revert ERC20TransferFailed(); } else { _asset.safeTransfer(msg.sender, _amount); } } /// @dev Helper that calculates the maximum amount to repay if type(uint256).max is passed /// @param _silo silo for which the debt will be repaid /// @param _asset asset being repaid /// @param _borrower user for which the debt being repaid function _getRepayAmount(ISilo _silo, IERC20 _asset, address _borrower) internal view returns(uint256) { ISilo.AssetStorage memory _assetStorage = _silo.assetStorage(address(_asset)); uint256 repayShare = _assetStorage.debtToken.balanceOf(_borrower); uint256 debtTokenTotalSupply = _assetStorage.debtToken.totalSupply(); uint256 totalBorrowed = _assetStorage.totalBorrowAmount; return repayShare.toAmountRoundUp(totalBorrowed, debtTokenTotalSupply); } }
// 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 IGuardedLaunch { /// @dev Stores info about maximum allowed liquidity in a Silo. This limit applies to deposit only. struct MaxLiquidityLimit { /// @dev flag to turn on/off all limits for all Silos bool globalLimit; /// @dev default value represents maximum allowed liquidity in Silo uint256 defaultMaxLiquidity; /// @notice siloMaxLiquidity maps silo => asset => maximum allowed deposit liquidity. /// @dev Deposit liquidity limit is denominated in quote token. For example, if set to 1e18, it means that any /// given Silo is allowed for deposits up to 1 quote token of value. Value is calculated using prices from the /// Oracle. mapping(address => mapping(address => uint256)) siloMaxLiquidity; } /// @dev Stores info about paused Silos /// if `globalPause` == `true`, all Silo are paused /// if `globalPause` == `false` and `siloPause[silo][0x0]` == `true`, all assets in a `silo` are paused /// if `globalPause` == `false` and `siloPause[silo][asset]` == `true`, only `asset` in a `silo` is paused struct Paused { bool globalPause; /// @dev maps silo address to asset address to bool mapping(address => mapping(address => bool)) siloPause; } /// @notice Emitted when all Silos are paused or unpaused /// @param globalPause current value of `globalPause` event GlobalPause(bool globalPause); /// @notice Emitted when a single Silo or single asset in a Silo is paused or unpaused /// @param silo address of Silo which is paused /// @param asset address of an asset which is paused /// @param pauseValue true when paused, otherwise false event SiloPause(address silo, address asset, bool pauseValue); /// @notice Emitted when max liquidity toggle is switched /// @param newLimitedMaxLiquidityState new value for max liquidity toggle event LimitedMaxLiquidityToggled(bool newLimitedMaxLiquidityState); /// @notice Emitted when deposit liquidity limit is changed for Silo and asset /// @param silo Silo address for which to set limit /// @param asset Silo asset for which to set limit /// @param newMaxDeposits deposit limit amount in quote token event SiloMaxDepositsLimitsUpdate(address indexed silo, address indexed asset, uint256 newMaxDeposits); /// @notice Emitted when default max liquidity limit is changed /// @param newMaxDeposits new deposit limit in quote token event DefaultSiloMaxDepositsLimitUpdate(uint256 newMaxDeposits); /// @notice Sets limited liquidity to provided value function setLimitedMaxLiquidity(bool _globalLimit) external; /// @notice Sets default deposit limit for all Silos /// @param _maxDeposits deposit limit amount in quote token function setDefaultSiloMaxDepositsLimit(uint256 _maxDeposits) external; /// @notice Sets deposit limit for Silo /// @param _silo Silo address for which to set limit /// @param _asset Silo asset for which to set limit /// @param _maxDeposits deposit limit amount in quote token function setSiloMaxDepositsLimit( address _silo, address _asset, uint256 _maxDeposits ) external; /// @notice Pause all Silos /// @dev Callable only by owner. /// @param _globalPause true to pause all Silos, otherwise false function setGlobalPause(bool _globalPause) external; /// @notice Pause single asset in a single Silo /// @dev Callable only by owner. /// @param _silo address of Silo in which `_asset` is being paused /// @param _asset address of an asset that is being paused /// @param _pauseValue true to pause, false to unpause function setSiloPause(address _silo, address _asset, bool _pauseValue) external; /// @notice Check given asset in a Silo is paused /// @param _silo address of Silo /// @param _asset address of an asset /// @return true if given asset in a Silo is paused, otherwise false function isSiloPaused(address _silo, address _asset) external view returns (bool); /// @notice Gets deposit limit for Silo /// @param _silo Silo address for which to set limit /// @param _asset Silo asset for which to set limit /// @return deposit limit for Silo function getMaxSiloDepositsValue(address _silo, address _asset) external view returns (uint256); }
// 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.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: 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.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.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 Solvency { 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 `Solvency.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 Solvency.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 `Solvency.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 `Solvency.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 `Solvency.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 `Solvency.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, _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 _protocolShareFee `siloRepository.protocolShareFee()` /// @param _rcomp compounded interest rate /// @return _totalDepositsWithInterests total deposits amount with interest function totalDepositsWithInterest(uint256 _assetTotalDeposits, uint256 _protocolShareFee, uint256 _rcomp) internal pure returns (uint256 _totalDepositsWithInterests) { uint256 depositorsShare = _PRECISION_DECIMALS - _protocolShareFee; return _assetTotalDeposits + _assetTotalDeposits * _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 Solvency._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 / Solvency._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: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; library TokenHelper { uint256 private constant _BYTES32_SIZE = 32; error TokenIsNotAContract(); function assertAndGetDecimals(address _token) internal view returns (uint256) { (bool hasMetadata, bytes memory data) = _tokenMetadataCall(_token, abi.encodeCall(IERC20Metadata.decimals,())); // decimals() is optional in the ERC20 standard, so if metadata is not accessible // we assume there are no decimals and use 0. if (!hasMetadata) { return 0; } return abi.decode(data, (uint8)); } /// @dev Returns the symbol for the provided ERC20 token. /// An empty string is returned if the call to the token didn't succeed. /// @param _token address of the token to get the symbol for /// @return assetSymbol the token symbol function symbol(address _token) internal view returns (string memory assetSymbol) { (bool hasMetadata, bytes memory data) = _tokenMetadataCall(_token, abi.encodeCall(IERC20Metadata.symbol,())); if (!hasMetadata || data.length == 0) { return "?"; } else if (data.length == _BYTES32_SIZE) { return string(removeZeros(data)); } else { return abi.decode(data, (string)); } } /// @dev Removes bytes with value equal to 0 from the provided byte array. /// @param _data byte array from which to remove zeroes /// @return result byte array with zeroes removed function removeZeros(bytes memory _data) internal pure returns (bytes memory result) { uint256 n = _data.length; unchecked { for (uint256 i; i < n; i++) { if (_data[i] == 0) continue; result = abi.encodePacked(result, _data[i]); } } } /// @dev Performs a staticcall to the token to get its metadata (symbol, decimals, name) function _tokenMetadataCall(address _token, bytes memory _data) private view returns(bool, bytes memory) { // We need to do this before the call, otherwise the call will succeed even for EOAs if (!Address.isContract(_token)) revert TokenIsNotAContract(); (bool success, bytes memory result) = _token.staticcall(_data); // If the call reverted we assume the token doesn't follow the metadata extension if (!success) { return (false, ""); } return (true, result); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "../interfaces/IGuardedLaunch.sol"; import "./TwoStepOwnable.sol"; import "./Manageable.sol"; /// @title GuardedLaunch /// @notice Implements security and risk averse functions for Silo /// @dev This contract is meant to limit Silo functionality for Beta Release in order to minimize any damage /// of potential critical vulnerability. /// @custom:security-contact [email protected] contract GuardedLaunch is IGuardedLaunch, TwoStepOwnable, Manageable { uint256 private constant _INFINITY = type(uint256).max; /// @dev Initial value for defaultMaxLiquidity is 250 quote tokens uint256 private constant _INITIAL_MAX_LIQUIDITY = 250 * 1e18; /// @dev stores max liquidity config MaxLiquidityLimit public maxLiquidity; /// @dev stores pause config Paused public isPaused; error GlobalLimitDidNotChange(); error GlobalPauseDidNotChange(); error MaxLiquidityDidNotChange(); error SiloMaxLiquidityDidNotChange(); error SiloPauseDidNotChange(); constructor() Manageable(msg.sender) { maxLiquidity.globalLimit = true; maxLiquidity.defaultMaxLiquidity = _INITIAL_MAX_LIQUIDITY; } /// @inheritdoc IGuardedLaunch function setLimitedMaxLiquidity(bool _globalLimit) external onlyManager override { if (maxLiquidity.globalLimit == _globalLimit) revert GlobalLimitDidNotChange(); maxLiquidity.globalLimit = _globalLimit; emit LimitedMaxLiquidityToggled(maxLiquidity.globalLimit); } /// @inheritdoc IGuardedLaunch function setDefaultSiloMaxDepositsLimit(uint256 _maxDeposits) external onlyManager override { if (maxLiquidity.defaultMaxLiquidity == _maxDeposits) { revert MaxLiquidityDidNotChange(); } maxLiquidity.defaultMaxLiquidity = _maxDeposits; emit DefaultSiloMaxDepositsLimitUpdate(_maxDeposits); } /// @inheritdoc IGuardedLaunch function setSiloMaxDepositsLimit( address _silo, address _asset, uint256 _maxDeposits ) external onlyManager override { if (maxLiquidity.siloMaxLiquidity[_silo][_asset] == _maxDeposits) { revert SiloMaxLiquidityDidNotChange(); } maxLiquidity.siloMaxLiquidity[_silo][_asset] = _maxDeposits; emit SiloMaxDepositsLimitsUpdate(_silo, _asset, _maxDeposits); } /// @inheritdoc IGuardedLaunch function setGlobalPause(bool _globalPause) external onlyManager override { if (isPaused.globalPause == _globalPause) revert GlobalPauseDidNotChange(); isPaused.globalPause = _globalPause; emit GlobalPause(_globalPause); } /// @inheritdoc IGuardedLaunch function setSiloPause(address _silo, address _asset, bool _pauseValue) external onlyManager override { if (isPaused.siloPause[_silo][_asset] == _pauseValue) { revert SiloPauseDidNotChange(); } isPaused.siloPause[_silo][_asset] = _pauseValue; emit SiloPause(_silo, _asset, _pauseValue); } /// @inheritdoc IGuardedLaunch function isSiloPaused(address _silo, address _asset) external view override returns (bool) { return isPaused.globalPause || isPaused.siloPause[_silo][address(0)] || isPaused.siloPause[_silo][_asset]; } /// @inheritdoc IGuardedLaunch function getMaxSiloDepositsValue(address _silo, address _asset) external view override returns (uint256) { if (maxLiquidity.globalLimit) { uint256 maxDeposits = maxLiquidity.siloMaxLiquidity[_silo][_asset]; if (maxDeposits != 0) { return maxDeposits; } return maxLiquidity.defaultMaxLiquidity; } return _INFINITY; } /// @dev Returns the address of the current owner. function owner() public view override(TwoStepOwnable, Manageable) virtual returns (address) { return TwoStepOwnable.owner(); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; /// @dev This is cloned solution of @openzeppelin/contracts/security/ReentrancyGuard.sol abstract contract LiquidationReentrancyGuard { error LiquidationReentrancyCall(); uint256 private constant _LIQUIDATION_NOT_ENTERED = 1; uint256 private constant _LIQUIDATION_ENTERED = 2; uint256 private _liquidationStatus; modifier liquidationNonReentrant() { if (_liquidationStatus == _LIQUIDATION_ENTERED) { revert LiquidationReentrancyCall(); } _liquidationStatus = _LIQUIDATION_ENTERED; _; _liquidationStatus = _LIQUIDATION_NOT_ENTERED; } constructor() { _liquidationStatus = _LIQUIDATION_NOT_ENTERED; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; /// @title Manageable /// @notice Implements simple manager role that can be changed by a manger or external owner role /// @dev This contract is designed to work with Ownable from openzeppelin /// @custom:security-contact [email protected] abstract contract Manageable { /// @notice wallet address of manager role address private _managerRole; /// @notice Emitted when manager is changed /// @param manager new manager address event ManagerChanged(address manager); error ManagerIsZero(); error OnlyManager(); error OnlyOwnerOrManager(); error ManagerDidNotChange(); modifier onlyManager() { if (_managerRole != msg.sender) revert OnlyManager(); _; } /// @param _manager new manager address constructor(address _manager) { if (_manager == address(0)) revert ManagerIsZero(); _managerRole = _manager; } /// @notice Change manager address /// @dev Callable by manager or external owner role /// @param _manager new manager address function changeManager(address _manager) external { if (msg.sender != owner() && msg.sender != _managerRole) { revert OnlyOwnerOrManager(); } if (_manager == address(0)) revert ManagerIsZero(); if (_manager == _managerRole) revert ManagerDidNotChange(); _managerRole = _manager; emit ManagerChanged(_manager); } function manager() public view virtual returns (address) { return _managerRole; } /// @notice Gets external owner role /// @return owner address function owner() public view virtual returns (address); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.7.6 <0.9.0; /// @title TwoStepOwnable /// @notice Contract that implements the same functionality as popular Ownable contract from openzeppelin library. /// The only difference is that it adds a possibility to transfer ownership in two steps. Single step ownership /// transfer is still supported. /// @dev Two step ownership transfer is meant to be used by humans to avoid human error. Single step ownership /// transfer is meant to be used by smart contracts to avoid over-complicated two step integration. For that reason, /// both ways are supported. abstract contract TwoStepOwnable { /// @dev current owner address private _owner; /// @dev candidate to an owner address private _pendingOwner; /// @notice Emitted when ownership is transferred on `transferOwnership` and `acceptOwnership` /// @param newOwner new owner event OwnershipTransferred(address indexed newOwner); /// @notice Emitted when ownership transfer is proposed, aka pending owner is set /// @param newPendingOwner new proposed/pending owner event OwnershipPending(address indexed newPendingOwner); /** * error OnlyOwner(); * error OnlyPendingOwner(); * error OwnerIsZero(); */ /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { if (owner() != msg.sender) revert("OnlyOwner"); _; } /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _setOwner(msg.sender); } /** * @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 { _setOwner(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 { if (newOwner == address(0)) revert("OwnerIsZero"); _setOwner(newOwner); } /** * @dev Transfers pending ownership of the contract to a new account (`newPendingOwner`) and clears any existing * pending ownership. * Can only be called by the current owner. */ function transferPendingOwnership(address newPendingOwner) public virtual onlyOwner { _setPendingOwner(newPendingOwner); } /** * @dev Clears the pending ownership. * Can only be called by the current owner. */ function removePendingOwnership() public virtual onlyOwner { _setPendingOwner(address(0)); } /** * @dev Transfers ownership of the contract to a pending owner * Can only be called by the pending owner. */ function acceptOwnership() public virtual { if (msg.sender != pendingOwner()) revert("OnlyPendingOwner"); _setOwner(pendingOwner()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Returns the address of the pending owner. */ function pendingOwner() public view virtual returns (address) { return _pendingOwner; } /** * @dev Sets the new owner and emits the corresponding event. */ function _setOwner(address newOwner) private { if (_owner == newOwner) revert("OwnerDidNotChange"); _owner = newOwner; emit OwnershipTransferred(newOwner); if (_pendingOwner != address(0)) { _setPendingOwner(address(0)); } } /** * @dev Sets the new pending owner and emits the corresponding event. */ function _setPendingOwner(address newPendingOwner) private { if (_pendingOwner == newPendingOwner) revert("PendingOwnerDidNotChange"); _pendingOwner = newPendingOwner; emit OwnershipPending(newPendingOwner); } }
{ "evmVersion": "london", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 200 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_siloFactory","type":"address"},{"internalType":"address","name":"_tokensFactory","type":"address"},{"internalType":"uint64","name":"_defaultMaxLTV","type":"uint64"},{"internalType":"uint64","name":"_defaultLiquidationThreshold","type":"uint64"},{"internalType":"address[]","name":"_initialBridgeAssets","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AssetAlreadyAdded","type":"error"},{"inputs":[],"name":"AssetIsNotABridge","type":"error"},{"inputs":[],"name":"AssetIsZero","type":"error"},{"inputs":[],"name":"BridgeAssetIsZero","type":"error"},{"inputs":[],"name":"ConfigDidNotChange","type":"error"},{"inputs":[],"name":"EmptyBridgeAssets","type":"error"},{"inputs":[],"name":"FeesDidNotChange","type":"error"},{"inputs":[],"name":"GlobalLimitDidNotChange","type":"error"},{"inputs":[],"name":"GlobalPauseDidNotChange","type":"error"},{"inputs":[],"name":"InterestRateModelDidNotChange","type":"error"},{"inputs":[],"name":"InvalidEntryFee","type":"error"},{"inputs":[],"name":"InvalidInterestRateModel","type":"error"},{"inputs":[],"name":"InvalidLTV","type":"error"},{"inputs":[],"name":"InvalidLiquidationThreshold","type":"error"},{"inputs":[],"name":"InvalidNotificationReceiver","type":"error"},{"inputs":[],"name":"InvalidPriceProvidersRepository","type":"error"},{"inputs":[],"name":"InvalidProtocolLiquidationFee","type":"error"},{"inputs":[],"name":"InvalidProtocolShareFee","type":"error"},{"inputs":[],"name":"InvalidSiloFactory","type":"error"},{"inputs":[],"name":"InvalidSiloRouter","type":"error"},{"inputs":[],"name":"InvalidSiloVersion","type":"error"},{"inputs":[],"name":"InvalidTokensFactory","type":"error"},{"inputs":[],"name":"LastBridgeAsset","type":"error"},{"inputs":[],"name":"LiquidationThresholdDidNotChange","type":"error"},{"inputs":[],"name":"ManagerDidNotChange","type":"error"},{"inputs":[],"name":"ManagerIsZero","type":"error"},{"inputs":[],"name":"MaxLiquidityDidNotChange","type":"error"},{"inputs":[],"name":"MaximumLTVDidNotChange","type":"error"},{"inputs":[],"name":"NoPriceProviderForAsset","type":"error"},{"inputs":[],"name":"NotificationReceiverDidNotChange","type":"error"},{"inputs":[],"name":"OnlyManager","type":"error"},{"inputs":[],"name":"OnlyOwnerOrManager","type":"error"},{"inputs":[],"name":"PriceProviderRepositoryDidNotChange","type":"error"},{"inputs":[],"name":"RouterDidNotChange","type":"error"},{"inputs":[],"name":"SiloAlreadyExistsForAsset","type":"error"},{"inputs":[],"name":"SiloAlreadyExistsForBridgeAssets","type":"error"},{"inputs":[],"name":"SiloDoesNotExist","type":"error"},{"inputs":[],"name":"SiloIsZero","type":"error"},{"inputs":[],"name":"SiloMaxLiquidityDidNotChange","type":"error"},{"inputs":[],"name":"SiloNotAllowedForBridgeAsset","type":"error"},{"inputs":[],"name":"SiloPauseDidNotChange","type":"error"},{"inputs":[],"name":"SiloVersionDoesNotExist","type":"error"},{"inputs":[],"name":"TokenIsNotAContract","type":"error"},{"inputs":[],"name":"VersionForAssetDidNotChange","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"silo","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"components":[{"internalType":"uint64","name":"maxLoanToValue","type":"uint64"},{"internalType":"uint64","name":"liquidationThreshold","type":"uint64"},{"internalType":"contract IInterestRateModel","name":"interestRateModel","type":"address"}],"indexed":false,"internalType":"struct ISiloRepository.AssetConfig","name":"assetConfig","type":"tuple"}],"name":"AssetConfigUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newBridgeAsset","type":"address"}],"name":"BridgeAssetAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"bridgeAssetRemoved","type":"address"}],"name":"BridgeAssetRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"BridgePool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMaxDeposits","type":"uint256"}],"name":"DefaultSiloMaxDepositsLimitUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"newEntryFee","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"newProtocolShareFee","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"newProtocolLiquidationFee","type":"uint64"}],"name":"FeeUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"globalPause","type":"bool"}],"name":"GlobalPause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IInterestRateModel","name":"newModel","type":"address"}],"name":"InterestRateModel","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"newLimitedMaxLiquidityState","type":"bool"}],"name":"LimitedMaxLiquidityToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"manager","type":"address"}],"name":"ManagerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"defaultLiquidationThreshold","type":"uint64"}],"name":"NewDefaultLiquidationThreshold","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"defaultMaximumLTV","type":"uint64"}],"name":"NewDefaultMaximumLTV","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"silo","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint128","name":"siloVersion","type":"uint128"}],"name":"NewSilo","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract INotificationReceiver","name":"newIncentiveContract","type":"address"}],"name":"NotificationReceiverUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newPendingOwner","type":"address"}],"name":"OwnershipPending","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IPriceProvidersRepository","name":"newProvider","type":"address"}],"name":"PriceProvidersRepositoryUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"},{"indexed":false,"internalType":"uint128","name":"siloLatestVersion","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"siloDefaultVersion","type":"uint128"}],"name":"RegisterSiloVersion","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newRouter","type":"address"}],"name":"RouterUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"newDefaultVersion","type":"uint128"}],"name":"SiloDefaultVersion","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"silo","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"newMaxDeposits","type":"uint256"}],"name":"SiloMaxDepositsLimitsUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"silo","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"bool","name":"pauseValue","type":"bool"}],"name":"SiloPause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newTokensFactory","type":"address"}],"name":"TokensFactoryUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"},{"indexed":false,"internalType":"uint128","name":"siloVersion","type":"uint128"}],"name":"UnregisterSiloVersion","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint128","name":"version","type":"uint128"}],"name":"VersionForAsset","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newBridgeAsset","type":"address"}],"name":"addBridgeAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"assetConfigs","outputs":[{"internalType":"uint64","name":"maxLoanToValue","type":"uint64"},{"internalType":"uint64","name":"liquidationThreshold","type":"uint64"},{"internalType":"contract IInterestRateModel","name":"interestRateModel","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bridgePool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"}],"name":"changeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultAssetConfig","outputs":[{"internalType":"uint64","name":"maxLoanToValue","type":"uint64"},{"internalType":"uint64","name":"liquidationThreshold","type":"uint64"},{"internalType":"contract IInterestRateModel","name":"interestRateModel","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"bool","name":"_assetIsABridge","type":"bool"}],"name":"ensureCanCreateSiloFor","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"entryFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fees","outputs":[{"internalType":"uint64","name":"entryFee","type":"uint64"},{"internalType":"uint64","name":"protocolShareFee","type":"uint64"},{"internalType":"uint64","name":"protocolLiquidationFee","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBridgeAssets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_silo","type":"address"},{"internalType":"address","name":"_asset","type":"address"}],"name":"getInterestRateModel","outputs":[{"internalType":"contract IInterestRateModel","name":"model","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_silo","type":"address"},{"internalType":"address","name":"_asset","type":"address"}],"name":"getLiquidationThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_silo","type":"address"},{"internalType":"address","name":"_asset","type":"address"}],"name":"getMaxSiloDepositsValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_silo","type":"address"},{"internalType":"address","name":"_asset","type":"address"}],"name":"getMaximumLTV","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getNotificationReceiver","outputs":[{"internalType":"contract INotificationReceiver","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRemovedBridgeAssets","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getSilo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getVersionForAsset","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"globalPause","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_silo","type":"address"}],"name":"isSilo","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_silo","type":"address"},{"internalType":"address","name":"_asset","type":"address"}],"name":"isSiloPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLiquidity","outputs":[{"internalType":"bool","name":"globalLimit","type":"bool"},{"internalType":"uint256","name":"defaultMaxLiquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_siloAsset","type":"address"},{"internalType":"bytes","name":"_siloData","type":"bytes"}],"name":"newSilo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceProvidersRepository","outputs":[{"internalType":"contract IPriceProvidersRepository","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolLiquidationFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolShareFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISiloFactory","name":"_factory","type":"address"},{"internalType":"bool","name":"_isDefault","type":"bool"}],"name":"registerSiloVersion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_bridgeAssetToRemove","type":"address"}],"name":"removeBridgeAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"removePendingOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_siloAsset","type":"address"},{"internalType":"uint128","name":"_siloVersion","type":"uint128"},{"internalType":"bytes","name":"_siloData","type":"bytes"}],"name":"replaceSilo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_silo","type":"address"},{"internalType":"address","name":"_asset","type":"address"},{"components":[{"internalType":"uint64","name":"maxLoanToValue","type":"uint64"},{"internalType":"uint64","name":"liquidationThreshold","type":"uint64"},{"internalType":"contract IInterestRateModel","name":"interestRateModel","type":"address"}],"internalType":"struct ISiloRepository.AssetConfig","name":"_assetConfig","type":"tuple"}],"name":"setAssetConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IInterestRateModel","name":"_defaultInterestRateModel","type":"address"}],"name":"setDefaultInterestRateModel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_defaultLiquidationThreshold","type":"uint64"}],"name":"setDefaultLiquidationThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_defaultMaxLTV","type":"uint64"}],"name":"setDefaultMaximumLTV","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxDeposits","type":"uint256"}],"name":"setDefaultSiloMaxDepositsLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"_defaultVersion","type":"uint128"}],"name":"setDefaultSiloVersion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"entryFee","type":"uint64"},{"internalType":"uint64","name":"protocolShareFee","type":"uint64"},{"internalType":"uint64","name":"protocolLiquidationFee","type":"uint64"}],"internalType":"struct ISiloRepository.Fees","name":"_fees","type":"tuple"}],"name":"setFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_globalPause","type":"bool"}],"name":"setGlobalPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_globalLimit","type":"bool"}],"name":"setLimitedMaxLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_silo","type":"address"},{"internalType":"contract INotificationReceiver","name":"_newNotificationReceiver","type":"address"}],"name":"setNotificationReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPriceProvidersRepository","name":"_repository","type":"address"}],"name":"setPriceProvidersRepository","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_router","type":"address"}],"name":"setRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_silo","type":"address"},{"internalType":"address","name":"_asset","type":"address"},{"internalType":"uint256","name":"_maxDeposits","type":"uint256"}],"name":"setSiloMaxDepositsLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_silo","type":"address"},{"internalType":"address","name":"_asset","type":"address"},{"internalType":"bool","name":"_pauseValue","type":"bool"}],"name":"setSiloPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokensFactory","type":"address"}],"name":"setTokensFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_siloAsset","type":"address"},{"internalType":"uint128","name":"_version","type":"uint128"}],"name":"setVersionForAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"siloFactory","outputs":[{"internalType":"contract ISiloFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"siloRepositoryPing","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"siloReverse","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"siloVersion","outputs":[{"internalType":"uint128","name":"byDefault","type":"uint128"},{"internalType":"uint128","name":"latest","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokensFactory","outputs":[{"internalType":"contract ITokensFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPendingOwner","type":"address"}],"name":"transferPendingOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"_siloVersion","type":"uint128"}],"name":"unregisterSiloVersion","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b50604051620041f8380380620041f8833981016040819052620000349162000887565b33620000408162000495565b6001600160a01b0381166200006857604051639c774ebf60e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b03929092169190911790556003805460ff19166001179055680d8d726b7177a800006004556001600160401b03838116908316670de0b6b3a76400008110620000d85760405162f9474b60e61b815260040160405180910390fd5b811580620000e65750808210155b156200010557604051632f2a24a960e11b815260040160405180910390fd5b62000129876001600160a01b0316635a0c4de46200055260201b620028af1760201c565b6200014757604051637b64d2bb60e11b815260040160405180910390fd5b6200016b866001600160a01b0316636849100f6200055260201b620028af1760201c565b6200018957604051630307663d60e61b815260040160405180910390fd5b8251600003620001ac57604051631398613b60e31b815260040160405180910390fd5b6040516296777d60e31b81523060048201526001600160a01b038816906304b3bbe890602401600060405180830381600087803b158015620001ed57600080fd5b505af115801562000202573d6000803e3d6000fd5b50506040516296777d60e31b81523060048201526001600160a01b03891692506304b3bbe89150602401600060405180830381600087803b1580156200024757600080fd5b505af11580156200025c573d6000803e3d6000fd5b5050505060005b83518110156200033f576200029f848281518110620002865762000286620009a7565b6020026020010151620005de60201b620029351760201c565b50620002d7848281518110620002b957620002b9620009a7565b602002602001015160166200065560201b620029a21790919060201c565b50838181518110620002ed57620002ed620009a7565b60200260200101516001600160a01b03167fda0511750b81a254e0b084c2c863785e3d1c5b2b989afd8ac0e884f8ee2e033c60405160405180910390a2806200033681620009bd565b91505062000263565b50700100000000000000000000000000000001600f5560016000819052601260209081527f71a67924699a20698523213e55fe499d539379d7769cd5567e2c45d583f815a380546001600160a01b0319166001600160a01b038b169081179091556040805184815292830193909352917f3878761190f637385b15245770921955dbd3f3652f86d6bdfcb0a6afe81db04f910160405180910390a2604051600181527f723c9edacabd261ad9586f194e9d5409ac4b22c939d90a8db99556690018bfe09060200160405180910390a1600b80546001600160a01b0319166001600160a01b0388169081179091556040517f2cf3169753956d21755ee0c43a6802b18cb71131bae8405d5a0b97e919e4ad2990600090a25050600880546001600160401b0393841668010000000000000000026001600160801b031990911693909416929092179290921790555062000a7b915050565b6000546001600160a01b03808316911603620004ec5760405162461bcd60e51b81526020600482015260116024820152704f776e65724469644e6f744368616e676560781b60448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f04dba622d284ed0014ee4b9a6a68386be1a4c08a4913ae272de89199cc68616391a26001546001600160a01b0316156200054f576200054f60006200066c565b50565b60006001600160a01b03831615801590620005d5575082826040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200059a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005c09190620009e5565b60e083901b6001600160e01b03199081169116145b90505b92915050565b6040805160048152602481019091526020810180516001600160e01b0390811663313ce56760e01b179091526000918291829162000620918691906200071516565b915091508162000634575060009392505050565b808060200190518101906200064a919062000a18565b60ff16949350505050565b6000620005d5836001600160a01b038416620007e4565b6001546001600160a01b03808316911603620006cb5760405162461bcd60e51b815260206004820152601860248201527f50656e64696e674f776e65724469644e6f744368616e676500000000000000006044820152606401620004e3565b600180546001600160a01b0319166001600160a01b0383169081179091556040517fd6aad444c90d39fb0eee1c6e357a7fad83d63f719ac5f880445a2beb0ff3ab5890600090a250565b600060606200072f846200083660201b620029b71760201c565b6200074d576040516373d39f9d60e01b815260040160405180910390fd5b600080856001600160a01b0316856040516200076a919062000a3d565b600060405180830381855afa9150503d8060008114620007a7576040519150601f19603f3d011682016040523d82523d6000602084013e620007ac565b606091505b509150915081620007d557600060405180602001604052806000815250935093505050620007dd565b600193509150505b9250929050565b60008181526001830160205260408120546200082d57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155620005d8565b506000620005d8565b3b151590565b80516001600160a01b03811681146200085457600080fd5b919050565b80516001600160401b03811681146200085457600080fd5b634e487b7160e01b600052604160045260246000fd5b600080600080600060a08688031215620008a057600080fd5b620008ab866200083c565b94506020620008bc8188016200083c565b9450620008cc6040880162000859565b9350620008dc6060880162000859565b60808801519093506001600160401b0380821115620008fa57600080fd5b818901915089601f8301126200090f57600080fd5b81518181111562000924576200092462000871565b8060051b604051601f19603f830116810181811085821117156200094c576200094c62000871565b60405291825284820192508381018501918c8311156200096b57600080fd5b938501935b82851015620009945762000984856200083c565b8452938501939285019262000970565b8096505050505050509295509295909350565b634e487b7160e01b600052603260045260246000fd5b600060018201620009de57634e487b7160e01b600052601160045260246000fd5b5060010190565b600060208284031215620009f857600080fd5b81516001600160e01b03198116811462000a1157600080fd5b9392505050565b60006020828403121562000a2b57600080fd5b815160ff8116811462000a1157600080fd5b6000825160005b8181101562000a60576020818601810151858301520162000a44565b8181111562000a70576000828501525b509190910192915050565b61376d8062000a8b6000396000f3fe608060405234801561001057600080fd5b50600436106103995760003560e01c80636645fd67116101e9578063c2fa74941161010f578063ed4c8bb3116100ad578063f2fde38b1161007c578063f2fde38b14610966578063f45f268214610979578063f887ea401461098c578063fa2a06391461099f57600080fd5b8063ed4c8bb3146108fa578063ed9407401461090d578063ee306a3414610920578063f297692a1461092857600080fd5b8063cc1fdf16116100e9578063cc1fdf16146108a9578063e30c3978146108bc578063e99ed41d146108cd578063eafecffa146108e257600080fd5b8063c2fa749414610870578063c552b7e014610883578063c96fad281461089657600080fd5b806379ba509711610187578063b187bd2611610156578063b187bd261461082a578063b310db2214610837578063ba0d69d31461084a578063c0d786551461085d57600080fd5b806379ba5097146107ab5780638da5cb5b146107b35780639af1d35a146107c4578063a3fbbaae1461081757600080fd5b80636a082764116101c35780636a0827641461073f57806370c0345c14610752578063715018a61461077a578063791183d11461078257600080fd5b80636645fd6714610706578063685f628a1461071957806369a6b3db1461072c57600080fd5b8063296041ea116102ce578063481c6a751161026c57806356fecf1a1161023b57806356fecf1a146106ba5780635ddf2be3146106cd5780635e46bea5146106e05780635f37af9e146106f357600080fd5b8063481c6a751461067057806348b3eabc146106815780634fbc58951461069457806355a2cc71146106a757600080fd5b806332936c44116102a857806332936c4414610619578063342d68c61461062c57806344552b5d1461065557806344cf3e931461065d57600080fd5b8063296041ea146105de5780632cd5f317146105f35780633278c6941461060657600080fd5b806315c3c2d11161033b57806319afb8981161031557806319afb8981461055f5780631b3665331461057257806325ed3d441461058557806327d434161461059d57600080fd5b806315c3c2d1146105105780631893ea141461052357806318b461721461053657600080fd5b806307e930331161037757806307e93033146104525780630b9290581461049357806312d04a42146104bf57806312f0dcd8146104d257600080fd5b806301d6b8131461039e57806304ab688414610422578063072ea61c14610437575b600080fd5b6103ee6103ac366004612fd8565b6015602090815260009283526040808420909152908252902080546001909101546001600160401b0380831692600160401b900416906001600160a01b031683565b604080516001600160401b0394851681529390921660208401526001600160a01b0316908201526060015b60405180910390f35b610435610430366004613026565b6109b2565b005b600a546001600160401b03165b604051908152602001610419565b61047b610460366004613043565b6012602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610419565b6008546009546103ee916001600160401b0380821692600160401b90920416906001600160a01b031683565b6104446104cd366004612fd8565b610ae3565b6105006104e036600461305c565b6001600160a01b0390811660009081526014602052604090205416151590565b6040519015158152602001610419565b61043561051e36600461305c565b610b39565b610435610531366004612fd8565b610c2a565b61047b61054436600461305c565b6013602052600090815260409020546001600160a01b031681565b61043561056d36600461305c565b610d36565b610435610580366004613087565b610f47565b600a54600160401b90046001600160401b0316610444565b6105c66105ab36600461305c565b6010602052600090815260409020546001600160801b031681565b6040516001600160801b039091168152602001610419565b6105e6611034565b60405161041991906130d2565b61043561060136600461305c565b611045565b61043561061436600461305c565b611261565b610444610627366004612fd8565b6112a3565b61047b61063a36600461305c565b6011602052600090815260409020546001600160a01b031681565b610435611303565b61050061066b366004612fd8565b611348565b6002546001600160a01b031661047b565b61047b61068f366004612fd8565b6113b6565b6104356106a236600461311f565b6113f8565b6104356106b536600461314d565b61158e565b6104356106c8366004613043565b611631565b600c5461047b906001600160a01b031681565b600e5461047b906001600160a01b031681565b61047b610701366004613228565b6116b4565b61043561071436600461311f565b611747565b61043561072736600461305c565b6117de565b61043561073a36600461314d565b6118cf565b61043561074d366004613287565b611966565b6003546004546107639160ff169082565b604080519215158352602083019190915201610419565b610435611a3c565b61047b61079036600461305c565b6014602052600090815260409020546001600160a01b031681565b610435611a7f565b6000546001600160a01b031661047b565b600a546107ed906001600160401b0380821691600160401b8104821691600160801b9091041683565b604080516001600160401b0394851681529284166020840152921691810191909152606001610419565b61043561082536600461305c565b611ae6565b6006546105009060ff1681565b6104356108453660046132a2565b611bce565b6104356108583660046132ef565b611d20565b61043561086b36600461305c565b612069565b61044461087e366004612fd8565b61215a565b610435610891366004613337565b6121ac565b6104356108a4366004613287565b612409565b600b5461047b906001600160a01b031681565b6001546001600160a01b031661047b565b60405163e99ed41d60e01b8152602001610419565b600a54600160801b90046001600160401b0316610444565b61043561090836600461305c565b61250b565b61043561091b366004613026565b6125ce565b6105e66126fa565b600f54610946906001600160801b0380821691600160801b90041682565b604080516001600160801b03938416815292909116602083015201610419565b61043561097436600461305c565b612706565b61047b610987366004613353565b61278c565b600d5461047b906001600160a01b031681565b6104356109ad3660046133a2565b6127dd565b6008546001600160401b0380831691600160401b900416670de0b6b3a764000081106109f05760405162f9474b60e61b815260040160405180910390fd5b8115806109fd5750808210155b15610a1b57604051632f2a24a960e11b815260040160405180910390fd5b33610a2e6000546001600160a01b031690565b6001600160a01b031614610a5d5760405162461bcd60e51b8152600401610a54906133e3565b60405180910390fd5b6008546001600160401b03808516911603610a8b57604051634b8bb4c160e01b815260040160405180910390fd5b6008805467ffffffffffffffff19166001600160401b0385169081179091556040519081527ff24256d9766456ecc09aacd623be7d6c4aea837de6d33aa190f39b6dfc9f58ce906020015b60405180910390a1505050565b60035460009060ff1615610b2e576001600160a01b038084166000908152600560209081526040808320938616835292905220548015610b24579050610b33565b5050600454610b33565b506000195b92915050565b33610b4c6000546001600160a01b031690565b6001600160a01b031614610b725760405162461bcd60e51b8152600401610a54906133e3565b610b89816001600160a01b031663eec3e6a76128af565b610ba65760405163057faf4d60e31b815260040160405180910390fd5b600c546001600160a01b03808316911603610bd457604051633ef9374960e01b815260040160405180910390fd5b6040516001600160a01b038216907f62277ad8137ec819c909a28dd0b1fe8f7facf9fe4f4596f71c35fb1179de751c90600090a2600c80546001600160a01b0319166001600160a01b0392909216919091179055565b33610c3d6000546001600160a01b031690565b6001600160a01b031614610c635760405162461bcd60e51b8152600401610a54906133e3565b610c7a816001600160a01b03166311279b4a6128af565b610c975760405163ee60b28160e01b815260040160405180910390fd5b6001600160a01b03828116600090815260116020526040902054818316911603610cd457604051635099b97f60e01b815260040160405180910390fd5b6040516001600160a01b038216907f8ac70028cbcdf11017e87b22003359f7577da04d0880c74ae3289a9029bef6a990600090a26001600160a01b03918216600090815260116020526040902080546001600160a01b03191691909216179055565b33610d496000546001600160a01b031690565b6001600160a01b031614610d6f5760405162461bcd60e51b8152600401610a54906133e3565b6001600160a01b038116610d9657604051631e84cbc160e31b815260040160405180910390fd5b610da060166129bd565b600103610dc05760405163437f484960e01b815260040160405180910390fd5b610dcb6016826129c7565b610de8576040516343e7dd2f60e01b815260040160405180910390fd5b610df36018826129a2565b506040516001600160a01b038216907f780c06b06ab07a0d47596bc3082bbd3f7ecc1907ff035c3ea0e2a1602876a52b90600090a26001600160a01b03808216600090815260136020526040902054168015610edb57806001600160a01b031663a388991b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e8457600080fd5b505af1158015610e98573d6000803e3d6000fd5b5050600e80546001600160a01b031916905550506040516000907f41df3a85176fe451d19027e7b43ecc444bc299ef0d82c2b3874fbc7441d62884908290a25050565b600e546001600160a01b03168015610f4157806001600160a01b031663a388991b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610f2857600080fd5b505af1158015610f3c573d6000803e3d6000fd5b505050505b50505b50565b6002546001600160a01b03163314610f725760405163605919ad60e11b815260040160405180910390fd5b6001600160a01b0380841660009081526007602090815260408083209386168352929052205481151560ff909116151503610fc05760405163715c41ad60e01b815260040160405180910390fd5b6001600160a01b03838116600081815260076020908152604080832094871680845294825291829020805460ff19168615159081179091558251938452908301939093528101919091527f25679bcc320e874c367bc177e782eec47457781ade29259bcea7c3f1fd5c3ef590606001610ad6565b606061104060186129dc565b905090565b336110586000546001600160a01b031690565b6001600160a01b03161461107e5760405162461bcd60e51b8152600401610a54906133e3565b600c546040516350ebbcc160e01b81526001600160a01b038381166004830152909116906350ebbcc190602401602060405180830381865afa1580156110c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ec9190613406565b61110957604051630241d97f60e11b815260040160405180910390fd5b61111281612935565b5061111e6016826129a2565b61113b57604051635ed2680160e01b815260040160405180910390fd5b6111466018826129c7565b506040516001600160a01b038216907fda0511750b81a254e0b084c2c863785e3d1c5b2b989afd8ac0e884f8ee2e033c90600090a26001600160a01b03808216600090815260136020526040902054600e5490821691168115611217576001600160a01b038116156111cb57604051631e0b881960e31b815260040160405180910390fd5b50600e80546001600160a01b0319166001600160a01b0383169081179091556040518291907f41df3a85176fe451d19027e7b43ecc444bc299ef0d82c2b3874fbc7441d6288490600090a25b6001600160a01b03811615610f4157806001600160a01b031663a388991b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610f2857600080fd5b336112746000546001600160a01b031690565b6001600160a01b03161461129a5760405162461bcd60e51b8152600401610a54906133e3565b610f44816129e9565b6001600160a01b038281166000908152601560209081526040808320938516835292905290812054600160401b90046001600160401b031680156112e8579050610b33565b5050600854600160401b90046001600160401b031692915050565b336113166000546001600160a01b031690565b6001600160a01b03161461133c5760405162461bcd60e51b8152600401610a54906133e3565b61134660006129e9565b565b60065460009060ff168061137f57506001600160a01b038316600090815260076020908152604080832083805290915290205460ff165b806113af57506001600160a01b0380841660009081526007602090815260408083209386168352929052205460ff165b9392505050565b6001600160a01b03808316600090815260156020908152604080832085851684529091529020600101541680610b3357506009546001600160a01b0316610b33565b3361140b6000546001600160a01b031690565b6001600160a01b0316146114315760405162461bcd60e51b8152600401610a54906133e3565b611448826001600160a01b0316635a0c4de46128af565b61146557604051637b64d2bb60e11b815260040160405180910390fd5b60408051808201909152600f546001600160801b038082168352600160801b909104166020820181815260019161149d908390613439565b6001600160801b0390811690915260208381018051831660009081526012835260409081902080546001600160a01b0319166001600160a01b038a1690811790915591518651825191861682529094169284019290925292507f3878761190f637385b15245770921955dbd3f3652f86d6bdfcb0a6afe81db04f910160405180910390a2811561156b576020818101516001600160801b03168083526040519081527f723c9edacabd261ad9586f194e9d5409ac4b22c939d90a8db99556690018bfe0910160405180910390a15b80516020909101516001600160801b03908116600160801b02911617600f555050565b6002546001600160a01b031633146115b95760405163605919ad60e11b815260040160405180910390fd5b60035481151560ff9091161515036115e45760405163c65b3d2960e01b815260040160405180910390fd5b6003805460ff191682151590811790915560405160ff909116151581527eace7bfa8dc4895fea42b63dd0304a5b26ee0982b8d764257b6b106b11a4541906020015b60405180910390a150565b6002546001600160a01b0316331461165c5760405163605919ad60e11b815260040160405180910390fd5b60045481900361167f57604051633225cebb60e01b815260040160405180910390fd5b60048190556040518181527f5b71e2324e73b2492a36b7c5f86de1bd745d891c29c3fa39e1ed97960a259eb390602001611626565b6000336116c96000546001600160a01b031690565b6001600160a01b0316146116ef5760405162461bcd60e51b8152600401610a54906133e3565b6001600160a01b0380851660009081526013602052604090205416806117285760405163ccacf28760e01b815260040160405180910390fd5b61173e8585611738601683612a90565b86612ab2565b95945050505050565b6001600160a01b03828116600090815260136020526040902054161561178057604051630907ddeb60e41b815260040160405180910390fd5b80156117da57600e546001600160a01b0316156117b057604051631e0b881960e31b815260040160405180910390fd5b6117ba60166129bd565b6001036117da57604051635dc8364160e11b815260040160405180910390fd5b5050565b336117f16000546001600160a01b031690565b6001600160a01b0316146118175760405162461bcd60e51b8152600401610a54906133e3565b61182e816001600160a01b031663c42401f16128af565b61184b5760405163ee39b72560e01b815260040160405180910390fd5b6009546001600160a01b0380831691160361187957604051630465d63560e01b815260040160405180910390fd5b6040516001600160a01b038216907fd196f9719fb4ff43e12fe6d14f3ab40fa45e350b24e2626b9504d055600f0c0790600090a2600980546001600160a01b0319166001600160a01b0392909216919091179055565b6002546001600160a01b031633146118fa5760405163605919ad60e11b815260040160405180910390fd5b60065481151560ff90911615150361192557604051634ad6576f60e01b815260040160405180910390fd5b6006805460ff19168215159081179091556040519081527fa5fea31b6dbd7aec6098ca4653b1d51af1ef786fcb19031c4c4e55b675535f1e90602001611626565b336119796000546001600160a01b031690565b6001600160a01b03161461199f5760405162461bcd60e51b8152600401610a54906133e3565b6001600160801b0381166000908152601260205260409020546001600160a01b03166119de57604051630d15fae960e01b815260040160405180910390fd5b6040516001600160801b03821681527f723c9edacabd261ad9586f194e9d5409ac4b22c939d90a8db99556690018bfe09060200160405180910390a1600f80546001600160801b0319166001600160801b0392909216919091179055565b33611a4f6000546001600160a01b031690565b6001600160a01b031614611a755760405162461bcd60e51b8152600401610a54906133e3565b6113466000612cc0565b6001546001600160a01b03163314611acc5760405162461bcd60e51b815260206004820152601060248201526f27b7363ca832b73234b733a7bbb732b960811b6044820152606401610a54565b611346611ae16001546001600160a01b031690565b612cc0565b6000546001600160a01b03163314801590611b0c57506002546001600160a01b03163314155b15611b2a5760405163b647320d60e01b815260040160405180910390fd5b6001600160a01b038116611b5157604051639c774ebf60e01b815260040160405180910390fd5b6002546001600160a01b0390811690821603611b8057604051632ebe652b60e21b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f198db6e425fb8aafd1823c6ca50be2d51e5764571a5ae0f0f21c6812e45def0b90602001611626565b33611be16000546001600160a01b031690565b6001600160a01b031614611c075760405162461bcd60e51b8152600401610a54906133e3565b6001600160a01b0382166000908152601060205260409020546001600160801b03808316911603611c4b5760405163f55015b160e01b815260040160405180910390fd5b6001600160801b03811615801590611c8257506001600160801b0381166000908152601260205260409020546001600160a01b0316155b15611ca057604051634eb171af60e01b815260040160405180910390fd5b6040516001600160801b03821681526001600160a01b038316907fb958cbba2ceb00710b20b9f7b874ecdef41b643e87f3ad9affe0d7739d6c919a9060200160405180910390a26001600160a01b0391909116600090815260106020526040902080546001600160801b0319166001600160801b03909216919091179055565b611d2d6020820182613026565b6001600160401b0316611d466040830160208401613026565b6001600160401b0316670de0b6b3a76400008110611d765760405162f9474b60e61b815260040160405180910390fd5b811580611d835750808210155b15611da157604051632f2a24a960e11b815260040160405180910390fd5b33611db46000546001600160a01b031690565b6001600160a01b031614611dda5760405162461bcd60e51b8152600401610a54906133e3565b6001600160a01b038516611e015760405163dfb94ea560e01b815260040160405180910390fd5b6001600160a01b038416611e285760405163281a22f360e11b815260040160405180910390fd5b611e4e611e3b606085016040860161305c565b6001600160a01b031663c42401f16128af565b1580611ec85750611e65606084016040850161305c565b6001600160a01b0316636bcc82166040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ea2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ec69190613464565b155b15611ee65760405163ee39b72560e01b815260040160405180910390fd5b6001600160a01b0380861660009081526015602090815260408083208885168452825291829020825160608101845281546001600160401b038082168352600160401b909104168184015260019091015490931691830191909152611f4d90850185613026565b6001600160401b031681600001516001600160401b0316148015611f955750611f7c6040850160208601613026565b6001600160401b031681602001516001600160401b0316145b8015611fc55750611fac606085016040860161305c565b6001600160a01b031681604001516001600160a01b0316145b15611fe35760405163f485675760e01b815260040160405180910390fd5b846001600160a01b0316866001600160a01b03167fa7e777b5635c66f73eb783f9073900f9ca2dd898e30bc77f81644f1c78aa0dd386604051612026919061347d565b60405180910390a36001600160a01b038087166000908152601560209081526040808320938916835292905220849061205f82826134d4565b5050505050505050565b3361207c6000546001600160a01b031690565b6001600160a01b0316146120a25760405162461bcd60e51b8152600401610a54906133e3565b6120b9816001600160a01b03166325d5bf4e6128af565b6120d657604051632425ecd160e21b815260040160405180910390fd5b600d546001600160a01b0380831691160361210457604051630cdb36f960e41b815260040160405180910390fd5b6040516001600160a01b038216907f66ce7706404042811db82deac21b76e6488aa6e912fd7d57b4cfa3c5a75587ab90600090a2600d80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0380831660009081526015602090815260408083209385168352929052908120546001600160401b03168015612198579050610b33565b50506008546001600160401b031692915050565b336121bf6000546001600160a01b031690565b6001600160a01b0316146121e55760405162461bcd60e51b8152600401610a54906133e3565b670de0b6b3a76400006121fb6020830183613026565b6001600160401b03161061222257604051636fe6a4c960e01b815260040160405180910390fd5b670de0b6b3a764000061223b6040830160208401613026565b6001600160401b031610612262576040516361902cb560e01b815260040160405180910390fd5b670de0b6b3a764000061227b6060830160408401613026565b6001600160401b0316106122a2576040516379e1ef2960e01b815260040160405180910390fd5b60408051606081018252600a546001600160401b03808216808452600160401b83048216602080860191909152600160801b909304909116938301939093529091906122f090840184613026565b6001600160401b031614801561232d575080602001516001600160401b03168260200160208101906123229190613026565b6001600160401b0316145b8015612360575080604001516001600160401b03168260400160208101906123559190613026565b6001600160401b0316145b1561237e576040516353c81ab960e11b815260040160405180910390fd5b7f0bb1069a7e7f20128a7b5ab748a6b3da407eacd702a47c82dad3d75c31ac1c496123ac6020840184613026565b6123bc6040850160208601613026565b6123cc6060860160408701613026565b604080516001600160401b039485168152928416602084015292168183015290519081900360600190a181600a612403828261355f565b50505050565b3361241c6000546001600160a01b031690565b6001600160a01b0316146124425760405162461bcd60e51b8152600401610a54906133e3565b6001600160801b0381166000908152601260205260409020546001600160a01b031680158061247e5750600f546001600160801b038381169116145b1561249c57604051634eb171af60e01b815260040160405180910390fd5b6040516001600160801b03831681526001600160a01b038216907f8bc0514e0cb2bd11d5786d5c8f856fb52056d319fc42c4a895807965a4f7791c9060200160405180910390a2506001600160801b0316600090815260126020526040902080546001600160a01b0319169055565b3361251e6000546001600160a01b031690565b6001600160a01b0316146125445760405162461bcd60e51b8152600401610a54906133e3565b61255b816001600160a01b0316636849100f6128af565b61257857604051630307663d60e61b815260040160405180910390fd5b6040516001600160a01b038216907f2cf3169753956d21755ee0c43a6802b18cb71131bae8405d5a0b97e919e4ad2990600090a2600b80546001600160a01b0319166001600160a01b0392909216919091179055565b6008546001600160401b03908116908216670de0b6b3a764000081106126065760405162f9474b60e61b815260040160405180910390fd5b8115806126135750808210155b1561263157604051632f2a24a960e11b815260040160405180910390fd5b336126446000546001600160a01b031690565b6001600160a01b03161461266a5760405162461bcd60e51b8152600401610a54906133e3565b6008546001600160401b03808516600160401b909204160361269f5760405163d5e8e5fb60e01b815260040160405180910390fd5b6008805467ffffffffffffffff60401b1916600160401b6001600160401b038616908102919091179091556040519081527f421dd56981f3a94f01459e7d9bc4cd85e070e7258f83a9622e5b09e40c4d20ee90602001610ad6565b606061104060166129dc565b336127196000546001600160a01b031690565b6001600160a01b03161461273f5760405162461bcd60e51b8152600401610a54906133e3565b6001600160a01b0381166127835760405162461bcd60e51b815260206004820152600b60248201526a4f776e657249735a65726f60a81b6044820152606401610a54565b610f4481612cc0565b60008061279a601685612a90565b90506127a68482611747565b6001600160a01b0384166000908152601060205260409020546127d59085906001600160801b03168386612ab2565b949350505050565b6002546001600160a01b031633146128085760405163605919ad60e11b815260040160405180910390fd5b6001600160a01b0380841660009081526005602090815260408083209386168352929052205481900361284e5760405163ce9e721f60e01b815260040160405180910390fd5b6001600160a01b0383811660008181526005602090815260408083209487168084529482529182902085905590518481527f25c16b7d0bcf048ed0d91eb5e50591eb538c034fd4ebee6cac8b7c9cf77c770b910160405180910390a3505050565b60006001600160a01b038316158015906113af575082826040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128f5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291991906135f0565b60e083901b6001600160e01b0319908116911614905092915050565b6040805160048152602481019091526020810180516001600160e01b031663313ce56760e01b17905260009081908190612970908590612d71565b9150915081612983575060009392505050565b80806020019051810190612997919061361a565b60ff16949350505050565b60006113af836001600160a01b038416612e25565b3b151590565b6000610b33825490565b60006113af836001600160a01b038416612e74565b606060006113af83612f67565b6001546001600160a01b03808316911603612a465760405162461bcd60e51b815260206004820152601860248201527f50656e64696e674f776e65724469644e6f744368616e676500000000000000006044820152606401610a54565b600180546001600160a01b0319166001600160a01b0383169081179091556040517fd6aad444c90d39fb0eee1c6e357a7fad83d63f719ac5f880445a2beb0ff3ab5890600090a250565b6001600160a01b038116600090815260018301602052604081205415156113af565b6000836001600160801b0316600003612ad457600f546001600160801b031693505b6001600160801b0384166000908152601260205260409020546001600160a01b031680612b1457604051634eb171af60e01b815260040160405180910390fd5b60405163573bbca560e01b81526001600160a01b0382169063573bbca590612b4490899089908890600401613669565b6020604051808303816000875af1158015612b63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b8791906136bb565b6001600160a01b03808816600081815260136020908152604080832080549587166001600160a01b0319968716811790915580845260149092528083208054909516909317909355815163a388991b60e01b81529151939550919263a388991b9260048084019391929182900301818387803b158015612c0657600080fd5b505af1158015612c1a573d6000803e3d6000fd5b50506040516001600160801b03881681526001600160a01b03808a169350851691507fd67d472c55ce8b249ca39858e9032ae0237147a9b81f4d1253c246c75876dd699060200160405180910390a38315612cb757600e80546001600160a01b0319166001600160a01b0384169081179091556040517f41df3a85176fe451d19027e7b43ecc444bc299ef0d82c2b3874fbc7441d6288490600090a25b50949350505050565b6000546001600160a01b03808316911603612d115760405162461bcd60e51b81526020600482015260116024820152704f776e65724469644e6f744368616e676560781b6044820152606401610a54565b600080546001600160a01b0319166001600160a01b038316908117825560405190917f04dba622d284ed0014ee4b9a6a68386be1a4c08a4913ae272de89199cc68616391a26001546001600160a01b031615610f4457610f4460006129e9565b60006060833b612d94576040516373d39f9d60e01b815260040160405180910390fd5b600080856001600160a01b031685604051612daf91906136d8565b600060405180830381855afa9150503d8060008114612dea576040519150601f19603f3d011682016040523d82523d6000602084013e612def565b606091505b509150915081612e1657600060405180602001604052806000815250935093505050612e1e565b600193509150505b9250929050565b6000818152600183016020526040812054612e6c57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610b33565b506000610b33565b60008181526001830160205260408120548015612f5d576000612e986001836136f4565b8554909150600090612eac906001906136f4565b9050818114612f11576000866000018281548110612ecc57612ecc61370b565b9060005260206000200154905080876000018481548110612eef57612eef61370b565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612f2257612f22613721565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610b33565b6000915050610b33565b606081600001805480602002602001604051908101604052809291908181526020018280548015612fb757602002820191906000526020600020905b815481526020019060010190808311612fa3575b50505050509050919050565b6001600160a01b0381168114610f4457600080fd5b60008060408385031215612feb57600080fd5b8235612ff681612fc3565b9150602083013561300681612fc3565b809150509250929050565b6001600160401b0381168114610f4457600080fd5b60006020828403121561303857600080fd5b81356113af81613011565b60006020828403121561305557600080fd5b5035919050565b60006020828403121561306e57600080fd5b81356113af81612fc3565b8015158114610f4457600080fd5b60008060006060848603121561309c57600080fd5b83356130a781612fc3565b925060208401356130b781612fc3565b915060408401356130c781613079565b809150509250925092565b6020808252825182820181905260009190848201906040850190845b818110156131135783516001600160a01b0316835292840192918401916001016130ee565b50909695505050505050565b6000806040838503121561313257600080fd5b823561313d81612fc3565b9150602083013561300681613079565b60006020828403121561315f57600080fd5b81356113af81613079565b80356001600160801b038116811461318157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126131ad57600080fd5b81356001600160401b03808211156131c7576131c7613186565b604051601f8301601f19908116603f011681019082821181831017156131ef576131ef613186565b8160405283815286602085880101111561320857600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561323d57600080fd5b833561324881612fc3565b92506132566020850161316a565b915060408401356001600160401b0381111561327157600080fd5b61327d8682870161319c565b9150509250925092565b60006020828403121561329957600080fd5b6113af8261316a565b600080604083850312156132b557600080fd5b82356132c081612fc3565b91506132ce6020840161316a565b90509250929050565b6000606082840312156132e957600080fd5b50919050565b600080600060a0848603121561330457600080fd5b833561330f81612fc3565b9250602084013561331f81612fc3565b915061332e85604086016132d7565b90509250925092565b60006060828403121561334957600080fd5b6113af83836132d7565b6000806040838503121561336657600080fd5b823561337181612fc3565b915060208301356001600160401b0381111561338c57600080fd5b6133988582860161319c565b9150509250929050565b6000806000606084860312156133b757600080fd5b83356133c281612fc3565b925060208401356133d281612fc3565b929592945050506040919091013590565b60208082526009908201526827b7363ca7bbb732b960b91b604082015260600190565b60006020828403121561341857600080fd5b81516113af81613079565b634e487b7160e01b600052601160045260246000fd5b60006001600160801b0380831681851680830382111561345b5761345b613423565b01949350505050565b60006020828403121561347657600080fd5b5051919050565b60608101823561348c81613011565b6001600160401b0390811683526020840135906134a882613011565b16602083015260408301356134bc81612fc3565b6001600160a01b031660409290920191909152919050565b81356134df81613011565b815467ffffffffffffffff19166001600160401b03821617825550602082013561350881613011565b815467ffffffffffffffff60401b1916604082901b67ffffffffffffffff60401b161782555060018101604083013561354081612fc3565b81546001600160a01b0319166001600160a01b03919091161790555050565b813561356a81613011565b815467ffffffffffffffff19166001600160401b03821617825550602082013561359381613011565b815467ffffffffffffffff60401b1916604082901b67ffffffffffffffff60401b161782555060408201356135c781613011565b815467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b1617905550565b60006020828403121561360257600080fd5b81516001600160e01b0319811681146113af57600080fd5b60006020828403121561362c57600080fd5b815160ff811681146113af57600080fd5b60005b83811015613658578181015183820152602001613640565b838111156124035750506000910152565b60018060a01b03841681526001600160801b038316602082015260606040820152600082518060608401526136a581608085016020870161363d565b601f01601f191691909101608001949350505050565b6000602082840312156136cd57600080fd5b81516113af81612fc3565b600082516136ea81846020870161363d565b9190910192915050565b60008282101561370657613706613423565b500390565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052603160045260246000fdfea2646970667358221220c87df71128452e0b33444e65e09d6200f248f1b9e8fb7261e32ea74bb0eaf78664736f6c634300080d00330000000000000000000000004d919cecfd4793c0d47866c8d0a02a09507375890000000000000000000000000e37df413f97fc198a84a21bc463c41b516ad62200000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000000853a0d2313c000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103995760003560e01c80636645fd67116101e9578063c2fa74941161010f578063ed4c8bb3116100ad578063f2fde38b1161007c578063f2fde38b14610966578063f45f268214610979578063f887ea401461098c578063fa2a06391461099f57600080fd5b8063ed4c8bb3146108fa578063ed9407401461090d578063ee306a3414610920578063f297692a1461092857600080fd5b8063cc1fdf16116100e9578063cc1fdf16146108a9578063e30c3978146108bc578063e99ed41d146108cd578063eafecffa146108e257600080fd5b8063c2fa749414610870578063c552b7e014610883578063c96fad281461089657600080fd5b806379ba509711610187578063b187bd2611610156578063b187bd261461082a578063b310db2214610837578063ba0d69d31461084a578063c0d786551461085d57600080fd5b806379ba5097146107ab5780638da5cb5b146107b35780639af1d35a146107c4578063a3fbbaae1461081757600080fd5b80636a082764116101c35780636a0827641461073f57806370c0345c14610752578063715018a61461077a578063791183d11461078257600080fd5b80636645fd6714610706578063685f628a1461071957806369a6b3db1461072c57600080fd5b8063296041ea116102ce578063481c6a751161026c57806356fecf1a1161023b57806356fecf1a146106ba5780635ddf2be3146106cd5780635e46bea5146106e05780635f37af9e146106f357600080fd5b8063481c6a751461067057806348b3eabc146106815780634fbc58951461069457806355a2cc71146106a757600080fd5b806332936c44116102a857806332936c4414610619578063342d68c61461062c57806344552b5d1461065557806344cf3e931461065d57600080fd5b8063296041ea146105de5780632cd5f317146105f35780633278c6941461060657600080fd5b806315c3c2d11161033b57806319afb8981161031557806319afb8981461055f5780631b3665331461057257806325ed3d441461058557806327d434161461059d57600080fd5b806315c3c2d1146105105780631893ea141461052357806318b461721461053657600080fd5b806307e930331161037757806307e93033146104525780630b9290581461049357806312d04a42146104bf57806312f0dcd8146104d257600080fd5b806301d6b8131461039e57806304ab688414610422578063072ea61c14610437575b600080fd5b6103ee6103ac366004612fd8565b6015602090815260009283526040808420909152908252902080546001909101546001600160401b0380831692600160401b900416906001600160a01b031683565b604080516001600160401b0394851681529390921660208401526001600160a01b0316908201526060015b60405180910390f35b610435610430366004613026565b6109b2565b005b600a546001600160401b03165b604051908152602001610419565b61047b610460366004613043565b6012602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610419565b6008546009546103ee916001600160401b0380821692600160401b90920416906001600160a01b031683565b6104446104cd366004612fd8565b610ae3565b6105006104e036600461305c565b6001600160a01b0390811660009081526014602052604090205416151590565b6040519015158152602001610419565b61043561051e36600461305c565b610b39565b610435610531366004612fd8565b610c2a565b61047b61054436600461305c565b6013602052600090815260409020546001600160a01b031681565b61043561056d36600461305c565b610d36565b610435610580366004613087565b610f47565b600a54600160401b90046001600160401b0316610444565b6105c66105ab36600461305c565b6010602052600090815260409020546001600160801b031681565b6040516001600160801b039091168152602001610419565b6105e6611034565b60405161041991906130d2565b61043561060136600461305c565b611045565b61043561061436600461305c565b611261565b610444610627366004612fd8565b6112a3565b61047b61063a36600461305c565b6011602052600090815260409020546001600160a01b031681565b610435611303565b61050061066b366004612fd8565b611348565b6002546001600160a01b031661047b565b61047b61068f366004612fd8565b6113b6565b6104356106a236600461311f565b6113f8565b6104356106b536600461314d565b61158e565b6104356106c8366004613043565b611631565b600c5461047b906001600160a01b031681565b600e5461047b906001600160a01b031681565b61047b610701366004613228565b6116b4565b61043561071436600461311f565b611747565b61043561072736600461305c565b6117de565b61043561073a36600461314d565b6118cf565b61043561074d366004613287565b611966565b6003546004546107639160ff169082565b604080519215158352602083019190915201610419565b610435611a3c565b61047b61079036600461305c565b6014602052600090815260409020546001600160a01b031681565b610435611a7f565b6000546001600160a01b031661047b565b600a546107ed906001600160401b0380821691600160401b8104821691600160801b9091041683565b604080516001600160401b0394851681529284166020840152921691810191909152606001610419565b61043561082536600461305c565b611ae6565b6006546105009060ff1681565b6104356108453660046132a2565b611bce565b6104356108583660046132ef565b611d20565b61043561086b36600461305c565b612069565b61044461087e366004612fd8565b61215a565b610435610891366004613337565b6121ac565b6104356108a4366004613287565b612409565b600b5461047b906001600160a01b031681565b6001546001600160a01b031661047b565b60405163e99ed41d60e01b8152602001610419565b600a54600160801b90046001600160401b0316610444565b61043561090836600461305c565b61250b565b61043561091b366004613026565b6125ce565b6105e66126fa565b600f54610946906001600160801b0380821691600160801b90041682565b604080516001600160801b03938416815292909116602083015201610419565b61043561097436600461305c565b612706565b61047b610987366004613353565b61278c565b600d5461047b906001600160a01b031681565b6104356109ad3660046133a2565b6127dd565b6008546001600160401b0380831691600160401b900416670de0b6b3a764000081106109f05760405162f9474b60e61b815260040160405180910390fd5b8115806109fd5750808210155b15610a1b57604051632f2a24a960e11b815260040160405180910390fd5b33610a2e6000546001600160a01b031690565b6001600160a01b031614610a5d5760405162461bcd60e51b8152600401610a54906133e3565b60405180910390fd5b6008546001600160401b03808516911603610a8b57604051634b8bb4c160e01b815260040160405180910390fd5b6008805467ffffffffffffffff19166001600160401b0385169081179091556040519081527ff24256d9766456ecc09aacd623be7d6c4aea837de6d33aa190f39b6dfc9f58ce906020015b60405180910390a1505050565b60035460009060ff1615610b2e576001600160a01b038084166000908152600560209081526040808320938616835292905220548015610b24579050610b33565b5050600454610b33565b506000195b92915050565b33610b4c6000546001600160a01b031690565b6001600160a01b031614610b725760405162461bcd60e51b8152600401610a54906133e3565b610b89816001600160a01b031663eec3e6a76128af565b610ba65760405163057faf4d60e31b815260040160405180910390fd5b600c546001600160a01b03808316911603610bd457604051633ef9374960e01b815260040160405180910390fd5b6040516001600160a01b038216907f62277ad8137ec819c909a28dd0b1fe8f7facf9fe4f4596f71c35fb1179de751c90600090a2600c80546001600160a01b0319166001600160a01b0392909216919091179055565b33610c3d6000546001600160a01b031690565b6001600160a01b031614610c635760405162461bcd60e51b8152600401610a54906133e3565b610c7a816001600160a01b03166311279b4a6128af565b610c975760405163ee60b28160e01b815260040160405180910390fd5b6001600160a01b03828116600090815260116020526040902054818316911603610cd457604051635099b97f60e01b815260040160405180910390fd5b6040516001600160a01b038216907f8ac70028cbcdf11017e87b22003359f7577da04d0880c74ae3289a9029bef6a990600090a26001600160a01b03918216600090815260116020526040902080546001600160a01b03191691909216179055565b33610d496000546001600160a01b031690565b6001600160a01b031614610d6f5760405162461bcd60e51b8152600401610a54906133e3565b6001600160a01b038116610d9657604051631e84cbc160e31b815260040160405180910390fd5b610da060166129bd565b600103610dc05760405163437f484960e01b815260040160405180910390fd5b610dcb6016826129c7565b610de8576040516343e7dd2f60e01b815260040160405180910390fd5b610df36018826129a2565b506040516001600160a01b038216907f780c06b06ab07a0d47596bc3082bbd3f7ecc1907ff035c3ea0e2a1602876a52b90600090a26001600160a01b03808216600090815260136020526040902054168015610edb57806001600160a01b031663a388991b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e8457600080fd5b505af1158015610e98573d6000803e3d6000fd5b5050600e80546001600160a01b031916905550506040516000907f41df3a85176fe451d19027e7b43ecc444bc299ef0d82c2b3874fbc7441d62884908290a25050565b600e546001600160a01b03168015610f4157806001600160a01b031663a388991b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610f2857600080fd5b505af1158015610f3c573d6000803e3d6000fd5b505050505b50505b50565b6002546001600160a01b03163314610f725760405163605919ad60e11b815260040160405180910390fd5b6001600160a01b0380841660009081526007602090815260408083209386168352929052205481151560ff909116151503610fc05760405163715c41ad60e01b815260040160405180910390fd5b6001600160a01b03838116600081815260076020908152604080832094871680845294825291829020805460ff19168615159081179091558251938452908301939093528101919091527f25679bcc320e874c367bc177e782eec47457781ade29259bcea7c3f1fd5c3ef590606001610ad6565b606061104060186129dc565b905090565b336110586000546001600160a01b031690565b6001600160a01b03161461107e5760405162461bcd60e51b8152600401610a54906133e3565b600c546040516350ebbcc160e01b81526001600160a01b038381166004830152909116906350ebbcc190602401602060405180830381865afa1580156110c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ec9190613406565b61110957604051630241d97f60e11b815260040160405180910390fd5b61111281612935565b5061111e6016826129a2565b61113b57604051635ed2680160e01b815260040160405180910390fd5b6111466018826129c7565b506040516001600160a01b038216907fda0511750b81a254e0b084c2c863785e3d1c5b2b989afd8ac0e884f8ee2e033c90600090a26001600160a01b03808216600090815260136020526040902054600e5490821691168115611217576001600160a01b038116156111cb57604051631e0b881960e31b815260040160405180910390fd5b50600e80546001600160a01b0319166001600160a01b0383169081179091556040518291907f41df3a85176fe451d19027e7b43ecc444bc299ef0d82c2b3874fbc7441d6288490600090a25b6001600160a01b03811615610f4157806001600160a01b031663a388991b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610f2857600080fd5b336112746000546001600160a01b031690565b6001600160a01b03161461129a5760405162461bcd60e51b8152600401610a54906133e3565b610f44816129e9565b6001600160a01b038281166000908152601560209081526040808320938516835292905290812054600160401b90046001600160401b031680156112e8579050610b33565b5050600854600160401b90046001600160401b031692915050565b336113166000546001600160a01b031690565b6001600160a01b03161461133c5760405162461bcd60e51b8152600401610a54906133e3565b61134660006129e9565b565b60065460009060ff168061137f57506001600160a01b038316600090815260076020908152604080832083805290915290205460ff165b806113af57506001600160a01b0380841660009081526007602090815260408083209386168352929052205460ff165b9392505050565b6001600160a01b03808316600090815260156020908152604080832085851684529091529020600101541680610b3357506009546001600160a01b0316610b33565b3361140b6000546001600160a01b031690565b6001600160a01b0316146114315760405162461bcd60e51b8152600401610a54906133e3565b611448826001600160a01b0316635a0c4de46128af565b61146557604051637b64d2bb60e11b815260040160405180910390fd5b60408051808201909152600f546001600160801b038082168352600160801b909104166020820181815260019161149d908390613439565b6001600160801b0390811690915260208381018051831660009081526012835260409081902080546001600160a01b0319166001600160a01b038a1690811790915591518651825191861682529094169284019290925292507f3878761190f637385b15245770921955dbd3f3652f86d6bdfcb0a6afe81db04f910160405180910390a2811561156b576020818101516001600160801b03168083526040519081527f723c9edacabd261ad9586f194e9d5409ac4b22c939d90a8db99556690018bfe0910160405180910390a15b80516020909101516001600160801b03908116600160801b02911617600f555050565b6002546001600160a01b031633146115b95760405163605919ad60e11b815260040160405180910390fd5b60035481151560ff9091161515036115e45760405163c65b3d2960e01b815260040160405180910390fd5b6003805460ff191682151590811790915560405160ff909116151581527eace7bfa8dc4895fea42b63dd0304a5b26ee0982b8d764257b6b106b11a4541906020015b60405180910390a150565b6002546001600160a01b0316331461165c5760405163605919ad60e11b815260040160405180910390fd5b60045481900361167f57604051633225cebb60e01b815260040160405180910390fd5b60048190556040518181527f5b71e2324e73b2492a36b7c5f86de1bd745d891c29c3fa39e1ed97960a259eb390602001611626565b6000336116c96000546001600160a01b031690565b6001600160a01b0316146116ef5760405162461bcd60e51b8152600401610a54906133e3565b6001600160a01b0380851660009081526013602052604090205416806117285760405163ccacf28760e01b815260040160405180910390fd5b61173e8585611738601683612a90565b86612ab2565b95945050505050565b6001600160a01b03828116600090815260136020526040902054161561178057604051630907ddeb60e41b815260040160405180910390fd5b80156117da57600e546001600160a01b0316156117b057604051631e0b881960e31b815260040160405180910390fd5b6117ba60166129bd565b6001036117da57604051635dc8364160e11b815260040160405180910390fd5b5050565b336117f16000546001600160a01b031690565b6001600160a01b0316146118175760405162461bcd60e51b8152600401610a54906133e3565b61182e816001600160a01b031663c42401f16128af565b61184b5760405163ee39b72560e01b815260040160405180910390fd5b6009546001600160a01b0380831691160361187957604051630465d63560e01b815260040160405180910390fd5b6040516001600160a01b038216907fd196f9719fb4ff43e12fe6d14f3ab40fa45e350b24e2626b9504d055600f0c0790600090a2600980546001600160a01b0319166001600160a01b0392909216919091179055565b6002546001600160a01b031633146118fa5760405163605919ad60e11b815260040160405180910390fd5b60065481151560ff90911615150361192557604051634ad6576f60e01b815260040160405180910390fd5b6006805460ff19168215159081179091556040519081527fa5fea31b6dbd7aec6098ca4653b1d51af1ef786fcb19031c4c4e55b675535f1e90602001611626565b336119796000546001600160a01b031690565b6001600160a01b03161461199f5760405162461bcd60e51b8152600401610a54906133e3565b6001600160801b0381166000908152601260205260409020546001600160a01b03166119de57604051630d15fae960e01b815260040160405180910390fd5b6040516001600160801b03821681527f723c9edacabd261ad9586f194e9d5409ac4b22c939d90a8db99556690018bfe09060200160405180910390a1600f80546001600160801b0319166001600160801b0392909216919091179055565b33611a4f6000546001600160a01b031690565b6001600160a01b031614611a755760405162461bcd60e51b8152600401610a54906133e3565b6113466000612cc0565b6001546001600160a01b03163314611acc5760405162461bcd60e51b815260206004820152601060248201526f27b7363ca832b73234b733a7bbb732b960811b6044820152606401610a54565b611346611ae16001546001600160a01b031690565b612cc0565b6000546001600160a01b03163314801590611b0c57506002546001600160a01b03163314155b15611b2a5760405163b647320d60e01b815260040160405180910390fd5b6001600160a01b038116611b5157604051639c774ebf60e01b815260040160405180910390fd5b6002546001600160a01b0390811690821603611b8057604051632ebe652b60e21b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f198db6e425fb8aafd1823c6ca50be2d51e5764571a5ae0f0f21c6812e45def0b90602001611626565b33611be16000546001600160a01b031690565b6001600160a01b031614611c075760405162461bcd60e51b8152600401610a54906133e3565b6001600160a01b0382166000908152601060205260409020546001600160801b03808316911603611c4b5760405163f55015b160e01b815260040160405180910390fd5b6001600160801b03811615801590611c8257506001600160801b0381166000908152601260205260409020546001600160a01b0316155b15611ca057604051634eb171af60e01b815260040160405180910390fd5b6040516001600160801b03821681526001600160a01b038316907fb958cbba2ceb00710b20b9f7b874ecdef41b643e87f3ad9affe0d7739d6c919a9060200160405180910390a26001600160a01b0391909116600090815260106020526040902080546001600160801b0319166001600160801b03909216919091179055565b611d2d6020820182613026565b6001600160401b0316611d466040830160208401613026565b6001600160401b0316670de0b6b3a76400008110611d765760405162f9474b60e61b815260040160405180910390fd5b811580611d835750808210155b15611da157604051632f2a24a960e11b815260040160405180910390fd5b33611db46000546001600160a01b031690565b6001600160a01b031614611dda5760405162461bcd60e51b8152600401610a54906133e3565b6001600160a01b038516611e015760405163dfb94ea560e01b815260040160405180910390fd5b6001600160a01b038416611e285760405163281a22f360e11b815260040160405180910390fd5b611e4e611e3b606085016040860161305c565b6001600160a01b031663c42401f16128af565b1580611ec85750611e65606084016040850161305c565b6001600160a01b0316636bcc82166040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ea2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ec69190613464565b155b15611ee65760405163ee39b72560e01b815260040160405180910390fd5b6001600160a01b0380861660009081526015602090815260408083208885168452825291829020825160608101845281546001600160401b038082168352600160401b909104168184015260019091015490931691830191909152611f4d90850185613026565b6001600160401b031681600001516001600160401b0316148015611f955750611f7c6040850160208601613026565b6001600160401b031681602001516001600160401b0316145b8015611fc55750611fac606085016040860161305c565b6001600160a01b031681604001516001600160a01b0316145b15611fe35760405163f485675760e01b815260040160405180910390fd5b846001600160a01b0316866001600160a01b03167fa7e777b5635c66f73eb783f9073900f9ca2dd898e30bc77f81644f1c78aa0dd386604051612026919061347d565b60405180910390a36001600160a01b038087166000908152601560209081526040808320938916835292905220849061205f82826134d4565b5050505050505050565b3361207c6000546001600160a01b031690565b6001600160a01b0316146120a25760405162461bcd60e51b8152600401610a54906133e3565b6120b9816001600160a01b03166325d5bf4e6128af565b6120d657604051632425ecd160e21b815260040160405180910390fd5b600d546001600160a01b0380831691160361210457604051630cdb36f960e41b815260040160405180910390fd5b6040516001600160a01b038216907f66ce7706404042811db82deac21b76e6488aa6e912fd7d57b4cfa3c5a75587ab90600090a2600d80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0380831660009081526015602090815260408083209385168352929052908120546001600160401b03168015612198579050610b33565b50506008546001600160401b031692915050565b336121bf6000546001600160a01b031690565b6001600160a01b0316146121e55760405162461bcd60e51b8152600401610a54906133e3565b670de0b6b3a76400006121fb6020830183613026565b6001600160401b03161061222257604051636fe6a4c960e01b815260040160405180910390fd5b670de0b6b3a764000061223b6040830160208401613026565b6001600160401b031610612262576040516361902cb560e01b815260040160405180910390fd5b670de0b6b3a764000061227b6060830160408401613026565b6001600160401b0316106122a2576040516379e1ef2960e01b815260040160405180910390fd5b60408051606081018252600a546001600160401b03808216808452600160401b83048216602080860191909152600160801b909304909116938301939093529091906122f090840184613026565b6001600160401b031614801561232d575080602001516001600160401b03168260200160208101906123229190613026565b6001600160401b0316145b8015612360575080604001516001600160401b03168260400160208101906123559190613026565b6001600160401b0316145b1561237e576040516353c81ab960e11b815260040160405180910390fd5b7f0bb1069a7e7f20128a7b5ab748a6b3da407eacd702a47c82dad3d75c31ac1c496123ac6020840184613026565b6123bc6040850160208601613026565b6123cc6060860160408701613026565b604080516001600160401b039485168152928416602084015292168183015290519081900360600190a181600a612403828261355f565b50505050565b3361241c6000546001600160a01b031690565b6001600160a01b0316146124425760405162461bcd60e51b8152600401610a54906133e3565b6001600160801b0381166000908152601260205260409020546001600160a01b031680158061247e5750600f546001600160801b038381169116145b1561249c57604051634eb171af60e01b815260040160405180910390fd5b6040516001600160801b03831681526001600160a01b038216907f8bc0514e0cb2bd11d5786d5c8f856fb52056d319fc42c4a895807965a4f7791c9060200160405180910390a2506001600160801b0316600090815260126020526040902080546001600160a01b0319169055565b3361251e6000546001600160a01b031690565b6001600160a01b0316146125445760405162461bcd60e51b8152600401610a54906133e3565b61255b816001600160a01b0316636849100f6128af565b61257857604051630307663d60e61b815260040160405180910390fd5b6040516001600160a01b038216907f2cf3169753956d21755ee0c43a6802b18cb71131bae8405d5a0b97e919e4ad2990600090a2600b80546001600160a01b0319166001600160a01b0392909216919091179055565b6008546001600160401b03908116908216670de0b6b3a764000081106126065760405162f9474b60e61b815260040160405180910390fd5b8115806126135750808210155b1561263157604051632f2a24a960e11b815260040160405180910390fd5b336126446000546001600160a01b031690565b6001600160a01b03161461266a5760405162461bcd60e51b8152600401610a54906133e3565b6008546001600160401b03808516600160401b909204160361269f5760405163d5e8e5fb60e01b815260040160405180910390fd5b6008805467ffffffffffffffff60401b1916600160401b6001600160401b038616908102919091179091556040519081527f421dd56981f3a94f01459e7d9bc4cd85e070e7258f83a9622e5b09e40c4d20ee90602001610ad6565b606061104060166129dc565b336127196000546001600160a01b031690565b6001600160a01b03161461273f5760405162461bcd60e51b8152600401610a54906133e3565b6001600160a01b0381166127835760405162461bcd60e51b815260206004820152600b60248201526a4f776e657249735a65726f60a81b6044820152606401610a54565b610f4481612cc0565b60008061279a601685612a90565b90506127a68482611747565b6001600160a01b0384166000908152601060205260409020546127d59085906001600160801b03168386612ab2565b949350505050565b6002546001600160a01b031633146128085760405163605919ad60e11b815260040160405180910390fd5b6001600160a01b0380841660009081526005602090815260408083209386168352929052205481900361284e5760405163ce9e721f60e01b815260040160405180910390fd5b6001600160a01b0383811660008181526005602090815260408083209487168084529482529182902085905590518481527f25c16b7d0bcf048ed0d91eb5e50591eb538c034fd4ebee6cac8b7c9cf77c770b910160405180910390a3505050565b60006001600160a01b038316158015906113af575082826040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128f5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291991906135f0565b60e083901b6001600160e01b0319908116911614905092915050565b6040805160048152602481019091526020810180516001600160e01b031663313ce56760e01b17905260009081908190612970908590612d71565b9150915081612983575060009392505050565b80806020019051810190612997919061361a565b60ff16949350505050565b60006113af836001600160a01b038416612e25565b3b151590565b6000610b33825490565b60006113af836001600160a01b038416612e74565b606060006113af83612f67565b6001546001600160a01b03808316911603612a465760405162461bcd60e51b815260206004820152601860248201527f50656e64696e674f776e65724469644e6f744368616e676500000000000000006044820152606401610a54565b600180546001600160a01b0319166001600160a01b0383169081179091556040517fd6aad444c90d39fb0eee1c6e357a7fad83d63f719ac5f880445a2beb0ff3ab5890600090a250565b6001600160a01b038116600090815260018301602052604081205415156113af565b6000836001600160801b0316600003612ad457600f546001600160801b031693505b6001600160801b0384166000908152601260205260409020546001600160a01b031680612b1457604051634eb171af60e01b815260040160405180910390fd5b60405163573bbca560e01b81526001600160a01b0382169063573bbca590612b4490899089908890600401613669565b6020604051808303816000875af1158015612b63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b8791906136bb565b6001600160a01b03808816600081815260136020908152604080832080549587166001600160a01b0319968716811790915580845260149092528083208054909516909317909355815163a388991b60e01b81529151939550919263a388991b9260048084019391929182900301818387803b158015612c0657600080fd5b505af1158015612c1a573d6000803e3d6000fd5b50506040516001600160801b03881681526001600160a01b03808a169350851691507fd67d472c55ce8b249ca39858e9032ae0237147a9b81f4d1253c246c75876dd699060200160405180910390a38315612cb757600e80546001600160a01b0319166001600160a01b0384169081179091556040517f41df3a85176fe451d19027e7b43ecc444bc299ef0d82c2b3874fbc7441d6288490600090a25b50949350505050565b6000546001600160a01b03808316911603612d115760405162461bcd60e51b81526020600482015260116024820152704f776e65724469644e6f744368616e676560781b6044820152606401610a54565b600080546001600160a01b0319166001600160a01b038316908117825560405190917f04dba622d284ed0014ee4b9a6a68386be1a4c08a4913ae272de89199cc68616391a26001546001600160a01b031615610f4457610f4460006129e9565b60006060833b612d94576040516373d39f9d60e01b815260040160405180910390fd5b600080856001600160a01b031685604051612daf91906136d8565b600060405180830381855afa9150503d8060008114612dea576040519150601f19603f3d011682016040523d82523d6000602084013e612def565b606091505b509150915081612e1657600060405180602001604052806000815250935093505050612e1e565b600193509150505b9250929050565b6000818152600183016020526040812054612e6c57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610b33565b506000610b33565b60008181526001830160205260408120548015612f5d576000612e986001836136f4565b8554909150600090612eac906001906136f4565b9050818114612f11576000866000018281548110612ecc57612ecc61370b565b9060005260206000200154905080876000018481548110612eef57612eef61370b565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612f2257612f22613721565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610b33565b6000915050610b33565b606081600001805480602002602001604051908101604052809291908181526020018280548015612fb757602002820191906000526020600020905b815481526020019060010190808311612fa3575b50505050509050919050565b6001600160a01b0381168114610f4457600080fd5b60008060408385031215612feb57600080fd5b8235612ff681612fc3565b9150602083013561300681612fc3565b809150509250929050565b6001600160401b0381168114610f4457600080fd5b60006020828403121561303857600080fd5b81356113af81613011565b60006020828403121561305557600080fd5b5035919050565b60006020828403121561306e57600080fd5b81356113af81612fc3565b8015158114610f4457600080fd5b60008060006060848603121561309c57600080fd5b83356130a781612fc3565b925060208401356130b781612fc3565b915060408401356130c781613079565b809150509250925092565b6020808252825182820181905260009190848201906040850190845b818110156131135783516001600160a01b0316835292840192918401916001016130ee565b50909695505050505050565b6000806040838503121561313257600080fd5b823561313d81612fc3565b9150602083013561300681613079565b60006020828403121561315f57600080fd5b81356113af81613079565b80356001600160801b038116811461318157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126131ad57600080fd5b81356001600160401b03808211156131c7576131c7613186565b604051601f8301601f19908116603f011681019082821181831017156131ef576131ef613186565b8160405283815286602085880101111561320857600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561323d57600080fd5b833561324881612fc3565b92506132566020850161316a565b915060408401356001600160401b0381111561327157600080fd5b61327d8682870161319c565b9150509250925092565b60006020828403121561329957600080fd5b6113af8261316a565b600080604083850312156132b557600080fd5b82356132c081612fc3565b91506132ce6020840161316a565b90509250929050565b6000606082840312156132e957600080fd5b50919050565b600080600060a0848603121561330457600080fd5b833561330f81612fc3565b9250602084013561331f81612fc3565b915061332e85604086016132d7565b90509250925092565b60006060828403121561334957600080fd5b6113af83836132d7565b6000806040838503121561336657600080fd5b823561337181612fc3565b915060208301356001600160401b0381111561338c57600080fd5b6133988582860161319c565b9150509250929050565b6000806000606084860312156133b757600080fd5b83356133c281612fc3565b925060208401356133d281612fc3565b929592945050506040919091013590565b60208082526009908201526827b7363ca7bbb732b960b91b604082015260600190565b60006020828403121561341857600080fd5b81516113af81613079565b634e487b7160e01b600052601160045260246000fd5b60006001600160801b0380831681851680830382111561345b5761345b613423565b01949350505050565b60006020828403121561347657600080fd5b5051919050565b60608101823561348c81613011565b6001600160401b0390811683526020840135906134a882613011565b16602083015260408301356134bc81612fc3565b6001600160a01b031660409290920191909152919050565b81356134df81613011565b815467ffffffffffffffff19166001600160401b03821617825550602082013561350881613011565b815467ffffffffffffffff60401b1916604082901b67ffffffffffffffff60401b161782555060018101604083013561354081612fc3565b81546001600160a01b0319166001600160a01b03919091161790555050565b813561356a81613011565b815467ffffffffffffffff19166001600160401b03821617825550602082013561359381613011565b815467ffffffffffffffff60401b1916604082901b67ffffffffffffffff60401b161782555060408201356135c781613011565b815467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b1617905550565b60006020828403121561360257600080fd5b81516001600160e01b0319811681146113af57600080fd5b60006020828403121561362c57600080fd5b815160ff811681146113af57600080fd5b60005b83811015613658578181015183820152602001613640565b838111156124035750506000910152565b60018060a01b03841681526001600160801b038316602082015260606040820152600082518060608401526136a581608085016020870161363d565b601f01601f191691909101608001949350505050565b6000602082840312156136cd57600080fd5b81516113af81612fc3565b600082516136ea81846020870161363d565b9190910192915050565b60008282101561370657613706613423565b500390565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052603160045260246000fdfea2646970667358221220c87df71128452e0b33444e65e09d6200f248f1b9e8fb7261e32ea74bb0eaf78664736f6c634300080d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000004d919cecfd4793c0d47866c8d0a02a09507375890000000000000000000000000e37df413f97fc198a84a21bc463c41b516ad62200000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000000853a0d2313c000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
-----Decoded View---------------
Arg [0] : _siloFactory (address): 0x4D919CEcfD4793c0D47866C8d0a02a0950737589
Arg [1] : _tokensFactory (address): 0x0e37Df413f97fC198a84A21BC463C41b516AD622
Arg [2] : _defaultMaxLTV (uint64): 500000000000000000
Arg [3] : _defaultLiquidationThreshold (uint64): 600000000000000000
Arg [4] : _initialBridgeAssets (address[]): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000004d919cecfd4793c0d47866c8d0a02a0950737589
Arg [1] : 0000000000000000000000000e37df413f97fc198a84a21bc463c41b516ad622
Arg [2] : 00000000000000000000000000000000000000000000000006f05b59d3b20000
Arg [3] : 0000000000000000000000000000000000000000000000000853a0d2313c0000
Arg [4] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [6] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.