{"ok":true,"contract":{"address":"0x51c3db485e3b21193636a83f05b3517f691cd68c","contract_name":"StabilityPool","deployed":"1711041917","fund":"0","fund_usd":"0.00000000","native_balance":"0","network":"linea","first_seen":"1779914383","verified":true,"is_proxy":false,"implementation_address":null,"proxy_contract_name":"StabilityPool","implementation_contract_name":null,"deploy_tx_hash":"0x22ea951e1cad910c227abe4f414608b9104b601848dfc12255cc09ecf127ae59","deployer_address":"0x109861a2a539ddb151ee591588a14cf86309e4e5","deploy_block_number":"3045986","deployed_at_timestamp":"1711041917","deployed_at":"2024-03-21T17:25:17.000Z","confidence":"precise","fetched_at":"2026-05-27T20:38:52.996Z","source_code":"// File: @chainlink/contracts/src/v0.8/interfaces/AggregatorInterface.sol\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorInterface {\n  function latestAnswer() external view returns (int256);\n\n  function latestTimestamp() external view returns (uint256);\n\n  function latestRound() external view returns (uint256);\n\n  function getAnswer(uint256 roundId) external view returns (int256);\n\n  function getTimestamp(uint256 roundId) external view returns (uint256);\n\n  event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);\n\n  event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);\n}\n\n\n// File: @chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./AggregatorInterface.sol\";\nimport \"./AggregatorV3Interface.sol\";\n\ninterface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}\n\n\n// File: @chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n  function decimals() external view returns (uint8);\n\n  function description() external view returns (string memory);\n\n  function version() external view returns (uint256);\n\n  function getRoundData(\n    uint80 _roundId\n  ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);\n\n  function latestRoundData()\n    external\n    view\n    returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);\n}\n\n\n// File: @openzeppelin/contracts/access/Ownable.sol\n// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.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. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling 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\n\n// File: @openzeppelin/contracts/interfaces/IERC20Metadata.sol\n// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../token/ERC20/extensions/IERC20Metadata.sol\";\n\n\n// File: @openzeppelin/contracts/security/ReentrancyGuard.sol\n// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.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    /**\n     * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n     * `nonReentrant` function in the call stack.\n     */\n    function _reentrancyGuardEntered() internal view returns (bool) {\n        return _status == _ENTERED;\n    }\n}\n\n\n// File: @openzeppelin/contracts/token/ERC20/ERC20.sol\n// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.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 * The default value of {decimals} is 18. To change this, you should override\n * this function so it returns a different value.\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     * 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 default value returned by this function, unless\n     * it's 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(address from, address to, uint256 amount) 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(address from, address to, uint256 amount) 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(address owner, address spender, uint256 amount) 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(address owner, address spender, uint256 amount) 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(address from, address to, uint256 amount) 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(address from, address to, uint256 amount) internal virtual {}\n}\n\n\n// File: @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\n// 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\n\n// File: @openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol\n// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/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 *\n * ==== Security Considerations\n *\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\n * generally recommended is:\n *\n * ```solidity\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\n *     doThing(..., value);\n * }\n *\n * function doThing(..., uint256 value) public {\n *     token.safeTransferFrom(msg.sender, address(this), value);\n *     ...\n * }\n * ```\n *\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\n * {SafeERC20-safeTransferFrom}).\n *\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\n * contracts should have entry points that don't rely on permit.\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     * CAUTION: See Security Considerations above.\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\n\n// File: @openzeppelin/contracts/token/ERC20/IERC20.sol\n// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.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(address from, address to, uint256 amount) external returns (bool);\n}\n\n\n// File: @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\n// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/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    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) 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(IERC20 token, address spender, uint256 value) 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    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n     * to be set to zero before setting it to a non-zero value, such as USDT.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\n            _callOptionalReturn(token, approvalCall);\n        }\n    }\n\n    /**\n     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\n     * Revert on invalid signature.\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        require(returndata.length == 0 || abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation 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     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\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 cannot use {Address-functionCall} here since this should return false\n        // and not revert is the subcall reverts.\n\n        (bool success, bytes memory returndata) = address(token).call(data);\n        return\n            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));\n    }\n}\n\n\n// File: @openzeppelin/contracts/utils/Address.sol\n// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.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     * Furthermore, `isContract` will also return true if the target contract within\n     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n     * which only has an effect at the end of a transaction.\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://consensys.net/diligence/blog/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.8.0/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(address target, bytes memory data, uint256 value) 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\n\n// File: @openzeppelin/contracts/utils/Context.sol\n// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (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    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n\n\n// File: @openzeppelin/contracts/utils/structs/EnumerableSet.sol\n// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)\n// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```solidity\n * contract Example {\n *     // Add the library methods\n *     using EnumerableSet for EnumerableSet.AddressSet;\n *\n *     // Declare a set state variable\n *     EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n *\n * [WARNING]\n * ====\n * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure\n * unusable.\n * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.\n *\n * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an\n * array of EnumerableSet.\n * ====\n */\nlibrary EnumerableSet {\n    // To implement this library for multiple types with as little code\n    // repetition as possible, we write it in terms of a generic Set type with\n    // bytes32 values.\n    // The Set implementation uses private functions, and user-facing\n    // implementations (such as AddressSet) are just wrappers around the\n    // underlying Set.\n    // This means that we can only create new EnumerableSets for types that fit\n    // in bytes32.\n\n    struct Set {\n        // Storage of set values\n        bytes32[] _values;\n        // Position of the value in the `values` array, plus 1 because index 0\n        // means a value is not in the set.\n        mapping(bytes32 => uint256) _indexes;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function _add(Set storage set, bytes32 value) private returns (bool) {\n        if (!_contains(set, value)) {\n            set._values.push(value);\n            // The value is stored at length-1, but we add 1 to all indexes\n            // and use 0 as a sentinel value\n            set._indexes[value] = set._values.length;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function _remove(Set storage set, bytes32 value) private returns (bool) {\n        // We read and store the value's index to prevent multiple reads from the same storage slot\n        uint256 valueIndex = set._indexes[value];\n\n        if (valueIndex != 0) {\n            // Equivalent to contains(set, value)\n            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n            // the array, and then remove the last element (sometimes called as 'swap and pop').\n            // This modifies the order of the array, as noted in {at}.\n\n            uint256 toDeleteIndex = valueIndex - 1;\n            uint256 lastIndex = set._values.length - 1;\n\n            if (lastIndex != toDeleteIndex) {\n                bytes32 lastValue = set._values[lastIndex];\n\n                // Move the last value to the index where the value to delete is\n                set._values[toDeleteIndex] = lastValue;\n                // Update the index for the moved value\n                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex\n            }\n\n            // Delete the slot where the moved value was stored\n            set._values.pop();\n\n            // Delete the index for the deleted slot\n            delete set._indexes[value];\n\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function _contains(Set storage set, bytes32 value) private view returns (bool) {\n        return set._indexes[value] != 0;\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function _length(Set storage set) private view returns (uint256) {\n        return set._values.length;\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function _at(Set storage set, uint256 index) private view returns (bytes32) {\n        return set._values[index];\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function _values(Set storage set) private view returns (bytes32[] memory) {\n        return set._values;\n    }\n\n    // Bytes32Set\n\n    struct Bytes32Set {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _add(set._inner, value);\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _remove(set._inner, value);\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n        return _contains(set._inner, value);\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(Bytes32Set storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n        return _at(set._inner, index);\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        bytes32[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n\n    // AddressSet\n\n    struct AddressSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(AddressSet storage set, address value) internal returns (bool) {\n        return _add(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(AddressSet storage set, address value) internal returns (bool) {\n        return _remove(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(AddressSet storage set, address value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(AddressSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(AddressSet storage set, uint256 index) internal view returns (address) {\n        return address(uint160(uint256(_at(set._inner, index))));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(AddressSet storage set) internal view returns (address[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        address[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n\n    // UintSet\n\n    struct UintSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(UintSet storage set, uint256 value) internal returns (bool) {\n        return _add(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(UintSet storage set, uint256 value) internal returns (bool) {\n        return _remove(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(UintSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n    /**\n     * @dev Returns the value stored at position `index` in the set. O(1).\n     *\n     * Note that there are no guarantees on the ordering of values inside the\n     * array, and it may change when more values are added or removed.\n     *\n     * Requirements:\n     *\n     * - `index` must be strictly less than {length}.\n     */\n    function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n        return uint256(_at(set._inner, index));\n    }\n\n    /**\n     * @dev Return the entire set in an array\n     *\n     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n     * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n     */\n    function values(UintSet storage set) internal view returns (uint256[] memory) {\n        bytes32[] memory store = _values(set._inner);\n        uint256[] memory result;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            result := store\n        }\n\n        return result;\n    }\n}\n\n\n// File: contracts/A3A.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/token/ERC20/ERC20.sol';\n\n/**\n * @title A3A\n * @dev A3A is an ERC20 token representing the 3A Utility Token.\n */\ncontract A3A is ERC20 {\n    /**\n     * @dev Total supply of A3A tokens.\n     */\n    uint256 public constant TOTAL_SUPPLY = 1_000_000_000 ether;\n\n    /**\n     * @dev Constructor that mints the total supply of A3A tokens to the deployer.\n     */\n    constructor() ERC20('3A Utility Token', 'A3A') {\n        _mint(msg.sender, TOTAL_SUPPLY);\n    }\n}\n\n\n// File: contracts/A3AStaking.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/interfaces/IERC20Metadata.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\nimport '@openzeppelin/contracts/access/Ownable.sol';\n\nimport './interfaces/IVaultFactory.sol';\nimport './interfaces/IFeeRecipient.sol';\nimport './interfaces/IVault.sol';\n\nimport './utils/constants.sol';\n\n/**\n * @title A3A Staking contract.\n * @dev Rewards stakers in StableCoin that is used to pay fee.\n */\ncontract A3AStaking is Ownable, Constants {\n    using SafeERC20 for IERC20;\n\n    // Mapping of stakers' addresses to their staked amount.\n    mapping(address => uint256) public stakes;\n    // Total amount of A3A staked.\n    uint256 public totalA3AStaked;\n\n    // Running sum of StableCoin fees per-A3A-staked.\n    uint256 public F_StableCoin;\n    // Timestamp of the last fee operation.\n    uint256 public lastFeeOperationTime;\n\n    // User snapshots of F_A3A and F_StableCoin, taken at the point at which their latest deposit was made.\n    mapping(address => uint256) public F_StableCoinSnapshots;\n    // User gains in StableCoin.\n    mapping(address => uint256) public stableCoinUserGains;\n\n    // Interfaces to interact with other contracts.\n    IVaultFactory public factory;\n    IERC20 public a3aToken;\n    IERC20 public stableCoin;\n\n    // --- Events ---\n    event FactoryAddressSet(address _factoryAddress);\n    event A3aTokenAddressSet(address _a3aTokenAddress);\n    event StableCoinAddressSet(address _stableCoinAddress);\n    event StakeChanged(address indexed _staker, uint256 _newStake);\n    event TotalA3AStakedUpdated(uint256 _totalA3AStaked);\n    event RewardRedeemed(\n        address _account,\n        uint256 _stableAmount,\n        address _vaultAddress\n    );\n    event StakerSnapshotsUpdated(\n        address _staker,\n        uint256 _F_StableCoin,\n        uint256 _stableGains\n    );\n    event FeeTaken(uint256 _amount, uint256 _F_StableCoin, bool _redemptionFee);\n\n    /**\n     * @notice Initializes the A3AStaking contract.\n     * @param _vaultFactory Address of the VaultFactory contract.\n     * @param _a3aToken Address of the A3A token contract.\n     */\n    constructor(address _vaultFactory, address _a3aToken) {\n        factory = IVaultFactory(_vaultFactory);\n        a3aToken = IERC20(_a3aToken);\n        stableCoin = IERC20(factory.stable());\n    }\n\n    // --- Functions ---\n\n    /**\n     * @dev Sets the timestamp to calculate the next decayed rate from.\n     * @param _timestamp uint256 value representing time in seconds.\n     */\n    function setInitialLastFee(uint256 _timestamp) public onlyOwner {\n        lastFeeOperationTime = _timestamp > 0 ? _timestamp : block.timestamp;\n    }\n\n    /**\n     * @dev Sets the VaultFactory contract if the address was updated.\n     * @param _factoryAddress Address of the updated VaultFactory contract.\n     */\n    function setFactory(address _factoryAddress) external onlyOwner {\n        factory = IVaultFactory(_factoryAddress);\n        stableCoin = IERC20(address(factory.stable()));\n        emit FactoryAddressSet(address(factory));\n        emit StableCoinAddressSet(address(stableCoin));\n    }\n\n    /**\n     * @dev Allows users to stake A3A tokens.\n     * @param _a3aAmount Amount of A3A tokens to stake.\n     * @notice If caller has a pre-existing stake, records any accumulated StableCoin gains to them.\n     */\n    function stake(uint256 _a3aAmount) external {\n        _requireNonZeroAmount(_a3aAmount);\n\n        uint256 currentStake = stakes[msg.sender];\n\n        // Transfer A3A from caller to this contract.\n        require(\n            a3aToken.transferFrom(msg.sender, address(this), _a3aAmount),\n            'transfer-from-failed'\n        );\n\n        // Grab and record accumulated StableCoin gains from the current stake and update Snapshot.\n        uint256 currentTotalA3AStaked = totalA3AStaked;\n        if (currentTotalA3AStaked == 0)\n            stableCoinUserGains[msg.sender] += F_StableCoin;\n        _updateUserSnapshot(msg.sender);\n\n        // Increase user’s stake and total A3A staked.\n        uint256 newTotalA3AStaked = currentTotalA3AStaked + _a3aAmount;\n        totalA3AStaked = newTotalA3AStaked;\n        uint256 newUserStake = currentStake + _a3aAmount;\n        stakes[msg.sender] = newUserStake;\n\n        emit TotalA3AStakedUpdated(newTotalA3AStaked);\n        emit StakeChanged(msg.sender, newUserStake);\n    }\n\n    /**\n     * @dev Allows user to unstake A3A.\n     * @param _a3aAmount Amount of A3A to unstake.\n     * @notice Unstake the A3A and send it back to the caller, and record accumulated StableCoin gains for the caller.\n     * If requested amount > stake, send their entire stake.\n     */\n    function unstake(uint256 _a3aAmount) external {\n        _requireNonZeroAmount(_a3aAmount);\n        uint256 currentStake = stakes[msg.sender];\n        _requireUserHasStake(currentStake);\n\n        // Grab and record accumulated StableCoin gains from the current stake and update Snapshot.\n        _updateUserSnapshot(msg.sender);\n\n        uint256 A3AToWithdraw = _a3aAmount > currentStake\n            ? currentStake\n            : _a3aAmount;\n        uint256 newStake = currentStake - A3AToWithdraw;\n\n        // Decrease user's stake and total A3A staked.\n        stakes[msg.sender] = newStake;\n        totalA3AStaked = totalA3AStaked - A3AToWithdraw;\n        emit TotalA3AStakedUpdated(totalA3AStaked);\n\n        // Transfer unstaked A3A to user.\n        a3aToken.safeTransfer(msg.sender, A3AToWithdraw);\n\n        emit StakeChanged(msg.sender, newStake);\n    }\n\n    /**\n     * @dev Increases the fees and updates F_StableCoin based on the received amount. Called by A3A core contracts.\n     * @param _amount Amount of StableCoin received as fees.\n     * @return bool Returns true if the fee operation is successful.\n     */\n    function takeFees(uint256 _amount) external returns (bool) {\n        _requireNonZeroAmount(_amount);\n        stableCoin.safeTransferFrom(msg.sender, address(this), _amount);\n        uint256 totalA3AStaked_cached = totalA3AStaked;\n        uint256 amountPerA3AStaked = _amount;\n        if (totalA3AStaked_cached > 0) {\n            amountPerA3AStaked =\n                ((_amount) * DECIMAL_PRECISION) /\n                totalA3AStaked_cached;\n        }\n        uint256 newF_StableCoin = F_StableCoin + amountPerA3AStaked;\n        F_StableCoin = newF_StableCoin;\n\n        lastFeeOperationTime = block.timestamp;\n        emit FeeTaken(_amount, newF_StableCoin, msg.sender == address(factory));\n        return true;\n    }\n\n    // --- Pending reward functions ---\n\n    /**\n     * @dev to redeem StableCoin rewards, transfers the amount only to repay debt of the Vault.\n     * @param _amount amount of StableCoin to repay the debt.\n     * @param _vaultAddress address of the valid vault to repay the debt.\n     * @notice user can redeem StableCoin rewards only to repay the debt of the vaults.\n     */\n    function redeemReward(uint256 _amount, address _vaultAddress) external {\n        _requireNonZeroAmount(_amount);\n        address account = msg.sender;\n        require(factory.containsVault(_vaultAddress), 'vault-not-found');\n        IVault _vault = IVault(_vaultAddress);\n        _amount = _vault.debt() > _amount ? _amount : _vault.debt();\n        require(\n            (_getUnpaidStableCoinGain(msg.sender)) >= _amount,\n            'amount-must-fit-rewards-amount'\n        );\n        _updateUserSnapshot(account);\n        stableCoinUserGains[account] = stableCoinUserGains[account] - _amount;\n        stableCoin.approve(address(factory), 0);\n        stableCoin.approve(address(factory), _amount);\n\n        factory.repay(_vaultAddress, _amount);\n\n        emit RewardRedeemed(msg.sender, _amount, _vaultAddress);\n    }\n\n    /**\n     * @dev Retrieves the total amount of A3A staked.\n     * @return uint256 Total amount of A3A staked.\n     */\n    function totalStake() external view returns (uint256) {\n        return totalA3AStaked;\n    }\n\n    /**\n     * @dev Retrieves the unpaid rewards of the user.\n     * @param _user Address of the user to check.\n     * @return uint256 Unpaid rewards of the user in StableCoin.\n     */\n    function getUnpaidStableCoinGain(\n        address _user\n    ) external view returns (uint256) {\n        return _getUnpaidStableCoinGain(_user);\n    }\n\n    /**\n     * @dev Retrieves the total rewards accumulated in StableCoin.\n     * @return uint256 Total rewards accumulated in StableCoin.\n     */\n    function getRewardsTotal() external view returns (uint256) {\n        return F_StableCoin;\n    }\n\n    // --- Internal helper functions ---\n\n    /**\n     * @dev Calculates the pending StableCoin gains for a user.\n     * @param _user Address of the user to calculate gains for.\n     * @return uint256 Pending StableCoin gains for the user.\n     */\n    function _getPendingStableCoinGain(\n        address _user\n    ) internal view returns (uint256) {\n        uint256 F_StableCoin_Snapshot = F_StableCoinSnapshots[_user];\n        uint256 stableCoinGain = (stakes[_user] *\n            (F_StableCoin - F_StableCoin_Snapshot)) / DECIMAL_PRECISION;\n        return stableCoinGain;\n    }\n\n    /**\n     * @dev Calculates the total unpaid StableCoin gains for a user.\n     * @param _user Address of the user to calculate gains for.\n     * @return uint256 Total unpaid StableCoin gains for the user.\n     */\n    function _getUnpaidStableCoinGain(\n        address _user\n    ) internal view returns (uint256) {\n        return stableCoinUserGains[_user] + _getPendingStableCoinGain(_user);\n    }\n\n    /**\n     * @dev Records the StableCoin gains for a user based on their stake.\n     * @param _user Address of the user to record gains for.\n     */\n    function _recordStableCoinGain(address _user) internal {\n        uint256 userStake = stakes[_user];\n        if (userStake > 0) {\n            uint256 F_StableCoin_Snapshot = F_StableCoinSnapshots[_user];\n            uint256 stableCoinGain = (userStake *\n                (F_StableCoin - F_StableCoin_Snapshot)) / DECIMAL_PRECISION;\n            stableCoinUserGains[_user] += stableCoinGain;\n        }\n    }\n\n    /**\n     * @dev Updates user's snapshot of StableCoin gains and F_StableCoin.\n     * @param _user Address of the user to update snapshot for.\n     */\n    function _updateUserSnapshot(address _user) internal {\n        _recordStableCoinGain(_user);\n        uint256 currentF_StableCoin = F_StableCoin;\n        F_StableCoinSnapshots[_user] = currentF_StableCoin;\n        emit StakerSnapshotsUpdated(\n            _user,\n            currentF_StableCoin,\n            stableCoinUserGains[_user]\n        );\n    }\n\n    // --- 'require' functions ---\n\n    /**\n     * @dev Requires the user to have a non-zero stake.\n     * @param currentStake Amount of current stake for the user.\n     */\n    function _requireUserHasStake(uint256 currentStake) internal pure {\n        require(currentStake > 0, 'stakes-is-zero');\n    }\n\n    /**\n     * @dev Requires the amount to be non-zero.\n     * @param _amount Amount to check for non-zero.\n     */\n    function _requireNonZeroAmount(uint256 _amount) internal pure {\n        require(_amount > 0, 'amount-is-zero');\n    }\n}\n\n\n// File: contracts/APToken.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/token/ERC20/ERC20.sol';\nimport '@openzeppelin/contracts/access/Ownable.sol';\nimport './MintableToken.sol';\n\n/// @title implements minting/burning functionality for owner\ncontract APToken is MintableToken {\n    /// @dev address of the token, it is used to be swapped against in ArbitragePool\n    address public baseToken;\n\n    // solhint-disable-next-line func-visibility\n    constructor(\n        string memory name,\n        string memory symbol,\n        address _baseToken\n    ) MintableToken(name, symbol) {\n        baseToken = _baseToken;\n    }\n}\n\n\n// File: contracts/AuctionManager.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/access/Ownable.sol';\nimport '@openzeppelin/contracts/token/ERC20/IERC20.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\nimport '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';\nimport '@openzeppelin/contracts/security/ReentrancyGuard.sol';\n\nimport './interfaces/IVault.sol';\nimport './interfaces/IVaultFactory.sol';\nimport './interfaces/IStabilityPool.sol';\nimport './interfaces/ILiquidationRouter.sol';\nimport './interfaces/ITokenPriceFeed.sol';\nimport './interfaces/IMintableToken.sol';\nimport './interfaces/ILastResortLiquidation.sol';\nimport './utils/constants.sol';\n\n/**\n * @title AuctionManager.\n * @dev Manages auctions for liquidating collateral in case of debt default.\n */\ncontract AuctionManager is Ownable, ReentrancyGuard, Constants {\n    using SafeERC20 for IERC20;\n    using SafeERC20 for IMintableToken;\n\n    // Auction duration and lowest health factor\n    uint256 public auctionDuration = 2 hours;\n\n    uint256 public lowestHF = 1.05 ether; // 105%\n\n    // Struct to hold auction data\n    struct auctionData {\n        uint256 originalDebt;\n        uint256 lowestDebtToAuction;\n        uint256 highestDebtToAuction;\n        uint256 collateralsLength;\n        address[] collateral;\n        uint256[] collateralAmount;\n        uint256 auctionStartTime;\n        uint256 auctionEndTime;\n        bool auctionEnded;\n    }\n\n    // Array to store auction data.\n    auctionData[] public auctions;\n\n    address public vaultFactory;\n\n    // Events.\n    event VaultFactoryUpdated(address indexed _vaultFactory);\n    event AuctionDurationUpdated(uint256 _auctionDuration);\n    event AuctionCreated(\n        uint256 indexed _auctionId,\n        uint256 _originalDebt,\n        uint256 _lowestDebtToAuction,\n        uint256 _highestDebtToAuction,\n        uint256 _collateralsLength,\n        address[] _collateral,\n        uint256[] _collateralAmount,\n        uint256 _auctionStartTime,\n        uint256 _auctionEndTime\n    );\n    event AuctionWon(\n        uint256 indexed _auctionId,\n        address indexed _winner,\n        uint256 _debtRepaid,\n        uint256 _collateralValueGained\n    );\n    event AuctionEnded(uint256 indexed _auctionId);\n\n    /**\n     * @notice Sets the duration of each auction.\n     * @dev Can only be called by the contract owner.\n     * @param _auctionDuration Duration of the auction.\n     */\n    function setAuctionDuration(uint256 _auctionDuration) external onlyOwner {\n        require(_auctionDuration > 0, 'auction-duration-is-0');\n        auctionDuration = _auctionDuration;\n        emit AuctionDurationUpdated(_auctionDuration);\n    }\n\n    /**\n     * @notice Sets the lowest health factor allowed for bidding.\n     * @dev Can only be called by the contract owner.\n     * @param _lowestHF Lowest health factor allowed for bidding.\n     */\n    function setLowestHealthFactor(uint256 _lowestHF) external onlyOwner {\n        require(_lowestHF > 0, 'lowest-hf-is-0');\n        lowestHF = _lowestHF;\n    }\n\n    /**\n     * @dev Sets the address of the vault factory. Can only be called by the contract owner.\n     * @param _vaultFactory Address of the vault factory.\n     */\n    function setVaultFactory(address _vaultFactory) external onlyOwner {\n        require(_vaultFactory != address(0x0), 'vault-factory-is-0');\n        vaultFactory = _vaultFactory;\n        emit VaultFactoryUpdated(_vaultFactory);\n    }\n\n    /**\n     * @dev Returns the total number of auctions created.\n     * @return The total number of auctions.\n     */\n    function auctionsLength() external view returns (uint256) {\n        return auctions.length;\n    }\n\n    /**\n     * @dev Get auction information by ID.\n     * @param _auctionId The ID of the auction.\n     * @return Auction data structure.\n     */\n    function auctionInfo(\n        uint256 _auctionId\n    ) external view returns (auctionData memory) {\n        return auctions[_auctionId];\n    }\n\n    /**\n     * @dev Contract constructor to initialize the vault factory address.\n     * @param _vaultFactory Address of the vault factory.\n     */\n    constructor(address _vaultFactory) {\n        require(_vaultFactory != address(0x0), 'vault-factory-is-0');\n        vaultFactory = _vaultFactory;\n        emit VaultFactoryUpdated(_vaultFactory);\n    }\n\n    /**\n     * @notice Calculate total collateral value for a specific auction.\n     * @param _auctionId The ID of the auction.\n     * @return Total collateral value.\n     */\n    function getTotalCollateralValue(\n        uint256 _auctionId\n    ) public view returns (uint256) {\n        auctionData memory _auction = auctions[_auctionId];\n        ITokenPriceFeed _priceFeed = ITokenPriceFeed(\n            IVaultFactory(vaultFactory).priceFeed()\n        );\n        uint256 _totalCollateralValue = 0;\n        for (uint256 i = 0; i < _auction.collateralsLength; i++) {\n            uint256 _price = _priceFeed.tokenPrice(_auction.collateral[i]);\n            uint256 _normalizedCollateralAmount = _auction.collateralAmount[i] *\n                (10 ** (18 - _priceFeed.decimals(_auction.collateral[i])));\n            uint256 _collateralValue = (_normalizedCollateralAmount * _price) /\n                DECIMAL_PRECISION;\n            _totalCollateralValue += _collateralValue;\n        }\n        return _totalCollateralValue;\n    }\n\n    /**\n     * @dev Creates a new auction to liquidate underwater debt against collaterals.\n     * Accessible only by the liquidation router.\n     * @notice Allows the liquidation router to initiate a new auction for the collateralized debt.\n     */\n    function newAuction() external {\n        ILiquidationRouter liquidationRouter = ILiquidationRouter(\n            IVaultFactory(vaultFactory).liquidationRouter()\n        );\n        require(msg.sender == address(liquidationRouter), 'not-allowed');\n\n        uint256 _debtToAuction = liquidationRouter.underWaterDebt();\n        require(_debtToAuction > 0, 'no-debt-to-auction');\n\n        address[] memory _collaterals = liquidationRouter.collaterals();\n        uint256[] memory _collateralAmounts = new uint256[](\n            _collaterals.length\n        );\n        uint256 _collateralsLength = _collaterals.length;\n        require(_collateralsLength > 0, 'no-collaterals');\n\n        uint256 _totalCollateralValue = 0;\n\n        ITokenPriceFeed _priceFeed = ITokenPriceFeed(\n            IVaultFactory(vaultFactory).priceFeed()\n        );\n\n        for (uint256 i = 0; i < _collateralsLength; i++) {\n            IERC20 collateralToken = IERC20(_collaterals[i]);\n            uint256 _collateralAmount = liquidationRouter.collateral(\n                _collaterals[i]\n            );\n            collateralToken.safeTransferFrom(\n                address(liquidationRouter),\n                address(this),\n                _collateralAmount\n            );\n            _collateralAmounts[i] = _collateralAmount;\n\n            uint256 _price = _priceFeed.tokenPrice(address(collateralToken));\n            uint256 _normalizedCollateralAmount = _collateralAmount *\n                (10 ** (18 - _priceFeed.decimals(address(collateralToken))));\n            uint256 _collateralValue = (_normalizedCollateralAmount * _price) /\n                DECIMAL_PRECISION;\n            _totalCollateralValue += _collateralValue;\n        }\n\n        uint256 _auctionStartTime = block.timestamp;\n        uint256 _auctionEndTime = _auctionStartTime + auctionDuration;\n\n        uint256 _lowestDebtToAuction = (_totalCollateralValue * lowestHF) /\n            DECIMAL_PRECISION;\n        uint256 _highestDebtToAuction = _debtToAuction;\n\n        if (_highestDebtToAuction < _lowestDebtToAuction) {\n            uint256 _debtToAuctionTmp = _lowestDebtToAuction;\n            _lowestDebtToAuction = _highestDebtToAuction;\n            _highestDebtToAuction = _debtToAuctionTmp;\n        }\n\n        auctions.push(\n            auctionData({\n                originalDebt: _debtToAuction,\n                lowestDebtToAuction: _lowestDebtToAuction,\n                highestDebtToAuction: _highestDebtToAuction,\n                collateralsLength: _collateralsLength,\n                collateral: _collaterals,\n                collateralAmount: _collateralAmounts,\n                auctionStartTime: _auctionStartTime,\n                auctionEndTime: _auctionEndTime,\n                auctionEnded: false\n            })\n        );\n\n        emit AuctionCreated(\n            auctions.length - 1,\n            _debtToAuction,\n            _lowestDebtToAuction,\n            _highestDebtToAuction,\n            _collateralsLength,\n            _collaterals,\n            _collateralAmounts,\n            _auctionStartTime,\n            _auctionEndTime\n        );\n    }\n\n    /**\n     * @dev Get auction bid information.\n     * @param _auctionId The ID of the auction.\n     * @return _totalCollateralValue Total collateral value.\n     * @return _debtToAuctionAtCurrentTime Debt to auction at the current time.\n     */\n    function bidInfo(\n        uint256 _auctionId\n    )\n        external\n        view\n        returns (\n            uint256 _totalCollateralValue,\n            uint256 _debtToAuctionAtCurrentTime\n        )\n    {\n        auctionData memory _auction = auctions[_auctionId];\n        require(\n            !_auction.auctionEnded &&\n                block.timestamp <= _auction.auctionEndTime,\n            'auction-ended'\n        );\n\n        _totalCollateralValue = getTotalCollateralValue(_auctionId);\n        uint256 _highestDebtToAuction = _auction.highestDebtToAuction;\n        uint256 _lowestDebtToAuction = _auction.lowestDebtToAuction;\n        // decrease _debtToAuction linearly to _lowestDebtToAuction over the auction duration\n        _debtToAuctionAtCurrentTime =\n            _highestDebtToAuction -\n            ((_highestDebtToAuction - _lowestDebtToAuction) *\n                (block.timestamp - _auction.auctionStartTime)) /\n            auctionDuration;\n    }\n\n    /**\n     * @dev Transfer collateral to the last resort liquidation contract.\n     * @param _auctionId The ID of the auction.\n     */\n    function _transferToLastResortLiquidation(uint256 _auctionId) internal {\n        ILiquidationRouter _liquidationRouter = ILiquidationRouter(\n            IVaultFactory(vaultFactory).liquidationRouter()\n        );\n        ILastResortLiquidation _lastResortLiquidation = ILastResortLiquidation(\n            _liquidationRouter.lastResortLiquidation()\n        );\n\n        auctionData memory _auction = auctions[_auctionId];\n        uint256 _collateralsLength = _auction.collateralsLength;\n        address[] memory _collaterals = _auction.collateral;\n        uint256[] memory _collateralAmounts = _auction.collateralAmount;\n        uint256 _badDebt = _auction.originalDebt;\n\n        _lastResortLiquidation.addBadDebt(_badDebt);\n        for (uint256 i = 0; i < _collateralsLength; i++) {\n            IERC20 collateralToken = IERC20(_collaterals[i]);\n            collateralToken.safeApprove(address(_lastResortLiquidation), 0);\n            collateralToken.safeApprove(\n                address(_lastResortLiquidation),\n                _collateralAmounts[i]\n            );\n            _lastResortLiquidation.addCollateral(\n                address(collateralToken),\n                _collateralAmounts[i]\n            );\n        }\n    }\n\n    /**\n     * @dev Sends a bid from the caller to the auction for a specific auction ID.\n     * @param _auctionId The ID of the auction.\n     * @notice Allows a bidder to participate in the auction by placing a bid.\n     * If the auction period is over or has been manually ended, it transfers the bid to the last resort liquidation.\n     */\n    function bid(uint256 _auctionId) external nonReentrant {\n        auctionData memory _auction = auctions[_auctionId];\n        require(!_auction.auctionEnded, 'auction-ended');\n\n        if (block.timestamp > _auction.auctionEndTime) {\n            // auction ended\n            auctions[_auctionId].auctionEnded = true;\n            _transferToLastResortLiquidation(_auctionId);\n            emit AuctionEnded(_auctionId);\n            return;\n        }\n\n        uint256 _totalCollateralValue = getTotalCollateralValue(_auctionId);\n        uint256 _highestDebtToAuction = _auction.highestDebtToAuction;\n        uint256 _lowestDebtToAuction = _auction.lowestDebtToAuction;\n        // decrease _debtToAuction linearly to _lowestDebtToAuction over the auction duration\n        uint256 _debtToAuctionAtCurrentTime = _highestDebtToAuction -\n            ((_highestDebtToAuction - _lowestDebtToAuction) *\n                (block.timestamp - _auction.auctionStartTime)) /\n            auctionDuration;\n\n        IMintableToken _stable = IMintableToken(\n            IVaultFactory(vaultFactory).stable()\n        );\n        _stable.safeTransferFrom(\n            msg.sender,\n            address(this),\n            _debtToAuctionAtCurrentTime\n        );\n        _stable.burn(_debtToAuctionAtCurrentTime);\n\n        uint256 _collateralsLength = _auction.collateralsLength;\n\n        for (uint256 i = 0; i < _collateralsLength; i++) {\n            IERC20 collateralToken = IERC20(_auction.collateral[i]);\n            collateralToken.safeTransfer(\n                msg.sender,\n                _auction.collateralAmount[i]\n            );\n        }\n\n        auctions[_auctionId].auctionEnded = true;\n        emit AuctionWon(\n            _auctionId,\n            msg.sender,\n            _debtToAuctionAtCurrentTime,\n            _totalCollateralValue\n        );\n    }\n}\n\n\n// File: contracts/interfaces/IAuctionManager.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IAuctionManager {\n    function newAuction() external;\n}\n\n\n// File: contracts/interfaces/IBONQStaking.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '../utils/constants.sol';\n\ninterface IBONQStaking {\n    /* view */\n    function totalStake() external view returns (uint256);\n\n    function getRewardsTotal() external view returns (uint256);\n\n    function getUnpaidStableCoinGain(\n        address _user\n    ) external view returns (uint256);\n\n    /* state changes*/\n    function stake(uint256 _amount) external;\n\n    function unstake(uint256 _amount) external;\n\n    function redeemReward(\n        uint256 _amount,\n        address _troveAddress,\n        address _newNextTrove\n    ) external;\n}\n\n\n// File: contracts/interfaces/IExternalPriceFeed.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IExternalPriceFeed {\n    function token() external view returns (address);\n\n    function price() external view returns (uint256);\n\n    function pricePoint() external view returns (uint256);\n\n    function setPrice(uint256 _price) external;\n}\n\n\n// File: contracts/interfaces/IFeeRecipient.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IFeeRecipient {\n    function baseRate() external view returns (uint256);\n\n    function getBorrowingFee(uint256 _amount) external view returns (uint256);\n\n    function calcDecayedBaseRate(\n        uint256 _currentBaseRate\n    ) external view returns (uint256);\n\n    /**\n     @dev is called to make the FeeRecipient contract transfer the fees to itself. It will use transferFrom to get the\n     fees from the msg.sender\n     @param _amount the amount in Wei of fees to transfer\n     */\n    function takeFees(uint256 _amount) external returns (bool);\n\n    function increaseBaseRate(uint256 _increase) external returns (uint256);\n}\n\n\n// File: contracts/interfaces/ILastResortLiquidation.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ILastResortLiquidation {\n    function addCollateral(address _collateral, uint256 _amount) external;\n    function addBadDebt(uint256 _amount) external;\n}\n\n\n// File: contracts/interfaces/ILiquidationRouter.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface ILiquidationRouter {\n    function addSeizedCollateral(address _collateral, uint256 _amount) external;\n\n    function addUnderWaterDebt(address _vault, uint256 _amount) external;\n\n    function removeUnderWaterDebt(uint256 _amount) external;\n\n    function underWaterDebt() external view returns (uint256);\n\n    function collaterals() external view returns (address[] memory);\n\n    function collateral(address _collateral) external view returns (uint256);\n\n    function tryLiquidate() external;\n\n    function stabilityPool() external view returns (address);\n    function auctionManager() external view returns (address);\n    function lastResortLiquidation() external view returns (address);\n    function distributeBadDebt(address _vault, uint256 _amount) external;\n    function transferOwnership(address newOwner) external;\n}\n\n\n// File: contracts/interfaces/IMintableToken.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/token/ERC20/IERC20.sol';\nimport './IOwnable.sol';\n\ninterface IMintableToken is IERC20, IOwnable {\n    function mint(address recipient, uint256 amount) external;\n\n    function burn(uint256 amount) external;\n\n    function name() external view returns (string memory);\n\n    function symbol() external view returns (string memory);\n\n    function approve(\n        address spender,\n        uint256 amount\n    ) external override returns (bool);\n}\n\n\n// File: contracts/interfaces/IMintableTokenOwner.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/token/ERC20/IERC20.sol';\nimport './IOwnable.sol';\nimport './IMintableToken.sol';\n\ninterface IMintableTokenOwner is IOwnable {\n    function token() external view returns (IMintableToken);\n\n    function mint(address _recipient, uint256 _amount) external;\n\n    function transferTokenOwnership(address _newOwner) external;\n\n    function addMinter(address _newMinter) external;\n\n    function revokeMinter(address _minter) external;\n}\n\n\n// File: contracts/interfaces/IOwnable.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IOwnable {\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() external view returns (address);\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) external;\n}\n\n\n// File: contracts/interfaces/IPriceFeed.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IPriceFeed {\n    function token() external view returns (address);\n\n    function price() external view returns (uint256);\n\n    function pricePoint() external view returns (uint256);\n\n    function emitPriceSignal() external;\n\n    event PriceUpdate(address token, uint256 price, uint256 average);\n}\n\n\n// File: contracts/interfaces/IRouter.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IRouter {\n    function swapExactTokensForTokens(\n        uint256 amountIn,\n        uint256 amountOutMin,\n        address[] memory path,\n        address to,\n        uint256 deadline\n    ) external;\n\n    function getAmountOut(\n        uint256 amountIn,\n        address token0,\n        address token1\n    ) external view returns (uint256 amountOut);\n}\n\n\n// File: contracts/interfaces/IStabilityPool.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IStabilityPool {\n    function liquidate() external;\n\n    function totalDeposit() external view returns (uint256);\n\n    function deposit(uint256 _amount) external;\n\n    function withdraw(uint256 _amount) external;\n\n    function a3aToken() external view returns (address);\n}\n\n\n// File: contracts/interfaces/ITokenPriceFeed.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport './IOwnable.sol';\n\ninterface ITokenPriceFeed is IOwnable {\n    struct TokenInfo {\n        address priceFeed;\n        uint256 mcr; // Minimum Collateralization Ratio\n        uint256 mlr; // Minimum Liquidation Ratio\n        uint256 borrowRate;\n        uint256 decimals;\n    }\n\n    function tokenPriceFeed(address) external view returns (address);\n\n    function tokenPrice(address _token) external view returns (uint256);\n\n    function mcr(address _token) external view returns (uint256);\n\n    function decimals(address _token) external view returns (uint256);\n\n    function mlr(address _token) external view returns (uint256);\n\n    function borrowRate(address _token) external view returns (uint256);\n\n    function setTokenPriceFeed(\n        address _token,\n        address _priceFeed,\n        uint256 _mcr,\n        uint256 _mlr,\n        uint256 _borrowRate,\n        uint256 /* _decimals */\n    ) external;\n\n    event NewTokenPriceFeed(\n        address _token,\n        address _priceFeed,\n        string _name,\n        string _symbol,\n        uint256 _mcr,\n        uint256 _mlr,\n        uint256 _borrowRate,\n        uint256 _decimals\n    );\n}\n\n\n// File: contracts/interfaces/IVault.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IVault {\n    function vaultOwner() external view returns (address);\n    function debt() external view returns (uint256);\n    function transferVaultOwnership(address _newOwner) external;\n    function setName(string memory _name) external;\n    function containsCollateral(\n        address _collateral\n    ) external view returns (bool);\n    function collateralsLength() external view returns (uint256);\n    function collateralAt(uint256 _index) external view returns (address);\n    function collaterals() external view returns (address[] memory);\n    function collateral(address _collateral) external view returns (uint256);\n    function factory() external view returns (address);\n    function addCollateral(address _collateral, uint256 _amount) external;\n    function removeCollateral(\n        address _collateral,\n        uint256 _amount,\n        address _to\n    ) external;\n    function addBadDebt(uint256 _amount) external;\n    function borrowable()\n        external\n        view\n        returns (uint256 _maxBorrowable, uint256 _borrowable);\n    function borrow(uint256 _amount) external;\n    function repay(uint256 _amount) external;\n    function calcRedeem(\n        address _collateral,\n        uint256 _collateralAmount\n    )\n        external\n        view\n        returns (uint256 _stableAmountNeeded, uint256 _redemptionFee);\n    function redeem(\n        address _collateral,\n        uint256 _collateralAmount\n    ) external returns (uint256 _debtRepaid, uint256 _feeCollected);\n    function healthFactor(\n        bool _useMlr\n    ) external view returns (uint256 _healthFactor);\n    function newHealthFactor(\n        uint256 _newDebt,\n        bool _useMlr\n    ) external view returns (uint256 _newHealthFactor);\n    function borrowableWithDiff(\n        address _collateral,\n        uint256 _diffAmount,\n        bool _isAdd,\n        bool _useMlr\n    ) external view returns (uint256 _maxBorrowable, uint256 _borrowable);\n    function liquidate() external returns (uint256 _forgivenDebt);\n}\n\n\n// File: contracts/interfaces/IVaultBorrowRate.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IVaultBorrowRate {\n    function getBorrowRate(\n        address _vaultAddress\n    ) external view returns (uint256);\n}\n\n\n// File: contracts/interfaces/IVaultDeployer.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IVaultDeployer {\n    function deployVault(\n        address _factory,\n        address _vaultOwner,\n        string memory _name\n    ) external returns (address);\n}\n\n\n// File: contracts/interfaces/IVaultExtraSettings.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IVaultExtraSettings {\n    function setMaxRedeemablePercentage(\n        uint256 _debtTreshold,\n        uint256 _maxRedeemablePercentage\n    ) external;\n    function setRedemptionKickback(uint256 _redemptionKickback) external;\n\n    function getExtraSettings()\n        external\n        view\n        returns (\n            uint256 _debtTreshold,\n            uint256 _maxRedeemablePercentage,\n            uint256 _redemptionKickback\n        );\n}\n\n\n// File: contracts/interfaces/IVaultFactory.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IVaultFactory {\n    event NewVault(address indexed vault, string name, address indexed owner);\n    event PriceFeedUpdated(address indexed priceFeed);\n\n    function setPriceFeed(address _priceFeed) external;\n    function vaultCount() external view returns (uint256);\n    function lastVault() external view returns (address);\n    function firstVault() external view returns (address);\n    function nextVault(address _vault) external view returns (address);\n    function prevVault(address _vault) external view returns (address);\n    function liquidationRouter() external view returns (address);\n    function MAX_TOKENS_PER_VAULT() external view returns (uint256);\n    function priceFeed() external view returns (address);\n    function transferVaultOwnership(address _vault, address _newOwner) external;\n    function createVault(string memory _name) external returns (address);\n    function addCollateralNative(address _vault) external payable;\n    function removeCollateralNative(\n        address _vault,\n        uint256 _amount,\n        address _to\n    ) external;\n    function addCollateral(\n        address _vault,\n        address _collateral,\n        uint256 _amount\n    ) external;\n    function removeCollateral(\n        address _vault,\n        address _collateral,\n        uint256 _amount,\n        address _to\n    ) external;\n    function borrow(address _vault, uint256 _amount, address _to) external;\n    function distributeBadDebt(address _vault, uint256 _amount) external;\n    function closeVault(address _vault) external;\n    function repay(address _vault, uint256 _amount) external;\n    function redeem(\n        address _vault,\n        address _collateral,\n        uint256 _collateralAmount,\n        address _to\n    ) external;\n    function liquidate(address _vault) external;\n    function isLiquidatable(address _vault) external view returns (bool);\n    function isReedemable(\n        address _vault,\n        address _collateral\n    ) external view returns (bool);\n    function containsVault(address _vault) external view returns (bool);\n    function stable() external view returns (address);\n    function isCollateralSupported(\n        address _collateral\n    ) external view returns (bool);\n    function vaultsByOwnerLength(\n        address _owner\n    ) external view returns (uint256);\n    function redemptionHealthFactorLimit() external view returns (uint256);\n}\n\n\n// File: contracts/interfaces/IVaultFactoryConfig.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IVaultFactoryConfig {\n    event PriceFeedUpdated(address indexed priceFeed);\n    event MaxTokensPerVaultUpdated(\n        uint256 oldMaxTokensPerVault,\n        uint256 newMaxTokensPerVault\n    );\n    event RedemptionRateUpdated(\n        uint256 oldRedemptionRate,\n        uint256 newRedemptionRate\n    );\n    event BorrowRateUpdated(uint256 oldBorrowRate, uint256 newBorrowRate);\n    event RedemptionHealthFactorLimitUpdated(\n        uint256 oldRedemptionHealthFactorLimit,\n        uint256 newRedemptionHealthFactorLimit\n    );\n\n    function setMaxTokensPerVault(uint256 _maxTokensPerVault) external;\n    function setPriceFeed(address _priceFeed) external;\n    function setRedemptionRate(uint256 _redemptionRate) external;\n    function setBorrowRate(uint256 _borrowRate) external;\n    function setRedemptionHealthFactorLimit(\n        uint256 _redemptionHealthFactorLimit\n    ) external;\n    function setBorrowFeeRecipient(address _borrowFeeRecipient) external;\n    function setRedemptionFeeRecipient(\n        address _redemptionFeeRecipient\n    ) external;\n\n    function priceFeed() external view returns (address);\n    function MAX_TOKENS_PER_VAULT() external view returns (uint256);\n    function redemptionRate() external view returns (uint256);\n    function borrowRate() external view returns (uint256);\n    function redemptionHealthFactorLimit() external view returns (uint256);\n    function borrowFeeRecipient() external view returns (address);\n    function redemptionFeeRecipient() external view returns (address);\n}\n\n\n// File: contracts/interfaces/IWETH.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\ninterface IWETH {\n    function deposit() external payable;\n\n    function approve(address, uint256) external returns (bool);\n\n    function transfer(address _to, uint256 _value) external returns (bool);\n\n    function withdraw(uint256) external;\n}\n\n\n// File: contracts/LastResortLiquidation.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/access/Ownable.sol';\nimport '@openzeppelin/contracts/token/ERC20/IERC20.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\nimport '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';\nimport '@openzeppelin/contracts/security/ReentrancyGuard.sol';\n\nimport './interfaces/IVault.sol';\nimport './interfaces/IVaultFactory.sol';\nimport './interfaces/IStabilityPool.sol';\nimport './interfaces/IAuctionManager.sol';\nimport './interfaces/IMintableToken.sol';\nimport './interfaces/ILiquidationRouter.sol';\n\n/**\n * @title LastResortLiquidation\n * @dev Contract to manage collateral and bad debt distribution for liquidation.\n */\ncontract LastResortLiquidation is Ownable, ReentrancyGuard {\n    event VaultFactoryUpdated(address indexed _vaultFactory);\n\n    using EnumerableSet for EnumerableSet.AddressSet;\n    using SafeERC20 for IERC20;\n    using SafeERC20 for IMintableToken;\n\n    EnumerableSet.AddressSet private collateralSet;\n    EnumerableSet.AddressSet private allowedSet;\n\n    address public vaultFactory;\n\n    mapping(address => uint256) public collateral;\n\n    uint256 public badDebt;\n\n    modifier onlyAllowed() {\n        require(allowedSet.contains(msg.sender), 'not-allowed');\n        _;\n    }\n\n    /**\n     * @dev Adds an address to the allowed set.\n     * @param _allowed The address to add to the allowed set.\n     */\n    function addAllowed(address _allowed) external onlyOwner {\n        require(_allowed != address(0x0), 'allowed-is-0');\n        allowedSet.add(_allowed);\n    }\n\n    /**\n     * @dev Removes an address from the allowed set.\n     * @param _allowed The address to remove from the allowed set.\n     */\n    function removeAllowed(address _allowed) external onlyOwner {\n        require(_allowed != address(0x0), 'allowed-is-0');\n        allowedSet.remove(_allowed);\n    }\n\n    /**\n     * @dev Gets the number of addresses in the allowed set.\n     * @return The number of addresses in the allowed set.\n     */\n    function allowedLength() external view returns (uint256) {\n        return allowedSet.length();\n    }\n\n    /**\n     * @dev Gets the address at the specified index in the allowed set.\n     * @param _index The index of the address.\n     * @return The address at the specified index in the allowed set.\n     */\n    function allowedAt(uint256 _index) external view returns (address) {\n        return allowedSet.at(_index);\n    }\n\n    /**\n     * @dev Gets the number of addresses in the collateral set.\n     * @return The number of addresses in the collateral set.\n     */\n    function collateralLength() external view returns (uint256) {\n        return collateralSet.length();\n    }\n\n    /**\n     * @dev Gets the address at the specified index in the collateral set.\n     * @param _index The index of the address.\n     * @return The address at the specified index in the collateral set.\n     */\n    function collateralAt(uint256 _index) external view returns (address) {\n        return collateralSet.at(_index);\n    }\n\n    /**\n     * @dev Sets the address of the vault factory.\n     * @param _vaultFactory Address of the vault factory.\n     */\n    function setVaultFactory(address _vaultFactory) external onlyOwner {\n        require(_vaultFactory != address(0x0), 'vault-factory-is-0');\n        vaultFactory = _vaultFactory;\n        emit VaultFactoryUpdated(_vaultFactory);\n    }\n\n    /**\n     * @dev Adds collateral to the contract and updates the collateral balance.\n     * @param _collateral The address of the collateral token.\n     * @param _amount The amount of collateral to add.\n     */\n    function addCollateral(\n        address _collateral,\n        uint256 _amount\n    ) external onlyAllowed {\n        require(_collateral != address(0x0), 'collateral-is-0');\n        require(_amount > 0, 'amount-is-0');\n\n        collateralSet.add(_collateral);\n        IERC20(_collateral).safeTransferFrom(\n            msg.sender,\n            address(this),\n            _amount\n        );\n\n        collateral[_collateral] += _amount;\n    }\n\n    /**\n     * @dev Withdraws collateral from the contract.\n     * @param _collateral The address of the collateral token.\n     * @param _amount The amount of collateral to withdraw.\n     * @param _to The address to receive the withdrawn collateral.\n     */\n    function withdrawCollateral(\n        address _collateral,\n        uint256 _amount,\n        address _to\n    ) external onlyOwner {\n        require(_collateral != address(0x0), 'collateral-is-0');\n        require(_amount > 0, 'amount-is-0');\n\n        collateral[_collateral] -= _amount;\n\n        if (collateral[_collateral] == 0) collateralSet.remove(_collateral);\n\n        IERC20(_collateral).safeTransfer(_to, _amount);\n    }\n\n    /**\n     * @dev Adds bad debt to the contract.\n     * @param _amount The amount of bad debt to add.\n     */\n    function addBadDebt(uint256 _amount) external onlyAllowed {\n        require(_amount > 0, 'amount-is-0');\n        badDebt += _amount;\n    }\n\n    /**\n     * @dev Repays bad debt by burning stable tokens.\n     * @param _amount The amount of stable tokens to burn.\n     */\n    function repayBadDebt(uint256 _amount) external onlyOwner {\n        require(_amount > 0, 'amount-is-0');\n        require(_amount <= badDebt, 'amount-too-high');\n\n        IMintableToken _stable = IMintableToken(\n            IVaultFactory(vaultFactory).stable()\n        );\n        _stable.safeTransferFrom(msg.sender, address(this), _amount);\n        _stable.burn(_amount);\n\n        badDebt -= _amount;\n    }\n\n    /**\n     * @dev Distributes bad debt to a specific vault.\n     * @param _vault The address of the vault to receive the bad debt.\n     * @param _amount The amount of bad debt to distribute.\n     */\n    function distributeBadDebt(\n        address _vault,\n        uint256 _amount\n    ) external onlyOwner {\n        require(_vault != address(0x0), 'vault-is-0');\n        require(_amount > 0, 'amount-is-0');\n        require(_amount <= badDebt, 'amount-too-high');\n        badDebt -= _amount;\n        IVaultFactory _vaultFactory = IVaultFactory(vaultFactory);\n        ILiquidationRouter _liquidationRouter = ILiquidationRouter(\n            _vaultFactory.liquidationRouter()\n        );\n        _liquidationRouter.distributeBadDebt(_vault, _amount);\n    }\n}\n\n\n// File: contracts/LiquidationRouter.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/access/Ownable.sol';\nimport '@openzeppelin/contracts/token/ERC20/IERC20.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\nimport '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';\nimport '@openzeppelin/contracts/security/ReentrancyGuard.sol';\n\nimport './interfaces/IVault.sol';\nimport './interfaces/IVaultFactory.sol';\nimport './interfaces/IStabilityPool.sol';\nimport './interfaces/IAuctionManager.sol';\n\n/**\n * @title LiquidationRouter\n * @dev Handles liquidation and redistribution of collaterals and debts in the system.\n */\ncontract LiquidationRouter is Ownable, ReentrancyGuard {\n    using EnumerableSet for EnumerableSet.AddressSet;\n\n    using SafeERC20 for IERC20;\n    event SeizedCollateralAdded(\n        address indexed collateral,\n        address indexed _vaultFactory,\n        address indexed _vault,\n        uint256 amount\n    );\n    event UnderWaterDebtAdded(\n        address indexed _vaultFactory,\n        address indexed _vault,\n        uint256 debtAmount\n    );\n    event UnderWaterDebtRemoved(\n        address indexed _vaultFactory,\n        uint256 debtAmount\n    );\n    event VaultFactoryUpdated(address indexed _vaultFactory);\n    event StabilityPoolUpdated(address indexed _stabilityPool);\n    event AuctionManagerUpdated(address indexed _auctionManager);\n    event LastResortLiquidationUpdated(address indexed _lastResortLiquidation);\n    event BadDebtDistributed(address indexed _vault, uint256 amount);\n\n    uint256 public underWaterDebt;\n\n    address public vaultFactory;\n    address public stabilityPool;\n    address public auctionManager;\n    address public lastResortLiquidation;\n\n    EnumerableSet.AddressSet private collateralSet;\n\n    mapping(address => uint256) public collateral;\n\n    /**\n     * @dev Sets the last resort liquidation contract address.\n     * @param _lastResortLiquidation Address of the last resort liquidation contract.\n     */\n    function setLastResortLiquidation(\n        address _lastResortLiquidation\n    ) external onlyOwner {\n        require(\n            _lastResortLiquidation != address(0x0),\n            'last-resort-liquidation-is-0'\n        );\n        lastResortLiquidation = _lastResortLiquidation;\n        emit LastResortLiquidationUpdated(_lastResortLiquidation);\n    }\n\n    /**\n     * @dev Sets the stability pool contract address.\n     * @param _stabilityPool Address of the stability pool contract.\n     */\n    function setStabilityPool(address _stabilityPool) external onlyOwner {\n        require(_stabilityPool != address(0x0), 'stability-pool-is-0');\n        stabilityPool = _stabilityPool;\n        emit StabilityPoolUpdated(_stabilityPool);\n    }\n\n    /**\n     * @dev Sets the auction manager contract address.\n     * @param _auctionManager Address of the auction manager contract.\n     */\n    function setAuctionManager(address _auctionManager) external onlyOwner {\n        require(_auctionManager != address(0x0), 'auction-manager-is-0');\n        auctionManager = _auctionManager;\n        emit AuctionManagerUpdated(_auctionManager);\n    }\n\n    modifier onlyVault() {\n        require(\n            IVaultFactory(vaultFactory).containsVault(msg.sender),\n            'not-a-vault'\n        );\n        _;\n    }\n\n    modifier onlyAllowed() {\n        require(msg.sender == stabilityPool, 'not-allowed');\n        _;\n    }\n\n    modifier onlyLastResortLiquidation() {\n        require(\n            msg.sender == lastResortLiquidation,\n            'not-last-resort-liquidation'\n        );\n        _;\n    }\n\n    /**\n     * @dev Checks if a specific collateral token is registered.\n     * @param _collateral Address of the collateral token to check.\n     * @return bool indicating the presence of the collateral token.\n     */\n    function containsCollateral(\n        address _collateral\n    ) external view returns (bool) {\n        return collateralSet.contains(_collateral);\n    }\n\n    /**\n     * @dev Returns the count of registered collateral tokens.\n     * @return uint256 representing the count of collateral tokens.\n     */\n    function collateralsLength() external view returns (uint256) {\n        return collateralSet.length();\n    }\n\n    /**\n     * @dev Gets the collateral token at a specific index in the list of registered collaterals.\n     * @param _index Index of the collateral token.\n     * @return address representing the collateral token address.\n     */\n    function collateralAt(uint256 _index) external view returns (address) {\n        return collateralSet.at(_index);\n    }\n\n    /**\n     * @dev Gets all the registered collateral tokens.\n     * @return address[] memory representing the list of collateral token addresses.\n     */\n    function collaterals() external view returns (address[] memory) {\n        address[] memory _collaterals = new address[](collateralSet.length());\n        for (uint256 i = 0; i < collateralSet.length(); i++) {\n            _collaterals[i] = collateralSet.at(i);\n        }\n        return _collaterals;\n    }\n\n    /**\n     * @dev Sets the vault factory contract address.\n     * @param _vaultFactory Address of the vault factory contract.\n     */\n    function setVaultFactory(address _vaultFactory) external onlyOwner {\n        require(_vaultFactory != address(0x0), 'vault-factory-is-0');\n        require(\n            IVaultFactory(_vaultFactory).liquidationRouter() == address(this),\n            'wrong-liquidation-router'\n        );\n        vaultFactory = _vaultFactory;\n        emit VaultFactoryUpdated(_vaultFactory);\n    }\n\n    /**\n     * @dev Adds seized collateral to the contract.\n     * @param _collateral Address of the seized collateral.\n     * @param _amount Amount of seized collateral.\n     */\n    function addSeizedCollateral(\n        address _collateral,\n        uint256 _amount\n    ) external onlyVault {\n        IERC20(_collateral).safeTransferFrom(\n            msg.sender,\n            address(this),\n            _amount\n        );\n\n        IERC20(_collateral).safeApprove(stabilityPool, 0);\n        IERC20(_collateral).safeApprove(stabilityPool, _amount);\n\n        IERC20(_collateral).safeApprove(auctionManager, 0);\n        IERC20(_collateral).safeApprove(auctionManager, _amount);\n\n        collateralSet.add(_collateral);\n        collateral[_collateral] += _amount;\n        emit SeizedCollateralAdded(\n            _collateral,\n            vaultFactory,\n            msg.sender,\n            _amount\n        );\n    }\n\n    /**\n     * @dev Adds underwater debt for a vault and increases the total underwater debt for the system.\n     * @param _vault Address of the vault.\n     * @param _amount Amount of underwater debt.\n     */\n    function addUnderWaterDebt(\n        address _vault,\n        uint256 _amount\n    ) external onlyVault {\n        underWaterDebt += _amount;\n        emit UnderWaterDebtAdded(vaultFactory, _vault, _amount);\n    }\n\n    /**\n     * @dev Removes underwater debt from the system and decreases the total underwater debt.\n     * @param _amount Amount of underwater debt to be removed.\n     */\n    function _removeUnderWaterDebt(uint256 _amount) internal {\n        underWaterDebt -= _amount;\n        emit UnderWaterDebtRemoved(vaultFactory, _amount);\n    }\n\n    /**\n     * @dev Withdraws liquidated collateral.\n     * @param _collateral Address of the liquidated collateral.\n     * @param _to Address to receive the liquidated collateral.\n     * @param _amount Amount of liquidated collateral to withdraw.\n     */\n    function withdrawLiquidatedCollateral(\n        address _collateral,\n        address _to,\n        uint256 _amount\n    ) external onlyOwner {\n        IERC20(_collateral).safeTransfer(_to, _amount);\n        collateral[_collateral] -= _amount;\n        if (collateral[_collateral] == 0) {\n            collateralSet.remove(_collateral);\n        }\n    }\n\n    /**\n     * @dev Removes all collaterals from the contract.\n     * This function sets the collateral amount for each collateral token to 0.\n     */\n    function _removeAllCollaterals() internal {\n        uint256 _length = collateralSet.length();\n        for (uint256 i; i < _length; i++) {\n            address _collateral = collateralSet.at(i);\n            collateral[_collateral] = 0;\n        }\n    }\n\n    /**\n     * @dev Initiates liquidation or auction if necessary.\n     */\n    function tryLiquidate() external nonReentrant {\n        require(underWaterDebt > 0, 'no-underwater-debt');\n        uint256 _stabilityPoolDeposit = IStabilityPool(stabilityPool)\n            .totalDeposit();\n        if (_stabilityPoolDeposit >= underWaterDebt) {\n            IStabilityPool(stabilityPool).liquidate();\n        } else {\n            IAuctionManager(auctionManager).newAuction();\n        }\n        _removeAllCollaterals();\n        _removeUnderWaterDebt(underWaterDebt);\n    }\n\n    /**\n     * @dev Distributes bad debt in the system.\n     * @param _vault Address of the vault with bad debt.\n     * @param _amount Amount of bad debt to distribute.\n     */\n    function distributeBadDebt(\n        address _vault,\n        uint256 _amount\n    ) external onlyLastResortLiquidation {\n        IVaultFactory(vaultFactory).distributeBadDebt(_vault, _amount);\n        emit BadDebtDistributed(_vault, _amount);\n    }\n}\n\n\n// File: contracts/MintableToken.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/token/ERC20/ERC20.sol';\nimport '@openzeppelin/contracts/access/Ownable.sol';\n\n/// @title implements minting/burning functionality for owner\ncontract MintableToken is ERC20, Ownable {\n    // solhint-disable-next-line func-visibility\n    constructor(string memory name, string memory symbol) ERC20(name, symbol) {}\n\n    /// @dev mints tokens to the recipient, to be called from owner\n    /// @param recipient address to mint\n    /// @param amount amount to be minted\n    function mint(address recipient, uint256 amount) public onlyOwner {\n        _mint(recipient, amount);\n    }\n\n    /// @dev burns token of specified amount from msg.sender\n    /// @param amount to burn\n    function burn(uint256 amount) public {\n        _burn(msg.sender, amount);\n    }\n}\n\n\n// File: contracts/MintableTokenOwner.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/access/Ownable.sol';\nimport './interfaces/IMintableToken.sol';\n\n/// @title implements owner of the MintableToken contract\ncontract MintableTokenOwner is Ownable {\n    IMintableToken public immutable token;\n    mapping(address => bool) public minters;\n\n    event MinterAdded(address newMinter);\n\n    // solhint-disable-next-line func-visibility\n    constructor(address _token) Ownable() {\n        token = IMintableToken(_token);\n    }\n\n    /// @dev mints tokens to the recipient, to be called from owner\n    /// @param _recipient address to mint\n    /// @param _amount amount to be minted\n    function mint(address _recipient, uint256 _amount) public {\n        require(\n            minters[msg.sender],\n            'MintableTokenOwner:mint: the sender must be in the minters list'\n        );\n        token.mint(_recipient, _amount);\n    }\n\n    function transferTokenOwnership(address _newOwner) public onlyOwner {\n        token.transferOwnership(_newOwner);\n    }\n\n    /// @dev adds new minter\n    /// @param _newMinter address of new minter\n    function addMinter(address _newMinter) public onlyOwner {\n        minters[_newMinter] = true;\n        emit MinterAdded(_newMinter);\n    }\n\n    /// @dev removes minter from minter list\n    /// @param _minter address of the minter\n    function revokeMinter(address _minter) public onlyOwner {\n        minters[_minter] = false;\n    }\n}\n\n\n// File: contracts/oracles/ChainlinkPriceFeed.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol';\nimport '../interfaces/IPriceFeed.sol';\nimport '../interfaces/ITokenPriceFeed.sol';\nimport '../utils/constants.sol';\n\n/**\n * @title ChainlinkPriceFeed\n * @dev Retrieves and manages price data from Chainlink's Oracle for specified tokens.\n */\ncontract ChainlinkPriceFeed is IPriceFeed, Constants {\n    AggregatorV2V3Interface public immutable oracle;\n    address public immutable override token;\n    uint256 public immutable precision;\n    uint256 public updateThreshold = 24 hours;\n\n    /**\n     * @dev Initializes the Chainlink price feed with the specified oracle and token.\n     * @param _oracle The address of the Chainlink oracle contract.\n     * @param _token The address of the associated token.\n     */\n    constructor(address _oracle, address _token) {\n        require(\n            _oracle != address(0x0),\n            'e2637b _oracle must not be address 0x0'\n        );\n        require(\n            _token != address(0x0),\n            'e2637b _token must not be address 0x0'\n        );\n        token = _token;\n        oracle = AggregatorV2V3Interface(_oracle);\n        uint8 decimals = oracle.decimals();\n        require(decimals > 0, 'e2637b decimals must be a positive number');\n        precision = 10 ** decimals;\n    }\n\n    /**\n     * @dev Retrieves the current price from the Chainlink oracle, ensuring it is not outdated.\n     * @return The latest recorded price of the associated token.\n     */\n    function price() public view virtual override returns (uint256) {\n        (, int256 _price, , uint256 _timestamp, ) = oracle.latestRoundData();\n        require(_price > 0, 'e2637b _price must be a positive number');\n        require(\n            block.timestamp - _timestamp <= updateThreshold,\n            'price-outdated'\n        );\n        return (uint256(_price) * DECIMAL_PRECISION) / precision;\n    }\n\n    /**\n     * @dev Retrieves the current price point.\n     * @return The current price of the associated token.\n     */\n    function pricePoint() public view override returns (uint256) {\n        return price();\n    }\n\n    /**\n     * @dev Emits a price update signal for the associated token.\n     */\n    function emitPriceSignal() public override {\n        emit PriceUpdate(token, price(), price());\n    }\n}\n\n\n// File: contracts/oracles/ConvertedPriceFeed.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport './ChainlinkPriceFeed.sol';\n\n/**\n * @title ConvertedPriceFeed\n * @dev Manages a price feed by converting between two different price feeds and emitting price signals.\n */\ncontract ConvertedPriceFeed is IPriceFeed, Constants {\n    IPriceFeed public immutable priceFeed;\n    IPriceFeed public immutable conversionPriceFeed;\n    address public immutable override token;\n\n    /**\n     * @dev Constructor sets up the price feeds and associated tokens for conversion.\n     * @param _priceFeed The primary price feed address.\n     * @param _conversionPriceFeed The conversion price feed address.\n     * @param _token The token address associated with the price feed.\n     */\n    constructor(\n        address _priceFeed,\n        address _conversionPriceFeed,\n        address _token\n    ) {\n        require(\n            _priceFeed != address(0x0),\n            'e2637b _priceFeed must not be address 0x0'\n        );\n        require(\n            _conversionPriceFeed != address(0x0),\n            'e2637b _conversionPriceFeed must not be address 0x0'\n        );\n        priceFeed = IPriceFeed(_priceFeed);\n        conversionPriceFeed = IPriceFeed(_conversionPriceFeed);\n        token = _token;\n    }\n\n    /**\n     * @dev Retrieves the converted price by multiplying the primary price with the conversion price.\n     */\n    function price() public view override returns (uint256) {\n        return\n            (priceFeed.price() * DECIMAL_PRECISION) /\n            conversionPriceFeed.price();\n    }\n\n    /**\n     * @dev Retrieves the current price point by calling the 'price()' function.\n     */\n    function pricePoint() public view override returns (uint256) {\n        return price();\n    }\n\n    /**\n     * @dev Emits a price signal using the converted price.\n     */\n    function emitPriceSignal() public {\n        emit PriceUpdate(token, price(), price());\n    }\n}\n\n\n// File: contracts/oracles/FixedPriceOracle.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport './ChainlinkPriceFeed.sol';\n\ncontract FixedPriceOracle is IPriceFeed, Constants {\n    IPriceFeed public immutable priceFeed = IPriceFeed(address(0));\n    IPriceFeed public immutable conversionPriceFeed = IPriceFeed(address(0));\n    address public immutable override token;\n    uint256 public fixedPrice;\n    constructor(address _token, uint256 _price) {\n        fixedPrice = _price;\n        token = _token;\n    }\n\n    function price() public view override returns (uint256) {\n        return fixedPrice;\n    }\n\n    function pricePoint() public view override returns (uint256) {\n        return price();\n    }\n\n    function emitPriceSignal() public {\n        emit PriceUpdate(token, price(), price());\n    }\n}\n\n\n// File: contracts/oracles/MockConvertedPriceFeed.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport './ChainlinkPriceFeed.sol';\n\ncontract MockConvertedPriceFeed is IPriceFeed, Constants {\n    IPriceFeed public immutable priceFeed = IPriceFeed(address(0));\n    IPriceFeed public immutable conversionPriceFeed = IPriceFeed(address(0));\n    address public immutable override token;\n\n    address public constant DAI = 0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063;\n    address public constant WETH = 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619;\n    address public constant WMATIC = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270;\n    address public constant QNT = 0x36B77a184bE8ee56f5E81C56727B20647A42e28E;\n    address public constant PAXG = 0x553d3D295e0f695B9228246232eDF400ed3560B5;\n    address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174;\n    address public constant USDT = 0xc2132D05D31c914a87C6611C10748AEb04B58e8F;\n    address public constant LINK = 0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39;\n    address public constant UNI = 0xb33EaAd8d922B1083446DC23f610c2567fB5180f;\n    address public constant SUSHI = 0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a;\n\n    constructor(address _token) {\n        token = _token;\n    }\n\n    function price() public view override returns (uint256) {\n        if (token == DAI) return 931895460772162633;\n        if (token == WETH) return 1775994046278866046632;\n        if (token == WMATIC) return 663421266959892649;\n        if (token == QNT) return 75127544993710105763;\n        if (token == PAXG) return 1828020314028793738060;\n        if (token == USDC) return 935946997491670098;\n        if (token == USDT) return 663421266959892649;\n        if (token == LINK) return 75127544993710105763;\n        if (token == UNI) return 1828020314028793738060;\n        if (token == SUSHI) return 935946997491670098;\n    }\n\n    function pricePoint() public view override returns (uint256) {\n        return price();\n    }\n\n    function emitPriceSignal() public {\n        emit PriceUpdate(token, price(), price());\n    }\n}\n\n\n// File: contracts/OwnerProxy.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/access/Ownable.sol';\nimport '@openzeppelin/contracts/utils/Address.sol';\nimport '@openzeppelin/contracts/utils/Context.sol';\n\n/**\n * @title OwnerProxy\n * @dev Allows the main owner to add fine-grained permissions to other operators.\n */\ncontract OwnerProxy is Context, Ownable {\n    mapping(uint256 => bool) public permissions;\n\n    event PermissionAdded(\n        address indexed caller,\n        address targetAddress,\n        bytes4 targetSignature,\n        uint256 permissionHash\n    );\n    event PermissionRemoved(uint256 indexed permissionHash);\n    event Executed(\n        address indexed caller,\n        address indexed target,\n        string func,\n        bytes data\n    );\n\n    /**\n     * @dev Adds permission for a specific caller to execute a function on a target address.\n     * @param caller The address allowed to call the function.\n     * @param targetAddress The target address where the function will be called.\n     * @param targetSignature The function signature to be executed.\n     */\n    function addPermission(\n        address caller,\n        address targetAddress,\n        bytes4 targetSignature\n    ) public onlyOwner {\n        require(caller != address(0), 'invalid-caller-address');\n        require(targetAddress != address(0), 'invalid-target-address');\n        uint256 _hash = uint256(\n            keccak256(abi.encodePacked(caller, targetAddress, targetSignature))\n        );\n        permissions[_hash] = true;\n        emit PermissionAdded(caller, targetAddress, targetSignature, _hash);\n    }\n\n    /**\n     * @dev Removes a specific permission.\n     * @param permissionHash The hash of the permission to be removed.\n     */\n    function removePermission(uint256 permissionHash) public onlyOwner {\n        delete permissions[permissionHash];\n        emit PermissionRemoved(permissionHash);\n    }\n\n    /**\n     * @dev Executes a function on a target address only if the caller has the required permission.\n     * @param target The contract address where the function will be called.\n     * @param func The name of the function to be executed.\n     * @param data The data to be passed to the function.\n     * @return _result The result of the function execution.\n     */\n    function execute(\n        address target,\n        string memory func,\n        bytes memory data\n    ) public returns (bytes memory _result) {\n        bytes4 _targetSignature = bytes4(keccak256(bytes(func)));\n        uint256 _hash = uint256(\n            keccak256(abi.encodePacked(_msgSender(), target, _targetSignature))\n        );\n        require(permissions[_hash], 'invalid-permission');\n        emit Executed(_msgSender(), target, func, data);\n        _result = Address.functionCall(\n            target,\n            bytes.concat(_targetSignature, data)\n        );\n    }\n}\n\n\n// File: contracts/StabilityPool.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/access/Ownable.sol';\n// import openzeppelin reentrancy guard\nimport '@openzeppelin/contracts/security/ReentrancyGuard.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\nimport './utils/constants.sol';\nimport './interfaces/IVaultFactory.sol';\nimport './interfaces/IMintableToken.sol';\nimport './interfaces/IVault.sol';\nimport './interfaces/ILiquidationRouter.sol';\n\n/**\n * @title StabilityPool\n * @dev A smart contract responsible for liquidating vaults and rewarding depositors with collateral redeemed.\n * @notice is used to liquidate vaults and reward depositors with collateral redeemed\n */\ncontract StabilityPool is Ownable, ReentrancyGuard, Constants {\n    using SafeERC20 for IERC20;\n\n    // A structure defining token addresses and their respective 'Stable' values\n    struct TokenToS {\n        address tokenAddress;\n        uint256 S_value;\n    }\n\n    // A structure defining token addresses and their corresponding uint256 values\n    struct TokenToUint256 {\n        address tokenAddress;\n        uint256 value;\n    }\n\n    // A structure that holds snapshots of token balances, 'P' and 'G', and epoch information\n    struct Snapshots {\n        TokenToS[] tokenToSArray;\n        uint256 P;\n        uint256 G;\n        uint128 scale;\n        uint128 epoch;\n    }\n\n    IVaultFactory public factory;\n    IMintableToken public immutable stableCoin;\n\n    IERC20 public immutable a3aToken;\n\n    // Track total deposits and error offsets\n    uint256 public totalDeposit;\n    mapping(address => uint256) public collateralToLastErrorOffset;\n    uint256 public lastStableCoinLossErrorOffset;\n    mapping(address => uint256) public deposits;\n    mapping(address => Snapshots) public depositSnapshots; // depositor address -> snapshots struct\n\n    // Variables related to A3A rewards and error tracking\n    uint256 public a3aPerMinute;\n    uint256 public totalA3ARewardsLeft;\n    uint256 public latestA3ARewardTime;\n    // Error tracker for the error correction in the A3A redistribution calculation\n    uint256 public lastA3AError;\n    /*  Product 'P': Running product by which to multiply an initial deposit, in order to find the current compounded deposit,\n     * after a series of liquidations have occurred, each of which cancel some StableCoin debt with the deposit.\n     *\n     * During its lifetime, a deposit's value evolves from d_t to d_t * P / P_t , where P_t\n     * is the snapshot of P taken at the instant the deposit was made. 18-digit decimal.\n     */\n    uint256 public P;\n\n    uint256 public constant SCALE_FACTOR = 1e9;\n\n    uint256 public constant SECONDS_IN_ONE_MINUTE = 60;\n\n    // Each time the scale of P shifts by SCALE_FACTOR, the scale is incremented by 1\n    uint128 public currentScale;\n\n    // With each offset that fully empties the Pool, the epoch is incremented by 1\n    uint128 public currentEpoch;\n\n    /* Collateral Gain sum 'S': During its lifetime, each deposit d_t earns an Collateral gain of ( d_t * [S - S_t] )/P_t, where S_t\n     * is the depositor's snapshot of S taken at the time t when the deposit was made.\n     *\n     * The 'S' sums are stored in a nested mapping (epoch => scale => sum):\n     *\n     * - The inner mapping records the sum S at different scales\n     * - The outer mapping records the (scale => sum) mappings, for different epochs.\n     */\n    mapping(uint128 => mapping(uint128 => TokenToS[]))\n        public epochToScaleToTokenToSum;\n\n    /*\n     * Similarly, the sum 'G' is used to calculate A3A gains. During it's lifetime, each deposit d_t earns a A3A gain of\n     *  ( d_t * [G - G_t] )/P_t, where G_t is the depositor's snapshot of G taken at time t when  the deposit was made.\n     *\n     *  A3A reward events occur are triggered by depositor operations (new deposit, topup, withdrawal), and liquidations.\n     *  In each case, the A3A reward is issued (i.e. G is updated), before other state changes are made.\n     */\n    mapping(uint128 => mapping(uint128 => uint256)) public epochToScaleToG;\n\n    event Deposit(address _contributor, uint256 _amount);\n    event TotalDepositUpdated(uint256 _newValue);\n    event Withdraw(address _contributor, uint256 _amount);\n\n    // Events\n    // solhint-disable-next-line event-name-camelcase\n    event A3ARewardRedeemed(address _contributor, uint256 _amount);\n    event A3ARewardIssue(uint256 issuance, uint256 _totalA3ARewardsLeft);\n    event A3APerMinuteUpdated(uint256 _newAmount);\n    event TotalA3ARewardsUpdated(uint256 _newAmount);\n    // solhint-disable-next-line event-name-camelcase\n    event CollateralRewardRedeemed(\n        address _contributor,\n        address _tokenAddress,\n        uint256 _amount\n    );\n    event DepositSnapshotUpdated(\n        address indexed _depositor,\n        uint256 _P,\n        uint256 _G,\n        uint256 _newDepositValue\n    );\n\n    /* solhint-disable event-name-camelcase */\n    event P_Updated(uint256 _P);\n    event S_Updated(\n        address _tokenAddress,\n        uint256 _S,\n        uint128 _epoch,\n        uint128 _scale\n    );\n    event G_Updated(uint256 _G, uint128 _epoch, uint128 _scale);\n    /* solhint-disable event-name-camelcase */\n    event EpochUpdated(uint128 _currentEpoch);\n    event ScaleUpdated(uint128 _currentScale);\n\n    /**\n     * @notice Initializes the StabilityPool contract with the given Vault factory and A3A token addresses.\n     * @dev The constructor sets up essential contract parameters upon deployment.\n     * @param _factory Address of the Vault Factory contract responsible for creating Vault instances.\n     * @param _a3aToken Address of the A3A token to be used within the Vault system.\n     */\n    constructor(address _factory, address _a3aToken) {\n        require(_factory != address(0x0), 'factory-is-0');\n        require(_a3aToken != address(0x0), 'a3a-is-0');\n        factory = IVaultFactory(_factory);\n        stableCoin = IMintableToken(address(IVaultFactory(_factory).stable()));\n        a3aToken = IERC20(_a3aToken);\n        P = DECIMAL_PRECISION;\n    }\n\n    /// @dev to deposit StableCoin into StabilityPool this must be protected against a reentrant attack from the arbitrage\n    /// @param  _amount amount to deposit\n    function deposit(uint256 _amount) public nonReentrant {\n        // address depositor = msg.sender;\n        require(_amount > 0, 'amount-is-0');\n\n        stableCoin.transferFrom(msg.sender, address(this), _amount);\n        uint256 initialDeposit = deposits[msg.sender];\n        _redeemReward();\n\n        Snapshots memory snapshots = depositSnapshots[msg.sender];\n\n        uint256 compoundedDeposit = _getCompoundedDepositFromSnapshots(\n            initialDeposit,\n            snapshots\n        );\n        // uint256 newValue = compoundedDeposit + _amount;\n        uint256 newTotalDeposit = totalDeposit + _amount;\n        totalDeposit = newTotalDeposit;\n\n        _updateDepositAndSnapshots(msg.sender, compoundedDeposit + _amount);\n\n        emit Deposit(msg.sender, _amount);\n        emit TotalDepositUpdated(newTotalDeposit);\n    }\n\n    /// @dev to withdraw StableCoin that was not spent if this function is called in a reentrantway during arbitrage  it\n    /// @dev would skew the token allocation and must be protected against\n    /// @param  _amount amount to withdraw\n    function withdraw(uint256 _amount) public nonReentrant {\n        uint256 contributorDeposit = deposits[msg.sender];\n        require(_amount > 0, 'amount-is-0');\n        require(contributorDeposit > 0, 'deposit-is-0');\n        _redeemReward();\n\n        Snapshots memory snapshots = depositSnapshots[msg.sender];\n\n        uint256 compoundedDeposit = _getCompoundedDepositFromSnapshots(\n            contributorDeposit,\n            snapshots\n        );\n        uint256 calculatedAmount = compoundedDeposit > _amount\n            ? _amount\n            : compoundedDeposit;\n        uint256 newValue = compoundedDeposit - calculatedAmount;\n\n        totalDeposit = totalDeposit - calculatedAmount;\n\n        _updateDepositAndSnapshots(msg.sender, newValue);\n\n        stableCoin.transfer(msg.sender, calculatedAmount);\n        emit Withdraw(msg.sender, calculatedAmount);\n        emit TotalDepositUpdated(totalDeposit);\n    }\n\n    /// @dev to withdraw collateral rewards earned after liquidations\n    /// @dev this function does not provide an opportunity for a reentrancy attack\n    function redeemReward() external {\n        Snapshots memory snapshots = depositSnapshots[msg.sender];\n        uint256 contributorDeposit = deposits[msg.sender];\n\n        uint256 compoundedDeposit = _getCompoundedDepositFromSnapshots(\n            contributorDeposit,\n            snapshots\n        );\n        _redeemReward();\n        _updateDepositAndSnapshots(msg.sender, compoundedDeposit);\n    }\n\n    function setVaultFactory(address _factory) external onlyOwner {\n        require(_factory != address(0x0), 'factory-is-0');\n        factory = IVaultFactory(_factory);\n    }\n\n    /// @dev liquidates vault, must be called from that vault\n    /// @dev this function does not provide an opportunity for a reentrancy attack even though it would make the arbitrage\n    /// @dev fail because of the lowering of the stablecoin balance\n    /// @notice must be called by the valid vault\n    function liquidate() external {\n        require(\n            msg.sender == factory.liquidationRouter(),\n            'not-liquidation-router'\n        );\n        IVaultFactory factory_cached = factory;\n\n        ILiquidationRouter _liquidationRouter = ILiquidationRouter(\n            factory_cached.liquidationRouter()\n        );\n        uint256 _underWaterDebt = _liquidationRouter.underWaterDebt();\n        address[] memory _collaterals = _liquidationRouter.collaterals();\n        uint256 _collateralCount = _collaterals.length;\n\n        uint256 totalStableCoin = totalDeposit; // cached to save an SLOAD\n\n        for (uint256 i; i < _collateralCount; i++) {\n            IERC20 _collateralToken = IERC20(_collaterals[i]);\n            uint256 _collateralAmount = _liquidationRouter.collateral(\n                address(_collateralToken)\n            );\n            _collateralToken.safeTransferFrom(\n                address(_liquidationRouter),\n                address(this),\n                _collateralAmount\n            );\n\n            (\n                uint256 collateralGainPerUnitStaked,\n                uint256 stableCoinLossPerUnitStaked\n            ) = _computeRewardsPerUnitStaked(\n                    address(_collateralToken),\n                    _collateralAmount,\n                    _underWaterDebt,\n                    totalStableCoin\n                );\n\n            _updateRewardSumAndProduct(\n                address(_collateralToken),\n                collateralGainPerUnitStaked,\n                stableCoinLossPerUnitStaked\n            );\n        }\n\n        _triggerA3Adistribution();\n\n        stableCoin.burn(_underWaterDebt);\n        uint256 newTotalDeposit = totalStableCoin - _underWaterDebt;\n        totalDeposit = newTotalDeposit;\n        emit TotalDepositUpdated(newTotalDeposit);\n        //factory_cached.emitLiquidationEvent(address(collateralToken), msg.sender, address(this), vaultCollateral);\n    }\n\n    /**\n     * @dev Gets the current withdrawable deposit of a specified staker.\n     * @param staker The address of the staker\n     * @return uint256 The withdrawable deposit amount\n     */ function getWithdrawableDeposit(\n        address staker\n    ) public view returns (uint256) {\n        uint256 initialDeposit = deposits[staker];\n        Snapshots memory snapshots = depositSnapshots[staker];\n        return _getCompoundedDepositFromSnapshots(initialDeposit, snapshots);\n    }\n\n    /**\n     * @dev Retrieves the collateral reward of a specified `_depositor` for a specific `_token`.\n     * @param _token The address of the collateral token\n     * @param _depositor The address of the depositor\n     * @return uint256 The collateral reward amount\n     */\n    function getCollateralReward(\n        address _token,\n        address _depositor\n    ) external view returns (uint256) {\n        Snapshots memory _snapshots = depositSnapshots[_depositor];\n        uint256 _initialDeposit = deposits[_depositor];\n\n        uint128 epochSnapshot = _snapshots.epoch;\n        uint128 scaleSnapshot = _snapshots.scale;\n\n        TokenToS[] memory tokensToSum_cached = epochToScaleToTokenToSum[\n            epochSnapshot\n        ][scaleSnapshot];\n        uint256 tokenArrayLength = tokensToSum_cached.length;\n\n        TokenToS memory cachedS;\n        for (uint128 i = 0; i < tokenArrayLength; i++) {\n            TokenToS memory S = tokensToSum_cached[i];\n            if (S.tokenAddress == _token) {\n                cachedS = S;\n                break;\n            }\n        }\n        if (cachedS.tokenAddress == address(0)) return 0;\n        uint256 relatedSValue_snapshot;\n        for (uint128 i = 0; i < _snapshots.tokenToSArray.length; i++) {\n            TokenToS memory S_snapsot = _snapshots.tokenToSArray[i];\n            if (S_snapsot.tokenAddress == _token) {\n                relatedSValue_snapshot = S_snapsot.S_value;\n                break;\n            }\n        }\n        TokenToS[] memory nextTokensToSum_cached = epochToScaleToTokenToSum[\n            epochSnapshot\n        ][scaleSnapshot + 1];\n        uint256 nextScaleS;\n        for (uint128 i = 0; i < nextTokensToSum_cached.length; i++) {\n            TokenToS memory nextScaleTokenToS = nextTokensToSum_cached[i];\n            if (nextScaleTokenToS.tokenAddress == _token) {\n                nextScaleS = nextScaleTokenToS.S_value;\n                break;\n            }\n        }\n\n        uint256 P_Snapshot = _snapshots.P;\n\n        uint256 collateralGain = _getCollateralGainFromSnapshots(\n            _initialDeposit,\n            cachedS.S_value,\n            nextScaleS,\n            relatedSValue_snapshot,\n            P_Snapshot\n        );\n\n        return collateralGain;\n    }\n\n    /**\n     * @dev Retrieves the A3A reward of a specified `_depositor`.\n     * @param _depositor The address of the user\n     * @return uint256 The A3A reward amount\n     */\n    function getDepositorA3AGain(\n        address _depositor\n    ) external view returns (uint256) {\n        uint256 totalA3ARewardsLeft_cached = totalA3ARewardsLeft;\n        uint256 totalStableCoin = totalDeposit;\n        if (\n            totalA3ARewardsLeft_cached == 0 ||\n            a3aPerMinute == 0 ||\n            totalStableCoin == 0\n        ) {\n            return 0;\n        }\n\n        uint256 _a3aIssuance = a3aPerMinute *\n            ((block.timestamp - latestA3ARewardTime) / SECONDS_IN_ONE_MINUTE);\n        if (totalA3ARewardsLeft_cached < _a3aIssuance) {\n            _a3aIssuance = totalA3ARewardsLeft_cached;\n        }\n\n        uint256 a3aGain = (_a3aIssuance * DECIMAL_PRECISION + lastA3AError) /\n            totalStableCoin;\n        uint256 marginalA3AGain = a3aGain * P;\n\n        return _getDepositorA3AGain(_depositor, marginalA3AGain);\n    }\n\n    /**\n     * @dev Sets the amount of A3A tokens per minute for rewards.\n     * @param _a3aPerMinute The A3A tokens per minute to be set\n     */\n    function setA3APerMinute(uint256 _a3aPerMinute) external onlyOwner {\n        _triggerA3Adistribution();\n        a3aPerMinute = _a3aPerMinute;\n        emit A3APerMinuteUpdated(a3aPerMinute);\n    }\n\n    /**\n     * @dev Sets the total amount of A3A tokens to be rewarded.\n     * It pays per minute until it reaches the specified rewarded amount.\n     */\n    function setA3AAmountForRewards() external onlyOwner {\n        _triggerA3Adistribution();\n        totalA3ARewardsLeft = a3aToken.balanceOf(address(this));\n        emit TotalA3ARewardsUpdated(totalA3ARewardsLeft);\n    }\n\n    /**\n     * @dev Redeems rewards, calling internal functions for collateral and A3A rewards.\n     * Private function for internal use.\n     */\n    function _redeemReward() private {\n        _redeemCollateralReward();\n        _triggerA3Adistribution();\n        _redeemA3AReward();\n    }\n\n    /**\n     * @notice Allows a depositor to redeem collateral rewards.\n     */\n    function _redeemCollateralReward() internal {\n        address depositor = msg.sender;\n        TokenToUint256[]\n            memory depositorCollateralGains = _getDepositorCollateralGains(\n                depositor\n            );\n        _sendCollateralRewardsToDepositor(depositorCollateralGains);\n    }\n\n    /**\n     * @notice Allows a depositor to redeem A3A rewards.\n     */\n    function _redeemA3AReward() internal {\n        address depositor = msg.sender;\n        uint256 depositorA3AGain = _getDepositorA3AGain(depositor, 0);\n        _sendA3ARewardsToDepositor(depositorA3AGain);\n        emit A3ARewardRedeemed(depositor, depositorA3AGain);\n    }\n\n    /**\n     * @dev Updates user deposit snapshot data for a new deposit value.\n     * @param _depositor The address of the depositor.\n     * @param _newValue The new deposit value.\n     */\n    function _updateDepositAndSnapshots(\n        address _depositor,\n        uint256 _newValue\n    ) private {\n        deposits[_depositor] = _newValue;\n        if (_newValue == 0) {\n            delete depositSnapshots[_depositor];\n            emit DepositSnapshotUpdated(_depositor, 0, 0, 0);\n            return;\n        }\n        uint128 cachedEpoch = currentEpoch;\n        uint128 cachedScale = currentScale;\n        TokenToS[] storage cachedTokenToSArray = epochToScaleToTokenToSum[\n            cachedEpoch\n        ][cachedScale]; // TODO: maybe remove and read twice?\n        uint256 cachedP = P;\n        uint256 cachedG = epochToScaleToG[cachedEpoch][cachedScale];\n\n        depositSnapshots[_depositor].tokenToSArray = cachedTokenToSArray; // TODO\n        depositSnapshots[_depositor].P = cachedP;\n        depositSnapshots[_depositor].G = cachedG;\n        depositSnapshots[_depositor].scale = cachedScale;\n        depositSnapshots[_depositor].epoch = cachedEpoch;\n        emit DepositSnapshotUpdated(_depositor, cachedP, cachedG, _newValue);\n    }\n\n    /**\n     * @notice Updates the reward sums and product based on collateral and stablecoin changes.\n     * @dev This function updates the reward sums and product based on changes in collateral and stablecoin values.\n     * @param _collateralTokenAddress Address of the collateral token.\n     * @param _collateralGainPerUnitStaked Collateral gains per unit staked.\n     * @param _stableCoinLossPerUnitStaked Stablecoin losses per unit staked.\n     */\n    function _updateRewardSumAndProduct(\n        address _collateralTokenAddress,\n        uint256 _collateralGainPerUnitStaked,\n        uint256 _stableCoinLossPerUnitStaked\n    ) internal {\n        assert(_stableCoinLossPerUnitStaked <= DECIMAL_PRECISION);\n\n        uint128 currentScaleCached = currentScale;\n        uint128 currentEpochCached = currentEpoch;\n        uint256 currentS;\n        uint256 currentSIndex;\n        bool _found;\n        TokenToS[] memory currentTokenToSArray = epochToScaleToTokenToSum[\n            currentEpochCached\n        ][currentScaleCached];\n        for (uint128 i = 0; i < currentTokenToSArray.length; i++) {\n            if (\n                currentTokenToSArray[i].tokenAddress == _collateralTokenAddress\n            ) {\n                currentS = currentTokenToSArray[i].S_value;\n                currentSIndex = i;\n                _found = true;\n            }\n        }\n        /*\n         * Calculate the new S first, before we update P.\n         * The Collateral gain for any given depositor from a liquidation depends on the value of their deposit\n         * (and the value of totalDeposits) prior to the Stability being depleted by the debt in the liquidation.\n         *\n         * Since S corresponds to Collateral gain, and P to deposit loss, we update S first.\n         */\n        uint256 marginalCollateralGain = _collateralGainPerUnitStaked * P;\n        uint256 newS = currentS + marginalCollateralGain;\n        if (currentTokenToSArray.length == 0 || !_found) {\n            TokenToS memory tokenToS;\n            tokenToS.S_value = newS;\n            tokenToS.tokenAddress = _collateralTokenAddress;\n            epochToScaleToTokenToSum[currentEpochCached][currentScaleCached]\n                .push() = tokenToS;\n        } else {\n            epochToScaleToTokenToSum[currentEpochCached][currentScaleCached][\n                currentSIndex\n            ].S_value = newS;\n        }\n        emit S_Updated(\n            _collateralTokenAddress,\n            newS,\n            currentEpochCached,\n            currentScaleCached\n        );\n        _updateP(_stableCoinLossPerUnitStaked, true);\n    }\n\n    function _updateP(\n        uint256 _stableCoinChangePerUnitStaked,\n        bool loss\n    ) internal {\n        /*\n         * The newProductFactor is the factor by which to change all deposits, due to the depletion of Stability Pool StableCoin in the liquidation.\n         * We make the product factor 0 if there was a pool-emptying. Otherwise, it is (1 - StableCoinLossPerUnitStaked)\n         */\n        uint256 newProductFactor;\n        if (loss) {\n            newProductFactor = uint256(\n                DECIMAL_PRECISION - _stableCoinChangePerUnitStaked\n            );\n        } else {\n            newProductFactor = uint256(\n                DECIMAL_PRECISION + _stableCoinChangePerUnitStaked\n            );\n        }\n        uint256 currentP = P;\n        uint256 newP;\n        // If the Stability Pool was emptied, increment the epoch, and reset the scale and product P\n        if (newProductFactor == 0) {\n            currentEpoch += 1;\n            emit EpochUpdated(currentEpoch);\n            currentScale = 0;\n            emit ScaleUpdated(0);\n            newP = DECIMAL_PRECISION;\n\n            // If multiplying P by a non-zero product factor would reduce P below the scale boundary, increment the scale\n        } else if (\n            (currentP * newProductFactor) / DECIMAL_PRECISION < SCALE_FACTOR\n        ) {\n            newP =\n                (currentP * newProductFactor * SCALE_FACTOR) /\n                DECIMAL_PRECISION;\n            currentScale += 1;\n            emit ScaleUpdated(currentScale);\n        } else {\n            newP = (currentP * newProductFactor) / DECIMAL_PRECISION;\n        }\n\n        assert(newP > 0);\n        P = newP;\n\n        emit P_Updated(newP);\n    }\n\n    /**\n     * @dev Updates G when a new A3A amount is issued.\n     * @param _a3aIssuance The new A3A issuance amount\n     */\n    function _updateG(uint256 _a3aIssuance) internal {\n        uint256 totalStableCoin = totalDeposit; // cached to save an SLOAD\n        /*\n         * When total deposits is 0, G is not updated. In this case, the A3A issued can not be obtained by later\n         * depositors - it is missed out on, and remains in the balanceof the Stability Pool.\n         *\n         */\n        if (totalStableCoin == 0 || _a3aIssuance == 0) {\n            return;\n        }\n\n        uint256 a3aPerUnitStaked;\n        a3aPerUnitStaked = _computeA3APerUnitStaked(\n            _a3aIssuance,\n            totalStableCoin\n        );\n\n        uint256 marginalA3AGain = a3aPerUnitStaked * P;\n        uint128 currentEpoch_cached = currentEpoch;\n        uint128 currentScale_cached = currentScale;\n\n        uint256 newEpochToScaleToG = epochToScaleToG[currentEpoch_cached][\n            currentScale_cached\n        ] + marginalA3AGain;\n        epochToScaleToG[currentEpoch_cached][\n            currentScale_cached\n        ] = newEpochToScaleToG;\n\n        emit G_Updated(\n            newEpochToScaleToG,\n            currentEpoch_cached,\n            currentScale_cached\n        );\n    }\n\n    /**\n     * @dev Retrieves the collateral gains of a specified `_depositor`.\n     * @param _depositor The address of the depositor\n     * @return TokenToUint256[] An array containing collateral gain information\n     */\n    function _getDepositorCollateralGains(\n        address _depositor\n    ) internal view returns (TokenToUint256[] memory) {\n        uint256 initialDeposit = deposits[_depositor];\n        if (initialDeposit == 0) {\n            TokenToUint256[] memory x;\n            return x;\n        }\n\n        Snapshots memory snapshots = depositSnapshots[_depositor];\n\n        TokenToUint256[]\n            memory gainPerCollateralArray = _getCollateralGainsArrayFromSnapshots(\n                initialDeposit,\n                snapshots\n            );\n        return gainPerCollateralArray;\n    }\n\n    // todo!\n    function _getCollateralGainsArrayFromSnapshots(\n        uint256 _initialDeposit,\n        Snapshots memory _snapshots\n    ) internal view returns (TokenToUint256[] memory) {\n        /*\n         * Grab the sum 'S' from the epoch at which the stake was made. The Collateral gain may span up to one scale change.\n         * If it does, the second portion of the Collateral gain is scaled by 1e9.\n         * If the gain spans no scale change, the second portion will be 0.\n         */\n        uint128 epochSnapshot = _snapshots.epoch;\n        uint128 scaleSnapshot = _snapshots.scale;\n        TokenToS[] memory tokensToSum_cached = epochToScaleToTokenToSum[\n            epochSnapshot\n        ][scaleSnapshot];\n        uint256 tokenArrayLength = tokensToSum_cached.length;\n        TokenToUint256[] memory CollateralGainsArray = new TokenToUint256[](\n            tokenArrayLength\n        );\n        for (uint128 i = 0; i < tokenArrayLength; i++) {\n            TokenToS memory S = tokensToSum_cached[i];\n            uint256 relatedS_snapshot;\n            for (uint128 j = 0; j < _snapshots.tokenToSArray.length; j++) {\n                TokenToS memory S_snapsot = _snapshots.tokenToSArray[j];\n                if (S_snapsot.tokenAddress == S.tokenAddress) {\n                    relatedS_snapshot = S_snapsot.S_value;\n                    break;\n                }\n            }\n            TokenToS[] memory nextTokensToSum_cached = epochToScaleToTokenToSum[\n                epochSnapshot\n            ][scaleSnapshot + 1];\n            uint256 nextScaleS;\n            for (uint128 j = 0; j < nextTokensToSum_cached.length; j++) {\n                TokenToS memory nextScaleTokenToS = nextTokensToSum_cached[j];\n                if (nextScaleTokenToS.tokenAddress == S.tokenAddress) {\n                    nextScaleS = nextScaleTokenToS.S_value;\n                    break;\n                }\n            }\n            uint256 P_Snapshot = _snapshots.P;\n\n            CollateralGainsArray[i].value = _getCollateralGainFromSnapshots(\n                _initialDeposit,\n                S.S_value,\n                nextScaleS,\n                relatedS_snapshot,\n                P_Snapshot\n            );\n            CollateralGainsArray[i].tokenAddress = S.tokenAddress;\n        }\n\n        return CollateralGainsArray;\n    }\n\n    function _getCollateralGainFromSnapshots(\n        uint256 initialDeposit,\n        uint256 S,\n        uint256 nextScaleS,\n        uint256 S_Snapshot,\n        uint256 P_Snapshot\n    ) internal pure returns (uint256) {\n        uint256 firstPortion = S - S_Snapshot;\n        uint256 secondPortion = nextScaleS / SCALE_FACTOR;\n        uint256 collateralGain = (initialDeposit *\n            (firstPortion + secondPortion)) /\n            P_Snapshot /\n            DECIMAL_PRECISION;\n\n        return collateralGain;\n    }\n\n    function _getDepositorA3AGain(\n        address _depositor,\n        uint256 _marginalA3AGain\n    ) internal view returns (uint256) {\n        uint256 initialDeposit = deposits[_depositor];\n        if (initialDeposit == 0) {\n            return 0;\n        }\n        Snapshots memory _snapshots = depositSnapshots[_depositor];\n        /*\n         * Grab the sum 'G' from the epoch at which the stake was made. The A3A gain may span up to one scale change.\n         * If it does, the second portion of the A3A gain is scaled by 1e9.\n         * If the gain spans no scale change, the second portion will be 0.\n         */\n        uint256 firstEpochPortion = epochToScaleToG[_snapshots.epoch][\n            _snapshots.scale\n        ];\n        uint256 secondEpochPortion = epochToScaleToG[_snapshots.epoch][\n            _snapshots.scale + 1\n        ];\n        if (_snapshots.epoch == currentEpoch) {\n            if (_snapshots.scale == currentScale)\n                firstEpochPortion += _marginalA3AGain;\n            if (_snapshots.scale + 1 == currentScale)\n                secondEpochPortion += _marginalA3AGain;\n        }\n        uint256 gainPortions = firstEpochPortion -\n            _snapshots.G +\n            secondEpochPortion /\n            SCALE_FACTOR;\n\n        return\n            (initialDeposit * (gainPortions)) /\n            _snapshots.P /\n            DECIMAL_PRECISION;\n    }\n\n    /// @dev gets compounded deposit of the user\n    function _getCompoundedDepositFromSnapshots(\n        uint256 _initialStake,\n        Snapshots memory _snapshots\n    ) internal view returns (uint256) {\n        uint256 snapshot_P = _snapshots.P;\n\n        // If stake was made before a pool-emptying event, then it has been fully cancelled with debt -- so, return 0\n        if (_snapshots.epoch < currentEpoch) {\n            return 0;\n        }\n\n        uint256 compoundedStake;\n        uint128 scaleDiff = currentScale - _snapshots.scale;\n\n        /* Compute the compounded stake. If a scale change in P was made during the stake's lifetime,\n         * account for it. If more than one scale change was made, then the stake has decreased by a factor of\n         * at least 1e-9 -- so return 0.\n         */\n        uint256 calculatedSnapshotP = snapshot_P == 0\n            ? DECIMAL_PRECISION\n            : snapshot_P;\n        if (scaleDiff == 0) {\n            compoundedStake = (_initialStake * P) / calculatedSnapshotP;\n        } else if (scaleDiff == 1) {\n            compoundedStake =\n                (_initialStake * P) /\n                calculatedSnapshotP /\n                SCALE_FACTOR;\n        } else {\n            // if scaleDiff >= 2\n            compoundedStake = 0;\n        }\n\n        /*\n         * If compounded deposit is less than a billionth of the initial deposit, return 0.\n         *\n         * NOTE: originally, this line was in place to stop rounding errors making the deposit too large. However, the error\n         * corrections should ensure the error in P \"favors the Pool\", i.e. any given compounded deposit should slightly less\n         * than it's theoretical value.\n         *\n         * Thus it's unclear whether this line is still really needed.\n         */\n        if (compoundedStake < _initialStake / 1e9) {\n            return 0;\n        }\n\n        return compoundedStake;\n    }\n\n    /// @dev Compute the StableCoin and Collateral rewards. Uses a \"feedback\" error correction, to keep\n    /// the cumulative error in the P and S state variables low:s\n    function _computeRewardsPerUnitStaked(\n        address _collateralTokenAddress,\n        uint256 _collToAdd,\n        uint256 _debtToOffset,\n        uint256 _totalStableCoinDeposits\n    )\n        internal\n        returns (\n            uint256 collateralGainPerUnitStaked,\n            uint256 stableCoinLossPerUnitStaked\n        )\n    {\n        /*\n         * Compute the StableCoin and Collateral rewards. Uses a \"feedback\" error correction, to keep\n         * the cumulative error in the P and S state variables low:\n         *\n         * 1) Form numerators which compensate for the floor division errors that occurred the last time this\n         * function was called.\n         * 2) Calculate \"per-unit-staked\" ratios.\n         * 3) Multiply each ratio back by its denominator, to reveal the current floor division error.\n         * 4) Store these errors for use in the next correction when this function is called.\n         * 5) Note: static analysis tools complain about this \"division before multiplication\", however, it is intended.\n         */\n        uint256 collateralNumerator = _collToAdd *\n            DECIMAL_PRECISION +\n            collateralToLastErrorOffset[_collateralTokenAddress];\n\n        assert(_debtToOffset <= _totalStableCoinDeposits);\n        if (_debtToOffset == _totalStableCoinDeposits) {\n            stableCoinLossPerUnitStaked = DECIMAL_PRECISION; // When the Pool depletes to 0, so does each deposit\n            lastStableCoinLossErrorOffset = 0;\n        } else {\n            uint256 stableCoinLossNumerator = _debtToOffset *\n                DECIMAL_PRECISION -\n                lastStableCoinLossErrorOffset;\n            /*\n             * Add 1 to make error in quotient positive. We want \"slightly too much\" StableCoin loss,\n             * which ensures the error in any given compoundedStableCoinDeposit favors the Stability Pool.\n             */\n            stableCoinLossPerUnitStaked =\n                stableCoinLossNumerator /\n                _totalStableCoinDeposits +\n                1;\n            lastStableCoinLossErrorOffset =\n                stableCoinLossPerUnitStaked *\n                _totalStableCoinDeposits -\n                stableCoinLossNumerator;\n        }\n\n        collateralGainPerUnitStaked = (_totalStableCoinDeposits != 0)\n            ? collateralNumerator / _totalStableCoinDeposits\n            : 0;\n        collateralToLastErrorOffset[_collateralTokenAddress] =\n            collateralNumerator -\n            collateralGainPerUnitStaked *\n            _totalStableCoinDeposits;\n\n        return (collateralGainPerUnitStaked, stableCoinLossPerUnitStaked);\n    }\n\n    /// @dev distributes A3A per minutes that was not spent yet\n    function _triggerA3Adistribution() internal {\n        uint256 issuance = _issueA3ARewards();\n        _updateG(issuance);\n    }\n\n    function _issueA3ARewards() internal returns (uint256) {\n        uint256 newA3ARewardTime = block.timestamp;\n        uint256 totalA3ARewardsLeft_cached = totalA3ARewardsLeft;\n        if (\n            totalA3ARewardsLeft_cached == 0 ||\n            a3aPerMinute == 0 ||\n            totalDeposit == 0\n        ) {\n            latestA3ARewardTime = newA3ARewardTime;\n            return 0;\n        }\n\n        uint256 timePassedInMinutes = (newA3ARewardTime - latestA3ARewardTime) /\n            SECONDS_IN_ONE_MINUTE;\n        uint256 issuance = a3aPerMinute * timePassedInMinutes;\n        if (totalA3ARewardsLeft_cached < issuance) {\n            issuance = totalA3ARewardsLeft_cached; // event will capture that 0 tokens left\n        }\n        uint256 newTotalA3ARewardsLeft = totalA3ARewardsLeft_cached - issuance;\n        totalA3ARewardsLeft = newTotalA3ARewardsLeft;\n        latestA3ARewardTime = newA3ARewardTime;\n\n        emit A3ARewardIssue(issuance, newTotalA3ARewardsLeft);\n\n        return issuance;\n    }\n\n    function _computeA3APerUnitStaked(\n        uint256 _a3aIssuance,\n        uint256 _totalStableCoinDeposits\n    ) internal returns (uint256) {\n        /*\n         * Calculate the A3A-per-unit staked.  Division uses a \"feedback\" error correction, to keep the\n         * cumulative error low in the running total G:\n         *\n         * 1) Form a numerator which compensates for the floor division error that occurred the last time this\n         * function was called.\n         * 2) Calculate \"per-unit-staked\" ratio.\n         * 3) Multiply the ratio back by its denominator, to reveal the current floor division error.\n         * 4) Store this error for use in the next correction when this function is called.\n         * 5) Note: static analysis tools complain about this \"division before multiplication\", however, it is intended.\n         */\n        uint256 a3aNumerator = _a3aIssuance * DECIMAL_PRECISION + lastA3AError;\n\n        uint256 a3aPerUnitStaked = a3aNumerator / _totalStableCoinDeposits;\n        lastA3AError =\n            a3aNumerator -\n            (a3aPerUnitStaked * _totalStableCoinDeposits);\n\n        return a3aPerUnitStaked;\n    }\n\n    /// @dev transfers collateral rewards tokens precalculated to the depositor\n    function _sendCollateralRewardsToDepositor(\n        TokenToUint256[] memory _depositorCollateralGains\n    ) internal {\n        for (uint256 i = 0; i < _depositorCollateralGains.length; i++) {\n            if (_depositorCollateralGains[i].value == 0) {\n                continue;\n            }\n            IERC20 collateralToken = IERC20(\n                _depositorCollateralGains[i].tokenAddress\n            );\n            collateralToken.safeTransfer(\n                msg.sender,\n                _depositorCollateralGains[i].value\n            );\n            emit CollateralRewardRedeemed(\n                msg.sender,\n                _depositorCollateralGains[i].tokenAddress,\n                _depositorCollateralGains[i].value\n            );\n        }\n    }\n\n    /// @dev transfers A3A amount to the user\n    function _sendA3ARewardsToDepositor(uint256 _a3aGain) internal {\n        a3aToken.transfer(msg.sender, _a3aGain);\n    }\n}\n\n\n// File: contracts/TokenToPriceFeed.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/access/Ownable.sol';\nimport '@openzeppelin/contracts/interfaces/IERC20Metadata.sol';\nimport './interfaces/IPriceFeed.sol';\nimport './utils/constants.sol';\nimport './interfaces/ITokenPriceFeed.sol';\n\n/**\n * @title TokenToPriceFeed\n * @dev Manages mapping of token addresses to their respective price feed contracts.\n */\ncontract TokenToPriceFeed is Ownable, Constants, ITokenPriceFeed {\n    /// @dev Mapping of token address to its associated price feed contract.\n    mapping(address => TokenInfo) public tokens;\n\n    /**\n     * @dev Retrieves the contract owner's address.\n     */\n    function owner() public view override(Ownable, IOwnable) returns (address) {\n        return Ownable.owner();\n    }\n\n    /**\n     * @dev Retrieves the token's current price from the respective price feed.\n     * @param  _token Address of the token.\n     */\n    function tokenPrice(address _token) public view override returns (uint256) {\n        return IPriceFeed(tokens[_token].priceFeed).price();\n    }\n\n    /**\n     * @dev Retrieves the price feed contract address for a given token.\n     * @param  _token Address of the token.\n     */\n    function tokenPriceFeed(\n        address _token\n    ) public view override returns (address) {\n        return tokens[_token].priceFeed;\n    }\n\n    /**\n     * @dev Retrieves the minimal collateral ratio for a given token.\n     * @param  _token Address of the token.\n     */\n    function mcr(address _token) public view override returns (uint256) {\n        return tokens[_token].mcr;\n    }\n\n    /**\n     * @dev Retrieves the decimal places of a given token.\n     * @param  _token Address of the token.\n     */\n    function decimals(address _token) public view override returns (uint256) {\n        return tokens[_token].decimals;\n    }\n\n    /**\n     * @dev Retrieves the minimal liquidation ratio for a given token.\n     * @param  _token Address of the token.\n     */\n    function mlr(address _token) public view override returns (uint256) {\n        return tokens[_token].mlr;\n    }\n\n    /**\n     * @dev Retrieves the borrow rate for a given token.\n     * @param  _token Address of the token.\n     */\n\n    function borrowRate(address _token) public view override returns (uint256) {\n        return tokens[_token].borrowRate;\n    }\n\n    /**\n     * @dev Sets or updates the price feed contract for a specific token.\n     * @param  _token Address of the token.\n     * @param  _priceFeed Address of the PriceFeed contract for the token.\n     * @param  _mcr Minimal Collateral Ratio of the token.\n     * @param  _mlr Minimal Liquidation Ratio of the token.\n     * @param  _borrowRate Borrow rate of the token.\n     */\n    function setTokenPriceFeed(\n        address _token,\n        address _priceFeed,\n        uint256 _mcr,\n        uint256 _mlr,\n        uint256 _borrowRate,\n        uint256 /* _decimals */\n    ) public override onlyOwner {\n        require(_mcr >= 100, 'MCR < 100');\n        require(_mlr >= 100 && _mlr <= _mcr, 'MLR < 100 or MLR > MCR');\n        require(_borrowRate < 10 ether, 'borrowRate >= 10%');\n        IERC20Metadata erc20 = IERC20Metadata(_token);\n        uint256 _decimals = erc20.decimals();\n        require(_decimals > 0 || _decimals <= 18, 'not-valid-decimals');\n\n        TokenInfo memory token = tokens[_token];\n        token.priceFeed = _priceFeed;\n        token.mcr = (DECIMAL_PRECISION * _mcr) / 100;\n        token.mlr = (DECIMAL_PRECISION * _mlr) / 100;\n        token.borrowRate = _borrowRate;\n        token.decimals = _decimals;\n\n        emit NewTokenPriceFeed(\n            _token,\n            _priceFeed,\n            erc20.name(),\n            erc20.symbol(),\n            token.mcr,\n            token.mlr,\n            token.borrowRate,\n            _decimals\n        );\n        tokens[_token] = token;\n    }\n\n    /**\n     * @dev Transfers ownership after revoking other roles from other addresses.\n     * @param _newOwner Address of the new owner.\n     */\n    function transferOwnership(\n        address _newOwner\n    ) public override(Ownable, IOwnable) {\n        Ownable.transferOwnership(_newOwner);\n    }\n}\n\n\n// File: contracts/utils/constants.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\n/**\n * @title Constants\n * @dev This contract defines various constants used within the system.\n */\ncontract Constants {\n    // Precision used for decimal calculations\n    uint256 public constant DECIMAL_PRECISION = 1e18;\n\n    // Reserve required for liquidation purposes\n    uint256 public constant LIQUIDATION_RESERVE = 1e18;\n\n    // Maximum value for uint256\n    uint256 public constant MAX_INT = 2 ** 256 - 1;\n\n    // Constants for percentage calculations\n    uint256 public constant PERCENT = (DECIMAL_PRECISION * 1) / 100; // Represents 1%\n    uint256 public constant PERCENT10 = PERCENT * 10; // Represents 10%\n    uint256 public constant PERCENT_05 = PERCENT / 2; // Represents 0.5%\n\n    // Maximum borrowing and redemption rates\n    uint256 public constant MAX_BORROWING_RATE = (DECIMAL_PRECISION * 5) / 100; // Represents 5%\n    uint256 public constant MAX_REDEMPTION_RATE =\n        (DECIMAL_PRECISION * 10) / 100; // Represents 10%\n}\n\n\n// File: contracts/utils/linked-address-list.sol\n// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity ^0.8.4;\n\n/**\n * @title LinkedAddressList\n * @dev Library implementing a linked list structure to store and operate sorted Troves.\n */\nlibrary LinkedAddressList {\n    struct EntryLink {\n        address prev;\n        address next;\n    }\n\n    struct List {\n        address _last;\n        address _first;\n        uint256 _size;\n        mapping(address => EntryLink) _values;\n    }\n\n    /**\n     * @dev Adds an element to the linked list.\n     * @param _list The storage pointer to the linked list.\n     * @param _element The element to be added.\n     * @param _reference The reference element to determine the position for addition.\n     * @param _before A boolean indicating whether to add the element before the reference.\n     * @return A boolean indicating the success of the addition.\n     */\n    function add(\n        List storage _list,\n        address _element,\n        address _reference,\n        bool _before\n    ) internal returns (bool) {\n        require(\n            _reference == address(0x0) ||\n                _list._values[_reference].next != address(0x0),\n            '79d3d _ref neither valid nor 0x'\n        );\n\n        // Element must not exist to be added\n        EntryLink storage element_values = _list._values[_element];\n        if (element_values.prev == address(0x0)) {\n            if (_list._last == address(0x0)) {\n                // If the list is empty, set the element as both first and last\n                element_values.prev = _element;\n                element_values.next = _element;\n                _list._first = _element;\n                _list._last = _element;\n            } else {\n                if (\n                    _before &&\n                    (_reference == address(0x0) || _reference == _list._first)\n                ) {\n                    // Adding the element as the first element\n                    address first = _list._first;\n                    _list._values[first].prev = _element;\n                    element_values.prev = _element;\n                    element_values.next = first;\n                    _list._first = _element;\n                } else if (\n                    !_before &&\n                    (_reference == address(0x0) || _reference == _list._last)\n                ) {\n                    // Adding the element as the last element\n                    address last = _list._last;\n                    _list._values[last].next = _element;\n                    element_values.prev = last;\n                    element_values.next = _element;\n                    _list._last = _element;\n                } else {\n                    // Inserting the element between two elements\n                    EntryLink memory ref = _list._values[_reference];\n                    if (_before) {\n                        element_values.prev = ref.prev;\n                        element_values.next = _reference;\n                        _list._values[_reference].prev = _element;\n                        _list._values[ref.prev].next = _element;\n                    } else {\n                        element_values.prev = _reference;\n                        element_values.next = ref.next;\n                        _list._values[_reference].next = _element;\n                        _list._values[ref.next].prev = _element;\n                    }\n                }\n            }\n            _list._size = _list._size + 1;\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * @dev Removes an element from the linked list.\n     * @param _list The storage pointer to the linked list.\n     * @param _element The element to be removed.\n     * @return A boolean indicating the success of the removal.\n     */\n    function remove(\n        List storage _list,\n        address _element\n    ) internal returns (bool) {\n        EntryLink memory element_values = _list._values[_element];\n        if (element_values.next != address(0x0)) {\n            if (_element == _list._last && _element == _list._first) {\n                // Removing the last and only element in the list\n                delete _list._last;\n                delete _list._first;\n            } else if (_element == _list._first) {\n                // Removing the first element\n                address next = element_values.next;\n                _list._values[next].prev = next;\n                _list._first = next;\n            } else if (_element == _list._last) {\n                // Removing the last element\n                address new_list_last = element_values.prev;\n                _list._last = new_list_last;\n                _list._values[new_list_last].next = new_list_last;\n            } else {\n                // Removing an element in between two other elements\n                address next = element_values.next;\n                address prev = element_values.prev;\n                _list._values[next].prev = prev;\n                _list._values[prev].next = next;\n            }\n            // Delete the element itself\n            delete _list._values[_element];\n            _list._size = _list._size - 1;\n            return true;\n        }\n        return false;\n    }\n}\n\n\n// File: contracts/utils/PoolAddress.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\n/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee\nlibrary PoolAddress {\n    bytes32 internal constant POOL_INIT_CODE_HASH =\n        0x5fd83e37b194e20b4858ffd8707ab464489099cc00c7985c0a048fa38836bbaa;\n\n    /// @notice The identifying key of the pool\n    struct PoolKey {\n        address token0;\n        address token1;\n        uint24 fee;\n    }\n\n    /// @notice Returns PoolKey: the ordered tokens with the matched fee levels\n    /// @param tokenA The first token of a pool, unsorted\n    /// @param tokenB The second token of a pool, unsorted\n    /// @param fee The fee level of the pool\n    /// @return Poolkey The pool details with ordered token0 and token1 assignments\n    function getPoolKey(\n        address tokenA,\n        address tokenB,\n        uint24 fee\n    ) internal pure returns (PoolKey memory) {\n        if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);\n        return PoolKey({token0: tokenA, token1: tokenB, fee: fee});\n    }\n\n    /// @notice Deterministically computes the pool address given the factory and PoolKey\n    /// @param factory The Uniswap V3 factory contract address\n    /// @param key The PoolKey\n    /// @return pool The contract address of the V3 pool\n    function computeAddress(\n        address factory,\n        PoolKey memory key\n    ) internal pure returns (address pool) {\n        require(key.token0 < key.token1);\n        pool = address(\n            uint160(\n                uint256(\n                    keccak256(\n                        abi.encodePacked(\n                            hex'ff',\n                            factory,\n                            keccak256(\n                                abi.encode(key.token0, key.token1, key.fee)\n                            ),\n                            POOL_INIT_CODE_HASH\n                        )\n                    )\n                )\n            )\n        );\n    }\n}\n\n\n// File: contracts/Vault.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/access/Ownable.sol';\nimport '@openzeppelin/contracts/interfaces/IERC20Metadata.sol';\nimport '@openzeppelin/contracts/token/ERC20/IERC20.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\nimport '@openzeppelin/contracts/utils/Context.sol';\nimport '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';\n\nimport './interfaces/IPriceFeed.sol';\nimport './interfaces/IVaultFactory.sol';\nimport './interfaces/IVaultFactoryConfig.sol';\nimport './interfaces/ILiquidationRouter.sol';\n\nimport './utils/constants.sol';\nimport './interfaces/ITokenPriceFeed.sol';\nimport './interfaces/IVaultExtraSettings.sol';\nimport './utils/linked-address-list.sol';\n\nimport 'hardhat/console.sol';\n/**\n * @title Vault\n * @dev Manages creation, collateralization, borrowing, and liquidation of Vaults.\n */\ncontract Vault is Context, Constants {\n    string public constant VERSION = '1.2.0';\n\n    // Events emitted by the contract\n    event CollateralAdded(\n        address indexed collateral,\n        uint256 amount,\n        uint256 newTotalAmount\n    );\n    event CollateralRemoved(\n        address indexed collateral,\n        uint256 amount,\n        uint256 newTotalAmount\n    );\n    event CollateralRedeemed(\n        address indexed collateral,\n        uint256 amount,\n        uint256 newTotalAmount,\n        uint256 stableAmountUsed,\n        uint256 feePaid\n    );\n\n    event DebtAdded(uint256 amount, uint256 newTotalDebt);\n    event DebtRepaid(uint256 amount, uint256 newTotalDebt);\n\n    modifier onlyFactory() {\n        require(_msgSender() == factory, 'only-factory');\n        _;\n    }\n\n    using SafeERC20 for IERC20;\n    using EnumerableSet for EnumerableSet.AddressSet;\n\n    address public immutable stable;\n    address public immutable factory;\n    address public vaultOwner;\n\n    string public name;\n\n    EnumerableSet.AddressSet private collateralSet;\n    EnumerableSet.AddressSet private operators;\n\n    IVaultExtraSettings public vaultExtraSettings;\n    mapping(address => uint256) public collateral;\n\n    uint256 public debt;\n\n    modifier onlyVaultOwner() {\n        require(_msgSender() == vaultOwner, 'only-vault-owner');\n        _;\n    }\n\n    /**\n     * @dev Constructor to initialize the Vault contract.\n     * @param _factory Address of the VaultFactory contract.\n     * @param _vaultOwner Address of the initial owner of the Vault.\n     * @param _name Name of the Vault.\n     */\n    constructor(\n        address _factory,\n        address _vaultOwner,\n        string memory _name,\n        IVaultExtraSettings _vaultExtraSettings\n    ) {\n        require(_vaultOwner != address(0x0), 'vault-owner-is-0');\n        require(bytes(_name).length > 0, 'name-is-empty');\n        require(_factory != address(0x0), 'factory-is-0');\n        require(\n            address(_vaultExtraSettings) != address(0x0),\n            'vault-extra-settings-is-0'\n        );\n\n        factory = _factory;\n        vaultOwner = _vaultOwner;\n        stable = IVaultFactory(factory).stable();\n        name = _name;\n        vaultExtraSettings = _vaultExtraSettings;\n    }\n\n    /**\n     * @dev Transfers ownership of the Vault to a new owner.\n     * @param _newOwner Address of the new owner.\n     */\n    function transferVaultOwnership(address _newOwner) external onlyFactory {\n        vaultOwner = _newOwner;\n    }\n\n    /**\n     * @dev Sets a new name for the Vault.\n     * @param _name New name for the Vault.\n     */\n    function setName(string memory _name) external onlyVaultOwner {\n        require(bytes(_name).length > 0, 'name-is-empty');\n        name = _name;\n    }\n\n    /**\n     * @dev Adds an operator to the Vault, allowing them certain permissions.\n     * @param _operator Address of the operator to be added.\n     */\n    function addOperator(address _operator) external onlyVaultOwner {\n        require(_operator != address(0x0), 'operator-is-0');\n        operators.add(_operator);\n    }\n\n    /**\n     * @dev Removes an operator from the Vault, revoking their permissions.\n     * @param _operator Address of the operator to be removed.\n     */\n    function removeOperator(address _operator) external onlyVaultOwner {\n        require(_operator != address(0x0), 'operator-is-0');\n        operators.remove(_operator);\n    }\n\n    /**\n     * @dev Checks if an address is an operator for this Vault.\n     * @param _operator Address to check.\n     * @return Boolean indicating whether the address is an operator.\n     */\n    function isOperator(address _operator) external view returns (bool) {\n        return operators.contains(_operator);\n    }\n\n    /**\n     * @dev Returns the number of operators in the Vault.\n     * @return Length of the operators set.\n     */\n    function operatorsLength() external view returns (uint256) {\n        return operators.length();\n    }\n\n    /**\n     * @dev Returns the operator at a given index in the operators set.\n     * @param _index Index of the operator.\n     * @return Address of the operator at the given index.\n     */\n    function operatorAt(uint256 _index) external view returns (address) {\n        return operators.at(_index);\n    }\n\n    /**\n     * @dev Checks if a collateral token is added to the Vault.\n     * @param _collateral Address of the collateral token to check.\n     * @return Boolean indicating whether the collateral token is added.\n     */\n    function containsCollateral(\n        address _collateral\n    ) external view returns (bool) {\n        return collateralSet.contains(_collateral);\n    }\n\n    /**\n     * @dev Returns the number of collateral tokens added to the Vault.\n     * @return Length of the collateral set.\n     */\n    function collateralsLength() external view returns (uint256) {\n        return collateralSet.length();\n    }\n\n    /**\n     * @dev Returns the collateral token address at a given index in the collateral set.\n     * @param _index Index of the collateral token.\n     * @return Address of the collateral token at the given index.\n     */\n    function collateralAt(uint256 _index) external view returns (address) {\n        return collateralSet.at(_index);\n    }\n\n    /**\n     * @dev Returns an array containing all collateral token addresses in the Vault.\n     * @return Array of collateral token addresses.\n     */\n    function collaterals() external view returns (address[] memory) {\n        address[] memory _collaterals = new address[](collateralSet.length());\n        for (uint256 i = 0; i < collateralSet.length(); i++) {\n            _collaterals[i] = collateralSet.at(i);\n        }\n        return _collaterals;\n    }\n\n    /**\n     * @dev Adds a new collateral token to the Vault and updates the collateral amount.\n     * @param _collateral Address of the collateral token to add.\n     * @param _amount Amount of the collateral token to add.\n     */\n    function addCollateral(\n        address _collateral,\n        uint256 _amount\n    ) external onlyFactory {\n        require(_collateral != address(0x0), 'collateral-is-0');\n        require(_amount > 0, 'amount-is-0');\n\n        collateralSet.add(_collateral);\n        uint256 _maxTokens = IVaultFactory(factory).MAX_TOKENS_PER_VAULT();\n        require(collateralSet.length() <= _maxTokens, 'max-tokens-reached');\n\n        collateral[_collateral] += _amount;\n\n        emit CollateralAdded(_collateral, _amount, collateral[_collateral]);\n    }\n\n    /**\n     * @dev Removes a collateral token from the Vault and transfers it back to the sender.\n     * @param _collateral Address of the collateral token to remove.\n     * @param _amount Amount of the collateral token to remove.\n     * @param _to Address to receive the removed collateral.\n     */\n    function removeCollateral(\n        address _collateral,\n        uint256 _amount,\n        address _to\n    ) external onlyFactory {\n        require(_collateral != address(0x0), 'collateral-is-0');\n        require(_amount > 0, 'amount-is-0');\n\n        collateral[_collateral] -= _amount;\n        if (collateral[_collateral] == 0) {\n            collateralSet.remove(_collateral);\n        }\n\n        uint256 _healthFactor = healthFactor(false);\n        require(_healthFactor >= DECIMAL_PRECISION, 'health-factor-below-1');\n\n        IERC20(_collateral).safeTransfer(_to, _amount);\n\n        emit CollateralRemoved(_collateral, _amount, collateral[_collateral]);\n    }\n\n    /**\n     * @dev Adds bad debt to the Vault.\n     * @param _amount Amount of bad debt to add.\n     */\n    function addBadDebt(uint256 _amount) external onlyFactory {\n        require(_amount > 0, 'amount-is-0');\n\n        debt += _amount;\n        emit DebtAdded(_amount, debt);\n    }\n\n    /**\n     * @dev Calculates the maximum borrowable amount and the current borrowable amount.\n     * @return _maxBorrowable Maximum borrowable amount.\n     * @return _borrowable Current borrowable amount.\n     */\n    function borrowable()\n        public\n        view\n        returns (uint256 _maxBorrowable, uint256 _borrowable)\n    {\n        (_maxBorrowable, _borrowable) = borrowableWithDiff(\n            address(0x0),\n            0,\n            false,\n            false\n        );\n    }\n\n    /**\n     * @dev Borrows a specified amount from the Vault.\n     * @param _amount Amount to borrow.\n     */\n    function borrow(uint256 _amount) external onlyFactory {\n        require(_amount > 0, 'amount-is-0');\n\n        (uint256 _maxBorrowable, uint256 _borrowable) = borrowable();\n        require(_amount <= _borrowable, 'not-enough-borrowable');\n\n        debt += _amount;\n        require(debt <= _maxBorrowable, 'max-borrowable-reached');\n\n        emit DebtAdded(_amount, debt);\n    }\n\n    /**\n     * @dev Repays a specified amount to the Vault's debt.\n     * @param _amount Amount to repay.\n     */\n    function repay(uint256 _amount) external onlyFactory {\n        require(_amount <= debt, 'amount-exceeds-debt');\n\n        debt -= _amount;\n        emit DebtRepaid(_amount, debt);\n    }\n\n    /**\n     * @dev Calculates the stable amount needed and the redemption fee for redeeming collateral.\n     * @param _collateral Address of the collateral token.\n     * @param _collateralAmount Amount of collateral to redeem.\n     * @return _stableAmountNeeded Stablecoin amount required to redeem collateral.\n     * @return _redemptionFee Fee charged for the redemption.\n     */\n    function calcRedeem(\n        address _collateral,\n        uint256 _collateralAmount\n    )\n        public\n        view\n        returns (uint256 _stableAmountNeeded, uint256 _redemptionFee)\n    {\n        ITokenPriceFeed _priceFeed = ITokenPriceFeed(\n            IVaultFactory(factory).priceFeed()\n        );\n        uint256 _price = _priceFeed.tokenPrice(_collateral);\n\n        uint256 _normalizedCollateralAmount = _collateralAmount *\n            (10 ** (18 - _priceFeed.decimals(_collateral)));\n        _stableAmountNeeded =\n            (_normalizedCollateralAmount * _price) /\n            DECIMAL_PRECISION;\n\n        (, , uint256 _redemptionKickbackRate) = vaultExtraSettings\n            .getExtraSettings();\n\n        if (_redemptionKickbackRate > 0) {\n            uint256 _kickbackAmount = (_stableAmountNeeded *\n                _redemptionKickbackRate) / DECIMAL_PRECISION;\n            _stableAmountNeeded += _kickbackAmount;\n        }\n        uint256 _redemptionRate = IVaultFactoryConfig(factory).redemptionRate();\n        _redemptionFee =\n            (_stableAmountNeeded * _redemptionRate) /\n            DECIMAL_PRECISION;\n    }\n\n    /**\n     * @dev Redeems a specified amount of collateral, repays debt, and transfers collateral back to the redeemer.\n     * @param _collateral Address of the collateral token to redeem.\n     * @param _collateralAmount Amount of collateral to redeem.\n     * @return _debtRepaid Amount of debt repaid.\n     * @return _feeCollected Fee collected for the redemption.\n     */\n    function redeem(\n        address _collateral,\n        uint256 _collateralAmount\n    )\n        external\n        onlyFactory\n        returns (uint256 _debtRepaid, uint256 _feeCollected)\n    {\n        require(_collateral != address(0x0), 'collateral-is-0');\n        require(_collateralAmount > 0, 'amount-is-0');\n        require(collateralSet.contains(_collateral), 'collateral-not-added');\n        require(\n            collateral[_collateral] >= _collateralAmount,\n            'not-enough-collateral'\n        );\n\n        uint256 _currentHealthFactor = healthFactor(true);\n        uint256 _redemptionHealthFactorLimit = IVaultFactoryConfig(factory)\n            .redemptionHealthFactorLimit();\n        require(\n            _currentHealthFactor < _redemptionHealthFactorLimit,\n            'health-factor-above-redemption-limit'\n        );\n\n        (uint256 _debtTreshold, uint256 _maxRedeemablePercentage, ) = vaultExtraSettings\n            .getExtraSettings();\n\n        \n        collateral[_collateral] -= _collateralAmount;\n        (_debtRepaid, _feeCollected) = calcRedeem(\n            _collateral,\n            _collateralAmount\n        );\n\n        \n        if (debt > _debtTreshold) {\n            uint256 _redeemableDebt = (debt * _maxRedeemablePercentage) /\n                DECIMAL_PRECISION;\n            require(_debtRepaid <= _redeemableDebt, 'redeemable-debt-exceeded');\n\n        }\n\n        debt -= _debtRepaid;\n\n        if (collateral[_collateral] == 0) {\n            collateralSet.remove(_collateral);\n        }\n\n        IERC20(_collateral).safeTransfer(_msgSender(), _collateralAmount);\n\n        emit CollateralRedeemed(\n            _collateral,\n            _collateralAmount,\n            collateral[_collateral],\n            _debtRepaid,\n            _feeCollected\n        );\n        emit DebtRepaid(_debtRepaid, debt);\n    }\n\n    /**\n     * @dev Computes the health factor of the Vault.\n     * @param _useMlr Flag to use Minimum Loan Ratio (MLR) in health factor computation.\n     * @return _healthFactor Current health factor.\n     */\n    function healthFactor(\n        bool _useMlr\n    ) public view returns (uint256 _healthFactor) {\n        if (debt == 0) {\n            return type(uint256).max;\n        }\n\n        (uint256 _maxBorrowable, ) = borrowableWithDiff(\n            address(0x0),\n            0,\n            false,\n            _useMlr\n        );\n\n        _healthFactor = (_maxBorrowable * DECIMAL_PRECISION) / debt;\n    }\n\n    /**\n     * @dev Computes a new health factor given a new debt value.\n     * @param _newDebt New debt amount to calculate the health factor.\n     * @param _useMlr Flag to use Minimum Loan Ratio (MLR) in health factor computation.\n     * @return _newHealthFactor Calculated new health factor based on the new debt value.\n     */\n    function newHealthFactor(\n        uint256 _newDebt,\n        bool _useMlr\n    ) public view returns (uint256 _newHealthFactor) {\n        if (_newDebt == 0) {\n            return type(uint256).max;\n        }\n\n        (uint256 _maxBorrowable, ) = borrowableWithDiff(\n            address(0x0),\n            0,\n            false,\n            _useMlr\n        );\n        _newHealthFactor = (_maxBorrowable * DECIMAL_PRECISION) / _newDebt;\n    }\n\n    /**\n     * @dev Computes the maximum borrowable amount and the current borrowable amount.\n     * @param _collateral Address of the collateral token (0x0 for total vault borrowable).\n     * @param _diffAmount Difference in collateral amount when adding/removing collateral.\n     * @param _isAdd Flag indicating whether the collateral is added or removed.\n     * @param _useMlr Flag to use Minimum Loan Ratio (MLR) in borrowable computation.\n     * @return _maxBorrowable Maximum borrowable amount.\n     * @return _borrowable Current borrowable amount based on the collateral.\n     */\n    function borrowableWithDiff(\n        address _collateral,\n        uint256 _diffAmount,\n        bool _isAdd,\n        bool _useMlr\n    ) public view returns (uint256 _maxBorrowable, uint256 _borrowable) {\n        uint256 _newCollateralAmount = collateral[_collateral];\n\n        uint256 _borrowableAmount = 0;\n\n        if (_collateral != address(0x0)) {\n            require(\n                IVaultFactory(factory).isCollateralSupported(_collateral),\n                'collateral-not-supported'\n            );\n            if (_isAdd) {\n                _newCollateralAmount += _diffAmount;\n            } else {\n                _newCollateralAmount -= _diffAmount;\n            }\n        }\n\n        ITokenPriceFeed _priceFeed = ITokenPriceFeed(\n            IVaultFactory(factory).priceFeed()\n        );\n\n        for (uint256 i = 0; i < collateralSet.length(); i++) {\n            address _c = collateralSet.at(i);\n            uint256 _collateralAmount = _c == _collateral\n                ? _newCollateralAmount\n                : collateral[_c];\n            uint256 _price = _priceFeed.tokenPrice(_c);\n            uint256 _divisor = _useMlr\n                ? _priceFeed.mlr(_c)\n                : _priceFeed.mcr(_c);\n            uint256 _normalizedCollateralAmount = _collateralAmount *\n                (10 ** (18 - _priceFeed.decimals(_c)));\n\n            uint256 _collateralBorrowable = (_normalizedCollateralAmount *\n                _price) / DECIMAL_PRECISION;\n\n            _borrowableAmount +=\n                (_collateralBorrowable * DECIMAL_PRECISION) /\n                _divisor;\n        }\n\n        return (\n            _borrowableAmount,\n            (_borrowableAmount > debt) ? _borrowableAmount - debt : 0\n        );\n    }\n\n    /**\n     * @dev Liquidates the vault by repaying all debts with seized collateral.\n     * @return _forgivenDebt Amount of debt forgiven during liquidation.\n     */\n    function liquidate() external onlyFactory returns (uint256 _forgivenDebt) {\n        require(\n            healthFactor(true) < DECIMAL_PRECISION,\n            'liquidation-factor-above-1'\n        );\n\n        uint256 _debt = debt;\n        debt = 0;\n        ILiquidationRouter router = ILiquidationRouter(\n            IVaultFactory(factory).liquidationRouter()\n        );\n        for (uint256 i = 0; i < collateralSet.length(); i++) {\n            address _collateral = collateralSet.at(i);\n            uint256 _collateralAmount = collateral[_collateral];\n            uint256 _actualCollateralBalance = IERC20(_collateral).balanceOf(\n                address(this)\n            );\n            if (_actualCollateralBalance < _collateralAmount) {\n                _collateralAmount = _actualCollateralBalance;\n            }\n            collateral[_collateral] = 0;\n\n            IERC20(_collateral).safeApprove(\n                IVaultFactory(factory).liquidationRouter(),\n                0\n            );\n            IERC20(_collateral).safeApprove(\n                IVaultFactory(factory).liquidationRouter(),\n                _collateralAmount\n            );\n\n            router.addSeizedCollateral(_collateral, _collateralAmount);\n        }\n        router.addUnderWaterDebt(address(this), _debt);\n        router.tryLiquidate();\n        _forgivenDebt = _debt;\n    }\n}\n\n\n// File: contracts/VaultBorrowRate.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport './interfaces/IVault.sol';\nimport './interfaces/ITokenPriceFeed.sol';\nimport './interfaces/IVaultFactory.sol';\n\n/**\n * @title VaultBorrowRate\n * @notice Contract to calculate the borrow rate for a given Vault\n */\ncontract VaultBorrowRate {\n    /**\n     * @notice Calculates the borrow rate for a specified Vault\n     * @param _vaultAddress The address of the Vault for which to calculate the borrow rate\n     * @return uint256 The calculated borrow rate\n     */\n    function getBorrowRate(\n        address _vaultAddress\n    ) external view returns (uint256) {\n        IVault _vault = IVault(_vaultAddress);\n        IVaultFactory _vaultFactory = IVaultFactory(_vault.factory());\n        ITokenPriceFeed _priceFeed = ITokenPriceFeed(_vaultFactory.priceFeed());\n        uint256 _totalWeightedFee;\n        uint256 _totalCollateralValue;\n        uint256 _collateralsLength = _vault.collateralsLength();\n\n        for (uint256 i; i < _collateralsLength; i++) {\n            address _collateralAddress = _vault.collateralAt(i);\n            uint256 _collateralAmount = _vault.collateral(_collateralAddress);\n            uint256 _price = _priceFeed.tokenPrice(_collateralAddress);\n            uint256 _borrowRate = _priceFeed.borrowRate(_collateralAddress);\n\n            uint256 _normalizedCollateralAmount = _collateralAmount *\n                (10 ** (18 - _priceFeed.decimals(_collateralAddress)));\n            uint256 _collateralValue = (_normalizedCollateralAmount * _price) /\n                (10 ** _priceFeed.decimals(_collateralAddress));\n            uint256 _weightedFee = (_collateralValue * _borrowRate) / 1e18;\n\n            _totalCollateralValue += _collateralValue;\n            _totalWeightedFee += _weightedFee;\n        }\n\n        return ((_totalWeightedFee * 1e18) / _totalCollateralValue) / 100;\n    }\n}\n\n\n// File: contracts/VaultDeployer.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport './Vault.sol';\nimport './interfaces/IVaultExtraSettings.sol';\n/**\n * @title VaultDeployer\n * @notice A contract responsible for deploying new instances of the Vault contract.\n */\ncontract VaultDeployer {\n    IVaultExtraSettings public immutable vaultExtraSettings;\n\n    constructor(address _vaultExtraSettings) {\n        require(\n            _vaultExtraSettings != address(0x0),\n            'vault-extra-settings-is-zero'\n        );\n        vaultExtraSettings = IVaultExtraSettings(_vaultExtraSettings);\n    }\n    /**\n     * @notice Deploys a new Vault contract.\n     * @param _factory The address of the factory contract managing the vaults.\n     * @param _vaultOwner The address of the intended owner of the new vault.\n     * @param _name The name of the new vault.\n     * @return The address of the newly created Vault contract.\n     */\n    function deployVault(\n        address _factory,\n        address _vaultOwner,\n        string memory _name\n    ) external returns (address) {\n        // Deploy a new instance of the Vault contract\n        Vault vault = new Vault(\n            _factory,\n            _vaultOwner,\n            _name,\n            vaultExtraSettings\n        );\n        return address(vault);\n    }\n}\n\n\n// File: contracts/VaultExtraSettings.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport './interfaces/IVaultExtraSettings.sol';\nimport '@openzeppelin/contracts/access/Ownable.sol';\n\n/**\n * @title VaultExtraSettings\n * @notice Contract to manage extra settings for a Vault\n */\ncontract VaultExtraSettings is IVaultExtraSettings, Ownable {\n    uint256 public debtTreshold;\n    uint256 public maxRedeemablePercentage;\n    uint256 public redemptionKickback;\n\n    /**\n     * @dev Sets the maximum redeemable percentage for a Vault.\n     * @param _debtTreshold The debt treshold for the Vault, in order to enable percentage redemption.\n     * @param _maxRedeemablePercentage The maximum redeemable percentage for the Vault.\n     */\n    function setMaxRedeemablePercentage(\n        uint256 _debtTreshold,\n        uint256 _maxRedeemablePercentage\n    ) external override onlyOwner {\n        debtTreshold = _debtTreshold;\n        maxRedeemablePercentage = _maxRedeemablePercentage;\n    }\n\n    /**\n     * @dev Sets the redemption kickback for a Vault.\n     * @param _redemptionKickback The redemption kickback for the Vault.\n     */\n    function setRedemptionKickback(\n        uint256 _redemptionKickback\n    ) external override onlyOwner {\n        redemptionKickback = _redemptionKickback;\n    }\n\n    /**\n     * @dev Retrieves the extra settings for a Vault.\n     * @return _debtTreshold debt treshold for enabling max redeemable percentage, _maxRedeemablePercentage maximum redeemable percentage, _redemptionKickback redemption fee kickback to the vault\n     */\n    function getExtraSettings()\n        external\n        view\n        override\n        returns (\n            uint256 _debtTreshold,\n            uint256 _maxRedeemablePercentage,\n            uint256 _redemptionKickback\n        )\n    {\n        return (debtTreshold, maxRedeemablePercentage, redemptionKickback);\n    }\n}\n\n\n// File: contracts/VaultFactory.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport '@openzeppelin/contracts/interfaces/IERC20Metadata.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\nimport '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';\nimport '@openzeppelin/contracts/security/ReentrancyGuard.sol';\n\nimport './utils/linked-address-list.sol';\nimport './Vault.sol';\nimport './VaultFactoryConfig.sol';\nimport './VaultFactoryList.sol';\nimport './interfaces/IWETH.sol';\nimport './interfaces/ITokenPriceFeed.sol';\nimport './interfaces/IPriceFeed.sol';\nimport './interfaces/IMintableTokenOwner.sol';\nimport './interfaces/IMintableToken.sol';\nimport './interfaces/IVaultDeployer.sol';\nimport './interfaces/IVaultBorrowRate.sol';\n\nimport 'hardhat/console.sol';\n/**\n * @title VaultFactory\n * @dev Manages the creation, configuration, and operations of Vaults with collateral and borrowing functionality.\n */\ncontract VaultFactory is ReentrancyGuard, VaultFactoryConfig, VaultFactoryList {\n    // Events emitted by the contract\n    event NewVault(address indexed vault, string name, address indexed owner);\n    event VaultOwnerChanged(\n        address indexed vault,\n        address indexed oldOwner,\n        address indexed newOwner\n    );\n\n    // Libraries used by the contract\n    using LinkedAddressList for LinkedAddressList.List;\n    using SafeERC20 for IERC20;\n    using SafeERC20 for IMintableToken;\n\n    // Immutable state variables\n    address public immutable stable;\n    address public immutable nativeWrapped;\n    IMintableTokenOwner public immutable mintableTokenOwner;\n\n    // State variables\n    mapping(address => uint256) public collateral;\n    uint256 public totalDebt;\n\n    /**\n     * @dev Constructor to initialize essential addresses and contracts for VaultFactory.\n     * @param _mintableTokenOwner Address of the Mintable Token Owner contract.\n     * @param _nativeWrapped Address of the native wrapped token.\n     * @param _priceFeed Address of the price feed contract.\n     * @param _vaultDeployer Address of the Vault Deployer contract.\n     * @param _liquidationRouter Address of the liquidation router contract.\n     * @param _borrowRate Address of the borrow rate contract.\n     */\n    constructor(\n        address _mintableTokenOwner,\n        address _nativeWrapped,\n        address _priceFeed,\n        address _vaultDeployer,\n        address _liquidationRouter,\n        address _borrowRate\n    ) VaultFactoryConfig(_vaultDeployer, _liquidationRouter) {\n        require(\n            _mintableTokenOwner != address(0x0),\n            'mintable-token-owner-is-0'\n        );\n\n        mintableTokenOwner = IMintableTokenOwner(_mintableTokenOwner);\n        stable = address(mintableTokenOwner.token());\n\n        require(stable != address(0x0), 'stable-is-0');\n        require(_nativeWrapped != address(0x0), 'nativew-is-0');\n        require(_priceFeed != address(0x0), 'pricefeed-is-0');\n        require(_borrowRate != address(0x0), 'borrow-rate-is-0');\n\n        borrowRate = _borrowRate;\n        nativeWrapped = _nativeWrapped;\n        priceFeed = _priceFeed;\n    }\n\n    /**\n     * @dev Fallback function to receive Ether and restricts its usage to a designated sender.\n     */\n    receive() external payable {\n        require(msg.sender == nativeWrapped, 'only-native-wrapped');\n    }\n\n    /**\n     * @dev Modifier: Allows function execution only by the owner of a specific vault.\n     * @param _vault The address of the vault to check ownership.\n     */\n    modifier onlyVaultOwner(address _vault) {\n        require(Vault(_vault).vaultOwner() == _msgSender(), 'only-vault-owner');\n        _;\n    }\n\n    /**\n     * @dev Modifier: Allows function execution by the owner or an operator of a specific vault.\n     * @param _vault The address of the vault to check ownership or operator status.\n     */\n    modifier onlyVaultOwnerOrOperator(address _vault) {\n        require(\n            Vault(_vault).vaultOwner() == _msgSender() ||\n                Vault(_vault).isOperator(_msgSender()),\n            'only-vault-owner-or-operator'\n        );\n        _;\n    }\n\n    /**\n     * @dev Modifier: Allows function execution only by the liquidation router.\n     */\n    modifier onlyLiquidationRouter() {\n        require(liquidationRouter == _msgSender(), 'only-liquidation-router');\n        _;\n    }\n\n    /**\n     * @dev Checks if a given collateral token is supported.\n     * @param _collateral The address of the collateral token.\n     * @return A boolean indicating whether the collateral token is supported.\n     */\n    function isCollateralSupported(\n        address _collateral\n    ) external view returns (bool) {\n        return _isCollateralSupported(_collateral);\n    }\n\n    /**\n     * @dev Transfers ownership of a vault to a new owner.\n     * @param _vault The address of the vault to transfer ownership.\n     * @param _newOwner The address of the new owner to receive the vault ownership.\n     */\n    function transferVaultOwnership(\n        address _vault,\n        address _newOwner\n    ) external onlyVaultOwner(_vault) {\n        address _msgSender = _msgSender();\n        require(_newOwner != address(0x0), 'new-owner-is-0');\n        require(containsVault(_vault), 'vault-not-found');\n\n        emit VaultOwnerChanged(_vault, _msgSender, _newOwner);\n        Vault(_vault).transferVaultOwnership(_newOwner);\n        _transferVault(_msgSender, _newOwner, _vault);\n    }\n\n    /**\n     * @dev Creates a new vault with a specified name.\n     * @param _name The name of the new vault.\n     * @return The address of the newly created vault.\n     */\n    function createVault(string memory _name) public returns (address) {\n        address _msgSender = _msgSender();\n        address _vaultAddress = IVaultDeployer(vaultDeployer).deployVault(\n            address(this),\n            _msgSender,\n            _name\n        );\n        _addVault(_msgSender, _vaultAddress);\n        emit NewVault(_vaultAddress, _name, _msgSender);\n\n        return _vaultAddress;\n    }\n\n    /**\n     * @dev Checks if a specific collateral token is supported for the vault.\n     * @param _collateral The address of the collateral token to check.\n     * @return A boolean indicating whether the collateral token is supported.\n     */\n    function _isCollateralSupported(\n        address _collateral\n    ) internal view returns (bool) {\n        ITokenPriceFeed _priceFeed = ITokenPriceFeed(priceFeed);\n        return (_priceFeed.tokenPriceFeed(_collateral) != address(0x0));\n    }\n\n    /**\n     * @dev Adds native-wrapped collateral to a specific vault.\n     * @param _vault The address of the vault to add collateral.\n     */\n    function addCollateralNative(address _vault) external payable {\n        require(containsVault(_vault), 'vault-not-found');\n        require(\n            _isCollateralSupported(nativeWrapped),\n            'collateral-not-supported'\n        );\n        uint256 _amount = msg.value;\n\n        collateral[nativeWrapped] += _amount;\n\n        require(\n            collateral[nativeWrapped] <= collateralCap[nativeWrapped],\n            'collateral-cap-reached'\n        );\n\n        IWETH(nativeWrapped).deposit{value: _amount}();\n        IERC20(nativeWrapped).safeTransferFrom(address(this), _vault, _amount);\n\n        Vault(_vault).addCollateral(nativeWrapped, _amount);\n    }\n\n    /**\n     * @dev Removes native-wrapped collateral from a specific vault.\n     * @param _vault The address of the vault to remove collateral.\n     * @param _amount The amount of collateral to be removed.\n     * @param _to The address where the removed collateral is transferred.\n     */\n    function removeCollateralNative(\n        address _vault,\n        uint256 _amount,\n        address _to\n    ) external onlyVaultOwner(_vault) {\n        require(containsVault(_vault), 'vault-not-found');\n        require(\n            _isCollateralSupported(nativeWrapped),\n            'collateral-not-supported'\n        );\n\n        Vault(_vault).removeCollateral(nativeWrapped, _amount, address(this));\n\n        collateral[nativeWrapped] -= _amount;\n\n        IWETH(nativeWrapped).withdraw(_amount);\n        (bool success, ) = payable(_to).call{value: _amount}(\"\");\n        require(success, 'transfer-failed');\n    }\n\n    /**\n     * @dev Adds a specific collateral to a vault.\n     * @param _vault The address of the vault to add collateral.\n     * @param _collateral The address of the collateral token to add.\n     * @param _amount The amount of collateral to add.\n     */\n    function addCollateral(\n        address _vault,\n        address _collateral,\n        uint256 _amount\n    ) external {\n        require(containsVault(_vault), 'vault-not-found');\n        require(\n            _isCollateralSupported(_collateral),\n            'collateral-not-supported'\n        );\n\n        collateral[_collateral] += _amount;\n\n        require(\n            collateral[_collateral] <= collateralCap[_collateral],\n            'collateral-cap-reached'\n        );\n\n        IERC20(_collateral).safeTransferFrom(_msgSender(), _vault, _amount);\n        Vault(_vault).addCollateral(_collateral, _amount);\n    }\n\n    /**\n     * @dev Removes a specific collateral from a vault.\n     * @param _vault The address of the vault to remove collateral.\n     * @param _collateral The address of the collateral token to remove.\n     * @param _amount The amount of collateral to remove.\n     * @param _to The address where the removed collateral is transferred.\n     */\n    function removeCollateral(\n        address _vault,\n        address _collateral,\n        uint256 _amount,\n        address _to\n    ) external onlyVaultOwner(_vault) {\n        require(containsVault(_vault), 'vault-not-found');\n        require(\n            _isCollateralSupported(_collateral),\n            'collateral-not-supported'\n        );\n\n        collateral[_collateral] -= _amount;\n        Vault(_vault).removeCollateral(_collateral, _amount, _to);\n    }\n\n    /**\n     * @dev Borrows funds from a vault by its owner or an operator.\n     * @param _vault The address of the vault from which funds are borrowed.\n     * @param _amount The amount of funds to borrow.\n     * @param _to The address where borrowed funds are sent.\n     */\n    function borrow(\n        address _vault,\n        uint256 _amount,\n        address _to\n    ) external onlyVaultOwnerOrOperator(_vault) {\n        require(containsVault(_vault), 'vault-not-found');\n        require(_to != address(0x0), 'to-is-0');\n\n        totalDebt += _amount;\n        _updateDebtWindow(_amount);\n        Vault(_vault).borrow(_amount);\n        uint256 _borrowRate = IVaultBorrowRate(borrowRate).getBorrowRate(\n            _vault\n        );\n        uint256 _feeAmount = (_amount * _borrowRate) / DECIMAL_PRECISION;\n\n        mintableTokenOwner.mint(_to, _amount - _feeAmount);\n        mintableTokenOwner.mint(borrowFeeRecipient, _feeAmount);\n    }\n\n    /**\n     * @dev Distributes bad debt to a specific vault.\n     * @param _vault The address of the vault to distribute bad debt.\n     * @param _amount The amount of bad debt to be distributed.\n     */\n    function distributeBadDebt(\n        address _vault,\n        uint256 _amount\n    ) external nonReentrant onlyLiquidationRouter {\n        require(containsVault(_vault), 'vault-not-found');\n        totalDebt += _amount;\n        Vault(_vault).addBadDebt(_amount);\n    }\n\n    /**\n     * @dev Closes a vault if it meets specific conditions.\n     * @param _vault The address of the vault to close.\n     */\n    function closeVault(address _vault) external onlyVaultOwner(_vault) {\n        require(containsVault(_vault), 'vault-not-found');\n        require(Vault(_vault).debt() == 0, 'debt-not-0');\n        require(Vault(_vault).collateralsLength() == 0, 'collateral-not-0');\n\n        _removeVault(_msgSender(), _vault);\n    }\n\n    /**\n     * @dev Repays borrowed funds for a specific vault.\n     * @param _vault The address of the vault for which funds are repaid.\n     * @param _amount The amount of funds to repay.\n     */\n    function repay(address _vault, uint256 _amount) external {\n        require(containsVault(_vault), 'vault-not-found');\n        totalDebt -= _amount;\n        Vault(_vault).repay(_amount);\n\n        IMintableToken(stable).safeTransferFrom(\n            _msgSender(),\n            address(this),\n            _amount\n        );\n        IMintableToken(stable).burn(_amount);\n    }\n\n    /**\n     * @dev Redeems collateral from a vault after meeting specific conditions.\n     * @param _vault The address of the vault from which collateral is redeemed.\n     * @param _collateral The address of the collateral token to redeem.\n     * @param _collateralAmount The amount of collateral to redeem.\n     * @param _to The address where the redeemed collateral is transferred.\n     */\n    function redeem(\n        address _vault,\n        address _collateral,\n        uint256 _collateralAmount,\n        address _to\n    ) external nonReentrant {\n        require(containsVault(_vault), 'vault-not-found');\n        require(_to != address(0x0), 'to-is-0');\n\n        require(isReedemable(_vault, _collateral), 'not-redeemable');\n\n        (uint256 _debtRepaid, uint256 _feeCollected) = Vault(_vault).redeem(\n            _collateral,\n            _collateralAmount\n        );\n\n        totalDebt -= _debtRepaid;\n        collateral[_collateral] -= _collateralAmount;\n\n        IMintableToken(stable).safeTransferFrom(\n            _msgSender(),\n            address(this),\n            _debtRepaid + _feeCollected\n        );\n        IMintableToken(stable).burn(_debtRepaid);\n        IMintableToken(stable).transfer(redemptionFeeRecipient, _feeCollected);\n\n        IERC20(_collateral).safeTransfer(_to, _collateralAmount);\n    }\n\n    /**\n     * @dev Liquidates a specific vault if it is eligible for liquidation.\n     * @param _vault The address of the vault to be liquidated.\n     */\n    function liquidate(address _vault) external nonReentrant {\n        require(containsVault(_vault), 'vault-not-found');\n\n        address _vaultOwner = Vault(_vault).vaultOwner();\n        uint256 _forgivenDebt = Vault(_vault).liquidate();\n\n        totalDebt -= _forgivenDebt;\n\n        _removeVault(_vaultOwner, _vault);\n    }\n\n    /**\n     * @dev Checks if a vault is eligible for liquidation.\n     * @param _vault The address of the vault to check for liquidation eligibility.\n     * @return A boolean indicating whether the vault is liquidatable.\n     */\n    function isLiquidatable(address _vault) external view returns (bool) {\n        require(containsVault(_vault), 'vault-not-found');\n        return Vault(_vault).healthFactor(true) < DECIMAL_PRECISION;\n    }\n\n    /**\n     * @dev Checks if a specific collateral can be redeemed from a vault based on conditions.\n     * @param _vault The address of the vault to check for collateral redemption.\n     * @param _collateral The address of the collateral token to check for redemption.\n     * @notice Collateral with higher MCR can be redeemed first\n     * @return A boolean indicating whether the collateral is redeemable.\n     */\n    function isReedemable(\n        address _vault,\n        address _collateral\n    ) public view returns (bool) {\n        require(\n            _isCollateralSupported(_collateral),\n            'collateral-not-supported'\n        );\n        if (!Vault(_vault).containsCollateral(_collateral)) {\n            return false;\n        }\n        uint256 _healthFactor = Vault(_vault).healthFactor(false);\n        if (_healthFactor >= redemptionHealthFactorLimit) {\n            return false;\n        }\n\n        ITokenPriceFeed _priceFeed = ITokenPriceFeed(priceFeed);\n        uint256 _collateralMcr = _priceFeed.mcr(_collateral);\n\n        address[] memory _collaterals = Vault(_vault).collaterals();\n        uint256 _length = _collaterals.length;\n\n        for (uint256 i; i < _length; i++) {\n            if (_collaterals[i] != _collateral) {\n                uint256 _mcr = _priceFeed.mcr(_collaterals[i]);\n                if (_mcr > _collateralMcr) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * @dev Updates the debt window with the newly incurred debt.\n     * @param _newDebt The amount of new debt to update in the debt window.\n     */\n    function _updateDebtWindow(uint256 _newDebt) internal {\n        require(totalDebt <= debtCeiling, 'debt-ceiling-reached');\n\n        if (block.timestamp > lastDebtWindow + debtWindowSize) {\n            debtWindowAmount = _newDebt;\n            lastDebtWindow = block.timestamp;\n        } else {\n            debtWindowAmount += _newDebt;\n        }\n        require(\n            debtWindowAmount <= maxDebtPerWindow,\n            'debt-window-amount-reached'\n        );\n    }\n}\n\n\n// File: contracts/VaultFactoryConfig.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\nimport './utils/constants.sol';\nimport '@openzeppelin/contracts/access/Ownable.sol';\n\nabstract contract VaultFactoryConfig is Constants, Ownable {\n    event PriceFeedUpdated(address indexed priceFeed);\n    event MaxTokensPerVaultUpdated(\n        uint256 oldMaxTokensPerVault,\n        uint256 newMaxTokensPerVault\n    );\n    event RedemptionRateUpdated(\n        uint256 oldRedemptionRate,\n        uint256 newRedemptionRate\n    );\n    event BorrowRateUpdated(address oldBorrowRate, address newBorrowRate);\n    event RedemptionHealthFactorLimitUpdated(\n        uint256 oldRedemptionHealthFactorLimit,\n        uint256 newRedemptionHealthFactorLimit\n    );\n    event DebtCeilingUpdated(uint256 oldDebtCeiling, uint256 newDebtCeiling);\n    event MaxDebtPerWindowUpdated(\n        uint256 oldMaxDebtPerWindow,\n        uint256 newMaxDebtPerWindow\n    );\n    event DebtWindowSizeUpdated(\n        uint256 oldDebtWindowSize,\n        uint256 newDebtWindowSize\n    );\n    event CollateralCapacityUpdated(\n        address indexed collateral,\n        uint256 oldCapacity,\n        uint256 newCapacity\n    );\n    event liquidationRouterUpdated(address indexed liquidationRouter);\n\n    // Various configuration parameters\n    address public priceFeed;\n    address public borrowRate;\n\n    uint256 public MAX_TOKENS_PER_VAULT = 5;\n    uint256 public redemptionRate = PERCENT_05; // 0.5%\n\n    uint256 public redemptionHealthFactorLimit = 1.5 ether; // 2.0 HF\n\n    address public borrowFeeRecipient;\n    address public redemptionFeeRecipient;\n\n    mapping(address => uint256) public collateralCap;\n\n    uint256 public debtCeiling = type(uint256).max; // max stablecoin debt issued by the protocol\n\n    uint256 public maxDebtPerWindow = 2000 ether; // 1M\n    uint256 public debtWindowSize = 1 hours;\n    uint256 public lastDebtWindow;\n    uint256 public debtWindowAmount;\n\n    address public vaultDeployer;\n    address public liquidationRouter;\n\n    /**\n     * @dev Set the address for the Vault Deployer\n     * @param _vaultDeployer Address of the Vault Deployer\n     */\n    function setVaultDeployer(address _vaultDeployer) external onlyOwner {\n        require(_vaultDeployer != address(0x0), 'vault-deployer-is-0');\n        vaultDeployer = _vaultDeployer;\n    }\n\n    /**\n     * @dev Set the address for the Liquidation Router\n     * @param _liquidationRouter Address of the Liquidation Router\n     */\n    function setLiquidationRouter(\n        address _liquidationRouter\n    ) external onlyOwner {\n        require(_liquidationRouter != address(0x0), 'liquidation-router-is-0');\n        liquidationRouter = _liquidationRouter;\n        emit liquidationRouterUpdated(_liquidationRouter);\n    }\n\n    /**\n     * @dev Set the collateral capacity for a specific collateral token\n     * @param _collateral Address of the collateral token\n     * @param _cap The new capacity for the collateral token\n     */\n    function setCollateralCapacity(\n        address _collateral,\n        uint256 _cap\n    ) external onlyOwner {\n        require(_collateral != address(0x0), 'collateral-is-0');\n        emit CollateralCapacityUpdated(\n            _collateral,\n            collateralCap[_collateral],\n            _cap\n        );\n        collateralCap[_collateral] = _cap;\n    }\n\n    /**\n     * @dev Set the debt ceiling value.\n     * @param _debtCeiling The new debt ceiling value to be set.\n     */\n    function setDebtCeiling(uint256 _debtCeiling) external onlyOwner {\n        emit DebtCeilingUpdated(debtCeiling, _debtCeiling);\n        debtCeiling = _debtCeiling;\n    }\n\n    /**\n     * @dev Set the maximum debt allowed per window.\n     * @param _maxDebtPerWindow The new maximum debt per window value to be set.\n     */\n    function setMaxDebtPerWindow(uint256 _maxDebtPerWindow) external onlyOwner {\n        emit MaxDebtPerWindowUpdated(maxDebtPerWindow, _maxDebtPerWindow);\n        maxDebtPerWindow = _maxDebtPerWindow;\n    }\n\n    /**\n     * @dev Set the window size for debt.\n     * @param _debtWindowSize The new debt window size value to be set.\n     */\n    function setDebtWindowSize(uint256 _debtWindowSize) external onlyOwner {\n        emit DebtWindowSizeUpdated(debtWindowSize, _debtWindowSize);\n        debtWindowSize = _debtWindowSize;\n    }\n\n    /**\n     * @dev Set the maximum tokens allowed per vault.\n     * @param _maxTokensPerVault The new maximum tokens per vault value to be set.\n     */\n    function setMaxTokensPerVault(\n        uint256 _maxTokensPerVault\n    ) external onlyOwner {\n        require(_maxTokensPerVault > 0, 'max-tokens-per-vault-is-0');\n        emit MaxTokensPerVaultUpdated(MAX_TOKENS_PER_VAULT, _maxTokensPerVault);\n        MAX_TOKENS_PER_VAULT = _maxTokensPerVault;\n    }\n\n    /**\n     * @dev Set the address for the price feed.\n     * @param _priceFeed Address of the new price feed contract.\n     */\n    function setPriceFeed(address _priceFeed) external onlyOwner {\n        require(_priceFeed != address(0x0), 'pricefeed-is-0');\n        priceFeed = _priceFeed;\n        emit PriceFeedUpdated(_priceFeed);\n    }\n\n    /**\n     * @dev Set the redemption rate for the protocol.\n     * @param _redemptionRate The new redemption rate value to be set.\n     */\n    function setRedemptionRate(uint256 _redemptionRate) external onlyOwner {\n        require(\n            _redemptionRate <= MAX_REDEMPTION_RATE,\n            'redemption-rate-too-high'\n        );\n        emit RedemptionRateUpdated(redemptionRate, _redemptionRate);\n        redemptionRate = _redemptionRate;\n    }\n\n    /**\n     * @dev Set the address for the borrow rate.\n     * @param _borrowRate Address of the new borrow rate contract.\n     */\n    function setBorrowRate(address _borrowRate) external onlyOwner {\n        require(_borrowRate != address(0), 'borrow-rate-is-0');\n        emit BorrowRateUpdated(borrowRate, _borrowRate);\n        borrowRate = _borrowRate;\n    }\n\n    /**\n     * @dev Set the redemption health factor limit.\n     * @param _redemptionHealthFactorLimit The new redemption health factor limit to be set.\n     */\n    function setRedemptionHealthFactorLimit(\n        uint256 _redemptionHealthFactorLimit\n    ) external onlyOwner {\n        emit RedemptionHealthFactorLimitUpdated(\n            redemptionHealthFactorLimit,\n            _redemptionHealthFactorLimit\n        );\n        redemptionHealthFactorLimit = _redemptionHealthFactorLimit;\n    }\n\n    /**\n     * @dev Set the address for the borrow fee recipient.\n     * @param _borrowFeeRecipient Address of the new borrow fee recipient.\n     */\n    function setBorrowFeeRecipient(\n        address _borrowFeeRecipient\n    ) external onlyOwner {\n        require(\n            _borrowFeeRecipient != address(0x0),\n            'borrow-fee-recipient-is-0'\n        );\n        borrowFeeRecipient = _borrowFeeRecipient;\n    }\n\n    /**\n     * @dev Set the address for the redemption fee recipient.\n     * @param _redemptionFeeRecipient Address of the new redemption fee recipient.\n     */\n    function setRedemptionFeeRecipient(\n        address _redemptionFeeRecipient\n    ) external onlyOwner {\n        require(\n            _redemptionFeeRecipient != address(0x0),\n            'redemption-fee-recipient-is-0'\n        );\n        redemptionFeeRecipient = _redemptionFeeRecipient;\n    }\n\n    /**\n     * @dev Constructor to initialize the configuration settings upon deployment\n     * @param _vaultDeployer Address of the Vault Deployer\n     * @param _liquidationRouter Address of the Liquidation Router\n     */\n    constructor(address _vaultDeployer, address _liquidationRouter) {\n        require(_vaultDeployer != address(0x0), 'vault-deployer-is-0');\n        require(_liquidationRouter != address(0x0), 'liquidation-factory-is-0');\n        vaultDeployer = _vaultDeployer;\n        borrowFeeRecipient = _msgSender();\n        redemptionFeeRecipient = _msgSender();\n        lastDebtWindow = block.timestamp;\n        liquidationRouter = _liquidationRouter;\n    }\n}\n\n\n// File: contracts/VaultFactoryHelper.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\n\nimport './interfaces/IVaultFactory.sol';\nimport './interfaces/IVault.sol';\nimport './interfaces/ITokenPriceFeed.sol';\n\n/**\n * @title VaultFactoryHelper\n * @notice Helper contract providing various functions to retrieve information about vaults in a vault factory\n */\ncontract VaultFactoryHelper {\n    uint256 public constant DECIMAL_PRECISION = 1e18;\n\n    /**\n     * @notice Retrieves all vault addresses within a vault factory\n     * @param _vaultFactory Address of the vault factory\n     * @return An array of vault addresses\n     */\n    function getAllVaults(\n        address _vaultFactory\n    ) public view returns (address[] memory) {\n        IVaultFactory vaultFactory = IVaultFactory(_vaultFactory);\n        uint256 vaultCount = vaultFactory.vaultCount();\n        if (vaultCount == 0) {\n            return new address[](0);\n        } else {\n            address[] memory vaults = new address[](vaultCount);\n            vaults[0] = vaultFactory.firstVault();\n            for (uint256 i = 1; i < vaultCount; i++) {\n                vaults[i] = vaultFactory.nextVault(vaults[i - 1]);\n            }\n            return vaults;\n        }\n    }\n\n    /**\n     * @notice Retrieves the Total Value Locked (TVL) of a specific vault based on a single collateral type\n     * @param _vaultAddress Address of the vault\n     * @param _collateralAddress Address of the collateral asset\n     * @return The TVL of the vault for the given collateral\n     */\n    function getVaultTvlByCollateral(\n        address _vaultAddress,\n        address _collateralAddress\n    ) public view returns (uint256) {\n        IVault _vault = IVault(_vaultAddress);\n        uint256 _collateralAmount = _vault.collateral(_collateralAddress);\n        ITokenPriceFeed _priceFeed = ITokenPriceFeed(\n            IVaultFactory(_vault.factory()).priceFeed()\n        );\n        uint256 _price = _priceFeed.tokenPrice(_collateralAddress);\n        uint256 _normalizedCollateralAmount = _collateralAmount *\n            (10 ** (18 - _priceFeed.decimals(_collateralAddress)));\n        uint256 _tvl = (_normalizedCollateralAmount * _price) /\n            DECIMAL_PRECISION;\n        return _tvl;\n    }\n\n    /**\n     * @notice Retrieves the Total Value Locked (TVL) of a vault across all collateral types it holds\n     * @param _vault Address of the vault\n     * @return The total TVL of the vault across all collateral types\n     */\n    function getVaultTvl(address _vault) public view returns (uint256) {\n        IVault vault = IVault(_vault);\n        uint256 tvl = 0;\n        for (uint256 i = 0; i < vault.collateralsLength(); i++) {\n            address _collateralAddress = vault.collateralAt(i);\n            tvl += getVaultTvlByCollateral(_vault, _collateralAddress);\n        }\n        return tvl;\n    }\n\n    /**\n     * @notice Retrieves an array of liquidatable vault addresses within a vault factory\n     * @param _vaultFactory Address of the vault factory\n     * @return An array of liquidatable vault addresses\n     */\n    function getLiquidatableVaults(\n        address _vaultFactory\n    ) public view returns (address[] memory) {\n        IVaultFactory vaultFactory = IVaultFactory(_vaultFactory);\n        uint256 vaultCount = vaultFactory.vaultCount();\n        uint256 liquidatableVaultCount = 0;\n        if (vaultCount == 0) {\n            return new address[](0);\n        } else {\n            address[] memory _vaults = getAllVaults(_vaultFactory);\n            address[] memory _liquidatableVaults = new address[](vaultCount);\n\n            for (uint256 i = 0; i < vaultCount; i++) {\n                IVault _vault = IVault(_vaults[i]);\n                if (vaultFactory.isLiquidatable(address(_vault))) {\n                    _liquidatableVaults[liquidatableVaultCount] = address(\n                        _vault\n                    );\n                    liquidatableVaultCount++;\n                }\n            }\n\n            address[] memory liquidatableVaults = new address[](\n                liquidatableVaultCount\n            );\n            for (uint256 i = 0; i < liquidatableVaultCount; i++) {\n                liquidatableVaults[i] = _liquidatableVaults[i];\n            }\n\n            return liquidatableVaults;\n        }\n    }\n\n    /**\n     * @notice Retrieves an array of redeemable vault addresses and their corresponding redeemable collaterals\n     * @param _vaultFactory Address of the vault factory\n     * @param _useMlr Boolean indicating whether to use MLR for health factor calculation\n     * @return redeemableVaults An array of redeemable vault addresses\n     * @return redeemableCollaterals An array of corresponding redeemable collateral addresses\n     */\n    function getRedeemableVaults(\n        address _vaultFactory,\n        bool _useMlr\n    )\n        public\n        view\n        returns (\n            address[] memory redeemableVaults,\n            address[] memory redeemableCollaterals\n        )\n    {\n        IVaultFactory vaultFactory = IVaultFactory(_vaultFactory);\n        uint256 vaultCount = vaultFactory.vaultCount();\n        uint256 redeemableVaultCount = 0;\n        uint256 healthFactorLimit = vaultFactory.redemptionHealthFactorLimit();\n        if (vaultCount == 0) {\n            return (new address[](0), new address[](0));\n        } else {\n            address[] memory _vaults = getAllVaults(_vaultFactory);\n            address[] memory _redeemableVaults = new address[](vaultCount);\n            address[] memory _redeemableCollaterals = new address[](vaultCount);\n\n            for (uint256 i = 0; i < vaultCount; i++) {\n                IVault _vault = IVault(_vaults[i]);\n                if (_vault.healthFactor(_useMlr) < healthFactorLimit) {\n                    _redeemableVaults[redeemableVaultCount] = address(_vault);\n\n                    address[] memory _collaterals = getVaultCollaterals(\n                        address(_vault)\n                    );\n\n                    for (uint256 j = 0; j < _collaterals.length; j++) {\n                        if (\n                            vaultFactory.isReedemable(\n                                address(_vault),\n                                _collaterals[j]\n                            )\n                        ) {\n                            _redeemableCollaterals[\n                                redeemableVaultCount\n                            ] = _collaterals[j];\n                            break;\n                        }\n                    }\n\n                    redeemableVaultCount++;\n                }\n            }\n\n            redeemableVaults = new address[](redeemableVaultCount);\n            redeemableCollaterals = new address[](redeemableVaultCount);\n\n            for (uint256 i = 0; i < redeemableVaultCount; i++) {\n                redeemableVaults[i] = _redeemableVaults[i];\n                redeemableCollaterals[i] = _redeemableCollaterals[i];\n            }\n        }\n    }\n\n    /**\n     * @notice Retrieves an array of collateral asset addresses held by a specific vault\n     * @param _vault Address of the vault\n     * @return An array of collateral asset addresses\n     */\n    function getVaultCollaterals(\n        address _vault\n    ) public view returns (address[] memory) {\n        IVault vault = IVault(_vault);\n        uint256 collateralsLength = vault.collateralsLength();\n        if (collateralsLength == 0) {\n            return new address[](0);\n        } else {\n            address[] memory collaterals = new address[](collateralsLength);\n            for (uint256 i = 0; i < collateralsLength; i++) {\n                collaterals[i] = vault.collateralAt(i);\n            }\n            return collaterals;\n        }\n    }\n\n    /**\n     * @notice Calculates the Total Value Locked (TVL) across all vaults within a vault factory\n     * @param _vaultFactory Address of the vault factory\n     * @return The total TVL across all vaults in the factory\n     */\n    function getProtocolTvl(\n        address _vaultFactory\n    ) public view returns (uint256) {\n        IVaultFactory vaultFactory = IVaultFactory(_vaultFactory);\n        uint256 vaultCount = vaultFactory.vaultCount();\n        uint256 tvl = 0;\n        if (vaultCount == 0) {\n            return 0;\n        } else {\n            address[] memory _vaults = getAllVaults(_vaultFactory);\n            for (uint256 i = 0; i < vaultCount; i++) {\n                tvl += getVaultTvl(_vaults[i]);\n            }\n            return tvl;\n        }\n    }\n}\n\n\n// File: contracts/VaultFactoryList.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.4;\nimport './utils/constants.sol';\nimport './utils/linked-address-list.sol';\n// import openzeppelin context\nimport '@openzeppelin/contracts/utils/Context.sol';\nimport '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';\n\n/**\n * @title VaultFactoryList\n * @dev Manages a list of vaults by their owners, allowing addition, removal, and transfer of vaults.\n */\nabstract contract VaultFactoryList is Context {\n    using LinkedAddressList for LinkedAddressList.List;\n    using EnumerableSet for EnumerableSet.AddressSet;\n\n    LinkedAddressList.List _vaults;\n    mapping(address => EnumerableSet.AddressSet) private _vaultsByOwner;\n\n    function vaultsByOwnerLength(\n        address _owner\n    ) external view returns (uint256) {\n        return _vaultsByOwner[_owner].length();\n    }\n\n    function vaultsByOwner(\n        address _owner,\n        uint256 _index\n    ) external view returns (address) {\n        return _vaultsByOwner[_owner].at(_index);\n    }\n\n    function _addVault(address _owner, address _vault) internal {\n        require(\n            _vaults.add(_vault, address(0x0), false),\n            'vault-could-not-be-added'\n        );\n        _vaultsByOwner[_owner].add(_vault);\n    }\n\n    function _transferVault(\n        address _from,\n        address _to,\n        address _vault\n    ) internal {\n        _vaultsByOwner[_from].remove(_vault);\n        _vaultsByOwner[_to].add(_vault);\n    }\n\n    function _removeVault(address _owner, address _vault) internal {\n        require(_vaults.remove(_vault), 'vault-could-not-be-removed');\n        _vaultsByOwner[_owner].remove(_vault);\n    }\n\n    /**\n     * @dev returns the number of vaults for specific token\n     */\n    function vaultCount() public view returns (uint256) {\n        return _vaults._size;\n    }\n\n    /**\n     * @dev returns the last vault by maximum collaterization ratio\n     */\n    function lastVault() public view returns (address) {\n        return _vaults._last;\n    }\n\n    /**\n     * @dev returns the first vault by minimal collaterization ratio\n     */\n    function firstVault() public view returns (address) {\n        return _vaults._first;\n    }\n\n    /**\n     * @dev returns the next vault by collaterization ratio\n     */\n    function nextVault(address _vault) public view returns (address) {\n        return _vaults._values[_vault].next;\n    }\n\n    /**\n     * @dev returns the previous vault by collaterization ratio\n     */\n    function prevVault(address _vault) public view returns (address) {\n        return _vaults._values[_vault].prev;\n    }\n\n    /**\n     * @dev Checks if a vault exists for a specific token.\n     * @param _vault The address of the vault to check.\n     * @return A boolean indicating whether the vault exists.\n     */\n    function containsVault(address _vault) public view returns (bool) {\n        return _vaults._values[_vault].next != address(0x0);\n    }\n}\n\n\n// File: contracts/VaultFactoryZapper.sol\n// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.19;\n\nimport './interfaces/IVaultFactory.sol';\nimport './interfaces/IVault.sol';\nimport './interfaces/ITokenPriceFeed.sol';\nimport '@openzeppelin/contracts/access/Ownable.sol';\nimport '@openzeppelin/contracts/token/ERC20/IERC20.sol';\nimport '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';\nimport 'hardhat/console.sol';\n/**\n * @title VaultFactoryZapper\n * @dev A contract that facilitates the creation of Vaults and manages their operations.\n */\ncontract VaultFactoryZapper is Ownable {\n    using SafeERC20 for IERC20;\n\n    IVaultFactory public vaultFactory; // Interface for interacting with VaultFactory\n    string public prefix = 'MyVault'; // Prefix for the Vault name\n\n    receive() external payable {} // Fallback function to receive Matic\n\n    /**\n     * @dev Sets the VaultFactory contract address.\n     * @param _vaultFactory Address of the VaultFactory contract.\n     */\n    function setVaultFactory(address _vaultFactory) public onlyOwner {\n        require(_vaultFactory != address(0), 'VaultFactory: zero address');\n        vaultFactory = IVaultFactory(_vaultFactory);\n    }\n\n    /**\n     * @dev Sets the prefix for Vault names.\n     * @param _prefix New prefix for Vault names.\n     */\n    function setPrefix(string memory _prefix) public onlyOwner {\n        prefix = _prefix;\n    }\n\n    /**\n     * @dev Constructor to initialize the contract with the VaultFactory address.\n     * @param _vaultFactory Address of the VaultFactory contract.\n     */\n    constructor(address _vaultFactory) {\n        setVaultFactory(_vaultFactory);\n    }\n\n    /**\n     * @dev Internal function to generate the name for the next Vault.\n     * @param _owner Address of the Vault owner.\n     * @return Name for the next Vault.\n     */\n    function _getNextVaultName(\n        address _owner\n    ) internal view returns (string memory) {\n        uint256 vaultCount = vaultFactory.vaultsByOwnerLength(_owner) + 1;\n        return string.concat(prefix, uint2str(vaultCount));\n    }\n\n    /**\n     * @dev Creates a new Vault.\n     * @param _collateralToken Address of the collateral token.\n     * @param _collateralAmount Amount of collateral tokens to be deposited.\n     * @param _borrowAmount Amount of tokens to be borrowed against the collateral.\n     * @return _vault Address of the newly created Vault.\n     */\n    function createVault(\n        address _collateralToken,\n        uint256 _collateralAmount,\n        uint256 _borrowAmount\n    ) external returns (address _vault) {\n        _vault = vaultFactory.createVault(_getNextVaultName(msg.sender));\n\n        if (_collateralAmount > 0) {\n            IERC20(_collateralToken).safeTransferFrom(\n                msg.sender,\n                address(this),\n                _collateralAmount\n            );\n            IERC20(_collateralToken).safeApprove(\n                address(vaultFactory),\n                _collateralAmount\n            );\n            vaultFactory.addCollateral(\n                _vault,\n                _collateralToken,\n                _collateralAmount\n            );\n            if (_borrowAmount > 0) {\n                vaultFactory.borrow(_vault, _borrowAmount, msg.sender);\n            }\n        }\n\n        vaultFactory.transferVaultOwnership(_vault, msg.sender);\n    }\n\n    /**\n     * @dev Creates a new Vault with native (Matic) collateral.\n     * @param _borrowAmount Amount of tokens to be borrowed against the collateral.\n     * @return _vault Address of the newly created Vault.\n     */\n    function createVaultNative(\n        uint256 _borrowAmount\n    ) external payable returns (address _vault) {\n        _vault = vaultFactory.createVault(_getNextVaultName(msg.sender));\n\n        if (msg.value > 0) {\n            vaultFactory.addCollateralNative{value: msg.value}(_vault);\n            if (_borrowAmount > 0) {\n                vaultFactory.borrow(_vault, _borrowAmount, msg.sender);\n            }\n        }\n        vaultFactory.transferVaultOwnership(_vault, msg.sender);\n    }\n\n    /**\n     * @dev Converts uint to a string.\n     * @param _i Unsigned integer to be converted.\n     * @return _uintAsString String representation of the input integer.\n     */\n    function uint2str(\n        uint _i\n    ) internal pure returns (string memory _uintAsString) {\n        if (_i == 0) {\n            return '0';\n        }\n        uint j = _i;\n        uint len;\n        while (j != 0) {\n            len++;\n            j /= 10;\n        }\n        bytes memory bstr = new bytes(len);\n        uint k = len;\n        while (_i != 0) {\n            k = k - 1;\n            uint8 temp = (48 + uint8(_i - (_i / 10) * 10));\n            bytes1 b1 = bytes1(temp);\n            bstr[k] = b1;\n            _i /= 10;\n        }\n        return string(bstr);\n    }\n}\n\n\n// File: hardhat/console.sol\n// SPDX-License-Identifier: MIT\npragma solidity >=0.4.22 <0.9.0;\n\nlibrary console {\n    address constant CONSOLE_ADDRESS =\n        0x000000000000000000636F6e736F6c652e6c6f67;\n\n    function _sendLogPayloadImplementation(bytes memory payload) internal view {\n        address consoleAddress = CONSOLE_ADDRESS;\n        /// @solidity memory-safe-assembly\n        assembly {\n            pop(\n                staticcall(\n                    gas(),\n                    consoleAddress,\n                    add(payload, 32),\n                    mload(payload),\n                    0,\n                    0\n                )\n            )\n        }\n    }\n\n    function _castToPure(\n      function(bytes memory) internal view fnIn\n    ) internal pure returns (function(bytes memory) pure fnOut) {\n        assembly {\n            fnOut := fnIn\n        }\n    }\n\n    function _sendLogPayload(bytes memory payload) internal pure {\n        _castToPure(_sendLogPayloadImplementation)(payload);\n    }\n\n    function log() internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log()\"));\n    }\n    function logInt(int256 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(int256)\", p0));\n    }\n\n    function logUint(uint256 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n    }\n\n    function logString(string memory p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n    }\n\n    function logBool(bool p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n    }\n\n    function logAddress(address p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n    }\n\n    function logBytes(bytes memory p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes)\", p0));\n    }\n\n    function logBytes1(bytes1 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes1)\", p0));\n    }\n\n    function logBytes2(bytes2 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes2)\", p0));\n    }\n\n    function logBytes3(bytes3 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes3)\", p0));\n    }\n\n    function logBytes4(bytes4 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes4)\", p0));\n    }\n\n    function logBytes5(bytes5 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes5)\", p0));\n    }\n\n    function logBytes6(bytes6 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes6)\", p0));\n    }\n\n    function logBytes7(bytes7 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes7)\", p0));\n    }\n\n    function logBytes8(bytes8 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes8)\", p0));\n    }\n\n    function logBytes9(bytes9 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes9)\", p0));\n    }\n\n    function logBytes10(bytes10 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes10)\", p0));\n    }\n\n    function logBytes11(bytes11 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes11)\", p0));\n    }\n\n    function logBytes12(bytes12 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes12)\", p0));\n    }\n\n    function logBytes13(bytes13 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes13)\", p0));\n    }\n\n    function logBytes14(bytes14 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes14)\", p0));\n    }\n\n    function logBytes15(bytes15 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes15)\", p0));\n    }\n\n    function logBytes16(bytes16 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes16)\", p0));\n    }\n\n    function logBytes17(bytes17 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes17)\", p0));\n    }\n\n    function logBytes18(bytes18 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes18)\", p0));\n    }\n\n    function logBytes19(bytes19 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes19)\", p0));\n    }\n\n    function logBytes20(bytes20 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes20)\", p0));\n    }\n\n    function logBytes21(bytes21 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes21)\", p0));\n    }\n\n    function logBytes22(bytes22 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes22)\", p0));\n    }\n\n    function logBytes23(bytes23 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes23)\", p0));\n    }\n\n    function logBytes24(bytes24 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes24)\", p0));\n    }\n\n    function logBytes25(bytes25 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes25)\", p0));\n    }\n\n    function logBytes26(bytes26 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes26)\", p0));\n    }\n\n    function logBytes27(bytes27 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes27)\", p0));\n    }\n\n    function logBytes28(bytes28 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes28)\", p0));\n    }\n\n    function logBytes29(bytes29 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes29)\", p0));\n    }\n\n    function logBytes30(bytes30 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes30)\", p0));\n    }\n\n    function logBytes31(bytes31 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes31)\", p0));\n    }\n\n    function logBytes32(bytes32 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bytes32)\", p0));\n    }\n\n    function log(uint256 p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256)\", p0));\n    }\n\n    function log(string memory p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string)\", p0));\n    }\n\n    function log(bool p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool)\", p0));\n    }\n\n    function log(address p0) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address)\", p0));\n    }\n\n    function log(uint256 p0, uint256 p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256)\", p0, p1));\n    }\n\n    function log(uint256 p0, string memory p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string)\", p0, p1));\n    }\n\n    function log(uint256 p0, bool p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool)\", p0, p1));\n    }\n\n    function log(uint256 p0, address p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address)\", p0, p1));\n    }\n\n    function log(string memory p0, uint256 p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256)\", p0, p1));\n    }\n\n    function log(string memory p0, string memory p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string)\", p0, p1));\n    }\n\n    function log(string memory p0, bool p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool)\", p0, p1));\n    }\n\n    function log(string memory p0, address p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address)\", p0, p1));\n    }\n\n    function log(bool p0, uint256 p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256)\", p0, p1));\n    }\n\n    function log(bool p0, string memory p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string)\", p0, p1));\n    }\n\n    function log(bool p0, bool p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool)\", p0, p1));\n    }\n\n    function log(bool p0, address p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address)\", p0, p1));\n    }\n\n    function log(address p0, uint256 p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256)\", p0, p1));\n    }\n\n    function log(address p0, string memory p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string)\", p0, p1));\n    }\n\n    function log(address p0, bool p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool)\", p0, p1));\n    }\n\n    function log(address p0, address p1) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address)\", p0, p1));\n    }\n\n    function log(uint256 p0, uint256 p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, uint256 p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, uint256 p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, uint256 p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, string memory p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, string memory p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, string memory p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, string memory p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, bool p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, bool p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, bool p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, bool p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, address p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, address p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, address p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, address p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, uint256 p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, uint256 p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, uint256 p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, uint256 p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, string memory p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, string memory p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, string memory p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, string memory p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, bool p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, bool p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, bool p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, bool p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, address p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, address p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, address p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool)\", p0, p1, p2));\n    }\n\n    function log(string memory p0, address p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address)\", p0, p1, p2));\n    }\n\n    function log(bool p0, uint256 p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256)\", p0, p1, p2));\n    }\n\n    function log(bool p0, uint256 p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string)\", p0, p1, p2));\n    }\n\n    function log(bool p0, uint256 p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool)\", p0, p1, p2));\n    }\n\n    function log(bool p0, uint256 p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address)\", p0, p1, p2));\n    }\n\n    function log(bool p0, string memory p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256)\", p0, p1, p2));\n    }\n\n    function log(bool p0, string memory p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string)\", p0, p1, p2));\n    }\n\n    function log(bool p0, string memory p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool)\", p0, p1, p2));\n    }\n\n    function log(bool p0, string memory p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address)\", p0, p1, p2));\n    }\n\n    function log(bool p0, bool p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256)\", p0, p1, p2));\n    }\n\n    function log(bool p0, bool p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string)\", p0, p1, p2));\n    }\n\n    function log(bool p0, bool p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool)\", p0, p1, p2));\n    }\n\n    function log(bool p0, bool p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address)\", p0, p1, p2));\n    }\n\n    function log(bool p0, address p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256)\", p0, p1, p2));\n    }\n\n    function log(bool p0, address p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string)\", p0, p1, p2));\n    }\n\n    function log(bool p0, address p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool)\", p0, p1, p2));\n    }\n\n    function log(bool p0, address p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address)\", p0, p1, p2));\n    }\n\n    function log(address p0, uint256 p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256)\", p0, p1, p2));\n    }\n\n    function log(address p0, uint256 p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string)\", p0, p1, p2));\n    }\n\n    function log(address p0, uint256 p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool)\", p0, p1, p2));\n    }\n\n    function log(address p0, uint256 p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address)\", p0, p1, p2));\n    }\n\n    function log(address p0, string memory p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256)\", p0, p1, p2));\n    }\n\n    function log(address p0, string memory p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string)\", p0, p1, p2));\n    }\n\n    function log(address p0, string memory p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool)\", p0, p1, p2));\n    }\n\n    function log(address p0, string memory p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address)\", p0, p1, p2));\n    }\n\n    function log(address p0, bool p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256)\", p0, p1, p2));\n    }\n\n    function log(address p0, bool p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string)\", p0, p1, p2));\n    }\n\n    function log(address p0, bool p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool)\", p0, p1, p2));\n    }\n\n    function log(address p0, bool p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address)\", p0, p1, p2));\n    }\n\n    function log(address p0, address p1, uint256 p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256)\", p0, p1, p2));\n    }\n\n    function log(address p0, address p1, string memory p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string)\", p0, p1, p2));\n    }\n\n    function log(address p0, address p1, bool p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool)\", p0, p1, p2));\n    }\n\n    function log(address p0, address p1, address p2) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address)\", p0, p1, p2));\n    }\n\n    function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,uint256,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, string memory p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,string,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, bool p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,bool,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(uint256 p0, address p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(uint256,address,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, uint256 p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,uint256,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, string memory p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,string,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, bool p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,bool,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(string memory p0, address p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(string,address,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, uint256 p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,uint256,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, string memory p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,string,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, bool p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,bool,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(bool p0, address p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(bool,address,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, uint256 p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,uint256,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, string memory p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,string,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, bool p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,bool,address,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, uint256 p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, uint256 p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, uint256 p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,uint256,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, string memory p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, string memory p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, string memory p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, string memory p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,string,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, bool p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, bool p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, bool p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, bool p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,bool,address)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, address p2, uint256 p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,uint256)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, address p2, string memory p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,string)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, address p2, bool p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,bool)\", p0, p1, p2, p3));\n    }\n\n    function log(address p0, address p1, address p2, address p3) internal pure {\n        _sendLogPayload(abi.encodeWithSignature(\"log(address,address,address,address)\", p0, p1, p2, p3));\n    }\n\n}","source_code_hash":null,"compiler_version":"v0.8.23+commit.f704f362","optimization_used":"1","runs":1000,"abi":"[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_factory\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_a3aToken\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_newAmount\",\"type\":\"uint256\"}],\"name\":\"A3APerMinuteUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"issuance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_totalA3ARewardsLeft\",\"type\":\"uint256\"}],\"name\":\"A3ARewardIssue\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_contributor\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"A3ARewardRedeemed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_contributor\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_tokenAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"CollateralRewardRedeemed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_contributor\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"_depositor\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_P\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_G\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_newDepositValue\",\"type\":\"uint256\"}],\"name\":\"DepositSnapshotUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"_currentEpoch\",\"type\":\"uint128\"}],\"name\":\"EpochUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_G\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"_epoch\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"_scale\",\"type\":\"uint128\"}],\"name\":\"G_Updated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_P\",\"type\":\"uint256\"}],\"name\":\"P_Updated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_tokenAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_S\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"_epoch\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"_scale\",\"type\":\"uint128\"}],\"name\":\"S_Updated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"_currentScale\",\"type\":\"uint128\"}],\"name\":\"ScaleUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_newAmount\",\"type\":\"uint256\"}],\"name\":\"TotalA3ARewardsUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_newValue\",\"type\":\"uint256\"}],\"name\":\"TotalDepositUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"_contributor\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"Withdraw\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DECIMAL_PRECISION\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LIQUIDATION_RESERVE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_BORROWING_RATE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_INT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REDEMPTION_RATE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"P\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PERCENT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PERCENT10\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PERCENT_05\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SCALE_FACTOR\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SECONDS_IN_ONE_MINUTE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"a3aPerMinute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"a3aToken\",\"outputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"collateralToLastErrorOffset\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentEpoch\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentScale\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"depositSnapshots\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"P\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"G\",\"type\":\"uint256\"},{\"internalType\":\"uint128\",\"name\":\"scale\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"epoch\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"deposits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"}],\"name\":\"epochToScaleToG\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"\",\"type\":\"uint128\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"epochToScaleToTokenToSum\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"S_value\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"factory\",\"outputs\":[{\"internalType\":\"contract IVaultFactory\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_depositor\",\"type\":\"address\"}],\"name\":\"getCollateralReward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_depositor\",\"type\":\"address\"}],\"name\":\"getDepositorA3AGain\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"}],\"name\":\"getWithdrawableDeposit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastA3AError\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastStableCoinLossErrorOffset\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestA3ARewardTime\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"liquidate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"redeemReward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"setA3AAmountForRewards\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_a3aPerMinute\",\"type\":\"uint256\"}],\"name\":\"setA3APerMinute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_factory\",\"type\":\"address\"}],\"name\":\"setVaultFactory\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stableCoin\",\"outputs\":[{\"internalType\":\"contract IMintableToken\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalA3ARewardsLeft\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalDeposit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]","contract_file_name":"contracts/StabilityPool.sol","compiler_type":"solc-j","evm_version":"paris","constructor_arguments":"00000000000000000000000065c6fd9b3a2a892096881e28f07c732ed128893e0000000000000000000000003d4b2132ed4ea0aa93903713a4de9f98e625a5c7","library":null,"license_type":null,"critical_count":0,"high_count":0,"medium_count":7,"low_count":0,"informational_count":0,"audit_status":"completed","audit_completed_at":"1780559534872","erc20_balances":[]}}