creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/Silo.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./interfaces/ISilo.sol\";\n\nimport \"./lib/EasyMath.sol\";\nimport \"./BaseSilo.sol\";\n\n/// @title Silo\n/// @notice Silo is the main component of the protocol. It implements lending logic, manages and isolates\n/// risk, acts as a vault for assets, and performs liquidations. Each Silo is composed of the unique asset\n/// for which it was created (ie. UNI) and bridge assets (ie. ETH and SiloDollar). There may be multiple\n/// bridge assets at any given time.\n/// @dev Main Silo contact that inherits from Base contract. It implements all user/UI facing methods.\n/// @custom:security-contact security@silo.finance\ncontract Silo is ISilo, BaseSilo {\n    using SafeERC20 for ERC20;\n    using EasyMath for uint256;\n\n    constructor (ISiloRepository _repository, address _siloAsset, uint128 _version)\n        BaseSilo(_repository, _siloAsset, _version)\n    {\n        // initial setup is done in BaseSilo, nothing to do here\n    }\n\n    /// @inheritdoc ISilo\n    function deposit(address _asset, uint256 _amount, bool _collateralOnly)\n        external\n        override\n        returns (uint256 collateralAmount, uint256 collateralShare)\n    {\n        return _deposit(_asset, msg.sender, msg.sender, _amount, _collateralOnly);\n    }\n\n    /// @inheritdoc ISilo\n    function depositFor(\n        address _asset,\n        address _depositor,\n        uint256 _amount,\n        bool _collateralOnly\n    )\n        external\n        override\n        returns (uint256 collateralAmount, uint256 collateralShare)\n    {\n        return _deposit(_asset, msg.sender, _depositor, _amount, _collateralOnly);\n    }\n\n    /// @inheritdoc ISilo\n    function withdraw(address _asset, uint256 _amount, bool _collateralOnly)\n        external\n        override\n        returns (uint256 withdrawnAmount, uint256 withdrawnShare)\n    {\n        return _withdraw(_asset, msg.sender, msg.sender, _amount, _collateralOnly);\n    }\n\n    /// @inheritdoc ISilo\n    function withdrawFor(address _asset, address _depositor, address _receiver, uint256 _amount, bool _collateralOnly)\n        external\n        override\n        onlyRouter\n        returns (uint256 withdrawnAmount, uint256 withdrawnShare)\n    {\n        return _withdraw(_asset, _depositor, _receiver, _amount, _collateralOnly);\n    }\n\n    /// @inheritdoc ISilo\n    function borrow(address _asset, uint256 _amount) external override returns (uint256 debtAmount, uint256 debtShare) {\n        return _borrow(_asset, msg.sender, msg.sender, _amount);\n    }\n\n    /// @inheritdoc ISilo\n    function borrowFor(address _asset, address _borrower, address _receiver, uint256 _amount)\n        external\n        override\n        onlyRouter\n        returns (uint256 debtAmount, uint256 debtShare)\n    {\n        return _borrow(_asset, _borrower, _receiver, _amount);\n    }\n\n    /// @inheritdoc ISilo\n    function repay(address _asset, uint256 _amount)\n        external\n        override\n        returns (uint256 repaidAmount, uint256 repaidShare)\n    {\n        return _repay(_asset, msg.sender, msg.sender, _amount);\n    }\n\n    /// @inheritdoc ISilo\n    function repayFor(address _asset, address _borrower, uint256 _amount)\n        external\n        override\n        returns (uint256 repaidAmount, uint256 repaidShare)\n    {\n        return _repay(_asset, _borrower, msg.sender, _amount);\n    }\n\n    /// @inheritdoc ISilo\n    function flashLiquidate(address[] memory _users, bytes memory _flashReceiverData)\n        external\n        override\n        returns (\n            address[] memory assets,\n            uint256[][] memory receivedCollaterals,\n            uint256[][] memory shareAmountsToRepay\n        )\n    {\n        assets = getAssets();\n        uint256 usersLength = _users.length;\n        receivedCollaterals = new uint256[][](usersLength);\n        shareAmountsToRepay = new uint256[][](usersLength);\n\n        for (uint256 i = 0; i < usersLength; i++) {\n            (\n                receivedCollaterals[i],\n                shareAmountsToRepay[i]\n            ) = _userLiquidation(assets, _users[i], IFlashLiquidationReceiver(msg.sender), _flashReceiverData);\n        }\n    }\n\n    /// @inheritdoc ISilo\n    function harvestProtocolFees() external override returns (uint256[] memory harvestedAmounts) {\n        address[] memory assets = getAssets();\n        harvestedAmounts = new uint256[](assets.length);\n\n        address repositoryOwner = siloRepository.owner();\n\n        for (uint256 i; i < assets.length;) {\n            unchecked {\n                // it will not overflow because fee is much lower than any other amounts\n                harvestedAmounts[i] = _harvestProtocolFees(assets[i], repositoryOwner);\n                // we run out of gas before we overflow i\n                i++;\n            }\n        }\n    }\n\n    /// @inheritdoc ISilo\n    function accrueInterest(address _asset) public override returns (uint256 interest) {\n        return _accrueInterest(_asset);\n    }\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/ERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n    mapping(address => uint256) private _balances;\n\n    mapping(address => mapping(address => uint256)) private _allowances;\n\n    uint256 private _totalSupply;\n\n    string private _name;\n    string private _symbol;\n\n    /**\n     * @dev Sets the values for {name} and {symbol}.\n     *\n     * The default value of {decimals} is 18. To select a different value for\n     * {decimals} you should overload it.\n     *\n     * All two of these values are immutable: they can only be set once during\n     * construction.\n     */\n    constructor(string memory name_, string memory symbol_) {\n        _name = name_;\n        _symbol = symbol_;\n    }\n\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() public view virtual override returns (string memory) {\n        return _name;\n    }\n\n    /**\n     * @dev Returns the symbol of the token, usually a shorter version of the\n     * name.\n     */\n    function symbol() public view virtual override returns (string memory) {\n        return _symbol;\n    }\n\n    /**\n     * @dev Returns the number of decimals used to get its user representation.\n     * For example, if `decimals` equals `2`, a balance of `505` tokens should\n     * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n     *\n     * Tokens usually opt for a value of 18, imitating the relationship between\n     * Ether and Wei. This is the value {ERC20} uses, unless this function is\n     * overridden;\n     *\n     * NOTE: This information is only used for _display_ purposes: it in\n     * no way affects any of the arithmetic of the contract, including\n     * {IERC20-balanceOf} and {IERC20-transfer}.\n     */\n    function decimals() public view virtual override returns (uint8) {\n        return 18;\n    }\n\n    /**\n     * @dev See {IERC20-totalSupply}.\n     */\n    function totalSupply() public view virtual override returns (uint256) {\n        return _totalSupply;\n    }\n\n    /**\n     * @dev See {IERC20-balanceOf}.\n     */\n    function balanceOf(address account) public view virtual override returns (uint256) {\n        return _balances[account];\n    }\n\n    /**\n     * @dev See {IERC20-transfer}.\n     *\n     * Requirements:\n     *\n     * - `recipient` cannot be the zero address.\n     * - the caller must have a balance of at least `amount`.\n     */\n    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n        _transfer(_msgSender(), recipient, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-allowance}.\n     */\n    function allowance(address owner, address spender) public view virtual override returns (uint256) {\n        return _allowances[owner][spender];\n    }\n\n    /**\n     * @dev See {IERC20-approve}.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function approve(address spender, uint256 amount) public virtual override returns (bool) {\n        _approve(_msgSender(), spender, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-transferFrom}.\n     *\n     * Emits an {Approval} event indicating the updated allowance. This is not\n     * required by the EIP. See the note at the beginning of {ERC20}.\n     *\n     * Requirements:\n     *\n     * - `sender` and `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `amount`.\n     * - the caller must have allowance for ``sender``'s tokens of at least\n     * `amount`.\n     */\n    function transferFrom(\n        address sender,\n        address recipient,\n        uint256 amount\n    ) public virtual override returns (bool) {\n        _transfer(sender, recipient, amount);\n\n        uint256 currentAllowance = _allowances[sender][_msgSender()];\n        require(currentAllowance >= amount, \"ERC20: transfer amount exceeds allowance\");\n        unchecked {\n            _approve(sender, _msgSender(), currentAllowance - amount);\n        }\n\n        return true;\n    }\n\n    /**\n     * @dev Atomically increases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);\n        return true;\n    }\n\n    /**\n     * @dev Atomically decreases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `spender` must have allowance for the caller of at least\n     * `subtractedValue`.\n     */\n    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n        uint256 currentAllowance = _allowances[_msgSender()][spender];\n        require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n        unchecked {\n            _approve(_msgSender(), spender, currentAllowance - subtractedValue);\n        }\n\n        return true;\n    }\n\n    /**\n     * @dev Moves `amount` of tokens from `sender` to `recipient`.\n     *\n     * This internal function is equivalent to {transfer}, and can be used to\n     * e.g. implement automatic token fees, slashing mechanisms, etc.\n     *\n     * Emits a {Transfer} event.\n     *\n     * Requirements:\n     *\n     * - `sender` cannot be the zero address.\n     * - `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `amount`.\n     */\n    function _transfer(\n        address sender,\n        address recipient,\n        uint256 amount\n    ) internal virtual {\n        require(sender != address(0), \"ERC20: transfer from the zero address\");\n        require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n        _beforeTokenTransfer(sender, recipient, amount);\n\n        uint256 senderBalance = _balances[sender];\n        require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n        unchecked {\n            _balances[sender] = senderBalance - amount;\n        }\n        _balances[recipient] += amount;\n\n        emit Transfer(sender, recipient, amount);\n\n        _afterTokenTransfer(sender, recipient, amount);\n    }\n\n    /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n     * the total supply.\n     *\n     * Emits a {Transfer} event with `from` set to the zero address.\n     *\n     * Requirements:\n     *\n     * - `account` cannot be the zero address.\n     */\n    function _mint(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: mint to the zero address\");\n\n        _beforeTokenTransfer(address(0), account, amount);\n\n        _totalSupply += amount;\n        _balances[account] += amount;\n        emit Transfer(address(0), account, amount);\n\n        _afterTokenTransfer(address(0), account, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, reducing the\n     * total supply.\n     *\n     * Emits a {Transfer} event with `to` set to the zero address.\n     *\n     * Requirements:\n     *\n     * - `account` cannot be the zero address.\n     * - `account` must have at least `amount` tokens.\n     */\n    function _burn(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: burn from the zero address\");\n\n        _beforeTokenTransfer(account, address(0), amount);\n\n        uint256 accountBalance = _balances[account];\n        require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n        unchecked {\n            _balances[account] = accountBalance - amount;\n        }\n        _totalSupply -= amount;\n\n        emit Transfer(account, address(0), amount);\n\n        _afterTokenTransfer(account, address(0), amount);\n    }\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n     *\n     * This internal function is equivalent to `approve`, and can be used to\n     * e.g. set automatic allowances for certain subsystems, etc.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `owner` cannot be the zero address.\n     * - `spender` cannot be the zero address.\n     */\n    function _approve(\n        address owner,\n        address spender,\n        uint256 amount\n    ) internal virtual {\n        require(owner != address(0), \"ERC20: approve from the zero address\");\n        require(spender != address(0), \"ERC20: approve to the zero address\");\n\n        _allowances[owner][spender] = amount;\n        emit Approval(owner, spender, amount);\n    }\n\n    /**\n     * @dev Hook that is called before any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n     * will be transferred to `to`.\n     * - when `from` is zero, `amount` tokens will be minted for `to`.\n     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _beforeTokenTransfer(\n        address from,\n        address to,\n        uint256 amount\n    ) internal virtual {}\n\n    /**\n     * @dev Hook that is called after any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n     * has been transferred to `to`.\n     * - when `from` is zero, `amount` tokens have been minted for `to`.\n     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _afterTokenTransfer(\n        address from,\n        address to,\n        uint256 amount\n    ) internal virtual {}\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    function safeTransfer(\n        IERC20 token,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    function safeTransferFrom(\n        IERC20 token,\n        address from,\n        address to,\n        uint256 value\n    ) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n        require(\n            (value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    function safeIncreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        uint256 newAllowance = token.allowance(address(this), spender) + value;\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    function safeDecreaseAllowance(\n        IERC20 token,\n        address spender,\n        uint256 value\n    ) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            uint256 newAllowance = oldAllowance - value;\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        if (returndata.length > 0) {\n            // Return data is optional\n            require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n        }\n    }\n}\n"
      },
      "contracts/interfaces/ISilo.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\nimport \"./IBaseSilo.sol\";\n\ninterface ISilo is IBaseSilo {\n    /// @notice Deposit `_amount` of `_asset` tokens from `msg.sender` to the Silo\n    /// @param _asset The address of the token to deposit\n    /// @param _amount The amount of the token to deposit\n    /// @param _collateralOnly True if depositing collateral only\n    /// @return collateralAmount deposited amount\n    /// @return collateralShare user collateral shares based on deposited amount\n    function deposit(address _asset, uint256 _amount, bool _collateralOnly)\n        external\n        returns (uint256 collateralAmount, uint256 collateralShare);\n\n    /// @notice Router function to deposit `_amount` of `_asset` tokens to the Silo for the `_depositor`\n    /// @param _asset The address of the token to deposit\n    /// @param _depositor The address of the recipient of collateral tokens\n    /// @param _amount The amount of the token to deposit\n    /// @param _collateralOnly True if depositing collateral only\n    /// @return collateralAmount deposited amount\n    /// @return collateralShare `_depositor` collateral shares based on deposited amount\n    function depositFor(address _asset, address _depositor, uint256 _amount, bool _collateralOnly)\n        external\n        returns (uint256 collateralAmount, uint256 collateralShare);\n\n    /// @notice Withdraw `_amount` of `_asset` tokens from the Silo to `msg.sender`\n    /// @param _asset The address of the token to withdraw\n    /// @param _amount The amount of the token to withdraw\n    /// @param _collateralOnly True if withdrawing collateral only deposit\n    /// @return withdrawnAmount withdrawn amount that was transferred to user\n    /// @return withdrawnShare burned share based on `withdrawnAmount`\n    function withdraw(address _asset, uint256 _amount, bool _collateralOnly)\n        external\n        returns (uint256 withdrawnAmount, uint256 withdrawnShare);\n\n    /// @notice Router function to withdraw `_amount` of `_asset` tokens from the Silo for the `_depositor`\n    /// @param _asset The address of the token to withdraw\n    /// @param _depositor The address that originally deposited the collateral tokens being withdrawn,\n    /// it should be the one initiating the withdrawal through the router\n    /// @param _receiver The address that will receive the withdrawn tokens\n    /// @param _amount The amount of the token to withdraw\n    /// @param _collateralOnly True if withdrawing collateral only deposit\n    /// @return withdrawnAmount withdrawn amount that was transferred to `_receiver`\n    /// @return withdrawnShare burned share based on `withdrawnAmount`\n    function withdrawFor(\n        address _asset,\n        address _depositor,\n        address _receiver,\n        uint256 _amount,\n        bool _collateralOnly\n    ) external returns (uint256 withdrawnAmount, uint256 withdrawnShare);\n\n    /// @notice Borrow `_amount` of `_asset` tokens from the Silo to `msg.sender`\n    /// @param _asset The address of the token to borrow\n    /// @param _amount The amount of the token to borrow\n    /// @return debtAmount borrowed amount\n    /// @return debtShare user debt share based on borrowed amount\n    function borrow(address _asset, uint256 _amount) external returns (uint256 debtAmount, uint256 debtShare);\n\n    /// @notice Router function to borrow `_amount` of `_asset` tokens from the Silo for the `_receiver`\n    /// @param _asset The address of the token to borrow\n    /// @param _borrower The address that will take the loan,\n    /// it should be the one initiating the borrowing through the router\n    /// @param _receiver The address of the asset receiver\n    /// @param _amount The amount of the token to borrow\n    /// @return debtAmount borrowed amount\n    /// @return debtShare `_receiver` debt share based on borrowed amount\n    function borrowFor(address _asset, address _borrower, address _receiver, uint256 _amount)\n        external\n        returns (uint256 debtAmount, uint256 debtShare);\n\n    /// @notice Repay `_amount` of `_asset` tokens from `msg.sender` to the Silo\n    /// @param _asset The address of the token to repay\n    /// @param _amount amount of asset to repay, includes interests\n    /// @return repaidAmount amount repaid\n    /// @return burnedShare burned debt share\n    function repay(address _asset, uint256 _amount) external returns (uint256 repaidAmount, uint256 burnedShare);\n\n    /// @notice Allows to repay in behalf of borrower to execute liquidation\n    /// @param _asset The address of the token to repay\n    /// @param _borrower The address of the user to have debt tokens burned\n    /// @param _amount amount of asset to repay, includes interests\n    /// @return repaidAmount amount repaid\n    /// @return burnedShare burned debt share\n    function repayFor(address _asset, address _borrower, uint256 _amount)\n        external\n        returns (uint256 repaidAmount, uint256 burnedShare);\n\n    /// @dev harvest protocol fees from an array of assets\n    /// @return harvestedAmounts amount harvested during tx execution for each of silo asset\n    function harvestProtocolFees() external returns (uint256[] memory harvestedAmounts);\n\n    /// @notice Function to update interests for `_asset` token since the last saved state\n    /// @param _asset The address of the token to be updated\n    /// @return interest accrued interest\n    function accrueInterest(address _asset) external returns (uint256 interest);\n\n    /// @notice this methods does not requires to have tokens in order to liquidate user\n    /// @dev during liquidation process, msg.sender will be notified once all collateral will be send to him\n    /// msg.sender needs to be `IFlashLiquidationReceiver`\n    /// @param _users array of users to liquidate\n    /// @param _flashReceiverData this data will be forward to msg.sender on notification\n    /// @return assets array of all processed assets (collateral + debt, including removed)\n    /// @return receivedCollaterals receivedCollaterals[userId][assetId] => amount\n    /// amounts of collaterals send to `_flashReceiver`\n    /// @return shareAmountsToRepaid shareAmountsToRepaid[userId][assetId] => amount\n    /// required amounts of debt to be repaid\n    function flashLiquidate(address[] memory _users, bytes memory _flashReceiverData)\n        external\n        returns (\n            address[] memory assets,\n            uint256[][] memory receivedCollaterals,\n            uint256[][] memory shareAmountsToRepaid\n        );\n}\n"
      },
      "contracts/lib/EasyMath.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\nlibrary EasyMath {\n    error ZeroAssets();\n    error ZeroShares();\n\n    function toShare(uint256 amount, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) {\n        if (totalShares == 0 || totalAmount == 0) {\n            return amount;\n        }\n\n        uint256 result = amount * totalShares / totalAmount;\n\n        // Prevent rounding error\n        if (result == 0 && amount != 0) {\n            revert ZeroShares();\n        }\n\n        return result;\n    }\n\n    function toShareRoundUp(uint256 amount, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) {\n        if (totalShares == 0 || totalAmount == 0) {\n            return amount;\n        }\n\n        uint256 numerator = amount * totalShares;\n        uint256 result = numerator / totalAmount;\n        \n        // Round up\n        if (numerator % totalAmount != 0) {\n            result += 1;\n        }\n\n        return result;\n    }\n\n    function toAmount(uint256 share, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) {\n        if (totalShares == 0 || totalAmount == 0) {\n            return 0;\n        }\n\n        uint256 result = share * totalAmount / totalShares;\n\n        // Prevent rounding error\n        if (result == 0 && share != 0) {\n            revert ZeroAssets();\n        }\n\n        return result;\n    }\n\n    function toAmountRoundUp(uint256 share, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) {\n        if (totalShares == 0 || totalAmount == 0) {\n            return 0;\n        }\n\n        uint256 numerator = share * totalAmount;\n        uint256 result = numerator / totalShares;\n        \n        // Round up\n        if (numerator % totalShares != 0) {\n            result += 1;\n        }\n\n        return result;\n    }\n\n    function toValue(uint256 _assetAmount, uint256 _assetPrice, uint256 _assetDecimals)\n        internal\n        pure\n        returns (uint256)\n    {\n        return _assetAmount * _assetPrice / 10 ** _assetDecimals;\n    }\n\n    function sum(uint256[] memory _numbers) internal pure returns (uint256 s) {\n        for(uint256 i; i < _numbers.length; i++) {\n            s += _numbers[i];\n        }\n    }\n\n    /// @notice Calculates fraction between borrowed and deposited amount of tokens denominated in percentage\n    /// @dev It assumes `_dp` = 100%.\n    /// @param _dp decimal points used by model\n    /// @param _totalDeposits current total deposits for assets\n    /// @param _totalBorrowAmount current total borrows for assets\n    /// @return utilization value\n    function calculateUtilization(uint256 _dp, uint256 _totalDeposits, uint256 _totalBorrowAmount)\n        internal\n        pure\n        returns (uint256)\n    {\n        if (_totalDeposits == 0 || _totalBorrowAmount == 0) return 0;\n\n        return _totalBorrowAmount * _dp / _totalDeposits;\n    }\n}\n"
      },
      "contracts/BaseSilo.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\n\nimport \"./utils/LiquidationReentrancyGuard.sol\";\n\nimport \"./interfaces/IBaseSilo.sol\";\nimport \"./interfaces/IGuardedLaunch.sol\";\nimport \"./interfaces/ISiloRepository.sol\";\nimport \"./interfaces/IPriceProvidersRepository.sol\";\nimport \"./interfaces/IInterestRateModel.sol\";\nimport \"./interfaces/IShareToken.sol\";\n\nimport \"./lib/Ping.sol\";\nimport \"./lib/EasyMath.sol\";\nimport \"./lib/TokenHelper.sol\";\nimport \"./lib/Solvency.sol\";\n\n/// @title BaseSilo\n/// @dev Base contract for Silo core logic.\n/// @custom:security-contact security@silo.finance\nabstract contract BaseSilo is IBaseSilo, ReentrancyGuard, LiquidationReentrancyGuard {\n    using SafeERC20 for ERC20;\n    using EasyMath for uint256;\n\n    ISiloRepository immutable public override siloRepository;\n\n    // asset address for which Silo was created\n    address public immutable siloAsset;\n\n    /// @dev version of silo\n    /// @notice It tells us which `SiloRepository.siloFactory(version)` created this Silo\n    uint128 public immutable VERSION; // solhint-disable-line var-name-mixedcase\n\n    // solhint-disable-next-line var-name-mixedcase\n    uint256 private immutable _ASSET_DECIMAL_POINTS;\n\n    /// @dev stores all *synced* assets (bridge assets + removed bridge assets + siloAsset)\n    address[] private _allSiloAssets;\n\n    /// @dev asset => AssetStorage\n    mapping(address => AssetStorage) private _assetStorage;\n\n    /// @dev asset => AssetInterestData\n    mapping(address => AssetInterestData) private _interestData;\n\n    error AssetDoesNotExist();\n    error BorrowNotPossible();\n    error DepositNotPossible();\n    error DepositsExceedLimit();\n    error InvalidRepository();\n    error InvalidSiloVersion();\n    error MaximumLTVReached();\n    error NotEnoughLiquidity();\n    error NotEnoughDeposits();\n    error NotSolvent();\n    error OnlyRouter();\n    error Paused();\n    error UnexpectedEmptyReturn();\n    error UserIsZero();\n\n    modifier onlyExistingAsset(address _asset) {\n        if (_interestData[_asset].status == AssetStatus.Undefined) {\n            revert AssetDoesNotExist();\n        }\n\n        _;\n    }\n\n    modifier onlyRouter() {\n        if (msg.sender != siloRepository.router()) revert OnlyRouter();\n\n        _;\n    }\n\n    modifier validateMaxDepositsAfter(address _asset) {\n        _;\n\n        IPriceProvidersRepository priceProviderRepo = siloRepository.priceProvidersRepository();\n\n        AssetStorage storage _assetState = _assetStorage[_asset];\n        uint256 allDeposits = _assetState.totalDeposits + _assetState.collateralOnlyDeposits;\n\n        if (\n            priceProviderRepo.getPrice(_asset) * allDeposits / (10 ** IERC20Metadata(_asset).decimals()) >\n            IGuardedLaunch(address(siloRepository)).getMaxSiloDepositsValue(address(this), _asset)\n        ) {\n            revert DepositsExceedLimit();\n        }\n    }\n\n    constructor (ISiloRepository _repository, address _siloAsset, uint128 _version) {\n        if (!Ping.pong(_repository.siloRepositoryPing)) revert InvalidRepository();\n        if (_version == 0) revert InvalidSiloVersion();\n\n        uint256 decimals = TokenHelper.assertAndGetDecimals(_siloAsset);\n\n        VERSION = _version;\n        siloRepository = _repository;\n        siloAsset = _siloAsset;\n        _ASSET_DECIMAL_POINTS = 10**decimals;\n    }\n\n    /// @dev this is exposed only for test purposes, but it is safe to leave it like that\n    function initAssetsTokens() external nonReentrant {\n        _initAssetsTokens();\n    }\n\n    /// @inheritdoc IBaseSilo\n    function syncBridgeAssets() external override nonReentrant {\n        // sync removed assets\n        address[] memory removedBridgeAssets = siloRepository.getRemovedBridgeAssets();\n\n        for (uint256 i = 0; i < removedBridgeAssets.length; i++) {\n            // If removed bridge asset is the silo asset for this silo, do not remove it\n            address removedBridgeAsset = removedBridgeAssets[i];\n            if (removedBridgeAsset != siloAsset) {\n                _interestData[removedBridgeAsset].status = AssetStatus.Removed;\n                emit AssetStatusUpdate(removedBridgeAsset, AssetStatus.Removed);\n            }\n        }\n\n        // must be called at the end, because we overriding `_assetStorage[removedBridgeAssets[i]].removed`\n        _initAssetsTokens();\n    }\n\n    /// @inheritdoc IBaseSilo\n    function assetStorage(address _asset) external view override returns (AssetStorage memory) {\n        return _assetStorage[_asset];\n    }\n\n    /// @inheritdoc IBaseSilo\n    function interestData(address _asset) external view override returns (AssetInterestData memory) {\n        return _interestData[_asset];\n    }\n\n    /// @inheritdoc IBaseSilo\n    function utilizationData(address _asset) external view override returns (UtilizationData memory data) {\n        AssetStorage storage _assetState = _assetStorage[_asset];\n\n        return UtilizationData(\n            _assetState.totalDeposits,\n            _assetState.totalBorrowAmount,\n            _interestData[_asset].interestRateTimestamp\n        );\n    }\n\n    /// @inheritdoc IBaseSilo\n    function getAssets() public view override returns (address[] memory assets) {\n        return _allSiloAssets;\n    }\n\n    /// @inheritdoc IBaseSilo\n    function getAssetsWithState() public view override returns (\n        address[] memory assets,\n        AssetStorage[] memory assetsStorage\n    ) {\n        assets = _allSiloAssets;\n        assetsStorage = new AssetStorage[](assets.length);\n\n        for (uint256 i = 0; i < assets.length; i++) {\n            assetsStorage[i] = _assetStorage[assets[i]];\n        }\n    }\n\n    /// @inheritdoc IBaseSilo\n    function isSolvent(address _user) public view override returns (bool) {\n        if (_user == address(0)) revert UserIsZero();\n\n        (address[] memory assets, AssetStorage[] memory assetsStates) = getAssetsWithState();\n\n        (uint256 userLTV, uint256 liquidationThreshold) = Solvency.calculateLTVs(\n            Solvency.SolvencyParams(\n                siloRepository,\n                ISilo(address(this)),\n                assets,\n                assetsStates,\n                _user\n            ),\n            Solvency.TypeofLTV.LiquidationThreshold\n        );\n\n        return userLTV <= liquidationThreshold;\n    }\n\n    /// @inheritdoc IBaseSilo\n    function depositPossible(address _asset, address _depositor) public view override returns (bool) {\n        return _assetStorage[_asset].debtToken.balanceOf(_depositor) == 0\n            && _interestData[_asset].status == AssetStatus.Active;\n    }\n\n    /// @inheritdoc IBaseSilo\n    function borrowPossible(address _asset, address _borrower) public view override returns (bool) {\n        AssetStorage storage _assetState = _assetStorage[_asset];\n\n        return _assetState.collateralToken.balanceOf(_borrower) == 0\n            && _assetState.collateralOnlyToken.balanceOf(_borrower) == 0\n            && _interestData[_asset].status == AssetStatus.Active;\n    }\n\n    /// @inheritdoc IBaseSilo\n    function liquidity(address _asset) public view returns (uint256) {\n        return ERC20(_asset).balanceOf(address(this)) - _assetStorage[_asset].collateralOnlyDeposits;\n    }\n\n    /// @dev Initiate asset by deploying accounting EC20 tokens for collateral and debt\n    /// @param _tokensFactory factory contract that deploys collateral and debt tokens\n    /// @param _asset which asset to initialize\n    /// @param _isBridgeAsset true if initialized asset is a bridge asset\n    function _initAsset(ITokensFactory _tokensFactory, address _asset, bool _isBridgeAsset) internal {\n        AssetSharesMetadata memory metadata = _generateSharesNames(_asset, _isBridgeAsset);\n\n        AssetStorage storage _assetState = _assetStorage[_asset];\n\n        _assetState.collateralToken = _tokensFactory.createShareCollateralToken(\n            metadata.collateralName, metadata.collateralSymbol, _asset\n        );\n\n        _assetState.collateralOnlyToken = _tokensFactory.createShareCollateralToken(\n            metadata.protectedName, metadata.protectedSymbol, _asset\n        );\n\n        _assetState.debtToken = _tokensFactory.createShareDebtToken(\n            metadata.debtName, metadata.debtSymbol, _asset\n        );\n\n        // keep synced asset in storage array\n        _allSiloAssets.push(_asset);\n        _interestData[_asset].status = AssetStatus.Active;\n        emit AssetStatusUpdate(_asset, AssetStatus.Active);\n    }\n\n    /// @dev Initializes all assets (bridge assets + unique asset) for Silo but only if asset has not been\n    /// initialized already. It's safe to call it multiple times. It's safe for anyone to call it at any time.\n    function _initAssetsTokens() internal {\n        ITokensFactory tokensFactory = siloRepository.tokensFactory();\n\n        // init silo asset if needed\n        if (address(_assetStorage[siloAsset].collateralToken) == address(0)) {\n            _initAsset(tokensFactory, siloAsset, false);\n        }\n\n        // sync active assets\n        address[] memory bridgeAssets = siloRepository.getBridgeAssets();\n\n        for (uint256 i = 0; i < bridgeAssets.length; i++) {\n            address bridgeAsset = bridgeAssets[i];\n            // In case a bridge asset is added that already has a Silo,\n            // do not initiate that asset in its Silo\n            if (address(_assetStorage[bridgeAsset].collateralToken) == address(0)) {\n                _initAsset(tokensFactory, bridgeAsset, true);\n            } else {\n                _interestData[bridgeAsset].status = AssetStatus.Active;\n                emit AssetStatusUpdate(bridgeAsset, AssetStatus.Active);\n            }\n        }\n    }\n\n    /// @dev Generate asset shares tokens names and symbols\n    /// @param _asset asset for which shares tokens will be initializaed\n    /// @param _isBridgeAsset true if initialized asset is a bridge asset\n    function _generateSharesNames(address _asset, bool _isBridgeAsset)\n        internal\n        view\n        returns (AssetSharesMetadata memory metadata)\n    {\n        // Naming convention in UNI example:\n        // - for siloAsset: sUNI, dUNI, spUNI\n        // - for bridgeAsset: sWETH-UNI, dWETH-UNI, spWETH-UNI\n        string memory assetSymbol = TokenHelper.symbol(_asset);\n\n        metadata = AssetSharesMetadata({\n            collateralName: string.concat(\"Silo Finance Borrowable \", assetSymbol, \" Deposit\"),\n            collateralSymbol: string.concat(\"s\", assetSymbol),\n            protectedName: string.concat(\"Silo Finance Protected \", assetSymbol, \" Deposit\"),\n            protectedSymbol: string.concat(\"sp\", assetSymbol),\n            debtName: string.concat(\"Silo Finance \", assetSymbol, \" Debt\"),\n            debtSymbol: string.concat(\"d\", assetSymbol)\n        });\n\n        if (_isBridgeAsset) {\n            string memory baseSymbol = TokenHelper.symbol(siloAsset);\n\n            metadata.collateralName = string.concat(metadata.collateralName, \" in \", baseSymbol, \" Silo\");\n            metadata.collateralSymbol = string.concat(metadata.collateralSymbol, \"-\", baseSymbol);\n\n            metadata.protectedName = string.concat(metadata.protectedName, \" in \", baseSymbol, \" Silo\");\n            metadata.protectedSymbol = string.concat(metadata.protectedSymbol, \"-\", baseSymbol);\n\n            metadata.debtName = string.concat(metadata.debtName, \" in \", baseSymbol, \" Silo\");\n            metadata.debtSymbol = string.concat(metadata.debtSymbol, \"-\", baseSymbol);\n        }\n    }\n\n    /// @dev Main deposit function that handles all deposit logic and validation\n    /// @param _asset asset address that is being deposited\n    /// @param _from wallet address form which to pull asset tokens\n    /// @param _depositor wallet address that will be granted ownership of deposited tokens. Keep in mind\n    /// that deposit can be made by Router contract but the owner of the deposit should be user.\n    /// @param _amount deposit amount\n    /// @param _collateralOnly true if deposit should be used for collateral only. Otherwise false.\n    /// Collateral only deposit cannot be borrowed by anyone and does not earn any interest. However,\n    /// it can be used as collateral and can be subject to liquidation.\n    /// @return collateralAmount deposited amount\n    /// @return collateralShare `_depositor` collateral shares based on deposited amount\n    function _deposit(\n        address _asset,\n        address _from,\n        address _depositor,\n        uint256 _amount,\n        bool _collateralOnly\n    )\n        internal\n        nonReentrant\n        validateMaxDepositsAfter(_asset)\n        returns (uint256 collateralAmount, uint256 collateralShare)\n    {\n        // MUST BE CALLED AS FIRST METHOD!\n        _accrueInterest(_asset);\n\n        if (!depositPossible(_asset, _depositor)) revert DepositNotPossible();\n\n        AssetStorage storage _state = _assetStorage[_asset];\n\n        collateralAmount = _amount;\n\n        uint256 totalDepositsCached = _collateralOnly ? _state.collateralOnlyDeposits : _state.totalDeposits;\n\n        if (_collateralOnly) {\n            collateralShare = _amount.toShare(totalDepositsCached, _state.collateralOnlyToken.totalSupply());\n            _state.collateralOnlyDeposits = totalDepositsCached + _amount;\n            _state.collateralOnlyToken.mint(_depositor, collateralShare);\n        } else {\n            collateralShare = _amount.toShare(totalDepositsCached, _state.collateralToken.totalSupply());\n            _state.totalDeposits = totalDepositsCached + _amount;\n            _state.collateralToken.mint(_depositor, collateralShare);\n        }\n\n        ERC20(_asset).safeTransferFrom(_from, address(this), _amount);\n\n        emit Deposit(_asset, _depositor, _amount, _collateralOnly);\n    }\n\n    /// @dev Main withdraw function that handles all withdraw logic and validation\n    /// @param _asset asset address that is being withdrawn\n    /// @param _depositor wallet address that is an owner of the deposited tokens\n    /// @param _receiver wallet address that will receive withdrawn tokens. It's possible that Router\n    /// contract is the owner of deposited tokens but we want user to get these tokens directly.\n    /// @param _amount amount to withdraw. If amount is equal to maximum value stored by uint256 type\n    /// (type(uint256).max), it will be assumed that user wants to withdraw all tokens and final account\n    /// will be dynamically calculated including interest.\n    /// @param _collateralOnly true if collateral only tokens are to be withdrawn. Otherwise false.\n    /// User can deposit the same asset as collateral only and as regular deposit. During withdraw,\n    /// it must be specified which tokens are to be withdrawn.\n    /// @return withdrawnAmount withdrawn amount that was transferred to user\n    /// @return withdrawnShare burned share based on `withdrawnAmount`\n    function _withdraw(address _asset, address _depositor, address _receiver, uint256 _amount, bool _collateralOnly)\n        internal\n        nonReentrant // because we transferring tokens\n        onlyExistingAsset(_asset)\n        returns (uint256 withdrawnAmount, uint256 withdrawnShare)\n    {\n        // MUST BE CALLED AS FIRST METHOD!\n        _accrueInterest(_asset);\n\n        (withdrawnAmount, withdrawnShare) = _withdrawAsset(\n            _asset,\n            _amount,\n            _depositor,\n            _receiver,\n            _collateralOnly,\n            0 // do not apply any fees on regular withdraw\n        );\n\n        if (withdrawnAmount == 0) revert UnexpectedEmptyReturn();\n\n        if (!isSolvent(_depositor)) revert NotSolvent();\n\n        emit Withdraw(_asset, _depositor, _receiver, withdrawnAmount, _collateralOnly);\n    }\n\n    /// @dev Main borrow function that handles all borrow logic and validation\n    /// @param _asset asset address that is being borrowed\n    /// @param _borrower wallet address that will own debt\n    /// @param _receiver wallet address that will receive borrowed tokens. It's possible that Router\n    /// contract is executing borrowing for user and should be the one receiving tokens, however,\n    /// the owner of the debt should be user himself.\n    /// @param _amount amount of asset to borrow\n    /// @return debtAmount borrowed amount\n    /// @return debtShare user debt share based on borrowed amount\n    function _borrow(address _asset, address _borrower, address _receiver, uint256 _amount)\n        internal\n        nonReentrant\n        returns (uint256 debtAmount, uint256 debtShare)\n    {\n        // MUST BE CALLED AS FIRST METHOD!\n        _accrueInterest(_asset);\n\n        if (!borrowPossible(_asset, _borrower)) revert BorrowNotPossible();\n\n        if (liquidity(_asset) < _amount) revert NotEnoughLiquidity();\n\n        AssetStorage storage _state = _assetStorage[_asset];\n\n        uint256 totalBorrowAmount = _state.totalBorrowAmount;\n        uint256 entryFee = siloRepository.entryFee();\n        uint256 fee = entryFee == 0 ? 0 : _amount * entryFee / Solvency._PRECISION_DECIMALS;\n        debtShare = (_amount + fee).toShareRoundUp(totalBorrowAmount, _state.debtToken.totalSupply());\n        debtAmount = _amount;\n\n        _state.totalBorrowAmount = totalBorrowAmount + _amount + fee;\n        _interestData[_asset].protocolFees += fee;\n\n        _state.debtToken.mint(_borrower, debtShare);\n\n        emit Borrow(_asset, _borrower, _amount);\n        ERC20(_asset).safeTransfer(_receiver, _amount);\n\n        // IMPORTANT - keep `validateBorrowAfter` at the end\n        _validateBorrowAfter(_borrower);\n    }\n\n    /// @dev Main repay function that handles all repay logic and validation\n    /// @param _asset asset address that is being repaid\n    /// @param _borrower wallet address for which debt is being repaid\n    /// @param _repayer wallet address that will pay the debt. It's possible that Router\n    /// contract is executing repay for user and should be the one paying the debt.\n    /// @param _amount amount of asset to repay\n    /// @return repaidAmount amount repaid\n    /// @return repaidShare burned debt share\n    function _repay(address _asset, address _borrower, address _repayer, uint256 _amount)\n        internal\n        onlyExistingAsset(_asset)\n        nonReentrant\n        returns (uint256 repaidAmount, uint256 repaidShare)\n    {\n        // MUST BE CALLED AS FIRST METHOD!\n        _accrueInterest(_asset);\n\n        AssetStorage storage _state = _assetStorage[_asset];\n        (repaidAmount, repaidShare) = _calculateDebtAmountAndShare(_state, _borrower, _amount);\n\n        if (repaidShare == 0) revert UnexpectedEmptyReturn();\n\n        emit Repay(_asset, _borrower, repaidAmount);\n\n        ERC20(_asset).safeTransferFrom(_repayer, address(this), repaidAmount);\n\n        // change debt state before, because share token state is changes the same way (notification is after burn)\n        _state.totalBorrowAmount -= repaidAmount;\n        _state.debtToken.burn(_borrower, repaidShare);\n    }\n\n    /// @param _assets all current assets, this is an optimization, so we don't have to read it from storage few times\n    /// @param _user user to liquidate\n    /// @param _flashReceiver address which will get all collaterals and will be notified once collaterals will be send\n    /// @param _flashReceiverData custom data to forward to receiver\n    /// @return receivedCollaterals amounts of collaterals transferred to `_flashReceiver`\n    /// @return shareAmountsToRepay expected amounts to repay\n    function _userLiquidation(\n        address[] memory _assets,\n        address _user,\n        IFlashLiquidationReceiver _flashReceiver,\n        bytes memory _flashReceiverData\n    )\n        internal\n        // we can not use `nonReentrant` because we are using it in `_repay`,\n        // and `_repay` needs to be reentered as part of a liquidation\n        liquidationNonReentrant\n        returns (uint256[] memory receivedCollaterals, uint256[] memory shareAmountsToRepay)\n    {\n        // gracefully fail if _user is solvent\n        if (isSolvent(_user)) {\n            uint256[] memory empty = new uint256[](_assets.length);\n            return (empty, empty);\n        }\n\n        (receivedCollaterals, shareAmountsToRepay) = _flashUserLiquidation(_assets, _user, address(_flashReceiver));\n\n        // _flashReceiver needs to repayFor user\n        _flashReceiver.siloLiquidationCallback(\n            _user,\n            _assets,\n            receivedCollaterals,\n            shareAmountsToRepay,\n            _flashReceiverData\n        );\n\n        for (uint256 i = 0; i < _assets.length; i++) {\n            if (receivedCollaterals[i] != 0 || shareAmountsToRepay[i] != 0) {\n                emit Liquidate(_assets[i], _user, shareAmountsToRepay[i], receivedCollaterals[i]);\n            }\n        }\n\n        if (!isSolvent(_user)) revert NotSolvent();\n    }\n\n    function _flashUserLiquidation(address[] memory _assets, address _borrower, address _liquidator)\n        internal\n        returns (uint256[] memory receivedCollaterals, uint256[] memory amountsToRepay)\n    {\n        uint256 assetsLength = _assets.length;\n        receivedCollaterals = new uint256[](assetsLength);\n        amountsToRepay = new uint256[](assetsLength);\n\n        uint256 protocolLiquidationFee = siloRepository.protocolLiquidationFee();\n\n        for (uint256 i = 0; i < assetsLength; i++) {\n            _accrueInterest(_assets[i]);\n\n            AssetStorage storage _state = _assetStorage[_assets[i]];\n\n            // we do not allow for partial repayment on liquidation, that's why max\n            (amountsToRepay[i],) = _calculateDebtAmountAndShare(_state, _borrower, type(uint256).max);\n\n            (uint256 withdrawnOnlyAmount,) = _withdrawAsset(\n                _assets[i],\n                type(uint256).max,\n                _borrower,\n                _liquidator,\n                true, // collateral only\n                protocolLiquidationFee\n            );\n\n            (uint256 withdrawnAmount,) = _withdrawAsset(\n                _assets[i],\n                type(uint256).max,\n                _borrower,\n                _liquidator,\n                false, // collateral only\n                protocolLiquidationFee\n            );\n\n            receivedCollaterals[i] = withdrawnOnlyAmount + withdrawnAmount;\n        }\n    }\n\n    /// @dev Utility function for withdrawing an asset\n    /// @param _asset asset to withdraw\n    /// @param _assetAmount amount of asset to withdraw\n    /// @param _depositor wallet address that is an owner of the deposit\n    /// @param _receiver wallet address that is receiving the token\n    /// @param _collateralOnly true if withdraw collateral only.\n    /// @param _protocolLiquidationFee if provided (!=0) liquidation fees will be applied and returned\n    /// `withdrawnAmount` will be decreased\n    /// @return withdrawnAmount amount of asset that has been sent to receiver\n    /// @return burnedShare burned share based on `withdrawnAmount`\n    function _withdrawAsset(\n        address _asset,\n        uint256 _assetAmount,\n        address _depositor,\n        address _receiver,\n        bool _collateralOnly,\n        uint256 _protocolLiquidationFee\n    )\n        internal\n        returns (uint256 withdrawnAmount, uint256 burnedShare)\n    {\n        (uint256 assetTotalDeposits, IShareToken shareToken, uint256 availableLiquidity) =\n            _getWithdrawAssetData(_asset, _collateralOnly);\n\n        if (_assetAmount == type(uint256).max) {\n            burnedShare = shareToken.balanceOf(_depositor);\n            withdrawnAmount = burnedShare.toAmount(assetTotalDeposits, shareToken.totalSupply());\n        } else {\n            burnedShare = _assetAmount.toShareRoundUp(assetTotalDeposits, shareToken.totalSupply());\n            withdrawnAmount = _assetAmount;\n        }\n\n        if (withdrawnAmount == 0) {\n            // we can not revert here, because liquidation will fail when one of collaterals will be empty\n            return (0, 0);\n        }\n\n        if (assetTotalDeposits < withdrawnAmount) revert NotEnoughDeposits();\n\n        unchecked {\n            // can be unchecked because of the `if` above\n            assetTotalDeposits -=  withdrawnAmount;\n        }\n\n        uint256 amountToTransfer = _applyLiquidationFee(_asset, withdrawnAmount, _protocolLiquidationFee);\n\n        if (availableLiquidity < amountToTransfer) revert NotEnoughLiquidity();\n\n        AssetStorage storage _state = _assetStorage[_asset];\n\n        if (_collateralOnly) {\n            _state.collateralOnlyDeposits = assetTotalDeposits;\n        } else {\n            _state.totalDeposits = assetTotalDeposits;\n        }\n\n        shareToken.burn(_depositor, burnedShare);\n        // in case token sent in fee-on-transfer type of token we do not care when withdrawing\n        ERC20(_asset).safeTransfer(_receiver, amountToTransfer);\n    }\n\n    /// @notice Calculates liquidations fee and returns amount of asset transferred to liquidator\n    /// @param _asset asset address\n    /// @param _amount amount on which we will apply fee\n    /// @param _protocolLiquidationFee liquidation fee in Solvency._PRECISION_DECIMALS\n    /// @return change amount left after subtracting liquidation fee\n    function _applyLiquidationFee(address _asset, uint256 _amount, uint256 _protocolLiquidationFee)\n        internal\n        returns (uint256 change)\n    {\n        if (_protocolLiquidationFee == 0) {\n            return _amount;\n        }\n\n        uint256 liquidationFeeAmount;\n\n        (\n            liquidationFeeAmount,\n            _interestData[_asset].protocolFees\n        ) = Solvency.calculateLiquidationFee(_interestData[_asset].protocolFees, _amount, _protocolLiquidationFee);\n\n        unchecked {\n            // if fees will not be higher than 100% this will not underflow, this is responsibility of siloRepository\n            // in case we do underflow, we can expect liquidator reject tx because of too little change\n            change = _amount - liquidationFeeAmount;\n        }\n    }\n\n    /// @dev harvest protocol fees from particular asset\n    /// @param _asset asset we want to harvest fees from\n    /// @param _receiver address of fees receiver\n    /// @return harvestedFees harvested fee\n    function _harvestProtocolFees(address _asset, address _receiver)\n        internal\n        nonReentrant\n        returns (uint256 harvestedFees)\n    {\n        AssetInterestData storage data = _interestData[_asset];\n\n        harvestedFees = data.protocolFees - data.harvestedProtocolFees;\n\n        uint256 currentLiquidity = liquidity(_asset);\n\n        if (harvestedFees > currentLiquidity) {\n            harvestedFees = currentLiquidity;\n        }\n\n        if (harvestedFees == 0) {\n            return 0;\n        }\n\n        unchecked {\n            // This can't overflow because this addition is less than or equal to data.protocolFees\n            data.harvestedProtocolFees += harvestedFees;\n        }\n\n        ERC20(_asset).safeTransfer(_receiver, harvestedFees);\n    }\n\n    /// @notice Accrue interest for asset\n    /// @dev Silo Interest Rate Model implements dynamic interest rate that changes every second. Returned\n    /// interest rate by the model is compounded rate so it can be used in math calculations as if it was\n    /// static. Rate is calculated for the time range between last update and current timestamp.\n    /// @param _asset address of the asset for which interest should be accrued\n    /// @return accruedInterest total accrued interest\n    function _accrueInterest(address _asset) internal returns (uint256 accruedInterest) {\n        /// @dev `_accrueInterest` is called on every user action, including liquidation. It's enough to check\n        /// if Silo is paused in this function.\n        if (IGuardedLaunch(address(siloRepository)).isSiloPaused(address(this), _asset)) {\n            revert Paused();\n        }\n\n        AssetStorage storage _state = _assetStorage[_asset];\n        AssetInterestData storage _assetInterestData = _interestData[_asset];\n        uint256 lastTimestamp = _assetInterestData.interestRateTimestamp;\n\n        // This is the first time, so we can return early and save some gas\n        if (lastTimestamp == 0) {\n            _assetInterestData.interestRateTimestamp = uint64(block.timestamp);\n            return 0;\n        }\n\n        // Interest has already been accrued this block\n        if (lastTimestamp == block.timestamp) {\n            return 0;\n        }\n\n        uint256 rcomp = _getModel(_asset).getCompoundInterestRateAndUpdate(_asset, block.timestamp);\n        uint256 protocolShareFee = siloRepository.protocolShareFee();\n\n        uint256 totalBorrowAmountCached = _state.totalBorrowAmount;\n        uint256 protocolFeesCached = _assetInterestData.protocolFees;\n        uint256 newProtocolFees;\n        uint256 protocolShare;\n        uint256 depositorsShare;\n\n        accruedInterest = totalBorrowAmountCached * rcomp / Solvency._PRECISION_DECIMALS;\n\n        unchecked {\n            // If we overflow on multiplication it should not revert tx, we will get lower fees\n            protocolShare = accruedInterest * protocolShareFee / Solvency._PRECISION_DECIMALS;\n            newProtocolFees = protocolFeesCached + protocolShare;\n\n            if (newProtocolFees < protocolFeesCached) {\n                protocolShare = type(uint256).max - protocolFeesCached;\n                newProtocolFees = type(uint256).max;\n            }\n    \n            depositorsShare = accruedInterest - protocolShare;\n        }\n\n        // update contract state\n        _state.totalBorrowAmount = totalBorrowAmountCached + accruedInterest;\n        _state.totalDeposits = _state.totalDeposits + depositorsShare;\n        _assetInterestData.protocolFees = newProtocolFees;\n        _assetInterestData.interestRateTimestamp = uint64(block.timestamp);\n    }\n\n    /// @dev gets interest rates model object\n    /// @param _asset asset for which to calculate interest rate\n    /// @return IInterestRateModel interest rates model object\n    function _getModel(address _asset) internal view returns (IInterestRateModel) {\n        return IInterestRateModel(siloRepository.getInterestRateModel(address(this), _asset));\n    }\n\n    /// @dev calculates amount to repay based on user shares, we do not apply virtual balances here,\n    /// if needed, they need to be apply beforehand\n    /// @param _state asset storage struct\n    /// @param _borrower borrower address\n    /// @param _amount proposed amount of asset to repay. Based on that,`repayShare` is calculated.\n    /// @return amount amount of asset to repay\n    /// @return repayShare amount of debt token representing debt ownership\n    function _calculateDebtAmountAndShare(AssetStorage storage _state, address _borrower, uint256 _amount)\n        internal\n        view\n        returns (uint256 amount, uint256 repayShare)\n    {\n        uint256 borrowerDebtShare = _state.debtToken.balanceOf(_borrower);\n        uint256 debtTokenTotalSupply = _state.debtToken.totalSupply();\n        uint256 totalBorrowed = _state.totalBorrowAmount;\n        uint256 maxAmount = borrowerDebtShare.toAmountRoundUp(totalBorrowed, debtTokenTotalSupply);\n\n        if (_amount >= maxAmount) {\n            amount = maxAmount;\n            repayShare = borrowerDebtShare;\n        } else {\n            amount = _amount;\n            repayShare = _amount.toShare(totalBorrowed, debtTokenTotalSupply);\n        }\n    }\n\n    /// @dev verifies if user did not borrow more than allowed maximum\n    function _validateBorrowAfter(address _user) private view {\n        (address[] memory assets, AssetStorage[] memory assetsStates) = getAssetsWithState();\n\n        (uint256 userLTV, uint256 maximumAllowedLTV) = Solvency.calculateLTVs(\n            Solvency.SolvencyParams(\n                siloRepository,\n                ISilo(address(this)),\n                assets,\n                assetsStates,\n                _user\n            ),\n            Solvency.TypeofLTV.MaximumLTV\n        );\n\n        if (userLTV > maximumAllowedLTV) revert MaximumLTVReached();\n    }\n\n    function _getWithdrawAssetData(address _asset, bool _collateralOnly)\n        private\n        view\n        returns(uint256 assetTotalDeposits, IShareToken shareToken, uint256 availableLiquidity)\n    {\n        AssetStorage storage _state = _assetStorage[_asset];\n\n        if (_collateralOnly) {\n            assetTotalDeposits = _state.collateralOnlyDeposits;\n            shareToken = _state.collateralOnlyToken;\n            availableLiquidity = assetTotalDeposits;\n        } else {\n            assetTotalDeposits = _state.totalDeposits;\n            shareToken = _state.collateralToken;\n            availableLiquidity = liquidity(_asset);\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `recipient`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `sender` to `recipient` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(\n        address sender,\n        address recipient,\n        uint256 amount\n    ) external returns (bool);\n\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n"
      },
      "@openzeppelin/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize, which returns 0 for contracts in\n        // construction, since the code is only stored at the end of the\n        // constructor execution.\n\n        uint256 size;\n        assembly {\n            size := extcodesize(account)\n        }\n        return size > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCall(target, data, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        require(isContract(target), \"Address: call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        require(isContract(target), \"Address: static call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(isContract(target), \"Address: delegate call to non-contract\");\n\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            // Look for revert reason and bubble it up if present\n            if (returndata.length > 0) {\n                // The easiest way to bubble the revert reason is using memory via assembly\n\n                assembly {\n                    let returndata_size := mload(returndata)\n                    revert(add(32, returndata), returndata_size)\n                }\n            } else {\n                revert(errorMessage);\n            }\n        }\n    }\n}\n"
      },
      "contracts/interfaces/IBaseSilo.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\nimport \"./IShareToken.sol\";\nimport \"./IFlashLiquidationReceiver.sol\";\nimport \"./ISiloRepository.sol\";\n\ninterface IBaseSilo {\n    enum AssetStatus { Undefined, Active, Removed }\n\n    /// @dev Storage struct that holds all required data for a single token market\n    struct AssetStorage {\n        /// @dev Token that represents a share in totalDeposits of Silo\n        IShareToken collateralToken;\n        /// @dev Token that represents a share in collateralOnlyDeposits of Silo\n        IShareToken collateralOnlyToken;\n        /// @dev Token that represents a share in totalBorrowAmount of Silo\n        IShareToken debtToken;\n        /// @dev COLLATERAL: Amount of asset token that has been deposited to Silo with interest earned by depositors.\n        /// It also includes token amount that has been borrowed.\n        uint256 totalDeposits;\n        /// @dev COLLATERAL ONLY: Amount of asset token that has been deposited to Silo that can be ONLY used\n        /// as collateral. These deposits do NOT earn interest and CANNOT be borrowed.\n        uint256 collateralOnlyDeposits;\n        /// @dev DEBT: Amount of asset token that has been borrowed with accrued interest.\n        uint256 totalBorrowAmount;\n    }\n\n    /// @dev Storage struct that holds data related to fees and interest\n    struct AssetInterestData {\n        /// @dev Total amount of already harvested protocol fees\n        uint256 harvestedProtocolFees;\n        /// @dev Total amount (ever growing) of asset token that has been earned by the protocol from\n        /// generated interest.\n        uint256 protocolFees;\n        /// @dev Timestamp of the last time `interestRate` has been updated in storage.\n        uint64 interestRateTimestamp;\n        /// @dev True if asset was removed from the protocol. If so, deposit and borrow functions are disabled\n        /// for that asset\n        AssetStatus status;\n    }\n\n    /// @notice data that InterestModel needs for calculations\n    struct UtilizationData {\n        uint256 totalDeposits;\n        uint256 totalBorrowAmount;\n        /// @dev timestamp of last interest accrual\n        uint64 interestRateTimestamp;\n    }\n\n    /// @dev Shares names and symbols that are generated while asset initialization\n    struct AssetSharesMetadata {\n        /// @dev Name for the collateral shares token\n        string collateralName;\n        /// @dev Symbol for the collateral shares token\n        string collateralSymbol;\n        /// @dev Name for the collateral only (protected collateral) shares token\n        string protectedName;\n        /// @dev Symbol for the collateral only (protected collateral) shares token\n        string protectedSymbol;\n        /// @dev Name for the debt shares token\n        string debtName;\n        /// @dev Symbol for the debt shares token\n        string debtSymbol;\n    }\n\n    /// @notice Emitted when deposit is made\n    /// @param asset asset address that was deposited\n    /// @param depositor wallet address that deposited asset\n    /// @param amount amount of asset that was deposited\n    /// @param collateralOnly type of deposit, true if collateralOnly deposit was used\n    event Deposit(address indexed asset, address indexed depositor, uint256 amount, bool collateralOnly);\n\n    /// @notice Emitted when withdraw is made\n    /// @param asset asset address that was withdrawn\n    /// @param depositor wallet address that deposited asset\n    /// @param receiver wallet address that received asset\n    /// @param amount amount of asset that was withdrew\n    /// @param collateralOnly type of withdraw, true if collateralOnly deposit was used\n    event Withdraw(\n        address indexed asset,\n        address indexed depositor,\n        address indexed receiver,\n        uint256 amount,\n        bool collateralOnly\n    );\n\n    /// @notice Emitted on asset borrow\n    /// @param asset asset address that was borrowed\n    /// @param user wallet address that borrowed asset\n    /// @param amount amount of asset that was borrowed\n    event Borrow(address indexed asset, address indexed user, uint256 amount);\n\n    /// @notice Emitted on asset repay\n    /// @param asset asset address that was repaid\n    /// @param user wallet address that repaid asset\n    /// @param amount amount of asset that was repaid\n    event Repay(address indexed asset, address indexed user, uint256 amount);\n\n    /// @notice Emitted on user liquidation\n    /// @param asset asset address that was liquidated\n    /// @param user wallet address that was liquidated\n    /// @param shareAmountRepaid amount of collateral-share token that was repaid. This is collateral token representing\n    /// ownership of underlying deposit.\n    /// @param seizedCollateral amount of underlying token that was seized by liquidator\n    event Liquidate(address indexed asset, address indexed user, uint256 shareAmountRepaid, uint256 seizedCollateral);\n\n    /// @notice Emitted when the status for an asset is updated\n    /// @param asset asset address that was updated\n    /// @param status new asset status\n    event AssetStatusUpdate(address indexed asset, AssetStatus indexed status);\n\n    /// @return version of the silo contract\n    function VERSION() external returns (uint128); // solhint-disable-line func-name-mixedcase\n\n    /// @notice Synchronize current bridge assets with Silo\n    /// @dev This function needs to be called on Silo deployment to setup all assets for Silo. It needs to be\n    /// called every time a bridged asset is added or removed. When bridge asset is removed, depositing and borrowing\n    /// should be disabled during asset sync.\n    function syncBridgeAssets() external;\n\n    /// @notice Get Silo Repository contract address\n    /// @return Silo Repository contract address\n    function siloRepository() external view returns (ISiloRepository);\n\n    /// @notice Get asset storage data\n    /// @param _asset asset address\n    /// @return AssetStorage struct\n    function assetStorage(address _asset) external view returns (AssetStorage memory);\n\n    /// @notice Get asset interest data\n    /// @param _asset asset address\n    /// @return AssetInterestData struct\n    function interestData(address _asset) external view returns (AssetInterestData memory);\n\n    /// @dev helper method for InterestRateModel calculations\n    function utilizationData(address _asset) external view returns (UtilizationData memory data);\n\n    /// @notice Calculates solvency of an account\n    /// @param _user wallet address for which solvency is calculated\n    /// @return true if solvent, false otherwise\n    function isSolvent(address _user) external view returns (bool);\n\n    /// @notice Returns all initialized (synced) assets of Silo including current and removed bridge assets\n    /// @return assets array of initialized assets of Silo\n    function getAssets() external view returns (address[] memory assets);\n\n    /// @notice Returns all initialized (synced) assets of Silo including current and removed bridge assets\n    /// with corresponding state\n    /// @return assets array of initialized assets of Silo\n    /// @return assetsStorage array of assets state corresponding to `assets` array\n    function getAssetsWithState() external view returns (address[] memory assets, AssetStorage[] memory assetsStorage);\n\n    /// @notice Check if depositing an asset for given account is possible\n    /// @dev Depositing an asset that has been already borrowed (and vice versa) is disallowed\n    /// @param _asset asset we want to deposit\n    /// @param _depositor depositor address\n    /// @return true if asset can be deposited by depositor\n    function depositPossible(address _asset, address _depositor) external view returns (bool);\n\n    /// @notice Check if borrowing an asset for given account is possible\n    /// @dev Borrowing an asset that has been already deposited (and vice versa) is disallowed\n    /// @param _asset asset we want to deposit\n    /// @param _borrower borrower address\n    /// @return true if asset can be borrowed by borrower\n    function borrowPossible(address _asset, address _borrower) external view returns (bool);\n\n    /// @dev Amount of token that is available for borrowing\n    /// @param _asset asset to get liquidity for\n    /// @return Silo liquidity\n    function liquidity(address _asset) external view returns (uint256);\n}\n"
      },
      "contracts/interfaces/IShareToken.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\nimport \"./INotificationReceiver.sol\";\n\ninterface IShareToken is IERC20Metadata {\n    /// @notice Emitted every time receiver is notified about token transfer\n    /// @param notificationReceiver receiver address\n    /// @param success false if TX reverted on `notificationReceiver` side, otherwise true\n    event NotificationSent(\n        INotificationReceiver indexed notificationReceiver,\n        bool success\n    );\n\n    /// @notice Mint method for Silo to create debt position\n    /// @param _account wallet for which to mint token\n    /// @param _amount amount of token to be minted\n    function mint(address _account, uint256 _amount) external;\n\n    /// @notice Burn method for Silo to close debt position\n    /// @param _account wallet for which to burn token\n    /// @param _amount amount of token to be burned\n    function burn(address _account, uint256 _amount) external;\n}\n"
      },
      "contracts/interfaces/IFlashLiquidationReceiver.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.13;\n\n/// @dev when performing Silo flash liquidation, FlashReceiver contract will receive all collaterals\ninterface IFlashLiquidationReceiver {\n    /// @dev this method is called when doing Silo flash liquidation\n    ///         one can NOT assume, that if _seizedCollateral[i] != 0, then _shareAmountsToRepaid[i] must be 0\n    ///         one should assume, that any combination of amounts is possible\n    ///         on callback, one must call `Silo.repayFor` because at the end of transaction,\n    ///         Silo will check if borrower is solvent.\n    /// @param _user user address, that is liquidated\n    /// @param _assets array of collateral assets received during user liquidation\n    ///         this array contains all assets (collateral borrowed) without any order\n    /// @param _receivedCollaterals array of collateral amounts received during user liquidation\n    ///         indexes of amounts are related to `_assets`,\n    /// @param _shareAmountsToRepaid array of amounts to repay for each asset\n    ///         indexes of amounts are related to `_assets`,\n    /// @param _flashReceiverData data that are passed from sender that executes liquidation\n    function siloLiquidationCallback(\n        address _user,\n        address[] calldata _assets,\n        uint256[] calldata _receivedCollaterals,\n        uint256[] calldata _shareAmountsToRepaid,\n        bytes memory _flashReceiverData\n    ) external;\n}\n"
      },
      "contracts/interfaces/ISiloRepository.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\nimport \"./ISiloFactory.sol\";\nimport \"./ITokensFactory.sol\";\nimport \"./IPriceProvidersRepository.sol\";\nimport \"./INotificationReceiver.sol\";\nimport \"./IInterestRateModel.sol\";\n\ninterface ISiloRepository {\n    /// @dev protocol fees in precision points (Solvency._PRECISION_DECIMALS), we do allow for fee == 0\n    struct Fees {\n        /// @dev One time protocol fee for opening a borrow position in precision points (Solvency._PRECISION_DECIMALS)\n        uint64 entryFee;\n        /// @dev Protocol revenue share in interest paid in precision points (Solvency._PRECISION_DECIMALS)\n        uint64 protocolShareFee;\n        /// @dev Protocol share in liquidation profit in precision points (Solvency._PRECISION_DECIMALS).\n        /// It's calculated from total collateral amount to be transferred to liquidator.\n        uint64 protocolLiquidationFee;\n    }\n\n    struct SiloVersion {\n        /// @dev Default version of Silo. If set to 0, it means it is not set. By default it is set to 1\n        uint128 byDefault;\n\n        /// @dev Latest added version of Silo. If set to 0, it means it is not set. By default it is set to 1\n        uint128 latest;\n    }\n\n    /// @dev AssetConfig struct represents configurable parameters for each Silo\n    struct AssetConfig {\n        /// @dev Loan-to-Value ratio represents the maximum borrowing power of a specific collateral.\n        ///      For example, if the collateral asset has an LTV of 75%, the user can borrow up to 0.75 worth\n        ///      of quote token in the principal currency for every quote token worth of collateral.\n        ///      value uses 18 decimals eg. 100% == 1e18\n        ///      max valid value is 1e18 so it needs storage of 60 bits\n        uint64 maxLoanToValue;\n\n        /// @dev Liquidation Threshold represents the threshold at which a borrow position will be considered\n        ///      undercollateralized and subject to liquidation for each collateral. For example,\n        ///      if a collateral has a liquidation threshold of 80%, it means that the loan will be\n        ///      liquidated when the borrowAmount value is worth 80% of the collateral value.\n        ///      value uses 18 decimals eg. 100% == 1e18\n        uint64 liquidationThreshold;\n\n        /// @dev interest rate model address\n        IInterestRateModel interestRateModel;\n    }\n\n    event NewDefaultMaximumLTV(uint64 defaultMaximumLTV);\n\n    event NewDefaultLiquidationThreshold(uint64 defaultLiquidationThreshold);\n\n    /// @notice Emitted on new Silo creation\n    /// @param silo deployed Silo address\n    /// @param asset unique asset for deployed Silo\n    /// @param siloVersion version of deployed Silo\n    event NewSilo(address indexed silo, address indexed asset, uint128 siloVersion);\n\n    /// @notice Emitted when new Silo (or existing one) becomes a bridge pool (pool with only bridge tokens).\n    /// @param pool address of the bridge pool, It can be zero address when bridge asset is removed and pool no longer\n    /// is treated as bridge pool\n    event BridgePool(address indexed pool);\n\n    /// @notice Emitted on new bridge asset\n    /// @param newBridgeAsset address of added bridge asset\n    event BridgeAssetAdded(address indexed newBridgeAsset);\n\n    /// @notice Emitted on removed bridge asset\n    /// @param bridgeAssetRemoved address of removed bridge asset\n    event BridgeAssetRemoved(address indexed bridgeAssetRemoved);\n\n    /// @notice Emitted when default interest rate model is changed\n    /// @param newModel address of new interest rate model\n    event InterestRateModel(IInterestRateModel indexed newModel);\n\n    /// @notice Emitted on price provider repository address update\n    /// @param newProvider address of new oracle repository\n    event PriceProvidersRepositoryUpdate(\n        IPriceProvidersRepository indexed newProvider\n    );\n\n    /// @notice Emitted on token factory address update\n    /// @param newTokensFactory address of new token factory\n    event TokensFactoryUpdate(address indexed newTokensFactory);\n\n    /// @notice Emitted on router address update\n    /// @param newRouter address of new router\n    event RouterUpdate(address indexed newRouter);\n\n    /// @notice Emitted on INotificationReceiver address update\n    /// @param newIncentiveContract address of new INotificationReceiver\n    event NotificationReceiverUpdate(INotificationReceiver indexed newIncentiveContract);\n\n    /// @notice Emitted when new Silo version is registered\n    /// @param factory factory address that deploys registered Silo version\n    /// @param siloLatestVersion Silo version of registered Silo\n    /// @param siloDefaultVersion current default Silo version\n    event RegisterSiloVersion(address indexed factory, uint128 siloLatestVersion, uint128 siloDefaultVersion);\n\n    /// @notice Emitted when Silo version is unregistered\n    /// @param factory factory address that deploys unregistered Silo version\n    /// @param siloVersion version that was unregistered\n    event UnregisterSiloVersion(address indexed factory, uint128 siloVersion);\n\n    /// @notice Emitted when default Silo version is updated\n    /// @param newDefaultVersion new default version\n    event SiloDefaultVersion(uint128 newDefaultVersion);\n\n    /// @notice Emitted when default fee is updated\n    /// @param newEntryFee new entry fee\n    /// @param newProtocolShareFee new protocol share fee\n    /// @param newProtocolLiquidationFee new protocol liquidation fee\n    event FeeUpdate(\n        uint64 newEntryFee,\n        uint64 newProtocolShareFee,\n        uint64 newProtocolLiquidationFee\n    );\n\n    /// @notice Emitted when asset config is updated for a silo\n    /// @param silo silo for which asset config is being set\n    /// @param asset asset for which asset config is being set\n    /// @param assetConfig new asset config\n    event AssetConfigUpdate(address indexed silo, address indexed asset, AssetConfig assetConfig);\n\n    /// @notice Emitted when silo (silo factory) version is set for asset\n    /// @param asset asset for which asset config is being set\n    /// @param version Silo version\n    event VersionForAsset(address indexed asset, uint128 version);\n\n    /// @param _siloAsset silo asset\n    /// @return version of Silo that is assigned for provided asset, if not assigned it returns zero (default)\n    function getVersionForAsset(address _siloAsset) external returns (uint128);\n\n    /// @notice setter for `getVersionForAsset` mapping\n    /// @param _siloAsset silo asset\n    /// @param _version version of Silo that will be assigned for `_siloAsset`, zero (default) is acceptable\n    function setVersionForAsset(address _siloAsset, uint128 _version) external;\n\n    /// @notice use this method only when off-chain verification is OFF\n    /// @dev Silo does NOT support rebase and deflationary tokens\n    /// @param _siloAsset silo asset\n    /// @param _siloData (optional) data that may be needed during silo creation\n    /// @return createdSilo address of created silo\n    function newSilo(address _siloAsset, bytes memory _siloData) external returns (address createdSilo);\n\n    /// @notice use this method to deploy new version of Silo for an asset that already has Silo deployed.\n    /// Only owner (DAO) can replace.\n    /// @dev Silo does NOT support rebase and deflationary tokens\n    /// @param _siloAsset silo asset\n    /// @param _siloVersion version of silo implementation. Use 0 for default version which is fine\n    /// for 99% of cases.\n    /// @param _siloData (optional) data that may be needed during silo creation\n    /// @return createdSilo address of created silo\n    function replaceSilo(\n        address _siloAsset,\n        uint128 _siloVersion,\n        bytes memory _siloData\n    ) external returns (address createdSilo);\n\n    /// @notice Set factory contract for debt and collateral tokens for each Silo asset\n    /// @dev Callable only by owner\n    /// @param _tokensFactory address of TokensFactory contract that deploys debt and collateral tokens\n    function setTokensFactory(address _tokensFactory) external;\n\n    /// @notice Set default fees\n    /// @dev Callable only by owner\n    /// @param _fees:\n    /// - _entryFee one time protocol fee for opening a borrow position in precision points\n    /// (Solvency._PRECISION_DECIMALS)\n    /// - _protocolShareFee protocol revenue share in interest paid in precision points\n    /// (Solvency._PRECISION_DECIMALS)\n    /// - _protocolLiquidationFee protocol share in liquidation profit in precision points\n    /// (Solvency._PRECISION_DECIMALS). It's calculated from total collateral amount to be transferred\n    /// to liquidator.\n    function setFees(Fees calldata _fees) external;\n\n    /// @notice Set configuration for given asset in given Silo\n    /// @dev Callable only by owner\n    /// @param _silo Silo address for which config applies\n    /// @param _asset asset address for which config applies\n    /// @param _assetConfig:\n    ///    - _maxLoanToValue maximum Loan-to-Value, for details see `Repository.AssetConfig.maxLoanToValue`\n    ///    - _liquidationThreshold liquidation threshold, for details see `Repository.AssetConfig.maxLoanToValue`\n    ///    - _interestRateModel interest rate model address, for details see `Repository.AssetConfig.interestRateModel`\n    function setAssetConfig(\n        address _silo,\n        address _asset,\n        AssetConfig calldata _assetConfig\n    ) external;\n\n    /// @notice Set default interest rate model\n    /// @dev Callable only by owner\n    /// @param _defaultInterestRateModel default interest rate model\n    function setDefaultInterestRateModel(IInterestRateModel _defaultInterestRateModel) external;\n\n    /// @notice Set default maximum LTV\n    /// @dev Callable only by owner\n    /// @param _defaultMaxLTV default maximum LTV in precision points (Solvency._PRECISION_DECIMALS)\n    function setDefaultMaximumLTV(uint64 _defaultMaxLTV) external;\n\n    /// @notice Set default liquidation threshold\n    /// @dev Callable only by owner\n    /// @param _defaultLiquidationThreshold default liquidation threshold in precision points\n    /// (Solvency._PRECISION_DECIMALS)\n    function setDefaultLiquidationThreshold(uint64 _defaultLiquidationThreshold) external;\n\n    /// @notice Set price provider repository\n    /// @dev Callable only by owner\n    /// @param _repository price provider repository address\n    function setPriceProvidersRepository(IPriceProvidersRepository _repository) external;\n\n    /// @notice Set router contract\n    /// @dev Callable only by owner\n    /// @param _router router address\n    function setRouter(address _router) external;\n\n    /// @notice Set NotificationReceiver contract\n    /// @dev Callable only by owner\n    /// @param _silo silo address for which to set `_notificationReceiver`\n    /// @param _notificationReceiver NotificationReceiver address\n    function setNotificationReceiver(address _silo, INotificationReceiver _notificationReceiver) external;\n\n    /// @notice Adds new bridge asset\n    /// @dev New bridge asset must be unique. Duplicates in bridge assets are not allowed. It's possible to add\n    /// bridge asset that has been removed in the past. Note that all Silos must be synced manually. Callable\n    /// only by owner.\n    /// @param _newBridgeAsset bridge asset address\n    function addBridgeAsset(address _newBridgeAsset) external;\n\n    /// @notice Removes bridge asset\n    /// @dev Note that all Silos must be synced manually. Callable only by owner.\n    /// @param _bridgeAssetToRemove bridge asset address to be removed\n    function removeBridgeAsset(address _bridgeAssetToRemove) external;\n\n    /// @notice Registers new Silo version\n    /// @dev User can choose which Silo version he wants to deploy. It's possible to have multiple versions of Silo.\n    /// Callable only by owner.\n    /// @param _factory factory contract that deploys new version of Silo\n    /// @param _isDefault true if this version should be used as default\n    function registerSiloVersion(ISiloFactory _factory, bool _isDefault) external;\n\n    /// @notice Unregisters Silo version\n    /// @dev Callable only by owner.\n    /// @param _siloVersion Silo version to be unregistered\n    function unregisterSiloVersion(uint128 _siloVersion) external;\n\n    /// @notice Sets default Silo version\n    /// @dev Callable only by owner.\n    /// @param _defaultVersion Silo version to be set as default\n    function setDefaultSiloVersion(uint128 _defaultVersion) external;\n\n    /// @notice Check if contract address is a Silo deployment\n    /// @param _silo address of expected Silo\n    /// @return true if address is Silo deployment, otherwise false\n    function isSilo(address _silo) external view returns (bool);\n\n    /// @notice Get Silo address of asset\n    /// @param _asset address of asset\n    /// @return address of corresponding Silo deployment\n    function getSilo(address _asset) external view returns (address);\n\n    /// @notice Get Silo Factory for given version\n    /// @param _siloVersion version of Silo implementation\n    /// @return ISiloFactory contract that deploys Silos of given version\n    function siloFactory(uint256 _siloVersion) external view returns (ISiloFactory);\n\n    /// @notice Get debt and collateral Token Factory\n    /// @return ITokensFactory contract that deploys debt and collateral tokens\n    function tokensFactory() external view returns (ITokensFactory);\n\n    /// @notice Get Router contract\n    /// @return address of router contract\n    function router() external view returns (address);\n\n    /// @notice Get current bridge assets\n    /// @dev Keep in mind that not all Silos may be synced with current bridge assets so it's possible that some\n    /// assets in that list are not part of given Silo.\n    /// @return address array of bridge assets\n    function getBridgeAssets() external view returns (address[] memory);\n\n    /// @notice Get removed bridge assets\n    /// @dev Keep in mind that not all Silos may be synced with bridge assets so it's possible that some\n    /// assets in that list are still part of given Silo.\n    /// @return address array of bridge assets\n    function getRemovedBridgeAssets() external view returns (address[] memory);\n\n    /// @notice Get maximum LTV for asset in given Silo\n    /// @dev If dedicated config is not set, method returns default config\n    /// @param _silo address of Silo\n    /// @param _asset address of an asset\n    /// @return maximum LTV in precision points (Solvency._PRECISION_DECIMALS)\n    function getMaximumLTV(address _silo, address _asset) external view returns (uint256);\n\n    /// @notice Get Interest Rate Model address for asset in given Silo\n    /// @dev If dedicated config is not set, method returns default config\n    /// @param _silo address of Silo\n    /// @param _asset address of an asset\n    /// @return address of interest rate model\n    function getInterestRateModel(address _silo, address _asset) external view returns (IInterestRateModel);\n\n    /// @notice Get liquidation threshold for asset in given Silo\n    /// @dev If dedicated config is not set, method returns default config\n    /// @param _silo address of Silo\n    /// @param _asset address of an asset\n    /// @return liquidation threshold in precision points (Solvency._PRECISION_DECIMALS)\n    function getLiquidationThreshold(address _silo, address _asset) external view returns (uint256);\n\n    /// @notice Get incentive contract address. Incentive contracts are responsible for distributing rewards\n    /// to debt and/or collateral token holders of given Silo\n    /// @param _silo address of Silo\n    /// @return incentive contract address\n    function getNotificationReceiver(address _silo) external view returns (INotificationReceiver);\n\n    /// @notice Get owner role address of Repository\n    /// @return owner role address\n    function owner() external view returns (address);\n\n    /// @notice get PriceProvidersRepository contract that manages price providers implementations\n    /// @return IPriceProvidersRepository address\n    function priceProvidersRepository() external view returns (IPriceProvidersRepository);\n\n    /// @dev Get protocol fee for opening a borrow position\n    /// @return fee in precision points (Solvency._PRECISION_DECIMALS == 100%)\n    function entryFee() external view returns (uint256);\n\n    /// @dev Get protocol share fee\n    /// @return protocol share fee in precision points (Solvency._PRECISION_DECIMALS == 100%)\n    function protocolShareFee() external view returns (uint256);\n\n    /// @dev Get protocol liquidation fee\n    /// @return protocol liquidation fee in precision points (Solvency._PRECISION_DECIMALS == 100%)\n    function protocolLiquidationFee() external view returns (uint256);\n\n    /// @dev Checks all conditions for new silo creation and throws when not possible to create\n    /// @param _asset address of asset for which you want to create silo\n    /// @param _assetIsABridge bool TRUE when `_asset` is bridge asset, FALSE when it is not\n    function ensureCanCreateSiloFor(address _asset, bool _assetIsABridge) external view;\n\n    function siloRepositoryPing() external pure returns (bytes4);\n}\n"
      },
      "contracts/interfaces/INotificationReceiver.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\n/// @title Common interface for Silo Incentive Contract\ninterface INotificationReceiver {\n    /// @dev Informs the contract about token transfer\n    /// @param _token address of the token that was transferred\n    /// @param _from sender\n    /// @param _to receiver\n    /// @param _amount amount that was transferred\n    function onAfterTransfer(address _token, address _from, address _to, uint256 _amount) external;\n\n    /// @dev Sanity check function\n    /// @return always true\n    function notificationReceiverPing() external pure returns (bytes4);\n}\n"
      },
      "contracts/interfaces/ISiloFactory.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\ninterface ISiloFactory {\n    /// @notice Emitted when Silo is deployed\n    /// @param silo address of deployed Silo\n    /// @param asset address of asset for which Silo was deployed\n    /// @param version version of silo implementation\n    event NewSiloCreated(address indexed silo, address indexed asset, uint128 version);\n\n    /// @notice Must be called by repository on constructor\n    /// @param _siloRepository the SiloRepository to set\n    function initRepository(address _siloRepository) external;\n\n    /// @notice Deploys Silo\n    /// @param _siloAsset unique asset for which Silo is deployed\n    /// @param _version version of silo implementation\n    /// @param _data (optional) data that may be needed during silo creation\n    /// @return silo deployed Silo address\n    function createSilo(address _siloAsset, uint128 _version, bytes memory _data) external returns (address silo);\n\n    /// @dev just a helper method to see if address is a factory\n    function siloFactoryPing() external pure returns (bytes4);\n}\n"
      },
      "contracts/interfaces/ITokensFactory.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\nimport \"./IShareToken.sol\";\n\ninterface ITokensFactory {\n    /// @notice Emitted when collateral token is deployed\n    /// @param token address of deployed collateral token\n    event NewShareCollateralTokenCreated(address indexed token);\n\n    /// @notice Emitted when collateral token is deployed\n    /// @param token address of deployed debt token\n    event NewShareDebtTokenCreated(address indexed token);\n\n    ///@notice Must be called by repository on constructor\n    /// @param _siloRepository the SiloRepository to set\n    function initRepository(address _siloRepository) external;\n\n    /// @notice Deploys collateral token\n    /// @param _name name of the token\n    /// @param _symbol symbol of the token\n    /// @param _asset underlying asset for which token is deployed\n    /// @return address of deployed collateral share token\n    function createShareCollateralToken(\n        string memory _name,\n        string memory _symbol,\n        address _asset\n    ) external returns (IShareToken);\n\n    /// @notice Deploys debt token\n    /// @param _name name of the token\n    /// @param _symbol symbol of the token\n    /// @param _asset underlying asset for which token is deployed\n    /// @return address of deployed debt share token\n    function createShareDebtToken(\n        string memory _name,\n        string memory _symbol,\n        address _asset\n    )\n        external\n        returns (IShareToken);\n\n    /// @dev just a helper method to see if address is a factory\n    /// @return always true\n    function tokensFactoryPing() external pure returns (bytes4);\n}\n"
      },
      "contracts/interfaces/IPriceProvidersRepository.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.7.6 <0.9.0;\n\nimport \"./IPriceProvider.sol\";\n\ninterface IPriceProvidersRepository {\n    /// @notice Emitted when price provider is added\n    /// @param newPriceProvider new price provider address\n    event NewPriceProvider(IPriceProvider indexed newPriceProvider);\n\n    /// @notice Emitted when price provider is removed\n    /// @param priceProvider removed price provider address\n    event PriceProviderRemoved(IPriceProvider indexed priceProvider);\n\n    /// @notice Emitted when asset is assigned to price provider\n    /// @param asset assigned asset   address\n    /// @param priceProvider price provider address\n    event PriceProviderForAsset(address indexed asset, IPriceProvider indexed priceProvider);\n\n    /// @notice Register new price provider\n    /// @param _priceProvider address of price provider\n    function addPriceProvider(IPriceProvider _priceProvider) external;\n\n    /// @notice Unregister price provider\n    /// @param _priceProvider address of price provider to be removed\n    function removePriceProvider(IPriceProvider _priceProvider) external;\n\n    /// @notice Sets price provider for asset\n    /// @dev Request for asset price is forwarded to the price provider assigned to that asset\n    /// @param _asset address of an asset for which price provider will be used\n    /// @param _priceProvider address of price provider\n    function setPriceProviderForAsset(address _asset, IPriceProvider _priceProvider) external;\n\n    /// @notice Returns \"Time-Weighted Average Price\" for an asset\n    /// @param _asset address of an asset for which to read price\n    /// @return price TWAP price of a token with 18 decimals\n    function getPrice(address _asset) external view returns (uint256 price);\n\n    /// @notice Gets price provider assigned to an asset\n    /// @param _asset address of an asset for which to get price provider\n    /// @return priceProvider address of price provider\n    function priceProviders(address _asset) external view returns (IPriceProvider priceProvider);\n\n    /// @notice Gets token address in which prices are quoted\n    /// @return quoteToken address\n    function quoteToken() external view returns (address);\n\n    /// @notice Gets manager role address\n    /// @return manager role address\n    function manager() external view returns (address);\n\n    /// @notice Checks if providers are available for an asset\n    /// @param _asset asset address to check\n    /// @return returns TRUE if price feed is ready, otherwise false\n    function providersReadyForAsset(address _asset) external view returns (bool);\n\n    /// @notice Returns true if address is a registered price provider\n    /// @param _provider address of price provider to be removed\n    /// @return true if address is a registered price provider, otherwise false\n    function isPriceProvider(IPriceProvider _provider) external view returns (bool);\n\n    /// @notice Gets number of price providers registered\n    /// @return number of price providers registered\n    function providersCount() external view returns (uint256);\n\n    /// @notice Gets an array of price providers\n    /// @return array of price providers\n    function providerList() external view returns (address[] memory);\n\n    /// @notice Sanity check function\n    /// @return returns always TRUE\n    function priceProvidersRepositoryPing() external pure returns (bytes4);\n}\n"
      },
      "contracts/interfaces/IInterestRateModel.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\ninterface IInterestRateModel {\n    /* solhint-disable */\n    struct Config {\n        // uopt ∈ (0, 1) – optimal utilization;\n        int256 uopt;\n        // ucrit ∈ (uopt, 1) – threshold of large utilization;\n        int256 ucrit;\n        // ulow ∈ (0, uopt) – threshold of low utilization\n        int256 ulow;\n        // ki > 0 – integrator gain\n        int256 ki;\n        // kcrit > 0 – proportional gain for large utilization\n        int256 kcrit;\n        // klow ≥ 0 – proportional gain for low utilization\n        int256 klow;\n        // klin ≥ 0 – coefficient of the lower linear bound\n        int256 klin;\n        // beta ≥ 0 - a scaling factor\n        int256 beta;\n        // ri ≥ 0 – initial value of the integrator\n        int256 ri;\n        // Tcrit ≥ 0 - the time during which the utilization exceeds the critical value\n        int256 Tcrit;\n    }\n    /* solhint-enable */\n\n    /// @dev Set dedicated config for given asset in a Silo. Config is per asset per Silo so different assets\n    /// in different Silo can have different configs.\n    /// It will try to call `_silo.accrueInterest(_asset)` before updating config, but it is not guaranteed,\n    /// that this call will be successful, if it fail config will be set anyway.\n    /// @param _silo Silo address for which config should be set\n    /// @param _asset asset address for which config should be set\n    function setConfig(address _silo, address _asset, Config calldata _config) external;\n\n    /// @dev get compound interest rate and update model storage\n    /// @param _asset address of an asset in Silo for which interest rate should be calculated\n    /// @param _blockTimestamp current block timestamp\n    /// @return rcomp compounded interest rate from last update until now (1e18 == 100%)\n    function getCompoundInterestRateAndUpdate(\n        address _asset,\n        uint256 _blockTimestamp\n    ) external returns (uint256 rcomp);\n\n    /// @dev Get config for given asset in a Silo. If dedicated config is not set, default one will be returned.\n    /// @param _silo Silo address for which config should be set\n    /// @param _asset asset address for which config should be set\n    /// @return Config struct for asset in Silo\n    function getConfig(address _silo, address _asset) external view returns (Config memory);\n\n    /// @dev get compound interest rate\n    /// @param _silo address of Silo\n    /// @param _asset address of an asset in Silo for which interest rate should be calculated\n    /// @param _blockTimestamp current block timestamp\n    /// @return rcomp compounded interest rate from last update until now (1e18 == 100%)\n    function getCompoundInterestRate(\n        address _silo,\n        address _asset,\n        uint256 _blockTimestamp\n    ) external view returns (uint256 rcomp);\n\n    /// @dev get current annual interest rate\n    /// @param _silo address of Silo\n    /// @param _asset address of an asset in Silo for which interest rate should be calculated\n    /// @param _blockTimestamp current block timestamp\n    /// @return rcur current annual interest rate (1e18 == 100%)\n    function getCurrentInterestRate(\n        address _silo,\n        address _asset,\n        uint256 _blockTimestamp\n    ) external view returns (uint256 rcur);\n\n    /// @notice get the flag to detect rcomp restriction (zero current interest) due to overflow\n    /// overflow boolean flag to detect rcomp restriction\n    function overflowDetected(\n        address _silo,\n        address _asset,\n        uint256 _blockTimestamp\n    ) external view returns (bool overflow);\n\n    /// @dev pure function that calculates current annual interest rate\n    /// @param _c configuration object, InterestRateModel.Config\n    /// @param _totalBorrowAmount current total borrows for asset\n    /// @param _totalDeposits current total deposits for asset\n    /// @param _interestRateTimestamp timestamp of last interest rate update\n    /// @param _blockTimestamp current block timestamp\n    /// @return rcur current annual interest rate (1e18 == 100%)\n    function calculateCurrentInterestRate(\n        Config memory _c,\n        uint256 _totalDeposits,\n        uint256 _totalBorrowAmount,\n        uint256 _interestRateTimestamp,\n        uint256 _blockTimestamp\n    ) external pure returns (uint256 rcur);\n\n    /// @dev pure function that calculates interest rate based on raw input data\n    /// @param _c configuration object, InterestRateModel.Config\n    /// @param _totalBorrowAmount current total borrows for asset\n    /// @param _totalDeposits current total deposits for asset\n    /// @param _interestRateTimestamp timestamp of last interest rate update\n    /// @param _blockTimestamp current block timestamp\n    /// @return rcomp compounded interest rate from last update until now (1e18 == 100%)\n    /// @return ri current integral part of the rate\n    /// @return Tcrit time during which the utilization exceeds the critical value\n    /// @return overflow boolean flag to detect rcomp restriction\n    function calculateCompoundInterestRateWithOverflowDetection(\n        Config memory _c,\n        uint256 _totalDeposits,\n        uint256 _totalBorrowAmount,\n        uint256 _interestRateTimestamp,\n        uint256 _blockTimestamp\n    ) external pure returns (\n        uint256 rcomp,\n        int256 ri,\n        int256 Tcrit, // solhint-disable-line var-name-mixedcase\n        bool overflow\n    );\n\n    /// @dev pure function that calculates interest rate based on raw input data\n    /// @param _c configuration object, InterestRateModel.Config\n    /// @param _totalBorrowAmount current total borrows for asset\n    /// @param _totalDeposits current total deposits for asset\n    /// @param _interestRateTimestamp timestamp of last interest rate update\n    /// @param _blockTimestamp current block timestamp\n    /// @return rcomp compounded interest rate from last update until now (1e18 == 100%)\n    /// @return ri current integral part of the rate\n    /// @return Tcrit time during which the utilization exceeds the critical value\n    function calculateCompoundInterestRate(\n        Config memory _c,\n        uint256 _totalDeposits,\n        uint256 _totalBorrowAmount,\n        uint256 _interestRateTimestamp,\n        uint256 _blockTimestamp\n    ) external pure returns (\n        uint256 rcomp,\n        int256 ri,\n        int256 Tcrit // solhint-disable-line var-name-mixedcase\n    );\n\n    /// @dev returns decimal points used by model\n    function DP() external pure returns (uint256); // solhint-disable-line func-name-mixedcase\n\n    /// @dev just a helper method to see if address is a InterestRateModel\n    /// @return always true\n    function interestRateModelPing() external pure returns (bytes4);\n}\n"
      },
      "contracts/interfaces/IPriceProvider.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.7.6 <0.9.0;\n\n/// @title Common interface for Silo Price Providers\ninterface IPriceProvider {\n    /// @notice Returns \"Time-Weighted Average Price\" for an asset. Calculates TWAP price for quote/asset.\n    /// It unifies all tokens decimal to 18, examples:\n    /// - if asses == quote it returns 1e18\n    /// - if asset is USDC and quote is ETH and ETH costs ~$3300 then it returns ~0.0003e18 WETH per 1 USDC\n    /// @param _asset address of an asset for which to read price\n    /// @return price of asses with 18 decimals, throws when pool is not ready yet to provide price\n    function getPrice(address _asset) external view returns (uint256 price);\n\n    /// @dev Informs if PriceProvider is setup for asset. It does not means PriceProvider can provide price right away.\n    /// Some providers implementations need time to \"build\" buffer for TWAP price,\n    /// so price may not be available yet but this method will return true.\n    /// @param _asset asset in question\n    /// @return TRUE if asset has been setup, otherwise false\n    function assetSupported(address _asset) external view returns (bool);\n\n    /// @notice Gets token address in which prices are quoted\n    /// @return quoteToken address\n    function quoteToken() external view returns (address);\n\n    /// @notice Helper method that allows easily detects, if contract is PriceProvider\n    /// @dev this can save us from simple human errors, in case we use invalid address\n    /// but this should NOT be treated as security check\n    /// @return always true\n    function priceProviderPing() external pure returns (bytes4);\n}\n"
      },
      "@openzeppelin/contracts/security/ReentrancyGuard.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot's contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler's defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction's gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant _NOT_ENTERED = 1;\n    uint256 private constant _ENTERED = 2;\n\n    uint256 private _status;\n\n    constructor() {\n        _status = _NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and making it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        // On the first call to nonReentrant, _notEntered will be true\n        require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n        // Any calls to nonReentrant after this point will fail\n        _status = _ENTERED;\n\n        _;\n\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _status = _NOT_ENTERED;\n    }\n}\n"
      },
      "contracts/utils/LiquidationReentrancyGuard.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\n/// @dev This is cloned solution of @openzeppelin/contracts/security/ReentrancyGuard.sol\nabstract contract LiquidationReentrancyGuard {\n    error LiquidationReentrancyCall();\n\n    uint256 private constant _LIQUIDATION_NOT_ENTERED = 1;\n    uint256 private constant _LIQUIDATION_ENTERED = 2;\n\n    uint256 private _liquidationStatus;\n\n    modifier liquidationNonReentrant() {\n        if (_liquidationStatus == _LIQUIDATION_ENTERED) {\n            revert LiquidationReentrancyCall();\n        }\n\n        _liquidationStatus = _LIQUIDATION_ENTERED;\n\n        _;\n\n        _liquidationStatus = _LIQUIDATION_NOT_ENTERED;\n    }\n\n    constructor() {\n        _liquidationStatus = _LIQUIDATION_NOT_ENTERED;\n    }\n}\n"
      },
      "contracts/interfaces/IGuardedLaunch.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\ninterface IGuardedLaunch {\n    /// @dev Stores info about maximum allowed liquidity in a Silo. This limit applies to deposit only.\n    struct MaxLiquidityLimit {\n        /// @dev flag to turn on/off all limits for all Silos\n        bool globalLimit;\n        /// @dev default value represents maximum allowed liquidity in Silo\n        uint256 defaultMaxLiquidity;\n        /// @notice siloMaxLiquidity maps silo => asset => maximum allowed deposit liquidity.\n        /// @dev Deposit liquidity limit is denominated in quote token. For example, if set to 1e18, it means that any\n        /// given Silo is allowed for deposits up to 1 quote token of value. Value is calculated using prices from the\n        /// Oracle.\n        mapping(address => mapping(address => uint256)) siloMaxLiquidity;\n    }\n\n    /// @dev Stores info about paused Silos\n    /// if `globalPause` == `true`, all Silo are paused\n    /// if `globalPause` == `false` and `siloPause[silo][0x0]` == `true`, all assets in a `silo` are paused\n    /// if `globalPause` == `false` and `siloPause[silo][asset]` == `true`, only `asset` in a `silo` is paused\n    struct Paused {\n        bool globalPause;\n        /// @dev maps silo address to asset address to bool\n        mapping(address => mapping(address => bool)) siloPause;\n    }\n\n    /// @notice Emitted when all Silos are paused or unpaused\n    /// @param globalPause current value of `globalPause`\n    event GlobalPause(bool globalPause);\n\n    /// @notice Emitted when a single Silo or single asset in a Silo is paused or unpaused\n    /// @param silo address of Silo which is paused\n    /// @param asset address of an asset which is paused\n    /// @param pauseValue true when paused, otherwise false\n    event SiloPause(address silo, address asset, bool pauseValue);\n\n    /// @notice Emitted when max liquidity toggle is switched\n    /// @param newLimitedMaxLiquidityState new value for max liquidity toggle\n    event LimitedMaxLiquidityToggled(bool newLimitedMaxLiquidityState);\n\n    /// @notice Emitted when deposit liquidity limit is changed for Silo and asset\n    /// @param silo Silo address for which to set limit\n    /// @param asset Silo asset for which to set limit\n    /// @param newMaxDeposits deposit limit amount in quote token\n    event SiloMaxDepositsLimitsUpdate(address indexed silo, address indexed asset, uint256 newMaxDeposits);\n\n    /// @notice Emitted when default max liquidity limit is changed\n    /// @param newMaxDeposits new deposit limit in quote token\n    event DefaultSiloMaxDepositsLimitUpdate(uint256 newMaxDeposits);\n\n    /// @notice Sets limited liquidity to provided value\n    function setLimitedMaxLiquidity(bool _globalLimit) external;\n\n    /// @notice Sets default deposit limit for all Silos\n    /// @param _maxDeposits deposit limit amount in quote token\n    function setDefaultSiloMaxDepositsLimit(uint256 _maxDeposits) external;\n\n    /// @notice Sets deposit limit for Silo\n    /// @param _silo Silo address for which to set limit\n    /// @param _asset Silo asset for which to set limit\n    /// @param _maxDeposits deposit limit amount in quote token\n    function setSiloMaxDepositsLimit(\n        address _silo,\n        address _asset,\n        uint256 _maxDeposits\n    ) external;\n\n    /// @notice Pause all Silos\n    /// @dev Callable only by owner.\n    /// @param _globalPause true to pause all Silos, otherwise false\n    function setGlobalPause(bool _globalPause) external;\n\n    /// @notice Pause single asset in a single Silo\n    /// @dev Callable only by owner.\n    /// @param _silo address of Silo in which `_asset` is being paused\n    /// @param _asset address of an asset that is being paused\n    /// @param _pauseValue true to pause, false to unpause\n    function setSiloPause(address _silo, address _asset, bool _pauseValue) external;\n\n    /// @notice Check given asset in a Silo is paused\n    /// @param _silo address of Silo\n    /// @param _asset address of an asset\n    /// @return true if given asset in a Silo is paused, otherwise false\n    function isSiloPaused(address _silo, address _asset) external view returns (bool);\n\n    /// @notice Gets deposit limit for Silo\n    /// @param _silo Silo address for which to set limit\n    /// @param _asset Silo asset for which to set limit\n    /// @return deposit limit for Silo\n    function getMaxSiloDepositsValue(address _silo, address _asset) external view returns (uint256);\n}\n"
      },
      "contracts/lib/Ping.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.7.6 <0.9.0;\n\n\nlibrary Ping {\n    function pong(function() external pure returns(bytes4) pingFunction) internal pure returns (bool) {\n        return pingFunction.address != address(0) && pingFunction.selector == pingFunction();\n    }\n}\n"
      },
      "contracts/lib/TokenHelper.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\nimport \"@openzeppelin/contracts/utils/Address.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\n\n\nlibrary TokenHelper {\n    uint256 private constant _BYTES32_SIZE = 32;\n\n    error TokenIsNotAContract();\n\n    function assertAndGetDecimals(address _token) internal view returns (uint256) {\n        (bool hasMetadata, bytes memory data) = _tokenMetadataCall(_token, abi.encodeCall(IERC20Metadata.decimals,()));\n\n        // decimals() is optional in the ERC20 standard, so if metadata is not accessible\n        // we assume there are no decimals and use 0.\n        if (!hasMetadata) {\n            return 0;\n        }\n\n        return abi.decode(data, (uint8));\n    }\n\n    /// @dev Returns the symbol for the provided ERC20 token.\n    /// An empty string is returned if the call to the token didn't succeed.\n    /// @param _token address of the token to get the symbol for\n    /// @return assetSymbol the token symbol\n    function symbol(address _token) internal view returns (string memory assetSymbol) {\n        (bool hasMetadata, bytes memory data) = _tokenMetadataCall(_token, abi.encodeCall(IERC20Metadata.symbol,()));\n\n        if (!hasMetadata || data.length == 0) {\n            return \"?\";\n        } else if (data.length == _BYTES32_SIZE) {\n            return string(removeZeros(data));\n        } else {\n            return abi.decode(data, (string));\n        }\n    }\n\n    /// @dev Removes bytes with value equal to 0 from the provided byte array.\n    /// @param _data byte array from which to remove zeroes\n    /// @return result byte array with zeroes removed \n    function removeZeros(bytes memory _data) internal pure returns (bytes memory result) {\n        uint256 n = _data.length;\n\n        unchecked {\n            for (uint256 i; i < n; i++) {\n                if (_data[i] == 0) continue;\n\n                result = abi.encodePacked(result, _data[i]);\n            }\n        }\n    }\n\n    /// @dev Performs a staticcall to the token to get its metadata (symbol, decimals, name)\n    function _tokenMetadataCall(address _token, bytes memory _data) private view returns(bool, bytes memory) {\n        // We need to do this before the call, otherwise the call will succeed even for EOAs\n        if (!Address.isContract(_token)) revert TokenIsNotAContract();\n\n        (bool success, bytes memory result) = _token.staticcall(_data);\n\n        // If the call reverted we assume the token doesn't follow the metadata extension\n        if (!success) {\n            return (false, \"\");\n        }\n\n        return (true, result);\n    }\n}\n"
      },
      "contracts/lib/Solvency.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.13;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\nimport \"../interfaces/IPriceProvidersRepository.sol\";\nimport \"../interfaces/ISilo.sol\";\nimport \"../interfaces/IInterestRateModel.sol\";\nimport \"../interfaces/ISiloRepository.sol\";\nimport \"./EasyMath.sol\";\n\nlibrary Solvency {\n    using EasyMath for uint256;\n\n    /// @notice\n    /// MaximumLTV - Maximum Loan-to-Value ratio represents the maximum borrowing power of all user's collateral\n    /// positions in a Silo\n    /// LiquidationThreshold - Liquidation Threshold represents the threshold at which all user's borrow positions\n    /// in a Silo will be considered under collateralized and subject to liquidation\n    enum TypeofLTV { MaximumLTV, LiquidationThreshold }\n\n    error DifferentArrayLength();\n    error UnsupportedLTVType();\n\n    struct SolvencyParams {\n        /// @param siloRepository SiloRepository address\n        ISiloRepository siloRepository;\n        /// @param silo Silo address\n        ISilo silo;\n        /// @param assets array with assets\n        address[] assets;\n        /// @param assetStates array of states for each asset, where index match the `assets` index\n        ISilo.AssetStorage[] assetStates;\n        /// @param user wallet address for which to read debt\n        address user;\n    }\n\n    /// @dev is value that used for integer calculations and decimal points for utilization ratios, LTV, protocol fees\n    uint256 internal constant _PRECISION_DECIMALS = 1e18;\n    uint256 internal constant _INFINITY = type(uint256).max;\n\n    /// @notice Returns current user LTV and second LTV chosen in params\n    /// @dev This function is optimized for protocol use. In some cases there is no need to keep the calculation\n    /// going and predefined results can be returned.\n    /// @param _params `Solvency.SolvencyParams` struct with needed params for calculation\n    /// @param _secondLtvType type of LTV to be returned as second value\n    /// @return currentUserLTV Loan-to-Value ratio represents current user's proportion of debt to collateral\n    /// @return secondLTV second type of LTV which depends on _secondLtvType, zero is returned if the value of the loan\n    /// or the collateral are zero\n    function calculateLTVs(SolvencyParams memory _params, TypeofLTV _secondLtvType)\n        internal\n        view\n        returns (uint256 currentUserLTV, uint256 secondLTV)\n    {\n        uint256[] memory totalBorrowAmounts = getBorrowAmounts(_params);\n\n        // this return avoids eg. additional checks on withdraw, when user did not borrow any asset\n        if (EasyMath.sum(totalBorrowAmounts) == 0) return (0, 0);\n\n        IPriceProvidersRepository priceProvidersRepository = _params.siloRepository.priceProvidersRepository();\n\n        uint256[] memory borrowValues = convertAmountsToValues(\n            priceProvidersRepository,\n            _params.assets,\n            totalBorrowAmounts\n        );\n\n        // value of user's total debt\n        uint256 borrowTotalValue = EasyMath.sum(borrowValues);\n\n        if (borrowTotalValue == 0) return (0, 0);\n\n        uint256[] memory collateralValues = getUserCollateralValues(priceProvidersRepository, _params);\n\n        // value of user's collateral\n        uint256 collateralTotalValue = EasyMath.sum(collateralValues);\n\n        if (collateralTotalValue == 0) return (_INFINITY, 0);\n\n        // value of theoretical debt user can have depending on TypeofLTV\n        uint256 borrowAvailableTotalValue = _getTotalAvailableToBorrowValue(\n            _params.siloRepository,\n            address(_params.silo),\n            _params.assets,\n            _secondLtvType,\n            collateralValues\n        );\n\n        currentUserLTV = borrowTotalValue * _PRECISION_DECIMALS / collateralTotalValue;\n\n        // one of Solvency.TypeofLTV\n        secondLTV = borrowAvailableTotalValue * _PRECISION_DECIMALS / collateralTotalValue;\n    }\n\n    /// @notice Calculates chosen LTV limit\n    /// @dev This function should be used by external actors like SiloLens and UI/subgraph. `calculateLTVs` is\n    /// optimized for protocol use and may not return second LVT calculation when they are not needed.\n    /// @param _params `Solvency.SolvencyParams` struct with needed params for calculation\n    /// @param _ltvType acceptable values are only TypeofLTV.MaximumLTV or TypeofLTV.LiquidationThreshold\n    /// @return limit theoretical LTV limit of `_ltvType`\n    function calculateLTVLimit(SolvencyParams memory _params, TypeofLTV _ltvType)\n        internal\n        view\n        returns (uint256 limit)\n    {\n        IPriceProvidersRepository priceProvidersRepository = _params.siloRepository.priceProvidersRepository();\n\n        uint256[] memory collateralValues = getUserCollateralValues(priceProvidersRepository, _params);\n\n        // value of user's collateral\n        uint256 collateralTotalValue = EasyMath.sum(collateralValues);\n\n        if (collateralTotalValue == 0) return 0;\n\n        // value of theoretical debt user can have depending on TypeofLTV\n        uint256 borrowAvailableTotalValue = _getTotalAvailableToBorrowValue(\n            _params.siloRepository,\n            address(_params.silo),\n            _params.assets,\n            _ltvType,\n            collateralValues\n        );\n\n        limit = borrowAvailableTotalValue * _PRECISION_DECIMALS / collateralTotalValue;\n    }\n\n    /// @notice Returns worth (in quote token) of each collateral deposit of a user\n    /// @param _priceProvidersRepository address of IPriceProvidersRepository where prices are read\n    /// @param _params `Solvency.SolvencyParams` struct with needed params for calculation\n    /// @return collateralValues worth of each collateral deposit of a user as an array\n    function getUserCollateralValues(IPriceProvidersRepository _priceProvidersRepository, SolvencyParams memory _params)\n        internal\n        view\n        returns(uint256[] memory collateralValues)\n    {\n        uint256[] memory collateralAmounts = getCollateralAmounts(_params);\n        collateralValues = convertAmountsToValues(_priceProvidersRepository, _params.assets, collateralAmounts);\n    }\n\n    /// @notice Convert assets amounts to values in quote token (amount * price)\n    /// @param _priceProviderRepo address of IPriceProvidersRepository where prices are read\n    /// @param _assets array with assets for which prices are read\n    /// @param _amounts array of amounts\n    /// @return values array of values for corresponding assets\n    function convertAmountsToValues(\n        IPriceProvidersRepository _priceProviderRepo,\n        address[] memory _assets,\n        uint256[] memory _amounts\n    ) internal view returns (uint256[] memory values) {\n        if (_assets.length != _amounts.length) revert DifferentArrayLength();\n\n        values = new uint256[](_assets.length);\n\n        for (uint256 i = 0; i < _assets.length; i++) {\n            if (_amounts[i] == 0) continue;\n\n            uint256 assetPrice = _priceProviderRepo.getPrice(_assets[i]);\n            uint8 assetDecimals = ERC20(_assets[i]).decimals();\n\n            values[i] = _amounts[i].toValue(assetPrice, assetDecimals);\n        }\n    }\n\n    /// @notice Get amount of collateral for each asset\n    /// @param _params `Solvency.SolvencyParams` struct with needed params for calculation\n    /// @return collateralAmounts array of amounts for each token in Silo. May contain zero values if user\n    /// did not deposit given collateral token.\n    function getCollateralAmounts(SolvencyParams memory _params)\n        internal\n        view\n        returns (uint256[] memory collateralAmounts)\n    {\n        if (_params.assets.length != _params.assetStates.length) {\n            revert DifferentArrayLength();\n        }\n\n        collateralAmounts = new uint256[](_params.assets.length);\n\n        for (uint256 i = 0; i < _params.assets.length; i++) {\n            uint256 userCollateralTokenBalance = _params.assetStates[i].collateralToken.balanceOf(_params.user);\n            uint256 userCollateralOnlyTokenBalance = _params.assetStates[i].collateralOnlyToken.balanceOf(_params.user);\n\n            if (userCollateralTokenBalance + userCollateralOnlyTokenBalance == 0) continue;\n\n            uint256 rcomp = getRcomp(_params.silo, _params.siloRepository, _params.assets[i], block.timestamp);\n\n            collateralAmounts[i] = getUserCollateralAmount(\n                _params.assetStates[i],\n                userCollateralTokenBalance,\n                userCollateralOnlyTokenBalance,\n                rcomp,\n                _params.siloRepository\n            );\n        }\n    }\n\n    /// @notice Get amount of debt for each asset\n    /// @param _params `Solvency.SolvencyParams` struct with needed params for calculation\n    /// @return totalBorrowAmounts array of amounts for each token in Silo. May contain zero values if user\n    /// did not borrow given token.\n    function getBorrowAmounts(SolvencyParams memory _params)\n        internal\n        view\n        returns (uint256[] memory totalBorrowAmounts)\n    {\n        if (_params.assets.length != _params.assetStates.length) {\n            revert DifferentArrayLength();\n        }\n\n        totalBorrowAmounts = new uint256[](_params.assets.length);\n\n        for (uint256 i = 0; i < _params.assets.length; i++) {\n            uint256 rcomp = getRcomp(_params.silo, _params.siloRepository, _params.assets[i], block.timestamp);\n            totalBorrowAmounts[i] = getUserBorrowAmount(_params.assetStates[i], _params.user, rcomp);\n        }\n    }\n\n    /// @notice Get amount of deposited token, including collateralOnly deposits\n    /// @param _assetStates state of deposited asset in Silo\n    /// @param _userCollateralTokenBalance balance of user's share collateral token\n    /// @param _userCollateralOnlyTokenBalance balance of user's share collateralOnly token\n    /// @param _rcomp compounded interest rate to account for during calculations, could be 0\n    /// @param _siloRepository SiloRepository address\n    /// @return amount of underlying token deposited, including collateralOnly deposit\n    function getUserCollateralAmount(\n        ISilo.AssetStorage memory _assetStates,\n        uint256 _userCollateralTokenBalance,\n        uint256 _userCollateralOnlyTokenBalance,\n        uint256 _rcomp,\n        ISiloRepository _siloRepository\n    ) internal view returns (uint256) {\n        uint256 assetAmount = _userCollateralTokenBalance == 0 ? 0 : _userCollateralTokenBalance.toAmount(\n            totalDepositsWithInterest(_assetStates.totalDeposits, _siloRepository.protocolShareFee(), _rcomp),\n            _assetStates.collateralToken.totalSupply()\n        );\n\n        uint256 assetCollateralOnlyAmount = _userCollateralOnlyTokenBalance == 0\n            ? 0\n            : _userCollateralOnlyTokenBalance.toAmount(\n                _assetStates.collateralOnlyDeposits,\n                _assetStates.collateralOnlyToken.totalSupply()\n            );\n\n        return assetAmount + assetCollateralOnlyAmount;\n    }\n\n    /// @notice Get amount of borrowed token\n    /// @param _assetStates state of borrowed asset in Silo\n    /// @param _user user wallet address for which to read debt\n    /// @param _rcomp compounded interest rate to account for during calculations, could be 0\n    /// @return amount of borrowed token\n    function getUserBorrowAmount(ISilo.AssetStorage memory _assetStates, address _user, uint256 _rcomp)\n        internal\n        view\n        returns (uint256)\n    {\n        uint256 balance = _assetStates.debtToken.balanceOf(_user);\n        if (balance == 0) return 0;\n\n        uint256 totalBorrowAmountCached = totalBorrowAmountWithInterest(_assetStates.totalBorrowAmount, _rcomp);\n        return balance.toAmountRoundUp(totalBorrowAmountCached, _assetStates.debtToken.totalSupply());\n    }\n\n    /// @notice Get compounded interest rate from the model\n    /// @param _silo Silo address\n    /// @param _siloRepository SiloRepository address\n    /// @param _asset address of asset for which to read interest rate\n    /// @param _timestamp used to determine amount of time from last rate update\n    /// @return rcomp compounded interest rate for an asset\n    function getRcomp(ISilo _silo, ISiloRepository _siloRepository, address _asset, uint256 _timestamp)\n        internal\n        view\n        returns (uint256 rcomp)\n    {\n        IInterestRateModel model = _siloRepository.getInterestRateModel(address(_silo), _asset);\n        rcomp = model.getCompoundInterestRate(address(_silo), _asset, _timestamp);\n    }\n\n    /// @notice Returns total deposits with interest dynamically calculated with the provided rComp\n    /// @param _assetTotalDeposits total deposits for asset\n    /// @param _protocolShareFee `siloRepository.protocolShareFee()`\n    /// @param _rcomp compounded interest rate\n    /// @return _totalDepositsWithInterests total deposits amount with interest\n    function totalDepositsWithInterest(uint256 _assetTotalDeposits, uint256 _protocolShareFee, uint256 _rcomp)\n        internal\n        pure\n        returns (uint256 _totalDepositsWithInterests)\n    {\n        uint256 depositorsShare = _PRECISION_DECIMALS - _protocolShareFee;\n\n        return _assetTotalDeposits + _assetTotalDeposits * _rcomp / _PRECISION_DECIMALS * depositorsShare /\n            _PRECISION_DECIMALS;\n    }\n\n    /// @notice Returns total borrow amount with interest dynamically calculated with the provided rComp\n    /// @param _totalBorrowAmount total borrow amount\n    /// @param _rcomp compounded interest rate\n    /// @return totalBorrowAmountWithInterests total borrow amount with interest\n    function totalBorrowAmountWithInterest(uint256 _totalBorrowAmount, uint256 _rcomp)\n        internal\n        pure\n        returns (uint256 totalBorrowAmountWithInterests)\n    {\n        totalBorrowAmountWithInterests = _totalBorrowAmount + _totalBorrowAmount * _rcomp / _PRECISION_DECIMALS;\n    }\n\n    /// @notice Calculates protocol liquidation fee and new protocol total fees collected\n    /// @param _protocolEarnedFees amount of total collected fees so far\n    /// @param _amount amount on which we will apply fee\n    /// @param _liquidationFee liquidation fee in Solvency._PRECISION_DECIMALS\n    /// @return liquidationFeeAmount calculated interest\n    /// @return newProtocolEarnedFees the new total amount of protocol fees\n    function calculateLiquidationFee(uint256 _protocolEarnedFees, uint256 _amount, uint256 _liquidationFee)\n        internal\n        pure\n        returns (uint256 liquidationFeeAmount, uint256 newProtocolEarnedFees)\n    {\n        unchecked {\n            // If we overflow on multiplication it should not revert tx, we will get lower fees\n            liquidationFeeAmount = _amount * _liquidationFee / Solvency._PRECISION_DECIMALS;\n\n            if (_protocolEarnedFees > type(uint256).max - liquidationFeeAmount) {\n                newProtocolEarnedFees = type(uint256).max;\n                liquidationFeeAmount = type(uint256).max - _protocolEarnedFees;\n            } else {\n                newProtocolEarnedFees = _protocolEarnedFees + liquidationFeeAmount;\n            }\n        }\n    }\n\n    /// @notice Calculates theoretical value (in quote token) that user could borrow based given collateral value\n    /// @param _siloRepository SiloRepository address\n    /// @param _silo Silo address\n    /// @param _asset address of collateral token\n    /// @param _type type of LTV limit to use for calculations\n    /// @param _collateralValue value of collateral deposit (in quote token)\n    /// @return availableToBorrow value (in quote token) that user can borrow against collateral value\n    function _getAvailableToBorrowValue(\n        ISiloRepository _siloRepository,\n        address _silo,\n        address _asset,\n        TypeofLTV _type,\n        uint256 _collateralValue\n    ) private view returns (uint256 availableToBorrow) {\n        uint256 assetLTV;\n\n        if (_type == TypeofLTV.MaximumLTV) {\n            assetLTV = _siloRepository.getMaximumLTV(_silo, _asset);\n        } else if (_type == TypeofLTV.LiquidationThreshold) {\n            assetLTV = _siloRepository.getLiquidationThreshold(_silo, _asset);\n        } else {\n            revert UnsupportedLTVType();\n        }\n\n        // value that can be borrowed against the deposit\n        // ie. for assetLTV = 50%, 1 ETH * 50% = 0.5 ETH of available to borrow\n        availableToBorrow = _collateralValue * assetLTV / _PRECISION_DECIMALS;\n    }\n\n    /// @notice Calculates theoretical value (in quote token) that user can borrow based on deposited collateral\n    /// @param _siloRepository SiloRepository address\n    /// @param _silo Silo address\n    /// @param _assets array with assets\n    /// @param _ltvType type of LTV limit to use for calculations\n    /// acceptable values are only TypeofLTV.MaximumLTV or TypeofLTV.LiquidationThreshold\n    /// @param _collateralValues value (worth in quote token) of each deposit made by user\n    /// @return totalAvailableToBorrowValue value (in quote token) that user can borrow against collaterals\n    function _getTotalAvailableToBorrowValue(\n        ISiloRepository _siloRepository,\n        address _silo,\n        address[] memory _assets,\n        TypeofLTV _ltvType,\n        uint256[] memory _collateralValues\n    ) private view returns (uint256 totalAvailableToBorrowValue) {\n        if (_assets.length != _collateralValues.length) revert DifferentArrayLength();\n\n        for (uint256 i = 0; i < _assets.length; i++) {\n            totalAvailableToBorrowValue += _getAvailableToBorrowValue(\n                _siloRepository,\n                _silo,\n                _assets[i],\n                _ltvType,\n                _collateralValues[i]\n            );\n        }\n    }\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "metadata": {
        "useLiteralContent": true
      },
      "libraries": {}
    }
  }}