creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor() {\n        _transferOwnership(_msgSender());\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions anymore. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby removing any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
      },
      "@openzeppelin/contracts/access/Ownable2Step.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./Ownable.sol\";\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2Step is Ownable {\n    address private _pendingOwner;\n\n    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Returns the address of the pending owner.\n     */\n    function pendingOwner() public view virtual returns (address) {\n        return _pendingOwner;\n    }\n\n    /**\n     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual override onlyOwner {\n        _pendingOwner = newOwner;\n        emit OwnershipTransferStarted(owner(), newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual override {\n        delete _pendingOwner;\n        super._transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev The new owner accepts the ownership transfer.\n     */\n    function acceptOwnership() external {\n        address sender = _msgSender();\n        require(pendingOwner() == sender, \"Ownable2Step: caller is not the new owner\");\n        _transferOwnership(sender);\n    }\n}\n"
      },
      "@openzeppelin/contracts/interfaces/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../token/ERC20/IERC20.sol\";\n"
      },
      "@openzeppelin/contracts/security/ReentrancyGuard.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (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        _nonReentrantBefore();\n        _;\n        _nonReentrantAfter();\n    }\n\n    function _nonReentrantBefore() private {\n        // On the first call to nonReentrant, _status will be _NOT_ENTERED\n        require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n        // Any calls to nonReentrant after this point will fail\n        _status = _ENTERED;\n    }\n\n    function _nonReentrantAfter() private {\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"
      },
      "@openzeppelin/contracts/token/ERC20/ERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (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.openzeppelin.com/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     * - `to` cannot be the zero address.\n     * - the caller must have a balance of at least `amount`.\n     */\n    function transfer(address to, uint256 amount) public virtual override returns (bool) {\n        address owner = _msgSender();\n        _transfer(owner, to, 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     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\n     * `transferFrom`. This is semantically equivalent to an infinite approval.\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        address owner = _msgSender();\n        _approve(owner, 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     * NOTE: Does not update the allowance if the current allowance\n     * is the maximum `uint256`.\n     *\n     * Requirements:\n     *\n     * - `from` and `to` cannot be the zero address.\n     * - `from` must have a balance of at least `amount`.\n     * - the caller must have allowance for ``from``'s tokens of at least\n     * `amount`.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) public virtual override returns (bool) {\n        address spender = _msgSender();\n        _spendAllowance(from, spender, amount);\n        _transfer(from, to, amount);\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        address owner = _msgSender();\n        _approve(owner, spender, allowance(owner, 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        address owner = _msgSender();\n        uint256 currentAllowance = allowance(owner, spender);\n        require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n        unchecked {\n            _approve(owner, spender, currentAllowance - subtractedValue);\n        }\n\n        return true;\n    }\n\n    /**\n     * @dev Moves `amount` of tokens from `from` to `to`.\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     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `from` must have a balance of at least `amount`.\n     */\n    function _transfer(\n        address from,\n        address to,\n        uint256 amount\n    ) internal virtual {\n        require(from != address(0), \"ERC20: transfer from the zero address\");\n        require(to != address(0), \"ERC20: transfer to the zero address\");\n\n        _beforeTokenTransfer(from, to, amount);\n\n        uint256 fromBalance = _balances[from];\n        require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n        unchecked {\n            _balances[from] = fromBalance - amount;\n            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by\n            // decrementing then incrementing.\n            _balances[to] += amount;\n        }\n\n        emit Transfer(from, to, amount);\n\n        _afterTokenTransfer(from, to, 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        unchecked {\n            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\n            _balances[account] += amount;\n        }\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            // Overflow not possible: amount <= accountBalance <= totalSupply.\n            _totalSupply -= amount;\n        }\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 Updates `owner` s allowance for `spender` based on spent `amount`.\n     *\n     * Does not update the allowance amount in case of infinite allowance.\n     * Revert if not enough allowance is available.\n     *\n     * Might emit an {Approval} event.\n     */\n    function _spendAllowance(\n        address owner,\n        address spender,\n        uint256 amount\n    ) internal virtual {\n        uint256 currentAllowance = allowance(owner, spender);\n        if (currentAllowance != type(uint256).max) {\n            require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n            unchecked {\n                _approve(owner, spender, currentAllowance - amount);\n            }\n        }\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/extensions/draft-IERC20Permit.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\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/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (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 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    /**\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 `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, 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 `from` to `to` 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 from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/draft-IERC20Permit.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    function safePermit(\n        IERC20Permit token,\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal {\n        uint256 nonceBefore = token.nonces(owner);\n        token.permit(owner, spender, value, deadline, v, r, s);\n        uint256 nonceAfter = token.nonces(owner);\n        require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\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"
      },
      "@openzeppelin/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\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     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 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 functionCallWithValue(target, data, 0, \"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        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, 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        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, 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        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or 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            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\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            /// @solidity memory-safe-assembly\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"
      },
      "@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/math/SafeCast.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)\n// This file was procedurally generated from scripts/generate/templates/SafeCast.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow\n * checks.\n *\n * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can\n * easily result in undesired exploitation or bugs, since developers usually\n * assume that overflows raise errors. `SafeCast` restores this intuition by\n * reverting the transaction when such an operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n *\n * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing\n * all math on `uint256` and `int256` and then downcasting.\n */\nlibrary SafeCast {\n    /**\n     * @dev Returns the downcasted uint248 from uint256, reverting on\n     * overflow (when the input is greater than largest uint248).\n     *\n     * Counterpart to Solidity's `uint248` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 248 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint248(uint256 value) internal pure returns (uint248) {\n        require(value <= type(uint248).max, \"SafeCast: value doesn't fit in 248 bits\");\n        return uint248(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint240 from uint256, reverting on\n     * overflow (when the input is greater than largest uint240).\n     *\n     * Counterpart to Solidity's `uint240` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 240 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint240(uint256 value) internal pure returns (uint240) {\n        require(value <= type(uint240).max, \"SafeCast: value doesn't fit in 240 bits\");\n        return uint240(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint232 from uint256, reverting on\n     * overflow (when the input is greater than largest uint232).\n     *\n     * Counterpart to Solidity's `uint232` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 232 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint232(uint256 value) internal pure returns (uint232) {\n        require(value <= type(uint232).max, \"SafeCast: value doesn't fit in 232 bits\");\n        return uint232(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint224 from uint256, reverting on\n     * overflow (when the input is greater than largest uint224).\n     *\n     * Counterpart to Solidity's `uint224` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 224 bits\n     *\n     * _Available since v4.2._\n     */\n    function toUint224(uint256 value) internal pure returns (uint224) {\n        require(value <= type(uint224).max, \"SafeCast: value doesn't fit in 224 bits\");\n        return uint224(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint216 from uint256, reverting on\n     * overflow (when the input is greater than largest uint216).\n     *\n     * Counterpart to Solidity's `uint216` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 216 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint216(uint256 value) internal pure returns (uint216) {\n        require(value <= type(uint216).max, \"SafeCast: value doesn't fit in 216 bits\");\n        return uint216(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint208 from uint256, reverting on\n     * overflow (when the input is greater than largest uint208).\n     *\n     * Counterpart to Solidity's `uint208` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 208 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint208(uint256 value) internal pure returns (uint208) {\n        require(value <= type(uint208).max, \"SafeCast: value doesn't fit in 208 bits\");\n        return uint208(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint200 from uint256, reverting on\n     * overflow (when the input is greater than largest uint200).\n     *\n     * Counterpart to Solidity's `uint200` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 200 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint200(uint256 value) internal pure returns (uint200) {\n        require(value <= type(uint200).max, \"SafeCast: value doesn't fit in 200 bits\");\n        return uint200(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint192 from uint256, reverting on\n     * overflow (when the input is greater than largest uint192).\n     *\n     * Counterpart to Solidity's `uint192` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 192 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint192(uint256 value) internal pure returns (uint192) {\n        require(value <= type(uint192).max, \"SafeCast: value doesn't fit in 192 bits\");\n        return uint192(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint184 from uint256, reverting on\n     * overflow (when the input is greater than largest uint184).\n     *\n     * Counterpart to Solidity's `uint184` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 184 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint184(uint256 value) internal pure returns (uint184) {\n        require(value <= type(uint184).max, \"SafeCast: value doesn't fit in 184 bits\");\n        return uint184(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint176 from uint256, reverting on\n     * overflow (when the input is greater than largest uint176).\n     *\n     * Counterpart to Solidity's `uint176` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 176 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint176(uint256 value) internal pure returns (uint176) {\n        require(value <= type(uint176).max, \"SafeCast: value doesn't fit in 176 bits\");\n        return uint176(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint168 from uint256, reverting on\n     * overflow (when the input is greater than largest uint168).\n     *\n     * Counterpart to Solidity's `uint168` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 168 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint168(uint256 value) internal pure returns (uint168) {\n        require(value <= type(uint168).max, \"SafeCast: value doesn't fit in 168 bits\");\n        return uint168(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint160 from uint256, reverting on\n     * overflow (when the input is greater than largest uint160).\n     *\n     * Counterpart to Solidity's `uint160` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 160 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint160(uint256 value) internal pure returns (uint160) {\n        require(value <= type(uint160).max, \"SafeCast: value doesn't fit in 160 bits\");\n        return uint160(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint152 from uint256, reverting on\n     * overflow (when the input is greater than largest uint152).\n     *\n     * Counterpart to Solidity's `uint152` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 152 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint152(uint256 value) internal pure returns (uint152) {\n        require(value <= type(uint152).max, \"SafeCast: value doesn't fit in 152 bits\");\n        return uint152(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint144 from uint256, reverting on\n     * overflow (when the input is greater than largest uint144).\n     *\n     * Counterpart to Solidity's `uint144` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 144 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint144(uint256 value) internal pure returns (uint144) {\n        require(value <= type(uint144).max, \"SafeCast: value doesn't fit in 144 bits\");\n        return uint144(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint136 from uint256, reverting on\n     * overflow (when the input is greater than largest uint136).\n     *\n     * Counterpart to Solidity's `uint136` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 136 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint136(uint256 value) internal pure returns (uint136) {\n        require(value <= type(uint136).max, \"SafeCast: value doesn't fit in 136 bits\");\n        return uint136(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint128 from uint256, reverting on\n     * overflow (when the input is greater than largest uint128).\n     *\n     * Counterpart to Solidity's `uint128` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 128 bits\n     *\n     * _Available since v2.5._\n     */\n    function toUint128(uint256 value) internal pure returns (uint128) {\n        require(value <= type(uint128).max, \"SafeCast: value doesn't fit in 128 bits\");\n        return uint128(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint120 from uint256, reverting on\n     * overflow (when the input is greater than largest uint120).\n     *\n     * Counterpart to Solidity's `uint120` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 120 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint120(uint256 value) internal pure returns (uint120) {\n        require(value <= type(uint120).max, \"SafeCast: value doesn't fit in 120 bits\");\n        return uint120(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint112 from uint256, reverting on\n     * overflow (when the input is greater than largest uint112).\n     *\n     * Counterpart to Solidity's `uint112` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 112 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint112(uint256 value) internal pure returns (uint112) {\n        require(value <= type(uint112).max, \"SafeCast: value doesn't fit in 112 bits\");\n        return uint112(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint104 from uint256, reverting on\n     * overflow (when the input is greater than largest uint104).\n     *\n     * Counterpart to Solidity's `uint104` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 104 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint104(uint256 value) internal pure returns (uint104) {\n        require(value <= type(uint104).max, \"SafeCast: value doesn't fit in 104 bits\");\n        return uint104(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint96 from uint256, reverting on\n     * overflow (when the input is greater than largest uint96).\n     *\n     * Counterpart to Solidity's `uint96` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 96 bits\n     *\n     * _Available since v4.2._\n     */\n    function toUint96(uint256 value) internal pure returns (uint96) {\n        require(value <= type(uint96).max, \"SafeCast: value doesn't fit in 96 bits\");\n        return uint96(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint88 from uint256, reverting on\n     * overflow (when the input is greater than largest uint88).\n     *\n     * Counterpart to Solidity's `uint88` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 88 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint88(uint256 value) internal pure returns (uint88) {\n        require(value <= type(uint88).max, \"SafeCast: value doesn't fit in 88 bits\");\n        return uint88(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint80 from uint256, reverting on\n     * overflow (when the input is greater than largest uint80).\n     *\n     * Counterpart to Solidity's `uint80` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 80 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint80(uint256 value) internal pure returns (uint80) {\n        require(value <= type(uint80).max, \"SafeCast: value doesn't fit in 80 bits\");\n        return uint80(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint72 from uint256, reverting on\n     * overflow (when the input is greater than largest uint72).\n     *\n     * Counterpart to Solidity's `uint72` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 72 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint72(uint256 value) internal pure returns (uint72) {\n        require(value <= type(uint72).max, \"SafeCast: value doesn't fit in 72 bits\");\n        return uint72(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint64 from uint256, reverting on\n     * overflow (when the input is greater than largest uint64).\n     *\n     * Counterpart to Solidity's `uint64` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 64 bits\n     *\n     * _Available since v2.5._\n     */\n    function toUint64(uint256 value) internal pure returns (uint64) {\n        require(value <= type(uint64).max, \"SafeCast: value doesn't fit in 64 bits\");\n        return uint64(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint56 from uint256, reverting on\n     * overflow (when the input is greater than largest uint56).\n     *\n     * Counterpart to Solidity's `uint56` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 56 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint56(uint256 value) internal pure returns (uint56) {\n        require(value <= type(uint56).max, \"SafeCast: value doesn't fit in 56 bits\");\n        return uint56(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint48 from uint256, reverting on\n     * overflow (when the input is greater than largest uint48).\n     *\n     * Counterpart to Solidity's `uint48` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 48 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint48(uint256 value) internal pure returns (uint48) {\n        require(value <= type(uint48).max, \"SafeCast: value doesn't fit in 48 bits\");\n        return uint48(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint40 from uint256, reverting on\n     * overflow (when the input is greater than largest uint40).\n     *\n     * Counterpart to Solidity's `uint40` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 40 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint40(uint256 value) internal pure returns (uint40) {\n        require(value <= type(uint40).max, \"SafeCast: value doesn't fit in 40 bits\");\n        return uint40(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint32 from uint256, reverting on\n     * overflow (when the input is greater than largest uint32).\n     *\n     * Counterpart to Solidity's `uint32` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 32 bits\n     *\n     * _Available since v2.5._\n     */\n    function toUint32(uint256 value) internal pure returns (uint32) {\n        require(value <= type(uint32).max, \"SafeCast: value doesn't fit in 32 bits\");\n        return uint32(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint24 from uint256, reverting on\n     * overflow (when the input is greater than largest uint24).\n     *\n     * Counterpart to Solidity's `uint24` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 24 bits\n     *\n     * _Available since v4.7._\n     */\n    function toUint24(uint256 value) internal pure returns (uint24) {\n        require(value <= type(uint24).max, \"SafeCast: value doesn't fit in 24 bits\");\n        return uint24(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint16 from uint256, reverting on\n     * overflow (when the input is greater than largest uint16).\n     *\n     * Counterpart to Solidity's `uint16` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 16 bits\n     *\n     * _Available since v2.5._\n     */\n    function toUint16(uint256 value) internal pure returns (uint16) {\n        require(value <= type(uint16).max, \"SafeCast: value doesn't fit in 16 bits\");\n        return uint16(value);\n    }\n\n    /**\n     * @dev Returns the downcasted uint8 from uint256, reverting on\n     * overflow (when the input is greater than largest uint8).\n     *\n     * Counterpart to Solidity's `uint8` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 8 bits\n     *\n     * _Available since v2.5._\n     */\n    function toUint8(uint256 value) internal pure returns (uint8) {\n        require(value <= type(uint8).max, \"SafeCast: value doesn't fit in 8 bits\");\n        return uint8(value);\n    }\n\n    /**\n     * @dev Converts a signed int256 into an unsigned uint256.\n     *\n     * Requirements:\n     *\n     * - input must be greater than or equal to 0.\n     *\n     * _Available since v3.0._\n     */\n    function toUint256(int256 value) internal pure returns (uint256) {\n        require(value >= 0, \"SafeCast: value must be positive\");\n        return uint256(value);\n    }\n\n    /**\n     * @dev Returns the downcasted int248 from int256, reverting on\n     * overflow (when the input is less than smallest int248 or\n     * greater than largest int248).\n     *\n     * Counterpart to Solidity's `int248` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 248 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt248(int256 value) internal pure returns (int248 downcasted) {\n        downcasted = int248(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 248 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int240 from int256, reverting on\n     * overflow (when the input is less than smallest int240 or\n     * greater than largest int240).\n     *\n     * Counterpart to Solidity's `int240` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 240 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt240(int256 value) internal pure returns (int240 downcasted) {\n        downcasted = int240(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 240 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int232 from int256, reverting on\n     * overflow (when the input is less than smallest int232 or\n     * greater than largest int232).\n     *\n     * Counterpart to Solidity's `int232` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 232 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt232(int256 value) internal pure returns (int232 downcasted) {\n        downcasted = int232(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 232 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int224 from int256, reverting on\n     * overflow (when the input is less than smallest int224 or\n     * greater than largest int224).\n     *\n     * Counterpart to Solidity's `int224` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 224 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt224(int256 value) internal pure returns (int224 downcasted) {\n        downcasted = int224(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 224 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int216 from int256, reverting on\n     * overflow (when the input is less than smallest int216 or\n     * greater than largest int216).\n     *\n     * Counterpart to Solidity's `int216` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 216 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt216(int256 value) internal pure returns (int216 downcasted) {\n        downcasted = int216(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 216 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int208 from int256, reverting on\n     * overflow (when the input is less than smallest int208 or\n     * greater than largest int208).\n     *\n     * Counterpart to Solidity's `int208` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 208 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt208(int256 value) internal pure returns (int208 downcasted) {\n        downcasted = int208(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 208 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int200 from int256, reverting on\n     * overflow (when the input is less than smallest int200 or\n     * greater than largest int200).\n     *\n     * Counterpart to Solidity's `int200` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 200 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt200(int256 value) internal pure returns (int200 downcasted) {\n        downcasted = int200(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 200 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int192 from int256, reverting on\n     * overflow (when the input is less than smallest int192 or\n     * greater than largest int192).\n     *\n     * Counterpart to Solidity's `int192` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 192 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt192(int256 value) internal pure returns (int192 downcasted) {\n        downcasted = int192(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 192 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int184 from int256, reverting on\n     * overflow (when the input is less than smallest int184 or\n     * greater than largest int184).\n     *\n     * Counterpart to Solidity's `int184` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 184 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt184(int256 value) internal pure returns (int184 downcasted) {\n        downcasted = int184(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 184 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int176 from int256, reverting on\n     * overflow (when the input is less than smallest int176 or\n     * greater than largest int176).\n     *\n     * Counterpart to Solidity's `int176` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 176 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt176(int256 value) internal pure returns (int176 downcasted) {\n        downcasted = int176(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 176 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int168 from int256, reverting on\n     * overflow (when the input is less than smallest int168 or\n     * greater than largest int168).\n     *\n     * Counterpart to Solidity's `int168` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 168 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt168(int256 value) internal pure returns (int168 downcasted) {\n        downcasted = int168(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 168 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int160 from int256, reverting on\n     * overflow (when the input is less than smallest int160 or\n     * greater than largest int160).\n     *\n     * Counterpart to Solidity's `int160` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 160 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt160(int256 value) internal pure returns (int160 downcasted) {\n        downcasted = int160(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 160 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int152 from int256, reverting on\n     * overflow (when the input is less than smallest int152 or\n     * greater than largest int152).\n     *\n     * Counterpart to Solidity's `int152` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 152 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt152(int256 value) internal pure returns (int152 downcasted) {\n        downcasted = int152(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 152 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int144 from int256, reverting on\n     * overflow (when the input is less than smallest int144 or\n     * greater than largest int144).\n     *\n     * Counterpart to Solidity's `int144` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 144 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt144(int256 value) internal pure returns (int144 downcasted) {\n        downcasted = int144(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 144 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int136 from int256, reverting on\n     * overflow (when the input is less than smallest int136 or\n     * greater than largest int136).\n     *\n     * Counterpart to Solidity's `int136` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 136 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt136(int256 value) internal pure returns (int136 downcasted) {\n        downcasted = int136(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 136 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int128 from int256, reverting on\n     * overflow (when the input is less than smallest int128 or\n     * greater than largest int128).\n     *\n     * Counterpart to Solidity's `int128` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 128 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt128(int256 value) internal pure returns (int128 downcasted) {\n        downcasted = int128(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 128 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int120 from int256, reverting on\n     * overflow (when the input is less than smallest int120 or\n     * greater than largest int120).\n     *\n     * Counterpart to Solidity's `int120` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 120 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt120(int256 value) internal pure returns (int120 downcasted) {\n        downcasted = int120(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 120 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int112 from int256, reverting on\n     * overflow (when the input is less than smallest int112 or\n     * greater than largest int112).\n     *\n     * Counterpart to Solidity's `int112` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 112 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt112(int256 value) internal pure returns (int112 downcasted) {\n        downcasted = int112(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 112 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int104 from int256, reverting on\n     * overflow (when the input is less than smallest int104 or\n     * greater than largest int104).\n     *\n     * Counterpart to Solidity's `int104` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 104 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt104(int256 value) internal pure returns (int104 downcasted) {\n        downcasted = int104(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 104 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int96 from int256, reverting on\n     * overflow (when the input is less than smallest int96 or\n     * greater than largest int96).\n     *\n     * Counterpart to Solidity's `int96` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 96 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt96(int256 value) internal pure returns (int96 downcasted) {\n        downcasted = int96(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 96 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int88 from int256, reverting on\n     * overflow (when the input is less than smallest int88 or\n     * greater than largest int88).\n     *\n     * Counterpart to Solidity's `int88` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 88 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt88(int256 value) internal pure returns (int88 downcasted) {\n        downcasted = int88(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 88 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int80 from int256, reverting on\n     * overflow (when the input is less than smallest int80 or\n     * greater than largest int80).\n     *\n     * Counterpart to Solidity's `int80` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 80 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt80(int256 value) internal pure returns (int80 downcasted) {\n        downcasted = int80(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 80 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int72 from int256, reverting on\n     * overflow (when the input is less than smallest int72 or\n     * greater than largest int72).\n     *\n     * Counterpart to Solidity's `int72` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 72 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt72(int256 value) internal pure returns (int72 downcasted) {\n        downcasted = int72(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 72 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int64 from int256, reverting on\n     * overflow (when the input is less than smallest int64 or\n     * greater than largest int64).\n     *\n     * Counterpart to Solidity's `int64` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 64 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt64(int256 value) internal pure returns (int64 downcasted) {\n        downcasted = int64(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 64 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int56 from int256, reverting on\n     * overflow (when the input is less than smallest int56 or\n     * greater than largest int56).\n     *\n     * Counterpart to Solidity's `int56` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 56 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt56(int256 value) internal pure returns (int56 downcasted) {\n        downcasted = int56(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 56 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int48 from int256, reverting on\n     * overflow (when the input is less than smallest int48 or\n     * greater than largest int48).\n     *\n     * Counterpart to Solidity's `int48` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 48 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt48(int256 value) internal pure returns (int48 downcasted) {\n        downcasted = int48(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 48 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int40 from int256, reverting on\n     * overflow (when the input is less than smallest int40 or\n     * greater than largest int40).\n     *\n     * Counterpart to Solidity's `int40` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 40 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt40(int256 value) internal pure returns (int40 downcasted) {\n        downcasted = int40(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 40 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int32 from int256, reverting on\n     * overflow (when the input is less than smallest int32 or\n     * greater than largest int32).\n     *\n     * Counterpart to Solidity's `int32` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 32 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt32(int256 value) internal pure returns (int32 downcasted) {\n        downcasted = int32(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 32 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int24 from int256, reverting on\n     * overflow (when the input is less than smallest int24 or\n     * greater than largest int24).\n     *\n     * Counterpart to Solidity's `int24` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 24 bits\n     *\n     * _Available since v4.7._\n     */\n    function toInt24(int256 value) internal pure returns (int24 downcasted) {\n        downcasted = int24(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 24 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int16 from int256, reverting on\n     * overflow (when the input is less than smallest int16 or\n     * greater than largest int16).\n     *\n     * Counterpart to Solidity's `int16` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 16 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt16(int256 value) internal pure returns (int16 downcasted) {\n        downcasted = int16(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 16 bits\");\n    }\n\n    /**\n     * @dev Returns the downcasted int8 from int256, reverting on\n     * overflow (when the input is less than smallest int8 or\n     * greater than largest int8).\n     *\n     * Counterpart to Solidity's `int8` operator.\n     *\n     * Requirements:\n     *\n     * - input must fit into 8 bits\n     *\n     * _Available since v3.1._\n     */\n    function toInt8(int256 value) internal pure returns (int8 downcasted) {\n        downcasted = int8(value);\n        require(downcasted == value, \"SafeCast: value doesn't fit in 8 bits\");\n    }\n\n    /**\n     * @dev Converts an unsigned uint256 into a signed int256.\n     *\n     * Requirements:\n     *\n     * - input must be less than or equal to maxInt256.\n     *\n     * _Available since v3.0._\n     */\n    function toInt256(uint256 value) internal pure returns (int256) {\n        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive\n        require(value <= uint256(type(int256).max), \"SafeCast: value doesn't fit in an int256\");\n        return int256(value);\n    }\n}\n"
      },
      "src/contracts/FraxlendPair.sol": {
        "content": "// SPDX-License-Identifier: ISC\npragma solidity ^0.8.18;\n\n// ====================================================================\n// |     ______                   _______                             |\n// |    / _____________ __  __   / ____(_____  ____ _____  ________   |\n// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \\/ __ `/ __ \\/ ___/ _ \\  |\n// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |\n// | /_/   /_/   \\__,_/_/|_|  /_/   /_/_/ /_/\\__,_/_/ /_/\\___/\\___/   |\n// |                                                                  |\n// ====================================================================\n// ========================== FraxlendPair ============================\n// ====================================================================\n// Frax Finance: https://github.com/FraxFinance\n\n// Primary Author\n// Drake Evans: https://github.com/DrakeEvans\n\n// Reviewers\n// Dennis: https://github.com/denett\n// Sam Kazemian: https://github.com/samkazemian\n// Travis Moore: https://github.com/FortisFortuna\n// Jack Corddry: https://github.com/corddry\n// Rich Gee: https://github.com/zer0blockchain\n\n// ====================================================================\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport \"./FraxlendPairConstants.sol\";\nimport \"./FraxlendPairCore.sol\";\nimport \"./libraries/VaultAccount.sol\";\nimport \"./interfaces/ISwapper.sol\";\n\n/// @title FraxlendPair\n/// @author Drake Evans (Frax Finance) https://github.com/drakeevans\n/// @notice  The FraxlendPair is a lending pair that allows users to engage in lending and borrowing activities\ncontract FraxlendPair is IERC20Metadata, FraxlendPairCore {\n    using VaultAccountingLibrary for VaultAccount;\n    using SafeERC20 for IERC20;\n    using SafeCast for uint256;\n\n    /// @param _configData abi.encode(address _asset, address _collateral, address _oracle, uint32 _maxOracleDeviation, address _rateContract, uint64 _fullUtilizationRate, uint256 _maxLTV, uint256 _cleanLiquidationFee, uint256 _dirtyLiquidationFee, uint256 _protocolLiquidationFee)\n    /// @param _immutables abi.encode(address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress)\n    /// @param _customConfigData abi.encode(string memory _nameOfContract, string memory _symbolOfContract, uint8 _decimalsOfContract)\n    constructor(\n        bytes memory _configData,\n        bytes memory _immutables,\n        bytes memory _customConfigData\n    ) FraxlendPairCore(_configData, _immutables, _customConfigData) {}\n\n    // ============================================================================================\n    // ERC20 Metadata\n    // ============================================================================================\n\n    function name() public view override(ERC20, IERC20Metadata) returns (string memory) {\n        return nameOfContract;\n    }\n\n    function symbol() public view override(ERC20, IERC20Metadata) returns (string memory) {\n        return symbolOfContract;\n    }\n\n    function decimals() public view override(ERC20, IERC20Metadata) returns (uint8) {\n        return decimalsOfContract;\n    }\n\n    // totalSupply for fToken ERC20 compatibility\n    function totalSupply() public view override(ERC20, IERC20) returns (uint256) {\n        return totalAsset.shares;\n    }\n\n    // ============================================================================================\n    // Functions: Helpers\n    // ============================================================================================\n\n    function asset() external view returns (address) {\n        return address(assetContract);\n    }\n\n    function getConstants()\n        external\n        pure\n        returns (\n            uint256 _LTV_PRECISION,\n            uint256 _LIQ_PRECISION,\n            uint256 _UTIL_PREC,\n            uint256 _FEE_PRECISION,\n            uint256 _EXCHANGE_PRECISION,\n            uint256 _DEVIATION_PRECISION,\n            uint256 _RATE_PRECISION,\n            uint256 _MAX_PROTOCOL_FEE\n        )\n    {\n        _LTV_PRECISION = LTV_PRECISION;\n        _LIQ_PRECISION = LIQ_PRECISION;\n        _UTIL_PREC = UTIL_PREC;\n        _FEE_PRECISION = FEE_PRECISION;\n        _EXCHANGE_PRECISION = EXCHANGE_PRECISION;\n        _DEVIATION_PRECISION = DEVIATION_PRECISION;\n        _RATE_PRECISION = RATE_PRECISION;\n        _MAX_PROTOCOL_FEE = MAX_PROTOCOL_FEE;\n    }\n\n    /// @notice The ```getUserSnapshot``` function gets user level accounting data\n    /// @param _address The user address\n    /// @return _userAssetShares The user fToken balance\n    /// @return _userBorrowShares The user borrow shares\n    /// @return _userCollateralBalance The user collateral balance\n    function getUserSnapshot(\n        address _address\n    ) external view returns (uint256 _userAssetShares, uint256 _userBorrowShares, uint256 _userCollateralBalance) {\n        _userAssetShares = balanceOf(_address);\n        _userBorrowShares = userBorrowShares[_address];\n        _userCollateralBalance = userCollateralBalance[_address];\n    }\n\n    /// @notice The ```getPairAccounting``` function gets all pair level accounting numbers\n    /// @return _totalAssetAmount Total assets deposited and interest accrued, total claims\n    /// @return _totalAssetShares Total fTokens\n    /// @return _totalBorrowAmount Total borrows\n    /// @return _totalBorrowShares Total borrow shares\n    /// @return _totalCollateral Total collateral\n    function getPairAccounting()\n        external\n        view\n        returns (\n            uint128 _totalAssetAmount,\n            uint128 _totalAssetShares,\n            uint128 _totalBorrowAmount,\n            uint128 _totalBorrowShares,\n            uint256 _totalCollateral\n        )\n    {\n        (, , , , VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow) = previewAddInterest();\n        _totalAssetAmount = _totalAsset.amount;\n        _totalAssetShares = _totalAsset.shares;\n        _totalBorrowAmount = _totalBorrow.amount;\n        _totalBorrowShares = _totalBorrow.shares;\n        _totalCollateral = totalCollateral;\n    }\n\n    /// @notice The ```toBorrowShares``` function converts a given amount of borrow debt into the number of shares\n    /// @param _amount Amount of borrow\n    /// @param _roundUp Whether to roundup during division\n    /// @param _previewInterest Whether to simulate interest accrual\n    /// @return _shares The number of shares\n    function toBorrowShares(\n        uint256 _amount,\n        bool _roundUp,\n        bool _previewInterest\n    ) external view returns (uint256 _shares) {\n        if (_previewInterest) {\n            (, , , , , VaultAccount memory _totalBorrow) = previewAddInterest();\n            _shares = _totalBorrow.toShares(_amount, _roundUp);\n        } else {\n            _shares = totalBorrow.toShares(_amount, _roundUp);\n        }\n    }\n\n    /// @notice The ```toBorrowAmount``` function converts a given amount of borrow debt into the number of shares\n    /// @param _shares Shares of borrow\n    /// @param _roundUp Whether to roundup during division\n    /// @param _previewInterest Whether to simulate interest accrual\n    /// @return _amount The amount of asset\n    function toBorrowAmount(\n        uint256 _shares,\n        bool _roundUp,\n        bool _previewInterest\n    ) external view returns (uint256 _amount) {\n        if (_previewInterest) {\n            (, , , , , VaultAccount memory _totalBorrow) = previewAddInterest();\n            _amount = _totalBorrow.toAmount(_shares, _roundUp);\n        } else {\n            _amount = totalBorrow.toAmount(_shares, _roundUp);\n        }\n    }\n\n    /// @notice The ```toAssetAmount``` function converts a given number of shares to an asset amount\n    /// @param _shares Shares of asset (fToken)\n    /// @param _roundUp Whether to round up after division\n    /// @param _previewInterest Whether to preview interest accrual before calculation\n    /// @return _amount The amount of asset\n    function toAssetAmount(\n        uint256 _shares,\n        bool _roundUp,\n        bool _previewInterest\n    ) public view returns (uint256 _amount) {\n        if (_previewInterest) {\n            (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();\n            _amount = _totalAsset.toAmount(_shares, _roundUp);\n        } else {\n            _amount = totalAsset.toAmount(_shares, _roundUp);\n        }\n    }\n\n    /// @notice The ```toAssetShares``` function converts a given asset amount to a number of asset shares (fTokens)\n    /// @param _amount The amount of asset\n    /// @param _roundUp Whether to round up after division\n    /// @param _previewInterest Whether to preview interest accrual before calculation\n    /// @return _shares The number of shares (fTokens)\n    function toAssetShares(\n        uint256 _amount,\n        bool _roundUp,\n        bool _previewInterest\n    ) public view returns (uint256 _shares) {\n        if (_previewInterest) {\n            (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();\n            _shares = _totalAsset.toShares(_amount, _roundUp);\n        } else {\n            _shares = totalAsset.toShares(_amount, _roundUp);\n        }\n    }\n\n    function convertToAssets(uint256 _shares) external view returns (uint256 _assets) {\n        _assets = toAssetAmount(_shares, false, true);\n    }\n\n    function convertToShares(uint256 _assets) external view returns (uint256 _shares) {\n        _shares = toAssetShares(_assets, false, true);\n    }\n\n    function pricePerShare() external view returns (uint256 _amount) {\n        _amount = toAssetAmount(1e18, false, true);\n    }\n\n    function totalAssets() external view returns (uint256) {\n        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();\n        return _totalAsset.amount;\n    }\n\n    function maxDeposit(address _receiver) public view returns (uint256 _maxAssets) {\n        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();\n        _maxAssets = _totalAsset.amount < depositLimit ? depositLimit : depositLimit - _totalAsset.amount;\n    }\n\n    function maxMint(address _receiver) external view returns (uint256 _maxShares) {\n        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();\n        uint256 _maxDeposit = _totalAsset.amount < depositLimit ? depositLimit : depositLimit - _totalAsset.amount;\n        _maxShares = _totalAsset.toShares(_maxDeposit, false);\n    }\n\n    function maxWithdraw(address _owner) external view returns (uint256 _maxAssets) {\n        if (isWithdrawPaused) return 0;\n        (\n            ,\n            ,\n            uint256 _feesShare,\n            ,\n            VaultAccount memory _totalAsset,\n            VaultAccount memory _totalBorrow\n        ) = previewAddInterest();\n        // Get the owner balance and include the fees share if owner is this contract\n        uint256 _ownerBalance = _owner == address(this) ? balanceOf(_owner) + _feesShare : balanceOf(_owner);\n\n        // Return the lower of total assets in contract or total assets available to _owner\n        uint256 _totalAssetsAvailable = _totalAssetAvailable(_totalAsset, _totalBorrow);\n        uint256 _totalUserWithdraw = _totalAsset.toAmount(_ownerBalance, false);\n        _maxAssets = _totalAssetsAvailable < _totalUserWithdraw ? _totalAssetsAvailable : _totalUserWithdraw;\n    }\n\n    function maxRedeem(address _owner) external view returns (uint256 _maxShares) {\n        if (isWithdrawPaused) return 0;\n        (\n            ,\n            ,\n            uint256 _feesShare,\n            ,\n            VaultAccount memory _totalAsset,\n            VaultAccount memory _totalBorrow\n        ) = previewAddInterest();\n\n        // Calculate the total shares available\n        uint256 _totalAssetsAvailable = _totalAssetAvailable(_totalAsset, _totalBorrow);\n        uint256 _totalSharesAvailable = _totalAsset.toShares(_totalAssetsAvailable, false);\n\n        // Get the owner balance and include the fees share if owner is this contract\n        uint256 _ownerBalance = _owner == address(this) ? balanceOf(_owner) + _feesShare : balanceOf(_owner);\n        _maxShares = _totalSharesAvailable < _ownerBalance ? _totalSharesAvailable : _ownerBalance;\n    }\n\n    // ============================================================================================\n    // Functions: Configuration\n    // ============================================================================================\n\n    bool public isOracleSetterRevoked;\n\n    /// @notice The ```RevokeOracleSetter``` event is emitted when the oracle setter is revoked\n    event RevokeOracleInfoSetter();\n\n    /// @notice The ```revokeOracleSetter``` function revokes the oracle setter\n    function revokeOracleInfoSetter() external {\n        _requireTimelock();\n        isOracleSetterRevoked = true;\n        emit RevokeOracleInfoSetter();\n    }\n\n    /// @notice The ```SetOracleInfo``` event is emitted when the oracle info (address and max deviation) is set\n    /// @param oldOracle The old oracle address\n    /// @param oldMaxOracleDeviation The old max oracle deviation\n    /// @param newOracle The new oracle address\n    /// @param newMaxOracleDeviation The new max oracle deviation\n    event SetOracleInfo(\n        address oldOracle,\n        uint32 oldMaxOracleDeviation,\n        address newOracle,\n        uint32 newMaxOracleDeviation\n    );\n\n    /// @notice The ```setOracleInfo``` function sets the oracle data\n    /// @param _newOracle The new oracle address\n    /// @param _newMaxOracleDeviation The new max oracle deviation\n    function setOracle(address _newOracle, uint32 _newMaxOracleDeviation) external {\n        _requireTimelock();\n        if (isOracleSetterRevoked) revert SetterRevoked();\n        ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;\n        emit SetOracleInfo(\n            _exchangeRateInfo.oracle,\n            _exchangeRateInfo.maxOracleDeviation,\n            _newOracle,\n            _newMaxOracleDeviation\n        );\n        _exchangeRateInfo.oracle = _newOracle;\n        _exchangeRateInfo.maxOracleDeviation = _newMaxOracleDeviation;\n        exchangeRateInfo = _exchangeRateInfo;\n    }\n\n    bool public isMaxLTVSetterRevoked;\n\n    /// @notice The ```RevokeMaxLTVSetter``` event is emitted when the max LTV setter is revoked\n    event RevokeMaxLTVSetter();\n\n    /// @notice The ```revokeMaxLTVSetter``` function revokes the max LTV setter\n    function revokeMaxLTVSetter() external {\n        _requireTimelock();\n        isMaxLTVSetterRevoked = true;\n        emit RevokeMaxLTVSetter();\n    }\n\n    /// @notice The ```SetMaxLTV``` event is emitted when the max LTV is set\n    /// @param oldMaxLTV The old max LTV\n    /// @param newMaxLTV The new max LTV\n    event SetMaxLTV(uint256 oldMaxLTV, uint256 newMaxLTV);\n\n    /// @notice The ```setMaxLTV``` function sets the max LTV\n    /// @param _newMaxLTV The new max LTV\n    function setMaxLTV(uint256 _newMaxLTV) external {\n        _requireTimelock();\n        if (isMaxLTVSetterRevoked) revert SetterRevoked();\n        emit SetMaxLTV(maxLTV, _newMaxLTV);\n        maxLTV = _newMaxLTV;\n    }\n\n    bool public isRateContractSetterRevoked;\n\n    /// @notice The ```RevokeRateContractSetter``` event is emitted when the rate contract setter is revoked\n    event RevokeRateContractSetter();\n\n    /// @notice The ```revokeRateContractSetter``` function revokes the rate contract setter\n    function revokeRateContractSetter() external {\n        _requireTimelock();\n        isRateContractSetterRevoked = true;\n        emit RevokeRateContractSetter();\n    }\n\n    /// @notice The ```SetRateContract``` event is emitted when the rate contract is set\n    /// @param oldRateContract The old rate contract\n    /// @param newRateContract The new rate contract\n    event SetRateContract(address oldRateContract, address newRateContract);\n\n    /// @notice The ```setRateContract``` function sets the rate contract address\n    /// @param _newRateContract The new rate contract address\n    function setRateContract(address _newRateContract) external {\n        _requireTimelock();\n        if (isRateContractSetterRevoked) revert SetterRevoked();\n        emit SetRateContract(address(rateContract), _newRateContract);\n        rateContract = IRateCalculatorV2(_newRateContract);\n    }\n\n    bool public isLiquidationFeeSetterRevoked;\n\n    /// @notice The ```RevokeLiquidationFeeSetter``` event is emitted when the liquidation fee setter is revoked\n    event RevokeLiquidationFeeSetter();\n\n    /// @notice The ```revokeLiquidationFeeSetter``` function revokes the liquidation fee setter\n    function revokeLiquidationFeeSetter() external {\n        _requireTimelock();\n        isLiquidationFeeSetterRevoked = true;\n        emit RevokeLiquidationFeeSetter();\n    }\n\n    /// @notice The ```SetLiquidationFees``` event is emitted when the liquidation fees are set\n    /// @param oldCleanLiquidationFee The old clean liquidation fee\n    /// @param oldDirtyLiquidationFee The old dirty liquidation fee\n    /// @param oldProtocolLiquidationFee The old protocol liquidation fee\n    /// @param newCleanLiquidationFee The new clean liquidation fee\n    /// @param newDirtyLiquidationFee The new dirty liquidation fee\n    /// @param newProtocolLiquidationFee The new protocol liquidation fee\n    event SetLiquidationFees(\n        uint256 oldCleanLiquidationFee,\n        uint256 oldDirtyLiquidationFee,\n        uint256 oldProtocolLiquidationFee,\n        uint256 newCleanLiquidationFee,\n        uint256 newDirtyLiquidationFee,\n        uint256 newProtocolLiquidationFee\n    );\n\n    /// @notice The ```setLiquidationFees``` function sets the liquidation fees\n    /// @param _newCleanLiquidationFee The new clean liquidation fee\n    /// @param _newDirtyLiquidationFee The new dirty liquidation fee\n    function setLiquidationFees(\n        uint256 _newCleanLiquidationFee,\n        uint256 _newDirtyLiquidationFee,\n        uint256 _newProtocolLiquidationFee\n    ) external {\n        _requireTimelock();\n        if (isLiquidationFeeSetterRevoked) revert SetterRevoked();\n        emit SetLiquidationFees(\n            cleanLiquidationFee,\n            dirtyLiquidationFee,\n            protocolLiquidationFee,\n            _newCleanLiquidationFee,\n            _newDirtyLiquidationFee,\n            _newProtocolLiquidationFee\n        );\n        cleanLiquidationFee = _newCleanLiquidationFee;\n        dirtyLiquidationFee = _newDirtyLiquidationFee;\n        protocolLiquidationFee = _newProtocolLiquidationFee;\n    }\n\n    /// @notice The ```ChangeFee``` event first when the fee is changed\n    /// @param newFee The new fee\n    event ChangeFee(uint32 newFee);\n\n    /// @notice The ```changeFee``` function changes the protocol fee, max 50%\n    /// @param _newFee The new fee\n    function changeFee(uint32 _newFee) external {\n        _requireTimelock();\n        if (isInterestPaused) revert InterestPaused();\n        if (_newFee > MAX_PROTOCOL_FEE) {\n            revert BadProtocolFee();\n        }\n        _addInterest();\n        currentRateInfo.feeToProtocolRate = _newFee;\n        emit ChangeFee(_newFee);\n    }\n\n    /// @notice The ```WithdrawFees``` event fires when the fees are withdrawn\n    /// @param shares Number of shares (fTokens) redeemed\n    /// @param recipient To whom the assets were sent\n    /// @param amountToTransfer The amount of fees redeemed\n    event WithdrawFees(uint128 shares, address recipient, uint256 amountToTransfer, uint256 collateralAmount);\n\n    /// @notice The ```withdrawFees``` function withdraws fees accumulated\n    /// @param _shares Number of fTokens to redeem\n    /// @param _recipient Address to send the assets\n    /// @return _amountToTransfer Amount of assets sent to recipient\n    function withdrawFees(uint128 _shares, address _recipient) external onlyOwner returns (uint256 _amountToTransfer) {\n        if (_recipient == address(0)) revert InvalidReceiver();\n\n        // Grab some data from state to save gas\n        VaultAccount memory _totalAsset = totalAsset;\n\n        // Take all available if 0 value passed\n        if (_shares == 0) _shares = uint128(balanceOf(address(this)));\n\n        // We must calculate this before we subtract from _totalAsset or invoke _burn\n        _amountToTransfer = _totalAsset.toAmount(_shares, true);\n\n        _approve(address(this), msg.sender, _shares);\n        _redeem(_totalAsset, _amountToTransfer.toUint128(), _shares, _recipient, address(this));\n        uint256 _collateralAmount = userCollateralBalance[address(this)];\n        _removeCollateral(_collateralAmount, _recipient, address(this));\n        emit WithdrawFees(_shares, _recipient, _amountToTransfer, _collateralAmount);\n    }\n\n    /// @notice The ```SetSwapper``` event fires whenever a swapper is black or whitelisted\n    /// @param swapper The swapper address\n    /// @param approval The approval\n    event SetSwapper(address swapper, bool approval);\n\n    /// @notice The ```setSwapper``` function is called to black or whitelist a given swapper address\n    /// @dev\n    /// @param _swapper The swapper address\n    /// @param _approval The approval\n    function setSwapper(address _swapper, bool _approval) external onlyOwner {\n        swappers[_swapper] = _approval;\n        emit SetSwapper(_swapper, _approval);\n    }\n\n    // ============================================================================================\n    // Functions: Access Control\n    // ============================================================================================\n\n    /// @notice The ```pause``` function is called to pause all contract functionality\n    function pause() external {\n        _requireProtocolOrOwner();\n        if (!isBorrowAccessControlRevoked) _setBorrowLimit(0);\n        if (!isDepositAccessControlRevoked) _setDepositLimit(0);\n        if (!isRepayAccessControlRevoked) _pauseRepay(true);\n        if (!isWithdrawAccessControlRevoked) _pauseWithdraw(true);\n        if (!isLiquidateAccessControlRevoked) _pauseLiquidate(true);\n        if (!isInterestAccessControlRevoked) {\n            _addInterest();\n            _pauseInterest(true);\n        }\n    }\n\n    /// @notice The ```unpause``` function is called to unpause all contract functionality\n    function unpause() external {\n        _requireTimelockOrOwner();\n        if (!isBorrowAccessControlRevoked) _setBorrowLimit(type(uint256).max);\n        if (!isDepositAccessControlRevoked) _setDepositLimit(type(uint256).max);\n        if (!isRepayAccessControlRevoked) _pauseRepay(false);\n        if (!isWithdrawAccessControlRevoked) _pauseWithdraw(false);\n        if (!isLiquidateAccessControlRevoked) _pauseLiquidate(false);\n        if (!isInterestAccessControlRevoked) {\n            _addInterest();\n            _pauseInterest(false);\n        }\n    }\n\n    /// @notice The ```pauseBorrow``` function sets borrow limit to 0\n    function pauseBorrow() external {\n        _requireProtocolOrOwner();\n        if (isBorrowAccessControlRevoked) revert AccessControlRevoked();\n        _setBorrowLimit(0);\n    }\n\n    /// @notice The ```setBorrowLimit``` function sets the borrow limit\n    /// @param _limit The new borrow limit\n    function setBorrowLimit(uint256 _limit) external {\n        _requireTimelockOrOwner();\n        if (isBorrowAccessControlRevoked) revert AccessControlRevoked();\n        _setBorrowLimit(_limit);\n    }\n\n    /// @notice The ```revokeBorrowLimitAccessControl``` function revokes borrow limit access control\n    /// @param _borrowLimit The new borrow limit\n    function revokeBorrowLimitAccessControl(uint256 _borrowLimit) external {\n        _requireTimelock();\n        _revokeBorrowAccessControl(_borrowLimit);\n    }\n\n    /// @notice The ```pauseDeposit``` function pauses deposit functionality\n    function pauseDeposit() external {\n        _requireProtocolOrOwner();\n        if (isDepositAccessControlRevoked) revert AccessControlRevoked();\n        _setDepositLimit(0);\n    }\n\n    /// @notice The ```setDepositLimit``` function sets the deposit limit\n    /// @param _limit The new deposit limit\n    function setDepositLimit(uint256 _limit) external {\n        _requireTimelockOrOwner();\n        if (isDepositAccessControlRevoked) revert AccessControlRevoked();\n        _setDepositLimit(_limit);\n    }\n\n    /// @notice The ```revokeDepositLimitAccessControl``` function revokes deposit limit access control\n    /// @param _depositLimit The new deposit limit\n    function revokeDepositLimitAccessControl(uint256 _depositLimit) external {\n        _requireTimelock();\n        _revokeDepositAccessControl(_depositLimit);\n    }\n\n    /// @notice The ```pauseRepay``` function pauses repay functionality\n    /// @param _isPaused The new pause state\n    function pauseRepay(bool _isPaused) external {\n        if (_isPaused) {\n            _requireProtocolOrOwner();\n        } else {\n            _requireTimelockOrOwner();\n        }\n        if (isRepayAccessControlRevoked) revert AccessControlRevoked();\n        _pauseRepay(_isPaused);\n    }\n\n    /// @notice The ```revokeRepayAccessControl``` function revokes repay access control\n    function revokeRepayAccessControl() external {\n        _requireTimelock();\n        _revokeRepayAccessControl();\n    }\n\n    /// @notice The ```pauseWithdraw``` function pauses withdraw functionality\n    /// @param _isPaused The new pause state\n    function pauseWithdraw(bool _isPaused) external {\n        if (_isPaused) {\n            _requireProtocolOrOwner();\n        } else {\n            _requireTimelockOrOwner();\n        }\n        if (isWithdrawAccessControlRevoked) revert AccessControlRevoked();\n        _pauseWithdraw(_isPaused);\n    }\n\n    /// @notice The ```revokeWithdrawAccessControl``` function revokes withdraw access control\n    function revokeWithdrawAccessControl() external {\n        _requireTimelock();\n        _revokeWithdrawAccessControl();\n    }\n\n    /// @notice The ```pauseLiquidate``` function pauses liquidate functionality\n    /// @param _isPaused The new pause state\n    function pauseLiquidate(bool _isPaused) external {\n        if (_isPaused) {\n            _requireProtocolOrOwner();\n        } else {\n            _requireTimelockOrOwner();\n        }\n        if (isLiquidateAccessControlRevoked) revert AccessControlRevoked();\n        _pauseLiquidate(_isPaused);\n    }\n\n    /// @notice The ```revokeLiquidateAccessControl``` function revokes liquidate access control\n    function revokeLiquidateAccessControl() external {\n        _requireTimelock();\n        _revokeLiquidateAccessControl();\n    }\n\n    /// @notice The ```pauseInterest``` function pauses interest functionality\n    /// @param _isPaused The new pause state\n    function pauseInterest(bool _isPaused) external {\n        if (_isPaused) {\n            _requireProtocolOrOwner();\n        } else {\n            _requireTimelockOrOwner();\n        }\n        if (isInterestAccessControlRevoked) revert AccessControlRevoked();\n        // Resets the lastTimestamp which has the effect of no interest accruing over the pause period\n        _addInterest();\n        _pauseInterest(_isPaused);\n    }\n\n    /// @notice The ```revokeInterestAccessControl``` function revokes interest access control\n    function revokeInterestAccessControl() external {\n        _requireTimelock();\n        _revokeInterestAccessControl();\n    }\n}\n"
      },
      "src/contracts/FraxlendPairAccessControl.sol": {
        "content": "// SPDX-License-Identifier: ISC\npragma solidity ^0.8.18;\n\n// ====================================================================\n// |     ______                   _______                             |\n// |    / _____________ __  __   / ____(_____  ____ _____  ________   |\n// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \\/ __ `/ __ \\/ ___/ _ \\  |\n// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |\n// | /_/   /_/   \\__,_/_/|_|  /_/   /_/_/ /_/\\__,_/_/ /_/\\___/\\___/   |\n// |                                                                  |\n// ====================================================================\n// ==================== FraxlendPairAccessControl =====================\n// ====================================================================\n// Frax Finance: https://github.com/FraxFinance\n\n// Primary Author\n// Drake Evans: https://github.com/DrakeEvans\n\n// Reviewers\n// Dennis: https://github.com/denett\n\n// ====================================================================\n\nimport \"@openzeppelin/contracts/access/Ownable2Step.sol\";\nimport \"./Timelock2Step.sol\";\nimport \"./FraxlendPairAccessControlErrors.sol\";\n\n/// @title FraxlendPairAccessControl\n/// @author Drake Evans (Frax Finance) https://github.com/drakeevans\n/// @notice  An abstract contract which contains the access control logic for FraxlendPair\nabstract contract FraxlendPairAccessControl is Timelock2Step, Ownable2Step, FraxlendPairAccessControlErrors {\n    // Deployer\n    address public immutable DEPLOYER_ADDRESS;\n\n    // Admin contracts\n    address public circuitBreakerAddress;\n\n    // access control\n    uint256 public borrowLimit = type(uint256).max;\n    bool public isBorrowAccessControlRevoked;\n\n    uint256 public depositLimit = type(uint256).max;\n    bool public isDepositAccessControlRevoked;\n\n    bool public isRepayPaused;\n    bool public isRepayAccessControlRevoked;\n\n    bool public isWithdrawPaused;\n    bool public isWithdrawAccessControlRevoked;\n\n    bool public isLiquidatePaused;\n    bool public isLiquidateAccessControlRevoked;\n\n    bool public isInterestPaused;\n    bool public isInterestAccessControlRevoked;\n\n    /// @param _immutables abi.encode(address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress)\n    constructor(bytes memory _immutables) Timelock2Step() Ownable() {\n        // Handle Immutables Configuration\n        (address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress) = abi.decode(\n            _immutables,\n            (address, address, address)\n        );\n        _setTimelock(_timelockAddress);\n        _transferOwnership(_comptrollerAddress);\n\n        // Deployer contract\n        DEPLOYER_ADDRESS = msg.sender;\n        circuitBreakerAddress = _circuitBreakerAddress;\n    }\n\n    // ============================================================================================\n    // Functions: Access Control\n    // ============================================================================================\n\n    function _requireProtocolOrOwner() internal view {\n        if (\n            msg.sender != circuitBreakerAddress &&\n            msg.sender != owner() &&\n            msg.sender != DEPLOYER_ADDRESS &&\n            msg.sender != timelockAddress\n        ) {\n            revert OnlyProtocolOrOwner();\n        }\n    }\n\n    function _requireTimelockOrOwner() internal view {\n        if (msg.sender != owner() && msg.sender != timelockAddress) {\n            revert OnlyTimelockOrOwner();\n        }\n    }\n\n    /// @notice The ```RevokeBorrowAccessControl``` event is emitted when access to borrow limit is revoked\n    /// @param borrowLimit The final permanent borrow limit\n    event RevokeBorrowAccessControl(uint256 borrowLimit);\n\n    function _revokeBorrowAccessControl(uint256 _borrowLimit) internal {\n        isBorrowAccessControlRevoked = true;\n        borrowLimit = _borrowLimit;\n        emit RevokeBorrowAccessControl(_borrowLimit);\n    }\n\n    /// @notice The ```SetBorrowLimit``` event is emitted when the borrow limit is set\n    /// @param limit The new borrow limit\n    event SetBorrowLimit(uint256 limit);\n\n    function _setBorrowLimit(uint256 _limit) internal {\n        borrowLimit = _limit;\n        emit SetBorrowLimit(_limit);\n    }\n\n    /// @notice The ```RevokeDepositAccessControl``` event is emitted when access to deposit limit is revoked\n    /// @param depositLimit The final permanent deposit limit\n    event RevokeDepositAccessControl(uint256 depositLimit);\n\n    function _revokeDepositAccessControl(uint256 _depositLimit) internal {\n        isDepositAccessControlRevoked = true;\n        depositLimit = _depositLimit;\n        emit RevokeDepositAccessControl(_depositLimit);\n    }\n\n    /// @notice The ```SetDepositLimit``` event is emitted when the deposit limit is set\n    /// @param limit The new deposit limit\n    event SetDepositLimit(uint256 limit);\n\n    function _setDepositLimit(uint256 _limit) internal {\n        depositLimit = _limit;\n        emit SetDepositLimit(_limit);\n    }\n\n    /// @notice The ```RevokeRepayAccessControl``` event is emitted when repay access control is revoked\n    event RevokeRepayAccessControl();\n\n    function _revokeRepayAccessControl() internal {\n        isRepayAccessControlRevoked = true;\n        emit RevokeRepayAccessControl();\n    }\n\n    /// @notice The ```PauseRepay``` event is emitted when repay is paused or unpaused\n    /// @param isPaused The new paused state\n    event PauseRepay(bool isPaused);\n\n    function _pauseRepay(bool _isPaused) internal {\n        isRepayPaused = _isPaused;\n        emit PauseRepay(_isPaused);\n    }\n\n    /// @notice The ```RevokeWithdrawAccessControl``` event is emitted when withdraw access control is revoked\n    event RevokeWithdrawAccessControl();\n\n    function _revokeWithdrawAccessControl() internal {\n        isWithdrawAccessControlRevoked = true;\n        emit RevokeWithdrawAccessControl();\n    }\n\n    /// @notice The ```PauseWithdraw``` event is emitted when withdraw is paused or unpaused\n    /// @param isPaused The new paused state\n    event PauseWithdraw(bool isPaused);\n\n    function _pauseWithdraw(bool _isPaused) internal {\n        isWithdrawPaused = _isPaused;\n        emit PauseWithdraw(_isPaused);\n    }\n\n    /// @notice The ```RevokeLiquidateAccessControl``` event is emitted when liquidate access control is revoked\n    event RevokeLiquidateAccessControl();\n\n    function _revokeLiquidateAccessControl() internal {\n        isLiquidateAccessControlRevoked = true;\n        emit RevokeLiquidateAccessControl();\n    }\n\n    /// @notice The ```PauseLiquidate``` event is emitted when liquidate is paused or unpaused\n    /// @param isPaused The new paused state\n    event PauseLiquidate(bool isPaused);\n\n    function _pauseLiquidate(bool _isPaused) internal {\n        isLiquidatePaused = _isPaused;\n        emit PauseLiquidate(_isPaused);\n    }\n\n    /// @notice The ```RevokeInterestAccessControl``` event is emitted when interest access control is revoked\n    event RevokeInterestAccessControl();\n\n    function _revokeInterestAccessControl() internal {\n        isInterestAccessControlRevoked = true;\n        emit RevokeInterestAccessControl();\n    }\n\n    /// @notice The ```PauseInterest``` event is emitted when interest is paused or unpaused\n    /// @param isPaused The new paused state\n    event PauseInterest(bool isPaused);\n\n    function _pauseInterest(bool _isPaused) internal {\n        isInterestPaused = _isPaused;\n        emit PauseInterest(_isPaused);\n    }\n\n    /// @notice The ```SetCircuitBreaker``` event is emitted when the circuit breaker address is set\n    /// @param oldCircuitBreaker The old circuit breaker address\n    /// @param newCircuitBreaker The new circuit breaker address\n    event SetCircuitBreaker(address oldCircuitBreaker, address newCircuitBreaker);\n\n    /// @notice The ```_setCircuitBreaker``` function is called to set the circuit breaker address\n    /// @param _newCircuitBreaker The new circuit breaker address\n    function _setCircuitBreaker(address _newCircuitBreaker) internal {\n        address oldCircuitBreaker = circuitBreakerAddress;\n        circuitBreakerAddress = _newCircuitBreaker;\n        emit SetCircuitBreaker(oldCircuitBreaker, _newCircuitBreaker);\n    }\n\n    /// @notice The ```setCircuitBreaker``` function is called to set the circuit breaker address\n    /// @param _newCircuitBreaker The new circuit breaker address\n    function setCircuitBreaker(address _newCircuitBreaker) external virtual {\n        _requireTimelock();\n        _setCircuitBreaker(_newCircuitBreaker);\n    }\n}\n"
      },
      "src/contracts/FraxlendPairAccessControlErrors.sol": {
        "content": "// SPDX-License-Identifier: ISC\npragma solidity ^0.8.18;\n\n// ====================================================================\n// |     ______                   _______                             |\n// |    / _____________ __  __   / ____(_____  ____ _____  ________   |\n// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \\/ __ `/ __ \\/ ___/ _ \\  |\n// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |\n// | /_/   /_/   \\__,_/_/|_|  /_/   /_/_/ /_/\\__,_/_/ /_/\\___/\\___/   |\n// |                                                                  |\n// ====================================================================\n// ================ FraxlendPairAccessControlErrors ===================\n// ====================================================================\n// Frax Finance: https://github.com/FraxFinance\n\n// Primary Author\n// Drake Evans: https://github.com/DrakeEvans\n\n// Reviewers\n// Dennis: https://github.com/denett\n\n// ====================================================================\n\n/// @title FraxlendPairAccessControlErrors\n/// @author Drake Evans (Frax Finance) https://github.com/drakeevans\n/// @notice  An abstract contract which contains the errors for the Access Control contract\nabstract contract FraxlendPairAccessControlErrors {\n    error OnlyProtocolOrOwner();\n    error OnlyTimelockOrOwner();\n    error ExceedsBorrowLimit();\n    error AccessControlRevoked();\n    error RepayPaused();\n    error ExceedsDepositLimit();\n    error WithdrawPaused();\n    error LiquidatePaused();\n    error InterestPaused();\n}\n"
      },
      "src/contracts/FraxlendPairConstants.sol": {
        "content": "// SPDX-License-Identifier: ISC\npragma solidity ^0.8.18;\n\n// ====================================================================\n// |     ______                   _______                             |\n// |    / _____________ __  __   / ____(_____  ____ _____  ________   |\n// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \\/ __ `/ __ \\/ ___/ _ \\  |\n// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |\n// | /_/   /_/   \\__,_/_/|_|  /_/   /_/_/ /_/\\__,_/_/ /_/\\___/\\___/   |\n// |                                                                  |\n// ====================================================================\n// ===================== FraxlendPairConstants ========================\n// ====================================================================\n// Frax Finance: https://github.com/FraxFinance\n\n// Primary Author\n// Drake Evans: https://github.com/DrakeEvans\n\n// Reviewers\n// Dennis: https://github.com/denett\n// Sam Kazemian: https://github.com/samkazemian\n// Travis Moore: https://github.com/FortisFortuna\n// Jack Corddry: https://github.com/corddry\n// Rich Gee: https://github.com/zer0blockchain\n\n// ====================================================================\n\n/// @title FraxlendPairConstants\n/// @author Drake Evans (Frax Finance) https://github.com/drakeevans\n/// @notice  An abstract contract which contains the errors and constants for the FraxlendPair contract\nabstract contract FraxlendPairConstants {\n    // ============================================================================================\n    // Constants\n    // ============================================================================================\n\n    // Precision settings\n    uint256 public constant LTV_PRECISION = 1e5; // 5 decimals\n    uint256 public constant LIQ_PRECISION = 1e5;\n    uint256 public constant UTIL_PREC = 1e5;\n    uint256 public constant FEE_PRECISION = 1e5;\n    uint256 public constant EXCHANGE_PRECISION = 1e18;\n    uint256 public constant DEVIATION_PRECISION = 1e5;\n    uint256 public constant RATE_PRECISION = 1e18;\n\n    // Protocol Fee\n    uint256 public constant MAX_PROTOCOL_FEE = 5e4; // 50% 1e5 precision\n\n    error Insolvent(uint256 _borrow, uint256 _collateral, uint256 _exchangeRate);\n    error BorrowerSolvent();\n    error InsufficientAssetsInContract(uint256 _assets, uint256 _request);\n    error SlippageTooHigh(uint256 _minOut, uint256 _actual);\n    error BadSwapper();\n    error InvalidPath(address _expected, address _actual);\n    error BadProtocolFee();\n    error PastDeadline(uint256 _blockTimestamp, uint256 _deadline);\n    error SetterRevoked();\n    error ExceedsMaxOracleDeviation();\n    error InvalidReceiver();\n}\n"
      },
      "src/contracts/FraxlendPairCore.sol": {
        "content": "// SPDX-License-Identifier: ISC\npragma solidity ^0.8.18;\n\n// ====================================================================\n// |     ______                   _______                             |\n// |    / _____________ __  __   / ____(_____  ____ _____  ________   |\n// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \\/ __ `/ __ \\/ ___/ _ \\  |\n// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |\n// | /_/   /_/   \\__,_/_/|_|  /_/   /_/_/ /_/\\__,_/_/ /_/\\___/\\___/   |\n// |                                                                  |\n// ====================================================================\n// ========================= FraxlendPairCore =========================\n// ====================================================================\n// Frax Finance: https://github.com/FraxFinance\n\n// Primary Author\n// Drake Evans: https://github.com/DrakeEvans\n\n// Reviewers\n// Dennis: https://github.com/denett\n// Sam Kazemian: https://github.com/samkazemian\n// Travis Moore: https://github.com/FortisFortuna\n// Jack Corddry: https://github.com/corddry\n// Rich Gee: https://github.com/zer0blockchain\n\n// ====================================================================\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeCast.sol\";\nimport \"./FraxlendPairAccessControl.sol\";\nimport \"./FraxlendPairConstants.sol\";\nimport \"./libraries/VaultAccount.sol\";\nimport \"./libraries/SafeERC20.sol\";\nimport \"./interfaces/IDualOracle.sol\";\nimport \"./interfaces/IRateCalculatorV2.sol\";\nimport \"./interfaces/ISwapper.sol\";\n\n/// @title FraxlendPairCore\n/// @author Drake Evans (Frax Finance) https://github.com/drakeevans\n/// @notice  An abstract contract which contains the core logic and storage for the FraxlendPair\nabstract contract FraxlendPairCore is FraxlendPairAccessControl, FraxlendPairConstants, ERC20, ReentrancyGuard {\n    using VaultAccountingLibrary for VaultAccount;\n    using SafeERC20 for IERC20;\n    using SafeCast for uint256;\n\n    function version() external pure returns (uint256 _major, uint256 _minor, uint256 _patch) {\n        _major = 3;\n        _minor = 0;\n        _patch = 0;\n    }\n\n    // ============================================================================================\n    // Settings set by constructor()\n    // ============================================================================================\n\n    // Asset and collateral contracts\n    IERC20 internal immutable assetContract;\n    IERC20 public immutable collateralContract;\n\n    // LTV Settings\n    /// @notice The maximum LTV allowed for this pair\n    /// @dev 1e5 precision\n    uint256 public maxLTV;\n\n    // Liquidation Fees\n    /// @notice The liquidation fee, given as a % of repayment amount, when all collateral is consumed in liquidation\n    /// @dev 1e5 precision\n    uint256 public cleanLiquidationFee;\n    /// @notice The liquidation fee, given as % of repayment amount, when some collateral remains for borrower\n    /// @dev 1e5 precision\n    uint256 public dirtyLiquidationFee;\n    /// @notice The portion of the liquidation fee given to protocol\n    /// @dev 1e5 precision\n    uint256 public protocolLiquidationFee;\n\n    // Interest Rate Calculator Contract\n    IRateCalculatorV2 public rateContract; // For complex rate calculations\n\n    // Swapper\n    mapping(address => bool) public swappers; // approved swapper addresses\n\n    // ERC20 Metadata\n    string internal nameOfContract;\n    string internal symbolOfContract;\n    uint8 internal immutable decimalsOfContract;\n\n    // ============================================================================================\n    // Storage\n    // ============================================================================================\n\n    /// @notice Stores information about the current interest rate\n    /// @dev struct is packed to reduce SLOADs. feeToProtocolRate is 1e5 precision, ratePerSec & fullUtilizationRate is 1e18 precision\n    CurrentRateInfo public currentRateInfo;\n    struct CurrentRateInfo {\n        uint32 lastBlock;\n        uint32 feeToProtocolRate; // Fee amount 1e5 precision\n        uint64 lastTimestamp;\n        uint64 ratePerSec;\n        uint64 fullUtilizationRate;\n    }\n\n    /// @notice Stores information about the current exchange rate. Collateral:Asset ratio\n    /// @dev Struct packed to save SLOADs. Amount of Collateral Token to buy 1e18 Asset Token\n    ExchangeRateInfo public exchangeRateInfo;\n    struct ExchangeRateInfo {\n        address oracle;\n        uint32 maxOracleDeviation; // % of larger number, 1e5 precision\n        uint184 lastTimestamp;\n        uint256 lowExchangeRate;\n        uint256 highExchangeRate;\n    }\n\n    // Contract Level Accounting\n    VaultAccount public totalAsset; // amount = total amount of assets, shares = total shares outstanding\n    VaultAccount public totalBorrow; // amount = total borrow amount with interest accrued, shares = total shares outstanding\n    uint256 public totalCollateral; // total amount of collateral in contract\n\n    // User Level Accounting\n    /// @notice Stores the balance of collateral for each user\n    mapping(address => uint256) public userCollateralBalance; // amount of collateral each user is backed\n    /// @notice Stores the balance of borrow shares for each user\n    mapping(address => uint256) public userBorrowShares; // represents the shares held by individuals\n\n    // NOTE: user shares of assets are represented as ERC-20 tokens and accessible via balanceOf()\n\n    // ============================================================================================\n    // Constructor\n    // ============================================================================================\n\n    /// @notice The ```constructor``` function is called on deployment\n    /// @param _configData abi.encode(address _asset, address _collateral, address _oracle, uint32 _maxOracleDeviation, address _rateContract, uint64 _fullUtilizationRate, uint256 _maxLTV, uint256 _cleanLiquidationFee, uint256 _dirtyLiquidationFee, uint256 _protocolLiquidationFee)\n    /// @param _immutables abi.encode(address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress)\n    /// @param _customConfigData abi.encode(string memory _nameOfContract, string memory _symbolOfContract, uint8 _decimalsOfContract)\n    constructor(\n        bytes memory _configData,\n        bytes memory _immutables,\n        bytes memory _customConfigData\n    ) FraxlendPairAccessControl(_immutables) ERC20(\"\", \"\") {\n        {\n            (\n                address _asset,\n                address _collateral,\n                address _oracle,\n                uint32 _maxOracleDeviation,\n                address _rateContract,\n                uint64 _fullUtilizationRate,\n                uint256 _maxLTV,\n                uint256 _liquidationFee,\n                uint256 _protocolLiquidationFee\n            ) = abi.decode(\n                    _configData,\n                    (address, address, address, uint32, address, uint64, uint256, uint256, uint256)\n                );\n\n            // Pair Settings\n            assetContract = IERC20(_asset);\n            collateralContract = IERC20(_collateral);\n\n            currentRateInfo.feeToProtocolRate = 0;\n            currentRateInfo.fullUtilizationRate = _fullUtilizationRate;\n            currentRateInfo.lastTimestamp = uint64(block.timestamp - 1);\n            currentRateInfo.lastBlock = uint32(block.number - 1);\n\n            exchangeRateInfo.oracle = _oracle;\n            exchangeRateInfo.maxOracleDeviation = _maxOracleDeviation;\n\n            rateContract = IRateCalculatorV2(_rateContract);\n\n            //Liquidation Fee Settings\n            cleanLiquidationFee = _liquidationFee;\n            dirtyLiquidationFee = (_liquidationFee * 90_000) / LIQ_PRECISION; // 90% of clean fee\n            protocolLiquidationFee = _protocolLiquidationFee;\n\n            // set maxLTV\n            maxLTV = _maxLTV;\n        }\n\n        {\n            (string memory _nameOfContract, string memory _symbolOfContract, uint8 _decimalsOfContract) = abi.decode(\n                _customConfigData,\n                (string, string, uint8)\n            );\n\n            // ERC20 Metadata\n            nameOfContract = _nameOfContract;\n            symbolOfContract = _symbolOfContract;\n            decimalsOfContract = _decimalsOfContract;\n\n            // Instantiate Interest\n            _addInterest();\n            // Instantiate Exchange Rate\n            _updateExchangeRate();\n        }\n    }\n\n    // ============================================================================================\n    // Internal Helpers\n    // ============================================================================================\n\n    /// @notice The ```_totalAssetAvailable``` function returns the total balance of Asset Tokens in the contract\n    /// @param _totalAsset VaultAccount struct which stores total amount and shares for assets\n    /// @param _totalBorrow VaultAccount struct which stores total amount and shares for borrows\n    /// @return The balance of Asset Tokens held by contract\n    function _totalAssetAvailable(\n        VaultAccount memory _totalAsset,\n        VaultAccount memory _totalBorrow\n    ) internal pure returns (uint256) {\n        return _totalAsset.amount - _totalBorrow.amount;\n    }\n\n    /// @notice The ```_isSolvent``` function determines if a given borrower is solvent given an exchange rate\n    /// @param _borrower The borrower address to check\n    /// @param _exchangeRate The exchange rate, i.e. the amount of collateral to buy 1e18 asset\n    /// @return Whether borrower is solvent\n    function _isSolvent(address _borrower, uint256 _exchangeRate) internal view returns (bool) {\n        if (maxLTV == 0) return true;\n        uint256 _borrowerAmount = totalBorrow.toAmount(userBorrowShares[_borrower], true);\n        if (_borrowerAmount == 0) return true;\n        uint256 _collateralAmount = userCollateralBalance[_borrower];\n        if (_collateralAmount == 0) return false;\n\n        uint256 _ltv = (((_borrowerAmount * _exchangeRate) / EXCHANGE_PRECISION) * LTV_PRECISION) / _collateralAmount;\n        return _ltv <= maxLTV;\n    }\n\n    // ============================================================================================\n    // Modifiers\n    // ============================================================================================\n\n    /// @notice Checks for solvency AFTER executing contract code\n    /// @param _borrower The borrower whose solvency we will check\n    modifier isSolvent(address _borrower) {\n        _;\n        ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;\n\n        if (!_isSolvent(_borrower, exchangeRateInfo.highExchangeRate)) {\n            revert Insolvent(\n                totalBorrow.toAmount(userBorrowShares[_borrower], true),\n                userCollateralBalance[_borrower],\n                exchangeRateInfo.highExchangeRate\n            );\n        }\n    }\n\n    // ============================================================================================\n    // Functions: Interest Accumulation and Adjustment\n    // ============================================================================================\n\n    /// @notice The ```AddInterest``` event is emitted when interest is accrued by borrowers\n    /// @param interestEarned The total interest accrued by all borrowers\n    /// @param rate The interest rate used to calculate accrued interest\n    /// @param feesAmount The amount of fees paid to protocol\n    /// @param feesShare The amount of shares distributed to protocol\n    event AddInterest(uint256 interestEarned, uint256 rate, uint256 feesAmount, uint256 feesShare);\n\n    /// @notice The ```UpdateRate``` event is emitted when the interest rate is updated\n    /// @param oldRatePerSec The old interest rate (per second)\n    /// @param oldFullUtilizationRate The old full utilization rate\n    /// @param newRatePerSec The new interest rate (per second)\n    /// @param newFullUtilizationRate The new full utilization rate\n    event UpdateRate(\n        uint256 oldRatePerSec,\n        uint256 oldFullUtilizationRate,\n        uint256 newRatePerSec,\n        uint256 newFullUtilizationRate\n    );\n\n    /// @notice The ```addInterest``` function is a public implementation of _addInterest and allows 3rd parties to trigger interest accrual\n    /// @return _interestEarned The amount of interest accrued by all borrowers\n    /// @return _feesAmount The amount of fees paid to protocol\n    /// @return _feesShare The amount of shares distributed to protocol\n    /// @return _currentRateInfo The new rate info struct\n    /// @return _totalAsset The new total asset struct\n    /// @return _totalBorrow The new total borrow struct\n    function addInterest(\n        bool _returnAccounting\n    )\n        external\n        nonReentrant\n        returns (\n            uint256 _interestEarned,\n            uint256 _feesAmount,\n            uint256 _feesShare,\n            CurrentRateInfo memory _currentRateInfo,\n            VaultAccount memory _totalAsset,\n            VaultAccount memory _totalBorrow\n        )\n    {\n        (, _interestEarned, _feesAmount, _feesShare, _currentRateInfo) = _addInterest();\n        if (_returnAccounting) {\n            _totalAsset = totalAsset;\n            _totalBorrow = totalBorrow;\n        }\n    }\n\n    /// @notice The ```previewAddInterest``` function\n    /// @return _interestEarned The amount of interest accrued by all borrowers\n    /// @return _feesAmount The amount of fees paid to protocol\n    /// @return _feesShare The amount of shares distributed to protocol\n    /// @return _newCurrentRateInfo The new rate info struct\n    /// @return _totalAsset The new total asset struct\n    /// @return _totalBorrow The new total borrow struct\n    function previewAddInterest()\n        public\n        view\n        returns (\n            uint256 _interestEarned,\n            uint256 _feesAmount,\n            uint256 _feesShare,\n            CurrentRateInfo memory _newCurrentRateInfo,\n            VaultAccount memory _totalAsset,\n            VaultAccount memory _totalBorrow\n        )\n    {\n        _newCurrentRateInfo = currentRateInfo;\n        // Write return values\n        InterestCalculationResults memory _results = _calculateInterest(_newCurrentRateInfo);\n\n        if (_results.isInterestUpdated) {\n            _interestEarned = _results.interestEarned;\n            _feesAmount = _results.feesAmount;\n            _feesShare = _results.feesShare;\n\n            _newCurrentRateInfo.ratePerSec = _results.newRate;\n            _newCurrentRateInfo.fullUtilizationRate = _results.newFullUtilizationRate;\n\n            _totalAsset = _results.totalAsset;\n            _totalBorrow = _results.totalBorrow;\n        } else {\n            _totalAsset = totalAsset;\n            _totalBorrow = totalBorrow;\n        }\n    }\n\n    struct InterestCalculationResults {\n        bool isInterestUpdated;\n        uint64 newRate;\n        uint64 newFullUtilizationRate;\n        uint256 interestEarned;\n        uint256 feesAmount;\n        uint256 feesShare;\n        VaultAccount totalAsset;\n        VaultAccount totalBorrow;\n    }\n\n    /// @notice The ```_calculateInterest``` function calculates the interest to be accrued and the new interest rate info\n    /// @param _currentRateInfo The current rate info\n    /// @return _results The results of the interest calculation\n    function _calculateInterest(\n        CurrentRateInfo memory _currentRateInfo\n    ) internal view returns (InterestCalculationResults memory _results) {\n        // Short circuit if interest already calculated this block OR if interest is paused\n        if (_currentRateInfo.lastTimestamp != block.timestamp && !isInterestPaused) {\n            // Indicate that interest is updated and calculated\n            _results.isInterestUpdated = true;\n\n            // Write return values and use these to save gas\n            _results.totalAsset = totalAsset;\n            _results.totalBorrow = totalBorrow;\n\n            // Time elapsed since last interest update\n            uint256 _deltaTime = block.timestamp - _currentRateInfo.lastTimestamp;\n\n            // Get the utilization rate\n            uint256 _utilizationRate = _results.totalAsset.amount == 0\n                ? 0\n                : (UTIL_PREC * _results.totalBorrow.amount) / _results.totalAsset.amount;\n\n            // Request new interest rate and full utilization rate from the rate calculator\n            (_results.newRate, _results.newFullUtilizationRate) = IRateCalculatorV2(rateContract).getNewRate(\n                _deltaTime,\n                _utilizationRate,\n                _currentRateInfo.fullUtilizationRate\n            );\n\n            // Calculate interest accrued\n            _results.interestEarned = (_deltaTime * _results.totalBorrow.amount * _results.newRate) / RATE_PRECISION;\n\n            // Accrue interest (if any) and fees iff no overflow\n            if (\n                _results.interestEarned > 0 &&\n                _results.interestEarned + _results.totalBorrow.amount <= type(uint128).max &&\n                _results.interestEarned + _results.totalAsset.amount <= type(uint128).max\n            ) {\n                // Increment totalBorrow and totalAsset by interestEarned\n                _results.totalBorrow.amount += uint128(_results.interestEarned);\n                _results.totalAsset.amount += uint128(_results.interestEarned);\n                if (_currentRateInfo.feeToProtocolRate > 0) {\n                    _results.feesAmount =\n                        (_results.interestEarned * _currentRateInfo.feeToProtocolRate) /\n                        FEE_PRECISION;\n\n                    _results.feesShare =\n                        (_results.feesAmount * _results.totalAsset.shares) /\n                        (_results.totalAsset.amount - _results.feesAmount);\n\n                    // Effects: Give new shares to this contract, effectively diluting lenders an amount equal to the fees\n                    // We can safely cast because _feesShare < _feesAmount < interestEarned which is always less than uint128\n                    _results.totalAsset.shares += uint128(_results.feesShare);\n                }\n            }\n        }\n    }\n\n    /// @notice The ```_addInterest``` function is invoked prior to every external function and is used to accrue interest and update interest rate\n    /// @dev Can only called once per block\n    /// @return _isInterestUpdated True if interest was calculated\n    /// @return _interestEarned The amount of interest accrued by all borrowers\n    /// @return _feesAmount The amount of fees paid to protocol\n    /// @return _feesShare The amount of shares distributed to protocol\n    /// @return _currentRateInfo The new rate info struct\n    function _addInterest()\n        internal\n        returns (\n            bool _isInterestUpdated,\n            uint256 _interestEarned,\n            uint256 _feesAmount,\n            uint256 _feesShare,\n            CurrentRateInfo memory _currentRateInfo\n        )\n    {\n        // Pull from storage and set default return values\n        _currentRateInfo = currentRateInfo;\n\n        // Calc interest\n        InterestCalculationResults memory _results = _calculateInterest(_currentRateInfo);\n\n        // Write return values only if interest was updated and calculated\n        if (_results.isInterestUpdated) {\n            _isInterestUpdated = _results.isInterestUpdated;\n            _interestEarned = _results.interestEarned;\n            _feesAmount = _results.feesAmount;\n            _feesShare = _results.feesShare;\n\n            // emit here so that we have access to the old values\n            emit UpdateRate(\n                _currentRateInfo.ratePerSec,\n                _currentRateInfo.fullUtilizationRate,\n                _results.newRate,\n                _results.newFullUtilizationRate\n            );\n            emit AddInterest(_interestEarned, _results.newRate, _feesAmount, _feesShare);\n\n            // overwrite original values\n            _currentRateInfo.ratePerSec = _results.newRate;\n            _currentRateInfo.fullUtilizationRate = _results.newFullUtilizationRate;\n            _currentRateInfo.lastTimestamp = uint64(block.timestamp);\n            _currentRateInfo.lastBlock = uint32(block.number);\n\n            // Effects: write to state\n            currentRateInfo = _currentRateInfo;\n            totalAsset = _results.totalAsset;\n            totalBorrow = _results.totalBorrow;\n            if (_feesShare > 0) _mint(address(this), _feesShare);\n        }\n    }\n\n    // ============================================================================================\n    // Functions: ExchangeRate\n    // ============================================================================================\n\n    /// @notice The ```UpdateExchangeRate``` event is emitted when the Collateral:Asset exchange rate is updated\n    /// @param lowExchangeRate The low exchange rate\n    /// @param highExchangeRate The high exchange rate\n    event UpdateExchangeRate(uint256 lowExchangeRate, uint256 highExchangeRate);\n\n    /// @notice The ```WarnOracleData``` event is emitted when one of the oracles has stale or otherwise problematic data\n    /// @param oracle The oracle address\n    event WarnOracleData(address oracle);\n\n    /// @notice The ```updateExchangeRate``` function is the external implementation of _updateExchangeRate.\n    /// @dev This function is invoked at most once per block as these queries can be expensive\n    /// @return _isBorrowAllowed True if deviation is within bounds\n    /// @return _lowExchangeRate The low exchange rate\n    /// @return _highExchangeRate The high exchange rate\n    function updateExchangeRate()\n        external\n        nonReentrant\n        returns (bool _isBorrowAllowed, uint256 _lowExchangeRate, uint256 _highExchangeRate)\n    {\n        return _updateExchangeRate();\n    }\n\n    /// @notice The ```_updateExchangeRate``` function retrieves the latest exchange rate. i.e how much collateral to buy 1e18 asset.\n    /// @dev This function is invoked at most once per block as these queries can be expensive\n    /// @return _isBorrowAllowed True if deviation is within bounds\n    /// @return _lowExchangeRate The low exchange rate\n    /// @return _highExchangeRate The high exchange rate\n\n    function _updateExchangeRate()\n        internal\n        returns (bool _isBorrowAllowed, uint256 _lowExchangeRate, uint256 _highExchangeRate)\n    {\n        // Pull from storage to save gas and set default return values\n        ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;\n\n        // Short circuit if already updated this block\n        if (_exchangeRateInfo.lastTimestamp != block.timestamp) {\n            // Get the latest exchange rate from the dual oracle\n            bool _oneOracleBad;\n            (_oneOracleBad, _lowExchangeRate, _highExchangeRate) = IDualOracle(_exchangeRateInfo.oracle).getPrices();\n\n            // If one oracle is bad data, emit an event for off-chain monitoring\n            if (_oneOracleBad) emit WarnOracleData(_exchangeRateInfo.oracle);\n\n            // Effects: Bookkeeping and write to storage\n            _exchangeRateInfo.lastTimestamp = uint184(block.timestamp);\n            _exchangeRateInfo.lowExchangeRate = _lowExchangeRate;\n            _exchangeRateInfo.highExchangeRate = _highExchangeRate;\n            exchangeRateInfo = _exchangeRateInfo;\n            emit UpdateExchangeRate(_lowExchangeRate, _highExchangeRate);\n        } else {\n            // Use default return values if already updated this block\n            _lowExchangeRate = _exchangeRateInfo.lowExchangeRate;\n            _highExchangeRate = _exchangeRateInfo.highExchangeRate;\n        }\n\n        uint256 _deviation = (DEVIATION_PRECISION *\n            (_exchangeRateInfo.highExchangeRate - _exchangeRateInfo.lowExchangeRate)) /\n            _exchangeRateInfo.highExchangeRate;\n        if (_deviation <= _exchangeRateInfo.maxOracleDeviation) {\n            _isBorrowAllowed = true;\n        }\n    }\n\n    // ============================================================================================\n    // Functions: Lending\n    // ============================================================================================\n\n    /// @notice The ```Deposit``` event fires when a user deposits assets to the pair\n    /// @param caller the msg.sender\n    /// @param owner the account the fTokens are sent to\n    /// @param assets the amount of assets deposited\n    /// @param shares the number of fTokens minted\n    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);\n\n    /// @notice The ```_deposit``` function is the internal implementation for lending assets\n    /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling function\n    /// @param _totalAsset An in memory VaultAccount struct representing the total amounts and shares for the Asset Token\n    /// @param _amount The amount of Asset Token to be transferred\n    /// @param _shares The amount of Asset Shares (fTokens) to be minted\n    /// @param _receiver The address to receive the Asset Shares (fTokens)\n    function _deposit(VaultAccount memory _totalAsset, uint128 _amount, uint128 _shares, address _receiver) internal {\n        // Effects: bookkeeping\n        _totalAsset.amount += _amount;\n        _totalAsset.shares += _shares;\n\n        // Effects: write back to storage\n        _mint(_receiver, _shares);\n        totalAsset = _totalAsset;\n\n        // Interactions\n        assetContract.safeTransferFrom(msg.sender, address(this), _amount);\n        emit Deposit(msg.sender, _receiver, _amount, _shares);\n    }\n\n    function previewDeposit(uint256 _assets) external view returns (uint256 _sharesReceived) {\n        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();\n        _sharesReceived = _totalAsset.toShares(_assets, false);\n    }\n\n    /// @notice The ```deposit``` function allows a user to Lend Assets by specifying the amount of Asset Tokens to lend\n    /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling function\n    /// @param _amount The amount of Asset Token to transfer to Pair\n    /// @param _receiver The address to receive the Asset Shares (fTokens)\n    /// @return _sharesReceived The number of fTokens received for the deposit\n    function deposit(uint256 _amount, address _receiver) external nonReentrant returns (uint256 _sharesReceived) {\n        if (_receiver == address(0)) revert InvalidReceiver();\n\n        // Accrue interest if necessary\n        _addInterest();\n\n        // Pull from storage to save gas\n        VaultAccount memory _totalAsset = totalAsset;\n\n        // Check if this deposit will violate the deposit limit\n        if (depositLimit < _totalAsset.amount + _amount) revert ExceedsDepositLimit();\n\n        // Calculate the number of fTokens to mint\n        _sharesReceived = _totalAsset.toShares(_amount, false);\n\n        // Execute the deposit effects\n        _deposit(_totalAsset, _amount.toUint128(), _sharesReceived.toUint128(), _receiver);\n    }\n\n    function previewMint(uint256 _shares) external view returns (uint256 _amount) {\n        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();\n        _amount = _totalAsset.toAmount(_shares, false);\n    }\n\n    function mint(uint256 _shares, address _receiver) external nonReentrant returns (uint256 _amount) {\n        if (_receiver == address(0)) revert InvalidReceiver();\n\n        // Accrue interest if necessary\n        _addInterest();\n\n        // Pull from storage to save gas\n        VaultAccount memory _totalAsset = totalAsset;\n\n        // Calculate the number of assets to transfer based on the shares to mint\n        _amount = _totalAsset.toAmount(_shares, false);\n\n        // Check if this deposit will violate the deposit limit\n        if (depositLimit < _totalAsset.amount + _amount) revert ExceedsDepositLimit();\n\n        // Execute the deposit effects\n        _deposit(_totalAsset, _amount.toUint128(), _shares.toUint128(), _receiver);\n    }\n\n    /// @notice The ```Withdraw``` event fires when a user redeems their fTokens for the underlying asset\n    /// @param caller the msg.sender\n    /// @param receiver The address to which the underlying asset will be transferred to\n    /// @param owner The owner of the fTokens\n    /// @param assets The assets transferred\n    /// @param shares The number of fTokens burned\n    event Withdraw(\n        address indexed caller,\n        address indexed receiver,\n        address indexed owner,\n        uint256 assets,\n        uint256 shares\n    );\n\n    /// @notice The ```_redeem``` function is an internal implementation which allows a Lender to pull their Asset Tokens out of the Pair\n    /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling function\n    /// @param _totalAsset An in-memory VaultAccount struct which holds the total amount of Asset Tokens and the total number of Asset Shares (fTokens)\n    /// @param _amountToReturn The number of Asset Tokens to return\n    /// @param _shares The number of Asset Shares (fTokens) to burn\n    /// @param _receiver The address to which the Asset Tokens will be transferred\n    /// @param _owner The owner of the Asset Shares (fTokens)\n    function _redeem(\n        VaultAccount memory _totalAsset,\n        uint128 _amountToReturn,\n        uint128 _shares,\n        address _receiver,\n        address _owner\n    ) internal {\n        // Check for sufficient allowance/approval if necessary\n        if (msg.sender != _owner) {\n            uint256 allowed = allowance(_owner, msg.sender);\n            // NOTE: This will revert on underflow ensuring that allowance > shares\n            if (allowed != type(uint256).max) _approve(_owner, msg.sender, allowed - _shares);\n        }\n\n        // Check for sufficient withdraw liquidity (not strictly necessary because balance will underflow)\n        uint256 _assetsAvailable = _totalAssetAvailable(_totalAsset, totalBorrow);\n        if (_assetsAvailable < _amountToReturn) {\n            revert InsufficientAssetsInContract(_assetsAvailable, _amountToReturn);\n        }\n\n        // Effects: bookkeeping\n        _totalAsset.amount -= _amountToReturn;\n        _totalAsset.shares -= _shares;\n\n        // Effects: write to storage\n        totalAsset = _totalAsset;\n        _burn(_owner, _shares);\n\n        // Interactions\n        assetContract.safeTransfer(_receiver, _amountToReturn);\n        emit Withdraw(msg.sender, _receiver, _owner, _amountToReturn, _shares);\n    }\n\n    function previewRedeem(uint256 _shares) external view returns (uint256 _assets) {\n        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();\n        _assets = _totalAsset.toAmount(_shares, false);\n    }\n\n    /// @notice The ```redeem``` function allows the caller to redeem their Asset Shares for Asset Tokens\n    /// @param _shares The number of Asset Shares (fTokens) to burn for Asset Tokens\n    /// @param _receiver The address to which the Asset Tokens will be transferred\n    /// @param _owner The owner of the Asset Shares (fTokens)\n    /// @return _amountToReturn The amount of Asset Tokens to be transferred\n    function redeem(\n        uint256 _shares,\n        address _receiver,\n        address _owner\n    ) external nonReentrant returns (uint256 _amountToReturn) {\n        if (_receiver == address(0)) revert InvalidReceiver();\n\n        // Check if withdraw is paused and revert if necessary\n        if (isWithdrawPaused) revert WithdrawPaused();\n\n        // Accrue interest if necessary\n        _addInterest();\n\n        // Pull from storage to save gas\n        VaultAccount memory _totalAsset = totalAsset;\n\n        // Calculate the number of assets to transfer based on the shares to burn\n        _amountToReturn = _totalAsset.toAmount(_shares, false);\n\n        // Execute the withdraw effects\n        _redeem(_totalAsset, _amountToReturn.toUint128(), _shares.toUint128(), _receiver, _owner);\n    }\n\n    /// @notice The ```previewWithdraw``` function returns the number of Asset Shares (fTokens) that would be burned for a given amount of Asset Tokens\n    /// @param _amount The amount of Asset Tokens to be withdrawn\n    /// @return _sharesToBurn The number of shares that would be burned\n    function previewWithdraw(uint256 _amount) external view returns (uint256 _sharesToBurn) {\n        (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest();\n        _sharesToBurn = _totalAsset.toShares(_amount, true);\n    }\n\n    /// @notice The ```withdraw``` function allows the caller to withdraw their Asset Tokens for a given amount of fTokens\n    /// @param _amount The amount to withdraw\n    /// @param _receiver The address to which the Asset Tokens will be transferred\n    /// @param _owner The owner of the Asset Shares (fTokens)\n    /// @return _sharesToBurn The number of shares (fTokens) that were burned\n    function withdraw(\n        uint256 _amount,\n        address _receiver,\n        address _owner\n    ) external nonReentrant returns (uint256 _sharesToBurn) {\n        if (_receiver == address(0)) revert InvalidReceiver();\n\n        // Check if withdraw is paused and revert if necessary\n        if (isWithdrawPaused) revert WithdrawPaused();\n\n        // Accrue interest if necessary\n        _addInterest();\n\n        // Pull from storage to save gas\n        VaultAccount memory _totalAsset = totalAsset;\n\n        // Calculate the number of shares to burn based on the amount to withdraw\n        _sharesToBurn = _totalAsset.toShares(_amount, true);\n\n        // Execute the withdraw effects\n        _redeem(_totalAsset, _amount.toUint128(), _sharesToBurn.toUint128(), _receiver, _owner);\n    }\n\n    // ============================================================================================\n    // Functions: Borrowing\n    // ============================================================================================\n\n    /// @notice The ```BorrowAsset``` event is emitted when a borrower increases their position\n    /// @param _borrower The borrower whose account was debited\n    /// @param _receiver The address to which the Asset Tokens were transferred\n    /// @param _borrowAmount The amount of Asset Tokens transferred\n    /// @param _sharesAdded The number of Borrow Shares the borrower was debited\n    event BorrowAsset(\n        address indexed _borrower,\n        address indexed _receiver,\n        uint256 _borrowAmount,\n        uint256 _sharesAdded\n    );\n\n    /// @notice The ```_borrowAsset``` function is the internal implementation for borrowing assets\n    /// @param _borrowAmount The amount of the Asset Token to borrow\n    /// @param _receiver The address to receive the Asset Tokens\n    /// @return _sharesAdded The amount of borrow shares the msg.sender will be debited\n    function _borrowAsset(uint128 _borrowAmount, address _receiver) internal returns (uint256 _sharesAdded) {\n        // Get borrow accounting from storage to save gas\n        VaultAccount memory _totalBorrow = totalBorrow;\n\n        // Check available capital (not strictly necessary because balance will underflow, but better revert message)\n        uint256 _assetsAvailable = _totalAssetAvailable(totalAsset, _totalBorrow);\n        if (_assetsAvailable < _borrowAmount) {\n            revert InsufficientAssetsInContract(_assetsAvailable, _borrowAmount);\n        }\n\n        // Calculate the number of shares to add based on the amount to borrow\n        _sharesAdded = _totalBorrow.toShares(_borrowAmount, true);\n\n        // Effects: Bookkeeping to add shares & amounts to total Borrow accounting\n        _totalBorrow.amount += _borrowAmount;\n        _totalBorrow.shares += uint128(_sharesAdded);\n        // NOTE: we can safely cast here because shares are always less than amount and _borrowAmount is uint128\n\n        // Effects: write back to storage\n        totalBorrow = _totalBorrow;\n        userBorrowShares[msg.sender] += _sharesAdded;\n\n        // Interactions\n        if (_receiver != address(this)) {\n            assetContract.safeTransfer(_receiver, _borrowAmount);\n        }\n        emit BorrowAsset(msg.sender, _receiver, _borrowAmount, _sharesAdded);\n    }\n\n    /// @notice The ```borrowAsset``` function allows a user to open/increase a borrow position\n    /// @dev Borrower must call ```ERC20.approve``` on the Collateral Token contract if applicable\n    /// @param _borrowAmount The amount of Asset Token to borrow\n    /// @param _collateralAmount The amount of Collateral Token to transfer to Pair\n    /// @param _receiver The address which will receive the Asset Tokens\n    /// @return _shares The number of borrow Shares the msg.sender will be debited\n    function borrowAsset(\n        uint256 _borrowAmount,\n        uint256 _collateralAmount,\n        address _receiver\n    ) external nonReentrant isSolvent(msg.sender) returns (uint256 _shares) {\n        if (_receiver == address(0)) revert InvalidReceiver();\n\n        // Accrue interest if necessary\n        _addInterest();\n\n        // Check if borrow will violate the borrow limit and revert if necessary\n        if (borrowLimit < totalBorrow.amount + _borrowAmount) revert ExceedsBorrowLimit();\n\n        // Update _exchangeRate and check if borrow is allowed based on deviation\n        (bool _isBorrowAllowed, , ) = _updateExchangeRate();\n        if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation();\n\n        // Only add collateral if necessary\n        if (_collateralAmount > 0) {\n            _addCollateral(msg.sender, _collateralAmount, msg.sender);\n        }\n\n        // Effects: Call internal borrow function\n        _shares = _borrowAsset(_borrowAmount.toUint128(), _receiver);\n    }\n\n    /// @notice The ```AddCollateral``` event is emitted when a borrower adds collateral to their position\n    /// @param sender The source of funds for the new collateral\n    /// @param borrower The borrower account for which the collateral should be credited\n    /// @param collateralAmount The amount of Collateral Token to be transferred\n    event AddCollateral(address indexed sender, address indexed borrower, uint256 collateralAmount);\n\n    /// @notice The ```_addCollateral``` function is an internal implementation for adding collateral to a borrowers position\n    /// @param _sender The source of funds for the new collateral\n    /// @param _collateralAmount The amount of Collateral Token to be transferred\n    /// @param _borrower The borrower account for which the collateral should be credited\n    function _addCollateral(address _sender, uint256 _collateralAmount, address _borrower) internal {\n        // Effects: write to state\n        userCollateralBalance[_borrower] += _collateralAmount;\n        totalCollateral += _collateralAmount;\n\n        // Interactions\n        if (_sender != address(this)) {\n            collateralContract.safeTransferFrom(_sender, address(this), _collateralAmount);\n        }\n        emit AddCollateral(_sender, _borrower, _collateralAmount);\n    }\n\n    /// @notice The ```addCollateral``` function allows the caller to add Collateral Token to a borrowers position\n    /// @dev msg.sender must call ERC20.approve() on the Collateral Token contract prior to invocation\n    /// @param _collateralAmount The amount of Collateral Token to be added to borrower's position\n    /// @param _borrower The account to be credited\n    function addCollateral(uint256 _collateralAmount, address _borrower) external nonReentrant {\n        if (_borrower == address(0)) revert InvalidReceiver();\n\n        _addInterest();\n        _addCollateral(msg.sender, _collateralAmount, _borrower);\n    }\n\n    /// @notice The ```RemoveCollateral``` event is emitted when collateral is removed from a borrower's position\n    /// @param _sender The account from which funds are transferred\n    /// @param _collateralAmount The amount of Collateral Token to be transferred\n    /// @param _receiver The address to which Collateral Tokens will be transferred\n    event RemoveCollateral(\n        address indexed _sender,\n        uint256 _collateralAmount,\n        address indexed _receiver,\n        address indexed _borrower\n    );\n\n    /// @notice The ```_removeCollateral``` function is the internal implementation for removing collateral from a borrower's position\n    /// @param _collateralAmount The amount of Collateral Token to remove from the borrower's position\n    /// @param _receiver The address to receive the Collateral Token transferred\n    /// @param _borrower The borrower whose account will be debited the Collateral amount\n    function _removeCollateral(uint256 _collateralAmount, address _receiver, address _borrower) internal {\n        // Effects: write to state\n        // NOTE: Following line will revert on underflow if _collateralAmount > userCollateralBalance\n        userCollateralBalance[_borrower] -= _collateralAmount;\n        // NOTE: Following line will revert on underflow if totalCollateral < _collateralAmount\n        totalCollateral -= _collateralAmount;\n\n        // Interactions\n        if (_receiver != address(this)) {\n            collateralContract.safeTransfer(_receiver, _collateralAmount);\n        }\n        emit RemoveCollateral(msg.sender, _collateralAmount, _receiver, _borrower);\n    }\n\n    /// @notice The ```removeCollateral``` function is used to remove collateral from msg.sender's borrow position\n    /// @dev msg.sender must be solvent after invocation or transaction will revert\n    /// @param _collateralAmount The amount of Collateral Token to transfer\n    /// @param _receiver The address to receive the transferred funds\n    function removeCollateral(\n        uint256 _collateralAmount,\n        address _receiver\n    ) external nonReentrant isSolvent(msg.sender) {\n        if (_receiver == address(0)) revert InvalidReceiver();\n\n        _addInterest();\n        // Note: exchange rate is irrelevant when borrower has no debt shares\n        if (userBorrowShares[msg.sender] > 0) {\n            (bool _isBorrowAllowed, , ) = _updateExchangeRate();\n            if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation();\n        }\n        _removeCollateral(_collateralAmount, _receiver, msg.sender);\n    }\n\n    /// @notice The ```RepayAsset``` event is emitted whenever a debt position is repaid\n    /// @param payer The address paying for the repayment\n    /// @param borrower The borrower whose account will be credited\n    /// @param amountToRepay The amount of Asset token to be transferred\n    /// @param shares The amount of Borrow Shares which will be debited from the borrower after repayment\n    event RepayAsset(address indexed payer, address indexed borrower, uint256 amountToRepay, uint256 shares);\n\n    /// @notice The ```_repayAsset``` function is the internal implementation for repaying a borrow position\n    /// @dev The payer must have called ERC20.approve() on the Asset Token contract prior to invocation\n    /// @param _totalBorrow An in memory copy of the totalBorrow VaultAccount struct\n    /// @param _amountToRepay The amount of Asset Token to transfer\n    /// @param _shares The number of Borrow Shares the sender is repaying\n    /// @param _payer The address from which funds will be transferred\n    /// @param _borrower The borrower account which will be credited\n    function _repayAsset(\n        VaultAccount memory _totalBorrow,\n        uint128 _amountToRepay,\n        uint128 _shares,\n        address _payer,\n        address _borrower\n    ) internal {\n        // Effects: Bookkeeping\n        _totalBorrow.amount -= _amountToRepay;\n        _totalBorrow.shares -= _shares;\n\n        // Effects: write to state\n        userBorrowShares[_borrower] -= _shares;\n        totalBorrow = _totalBorrow;\n\n        // Interactions\n        if (_payer != address(this)) {\n            assetContract.safeTransferFrom(_payer, address(this), _amountToRepay);\n        }\n        emit RepayAsset(_payer, _borrower, _amountToRepay, _shares);\n    }\n\n    /// @notice The ```repayAsset``` function allows the caller to pay down the debt for a given borrower.\n    /// @dev Caller must first invoke ```ERC20.approve()``` for the Asset Token contract\n    /// @param _shares The number of Borrow Shares which will be repaid by the call\n    /// @param _borrower The account for which the debt will be reduced\n    /// @return _amountToRepay The amount of Asset Tokens which were transferred in order to repay the Borrow Shares\n    function repayAsset(uint256 _shares, address _borrower) external nonReentrant returns (uint256 _amountToRepay) {\n        if (_borrower == address(0)) revert InvalidReceiver();\n\n        // Check if repay is paused revert if necessary\n        if (isRepayPaused) revert RepayPaused();\n\n        // Accrue interest if necessary\n        _addInterest();\n\n        // Calculate amount to repay based on shares\n        VaultAccount memory _totalBorrow = totalBorrow;\n        _amountToRepay = _totalBorrow.toAmount(_shares, true);\n\n        // Execute repayment effects\n        _repayAsset(_totalBorrow, _amountToRepay.toUint128(), _shares.toUint128(), msg.sender, _borrower);\n    }\n\n    // ============================================================================================\n    // Functions: Liquidations\n    // ============================================================================================\n    /// @notice The ```Liquidate``` event is emitted when a liquidation occurs\n    /// @param _borrower The borrower account for which the liquidation occurred\n    /// @param _collateralForLiquidator The amount of Collateral Token transferred to the liquidator\n    /// @param _sharesToLiquidate The number of Borrow Shares the liquidator repaid on behalf of the borrower\n    /// @param _sharesToAdjust The number of Borrow Shares that were adjusted on liabilities and assets (a writeoff)\n    event Liquidate(\n        address indexed _borrower,\n        uint256 _collateralForLiquidator,\n        uint256 _sharesToLiquidate,\n        uint256 _amountLiquidatorToRepay,\n        uint256 _feesAmount,\n        uint256 _sharesToAdjust,\n        uint256 _amountToAdjust\n    );\n\n    /// @notice The ```liquidate``` function allows a third party to repay a borrower's debt if they have become insolvent\n    /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling ```Liquidate()```\n    /// @param _sharesToLiquidate The number of Borrow Shares repaid by the liquidator\n    /// @param _deadline The timestamp after which tx will revert\n    /// @param _borrower The account for which the repayment is credited and from whom collateral will be taken\n    /// @return _collateralForLiquidator The amount of Collateral Token transferred to the liquidator\n    function liquidate(\n        uint128 _sharesToLiquidate,\n        uint256 _deadline,\n        address _borrower\n    ) external nonReentrant returns (uint256 _collateralForLiquidator) {\n        if (_borrower == address(0)) revert InvalidReceiver();\n\n        // Check if liquidate is paused revert if necessary\n        if (isLiquidatePaused) revert LiquidatePaused();\n\n        // Ensure deadline has not passed\n        if (block.timestamp > _deadline) revert PastDeadline(block.timestamp, _deadline);\n\n        // accrue interest if necessary\n        _addInterest();\n\n        // Update exchange rate and use the lower rate for liquidations\n        (, uint256 _exchangeRate, ) = _updateExchangeRate();\n\n        // Check if borrower is solvent, revert if they are\n        if (_isSolvent(_borrower, _exchangeRate)) {\n            revert BorrowerSolvent();\n        }\n\n        // Read from state\n        VaultAccount memory _totalBorrow = totalBorrow;\n        uint256 _userCollateralBalance = userCollateralBalance[_borrower];\n        uint128 _borrowerShares = userBorrowShares[_borrower].toUint128();\n\n        // Prevent stack-too-deep\n        int256 _leftoverCollateral;\n        uint256 _feesAmount;\n        {\n            // Checks & Calculations\n            // Determine the liquidation amount in collateral units (i.e. how much debt liquidator is going to repay)\n            uint256 _liquidationAmountInCollateralUnits = ((_totalBorrow.toAmount(_sharesToLiquidate, false) *\n                _exchangeRate) / EXCHANGE_PRECISION);\n\n            // We first optimistically calculate the amount of collateral to give the liquidator based on the higher clean liquidation fee\n            // This fee only applies if the liquidator does a full liquidation\n            uint256 _optimisticCollateralForLiquidator = (_liquidationAmountInCollateralUnits *\n                (LIQ_PRECISION + cleanLiquidationFee)) / LIQ_PRECISION;\n\n            // Because interest accrues every block, _liquidationAmountInCollateralUnits from a few lines up is an ever increasing value\n            // This means that leftoverCollateral can occasionally go negative by a few hundred wei (cleanLiqFee premium covers this for liquidator)\n            _leftoverCollateral = (_userCollateralBalance.toInt256() - _optimisticCollateralForLiquidator.toInt256());\n\n            // If cleanLiquidation fee results in no leftover collateral, give liquidator all the collateral\n            // This will only be true when there liquidator is cleaning out the position\n            _collateralForLiquidator = _leftoverCollateral <= 0\n                ? _userCollateralBalance\n                : (_liquidationAmountInCollateralUnits * (LIQ_PRECISION + dirtyLiquidationFee)) / LIQ_PRECISION;\n\n            if (protocolLiquidationFee > 0) {\n                _feesAmount = (protocolLiquidationFee * _collateralForLiquidator) / LIQ_PRECISION;\n                _collateralForLiquidator = _collateralForLiquidator - _feesAmount;\n            }\n        }\n\n        // Calculated here for use during repayment, grouped with other calcs before effects start\n        uint128 _amountLiquidatorToRepay = (_totalBorrow.toAmount(_sharesToLiquidate, true)).toUint128();\n\n        // Determine if and how much debt to adjust\n        uint128 _sharesToAdjust = 0;\n        {\n            uint128 _amountToAdjust = 0;\n            if (_leftoverCollateral <= 0) {\n                // Determine if we need to adjust any shares\n                _sharesToAdjust = _borrowerShares - _sharesToLiquidate;\n                if (_sharesToAdjust > 0) {\n                    // Write off bad debt\n                    _amountToAdjust = (_totalBorrow.toAmount(_sharesToAdjust, false)).toUint128();\n\n                    // Note: Ensure this memory struct will be passed to _repayAsset for write to state\n                    _totalBorrow.amount -= _amountToAdjust;\n\n                    // Effects: write to state\n                    totalAsset.amount -= _amountToAdjust;\n                }\n            }\n            emit Liquidate(\n                _borrower,\n                _collateralForLiquidator,\n                _sharesToLiquidate,\n                _amountLiquidatorToRepay,\n                _feesAmount,\n                _sharesToAdjust,\n                _amountToAdjust\n            );\n        }\n\n        // Effects & Interactions\n        // NOTE: reverts if _shares > userBorrowShares\n        _repayAsset(\n            _totalBorrow,\n            _amountLiquidatorToRepay,\n            _sharesToLiquidate + _sharesToAdjust,\n            msg.sender,\n            _borrower\n        ); // liquidator repays shares on behalf of borrower\n        // NOTE: reverts if _collateralForLiquidator > userCollateralBalance\n        // Collateral is removed on behalf of borrower and sent to liquidator\n        // NOTE: reverts if _collateralForLiquidator > userCollateralBalance\n        _removeCollateral(_collateralForLiquidator, msg.sender, _borrower);\n        // Adjust bookkeeping only (decreases collateral held by borrower)\n        _removeCollateral(_feesAmount, address(this), _borrower);\n        // Adjusts bookkeeping only (increases collateral held by protocol)\n        _addCollateral(address(this), _feesAmount, address(this));\n    }\n\n    // ============================================================================================\n    // Functions: Leverage\n    // ============================================================================================\n\n    /// @notice The ```LeveragedPosition``` event is emitted when a borrower takes out a new leveraged position\n    /// @param _borrower The account for which the debt is debited\n    /// @param _swapperAddress The address of the swapper which conforms the FraxSwap interface\n    /// @param _borrowAmount The amount of Asset Token to be borrowed to be borrowed\n    /// @param _borrowShares The number of Borrow Shares the borrower is credited\n    /// @param _initialCollateralAmount The amount of initial Collateral Tokens supplied by the borrower\n    /// @param _amountCollateralOut The amount of Collateral Token which was received for the Asset Tokens\n    event LeveragedPosition(\n        address indexed _borrower,\n        address _swapperAddress,\n        uint256 _borrowAmount,\n        uint256 _borrowShares,\n        uint256 _initialCollateralAmount,\n        uint256 _amountCollateralOut\n    );\n\n    /// @notice The ```leveragedPosition``` function allows a user to enter a leveraged borrow position with minimal upfront Collateral\n    /// @dev Caller must invoke ```ERC20.approve()``` on the Collateral Token contract prior to calling function\n    /// @param _swapperAddress The address of the whitelisted swapper to use to swap borrowed Asset Tokens for Collateral Tokens\n    /// @param _borrowAmount The amount of Asset Tokens borrowed\n    /// @param _initialCollateralAmount The initial amount of Collateral Tokens supplied by the borrower\n    /// @param _amountCollateralOutMin The minimum amount of Collateral Tokens to be received in exchange for the borrowed Asset Tokens\n    /// @param _path An array containing the addresses of ERC20 tokens to swap.  Adheres to UniV2 style path params.\n    /// @return _totalCollateralBalance The total amount of Collateral Tokens added to a users account (initial + swap)\n    function leveragedPosition(\n        address _swapperAddress,\n        uint256 _borrowAmount,\n        uint256 _initialCollateralAmount,\n        uint256 _amountCollateralOutMin,\n        address[] memory _path\n    ) external nonReentrant isSolvent(msg.sender) returns (uint256 _totalCollateralBalance) {\n        // Accrue interest if necessary\n        _addInterest();\n\n        // Update exchange rate and check if borrow is allowed, revert if not\n        {\n            (bool _isBorrowAllowed, , ) = _updateExchangeRate();\n            if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation();\n        }\n\n        IERC20 _assetContract = assetContract;\n        IERC20 _collateralContract = collateralContract;\n\n        if (!swappers[_swapperAddress]) {\n            revert BadSwapper();\n        }\n        if (_path[0] != address(_assetContract)) {\n            revert InvalidPath(address(_assetContract), _path[0]);\n        }\n        if (_path[_path.length - 1] != address(_collateralContract)) {\n            revert InvalidPath(address(_collateralContract), _path[_path.length - 1]);\n        }\n\n        // Add initial collateral\n        if (_initialCollateralAmount > 0) {\n            _addCollateral(msg.sender, _initialCollateralAmount, msg.sender);\n        }\n\n        // Debit borrowers account\n        // setting recipient to address(this) means no transfer will happen\n        uint256 _borrowShares = _borrowAsset(_borrowAmount.toUint128(), address(this));\n\n        // Interactions\n        _assetContract.approve(_swapperAddress, _borrowAmount);\n\n        // Even though swappers are trusted, we verify the balance before and after swap\n        uint256 _initialCollateralBalance = _collateralContract.balanceOf(address(this));\n        ISwapper(_swapperAddress).swapExactTokensForTokens(\n            _borrowAmount,\n            _amountCollateralOutMin,\n            _path,\n            address(this),\n            block.timestamp\n        );\n        uint256 _finalCollateralBalance = _collateralContract.balanceOf(address(this));\n\n        // Note: VIOLATES CHECKS-EFFECTS-INTERACTION pattern, make sure function is NONREENTRANT\n        // Effects: bookkeeping & write to state\n        uint256 _amountCollateralOut = _finalCollateralBalance - _initialCollateralBalance;\n        if (_amountCollateralOut < _amountCollateralOutMin) {\n            revert SlippageTooHigh(_amountCollateralOutMin, _amountCollateralOut);\n        }\n\n        // address(this) as _sender means no transfer occurs as the pair has already received the collateral during swap\n        _addCollateral(address(this), _amountCollateralOut, msg.sender);\n\n        _totalCollateralBalance = _initialCollateralAmount + _amountCollateralOut;\n        emit LeveragedPosition(\n            msg.sender,\n            _swapperAddress,\n            _borrowAmount,\n            _borrowShares,\n            _initialCollateralAmount,\n            _amountCollateralOut\n        );\n    }\n\n    /// @notice The ```RepayAssetWithCollateral``` event is emitted whenever ```repayAssetWithCollateral()``` is invoked\n    /// @param _borrower The borrower account for which the repayment is taking place\n    /// @param _swapperAddress The address of the whitelisted swapper to use for token swaps\n    /// @param _collateralToSwap The amount of Collateral Token to swap and use for repayment\n    /// @param _amountAssetOut The amount of Asset Token which was repaid\n    /// @param _sharesRepaid The number of Borrow Shares which were repaid\n    event RepayAssetWithCollateral(\n        address indexed _borrower,\n        address _swapperAddress,\n        uint256 _collateralToSwap,\n        uint256 _amountAssetOut,\n        uint256 _sharesRepaid\n    );\n\n    /// @notice The ```repayAssetWithCollateral``` function allows a borrower to repay their debt using existing collateral in contract\n    /// @param _swapperAddress The address of the whitelisted swapper to use for token swaps\n    /// @param _collateralToSwap The amount of Collateral Tokens to swap for Asset Tokens\n    /// @param _amountAssetOutMin The minimum amount of Asset Tokens to receive during the swap\n    /// @param _path An array containing the addresses of ERC20 tokens to swap.  Adheres to UniV2 style path params.\n    /// @return _amountAssetOut The amount of Asset Tokens received for the Collateral Tokens, the amount the borrowers account was credited\n    function repayAssetWithCollateral(\n        address _swapperAddress,\n        uint256 _collateralToSwap,\n        uint256 _amountAssetOutMin,\n        address[] calldata _path\n    ) external nonReentrant isSolvent(msg.sender) returns (uint256 _amountAssetOut) {\n        // Accrue interest if necessary\n        _addInterest();\n\n        // Update exchange rate and check if borrow is allowed, revert if not\n        (bool _isBorrowAllowed, , ) = _updateExchangeRate();\n        if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation();\n\n        IERC20 _assetContract = assetContract;\n        IERC20 _collateralContract = collateralContract;\n\n        if (!swappers[_swapperAddress]) {\n            revert BadSwapper();\n        }\n        if (_path[0] != address(_collateralContract)) {\n            revert InvalidPath(address(_collateralContract), _path[0]);\n        }\n        if (_path[_path.length - 1] != address(_assetContract)) {\n            revert InvalidPath(address(_assetContract), _path[_path.length - 1]);\n        }\n\n        // Effects: bookkeeping & write to state\n        // Debit users collateral balance in preparation for swap, setting _recipient to address(this) means no transfer occurs\n        _removeCollateral(_collateralToSwap, address(this), msg.sender);\n\n        // Interactions\n        _collateralContract.approve(_swapperAddress, _collateralToSwap);\n\n        // Even though swappers are trusted, we verify the balance before and after swap\n        uint256 _initialAssetBalance = _assetContract.balanceOf(address(this));\n        ISwapper(_swapperAddress).swapExactTokensForTokens(\n            _collateralToSwap,\n            _amountAssetOutMin,\n            _path,\n            address(this),\n            block.timestamp\n        );\n        uint256 _finalAssetBalance = _assetContract.balanceOf(address(this));\n\n        // Note: VIOLATES CHECKS-EFFECTS-INTERACTION pattern, make sure function is NONREENTRANT\n        // Effects: bookkeeping\n        _amountAssetOut = _finalAssetBalance - _initialAssetBalance;\n        if (_amountAssetOut < _amountAssetOutMin) {\n            revert SlippageTooHigh(_amountAssetOutMin, _amountAssetOut);\n        }\n\n        VaultAccount memory _totalBorrow = totalBorrow;\n        uint256 _sharesToRepay = _totalBorrow.toShares(_amountAssetOut, false);\n\n        // Effects: write to state\n        // Note: setting _payer to address(this) means no actual transfer will occur.  Contract already has funds\n        _repayAsset(_totalBorrow, _amountAssetOut.toUint128(), _sharesToRepay.toUint128(), address(this), msg.sender);\n\n        emit RepayAssetWithCollateral(msg.sender, _swapperAddress, _collateralToSwap, _amountAssetOut, _sharesToRepay);\n    }\n}\n"
      },
      "src/contracts/interfaces/IDualOracle.sol": {
        "content": "// SPDX-License-Identifier: ISC\npragma solidity >=0.8.18;\n\ninterface IDualOracle {\n    function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh);\n\n    function decimals() external view returns (uint8);\n\n    function oracleType() external view returns (uint256);\n\n    function name() external view returns (string memory);\n}\n"
      },
      "src/contracts/interfaces/IRateCalculatorV2.sol": {
        "content": "// SPDX-License-Identifier: ISC\npragma solidity ^0.8.18;\n\ninterface IRateCalculatorV2 {\n    function name() external view returns (string memory);\n\n    function version() external view returns (uint256, uint256, uint256);\n\n    function getNewRate(\n        uint256 _deltaTime,\n        uint256 _utilization,\n        uint64 _maxInterest\n    ) external view returns (uint64 _newRatePerSec, uint64 _newMaxInterest);\n}\n"
      },
      "src/contracts/interfaces/ISwapper.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.18;\n\ninterface ISwapper {\n    function swapExactTokensForTokens(\n        uint256 amountIn,\n        uint256 amountOutMin,\n        address[] calldata path,\n        address to,\n        uint256 deadline\n    ) external returns (uint256[] memory amounts);\n}\n"
      },
      "src/contracts/libraries/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: ISC\npragma solidity ^0.8.18;\n\nimport \"@openzeppelin/contracts/interfaces/IERC20.sol\";\nimport { SafeERC20 as OZSafeERC20 } from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\n// solhint-disable avoid-low-level-calls\n// solhint-disable max-line-length\n\n/// @title SafeERC20 provides helper functions for safe transfers as well as safe metadata access\n/// @author Library originally written by @Boring_Crypto github.com/boring_crypto, modified by Drake Evans (Frax Finance) github.com/drakeevans\n/// @dev original: https://github.com/boringcrypto/BoringSolidity/blob/fed25c5d43cb7ce20764cd0b838e21a02ea162e9/contracts/libraries/BoringERC20.sol\nlibrary SafeERC20 {\n    bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol()\n    bytes4 private constant SIG_NAME = 0x06fdde03; // name()\n    bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals()\n\n    function returnDataToString(bytes memory data) internal pure returns (string memory) {\n        if (data.length >= 64) {\n            return abi.decode(data, (string));\n        } else if (data.length == 32) {\n            uint8 i = 0;\n            while (i < 32 && data[i] != 0) {\n                i++;\n            }\n            bytes memory bytesArray = new bytes(i);\n            for (i = 0; i < 32 && data[i] != 0; i++) {\n                bytesArray[i] = data[i];\n            }\n            return string(bytesArray);\n        } else {\n            return \"???\";\n        }\n    }\n\n    /// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string.\n    /// @param token The address of the ERC-20 token contract.\n    /// @return (string) Token symbol.\n    function safeSymbol(IERC20 token) internal view returns (string memory) {\n        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL));\n        return success ? returnDataToString(data) : \"???\";\n    }\n\n    /// @notice Provides a safe ERC20.name version which returns '???' as fallback string.\n    /// @param token The address of the ERC-20 token contract.\n    /// @return (string) Token name.\n    function safeName(IERC20 token) internal view returns (string memory) {\n        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME));\n        return success ? returnDataToString(data) : \"???\";\n    }\n\n    /// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.\n    /// @param token The address of the ERC-20 token contract.\n    /// @return (uint8) Token decimals.\n    function safeDecimals(IERC20 token) internal view returns (uint8) {\n        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS));\n        return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;\n    }\n\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        OZSafeERC20.safeTransfer(token, to, value);\n    }\n\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        OZSafeERC20.safeTransferFrom(token, from, to, value);\n    }\n}\n"
      },
      "src/contracts/libraries/VaultAccount.sol": {
        "content": "// SPDX-License-Identifier: ISC\npragma solidity ^0.8.18;\n\nstruct VaultAccount {\n    uint128 amount; // Total amount, analogous to market cap\n    uint128 shares; // Total shares, analogous to shares outstanding\n}\n\n/// @title VaultAccount Library\n/// @author Drake Evans (Frax Finance) github.com/drakeevans, modified from work by @Boring_Crypto github.com/boring_crypto\n/// @notice Provides a library for use with the VaultAccount struct, provides convenient math implementations\n/// @dev Uses uint128 to save on storage\nlibrary VaultAccountingLibrary {\n    /// @notice Calculates the shares value in relationship to `amount` and `total`\n    /// @dev Given an amount, return the appropriate number of shares\n    function toShares(VaultAccount memory total, uint256 amount, bool roundUp) internal pure returns (uint256 shares) {\n        if (total.amount == 0) {\n            shares = amount;\n        } else {\n            shares = (amount * total.shares) / total.amount;\n            if (roundUp && (shares * total.amount) / total.shares < amount) {\n                shares = shares + 1;\n            }\n        }\n    }\n\n    /// @notice Calculates the amount value in relationship to `shares` and `total`\n    /// @dev Given a number of shares, returns the appropriate amount\n    function toAmount(VaultAccount memory total, uint256 shares, bool roundUp) internal pure returns (uint256 amount) {\n        if (total.shares == 0) {\n            amount = shares;\n        } else {\n            amount = (shares * total.amount) / total.shares;\n            if (roundUp && (amount * total.shares) / total.amount < shares) {\n                amount = amount + 1;\n            }\n        }\n    }\n}\n"
      },
      "src/contracts/Timelock2Step.sol": {
        "content": "// SPDX-License-Identifier: ISC\npragma solidity ^0.8.18;\n\n// ====================================================================\n// |     ______                   _______                             |\n// |    / _____________ __  __   / ____(_____  ____ _____  ________   |\n// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \\/ __ `/ __ \\/ ___/ _ \\  |\n// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |\n// | /_/   /_/   \\__,_/_/|_|  /_/   /_/_/ /_/\\__,_/_/ /_/\\___/\\___/   |\n// |                                                                  |\n// ====================================================================\n// ========================== Timelock2Step ===========================\n// ====================================================================\n// Frax Finance: https://github.com/FraxFinance\n\n// Primary Author\n// Drake Evans: https://github.com/DrakeEvans\n\n// Reviewers\n// Dennis: https://github.com/denett\n\n// ====================================================================\n\n/// @title FraxlendPairCore\n/// @author Drake Evans (Frax Finance) https://github.com/drakeevans\n/// @dev Inspired by the OpenZeppelin's Ownable2Step contract\n/// @notice  An abstract contract which contains 2-step transfer and renounce logic for a timelock address\nabstract contract Timelock2Step {\n    /// @notice The pending timelock address\n    address public pendingTimelockAddress;\n\n    /// @notice The current timelock address\n    address public timelockAddress;\n\n    constructor() {\n        timelockAddress = msg.sender;\n    }\n\n    /// @notice Emitted when timelock is transferred\n    error OnlyTimelock();\n\n    /// @notice Emitted when pending timelock is transferred\n    error OnlyPendingTimelock();\n\n    /// @notice The ```TimelockTransferStarted``` event is emitted when the timelock transfer is initiated\n    /// @param previousTimelock The address of the previous timelock\n    /// @param newTimelock The address of the new timelock\n    event TimelockTransferStarted(address indexed previousTimelock, address indexed newTimelock);\n\n    /// @notice The ```TimelockTransferred``` event is emitted when the timelock transfer is completed\n    /// @param previousTimelock The address of the previous timelock\n    /// @param newTimelock The address of the new timelock\n    event TimelockTransferred(address indexed previousTimelock, address indexed newTimelock);\n\n    /// @notice The ```_isSenderTimelock``` function checks if msg.sender is current timelock address\n    /// @return Whether or not msg.sender is current timelock address\n    function _isSenderTimelock() internal view returns (bool) {\n        return msg.sender == timelockAddress;\n    }\n\n    /// @notice The ```_requireTimelock``` function reverts if msg.sender is not current timelock address\n    function _requireTimelock() internal view {\n        if (msg.sender != timelockAddress) revert OnlyTimelock();\n    }\n\n    /// @notice The ```_isSenderPendingTimelock``` function checks if msg.sender is pending timelock address\n    /// @return Whether or not msg.sender is pending timelock address\n    function _isSenderPendingTimelock() internal view returns (bool) {\n        return msg.sender == pendingTimelockAddress;\n    }\n\n    /// @notice The ```_requirePendingTimelock``` function reverts if msg.sender is not pending timelock address\n    function _requirePendingTimelock() internal view {\n        if (msg.sender != pendingTimelockAddress) revert OnlyPendingTimelock();\n    }\n\n    /// @notice The ```_transferTimelock``` function initiates the timelock transfer\n    /// @dev This function is to be implemented by a public function\n    /// @param _newTimelock The address of the nominated (pending) timelock\n    function _transferTimelock(address _newTimelock) internal {\n        pendingTimelockAddress = _newTimelock;\n        emit TimelockTransferStarted(timelockAddress, _newTimelock);\n    }\n\n    /// @notice The ```_acceptTransferTimelock``` function completes the timelock transfer\n    /// @dev This function is to be implemented by a public function\n    function _acceptTransferTimelock() internal {\n        pendingTimelockAddress = address(0);\n        _setTimelock(msg.sender);\n    }\n\n    /// @notice The ```_setTimelock``` function sets the timelock address\n    /// @dev This function is to be implemented by a public function\n    /// @param _newTimelock The address of the new timelock\n    function _setTimelock(address _newTimelock) internal {\n        emit TimelockTransferred(timelockAddress, _newTimelock);\n        timelockAddress = _newTimelock;\n    }\n\n    /// @notice The ```transferTimelock``` function initiates the timelock transfer\n    /// @dev Must be called by the current timelock\n    /// @param _newTimelock The address of the nominated (pending) timelock\n    function transferTimelock(address _newTimelock) external virtual {\n        _requireTimelock();\n        _transferTimelock(_newTimelock);\n    }\n\n    /// @notice The ```acceptTransferTimelock``` function completes the timelock transfer\n    /// @dev Must be called by the pending timelock\n    function acceptTransferTimelock() external virtual {\n        _requirePendingTimelock();\n        _acceptTransferTimelock();\n    }\n\n    /// @notice The ```renounceTimelock``` function renounces the timelock after setting pending timelock to current timelock\n    /// @dev Pending timelock must be set to current timelock before renouncing, creating a 2-step renounce process\n    function renounceTimelock() external virtual {\n        _requireTimelock();\n        _requirePendingTimelock();\n        _transferTimelock(address(0));\n        _setTimelock(address(0));\n    }\n}\n"
      }
    },
    "settings": {
      "metadata": {
        "bytecodeHash": "none"
      },
      "viaIR": true,
      "optimizer": {
        "enabled": true,
        "runs": 1660
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}